MSBuild: How to conditionally use one of two targets with same name? - visual-studio

I've seen some answers to a similar question but not exactly the same and not with positive luck trying to use suggested solutions... so I'm trying something like this:
<project>
<target name="foo" Condition="'$(Configuration)' == 'Debug' ">
<Message Text="=== RUNNING FOO DEBUG TARGET ===" />
</target>
<target name="foo" Condition="'$(Configuration)' == 'Release' ">
<Message Text="=== RUNNING FOO RELEASE TARGET ===" />
</target>
</project>
... but I'm finding that it doesn't appear to be possible to have two targets with the same name working properly under these conditions. One will negate the other.
How can I do this?

Provide a wrapper target, that depends on both targets.
The both will be called, but only the one matching the
condition will actually do something.
<Project>
<Target Name="foo" DependsOnTargets="_fooDebug;_fooRelease"/>
<Target Name="_fooDebug" Condition="'$(Configuration)' == 'Debug' ">
<Message Text="=== RUNNING FOO DEBUG TARGET ===" />
</Target>
<Target Name="_fooRelease" Condition="'$(Configuration)' == 'Release' ">
<Message Text="=== RUNNING FOO RELEASE TARGET ===" />
</Target>
</Project>

Related

Minimal Visual Studio project with custom target only: Disable IntelliSense and allow single file processing?

For our Visual Studio Solution, I want to create one project that just generates some files with custom scripts. I have a somewhat-working solution, further reduced to provide as an example here:
This is my minimal.vcxproj:
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Universal|Win32">
<Configuration>Universal</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<None Include="common.fooconf" />
</ItemGroup>
<ItemGroup>
<Foo Include="file1.foo" />
<Foo Include="file2.foo" />
</ItemGroup>
<Target Name="Build" Inputs="common.fooconf;#(Foo)" Outputs="Output\%(Foo.Filename).bar">
<MakeDir Directories="Output" />
<Exec Command='TYPE "common.fooconf" "%(Foo.FullPath)" > "Output\%(Foo.Filename).bar"' />
</Target>
<Target Name="Clean">
<RemoveDir Directories="Output" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build" />
</Project>
And I need this minimal.vcxproj.filters to make the three contained files show up in VS's solution explorer:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<None Include="common.fooconf"/>
</ItemGroup>
<ItemGroup>
<Foo Include="file1.foo"/>
<Foo Include="file2.foo"/>
</ItemGroup>
</Project>
Now there are still two problems:
When loading the project, I get error : Designtime build failed for project '[...]\minimal.vcxproj' configuration 'Universal|Win32'. IntelliSense might be unavailable. Check *.designtime.log files in your %TEMP% directory. If I look up in the log, the error is error MSB4057: The target "GetClCommandLines" does not exist in the project. Of course IntelliSense doesn't make sense here (TYPE is just a placeholder for a custom script), but there must be a way of getting rid of this warning.
Build (e.g. via F7) works like a charm (even incremental), but it would be really important for us to trigger processing single files (e.g. CtrlF7). But I can't find out how to make this work.
Using Visual Studio 2019, I needed to add the following targets to have the project load without warnings. This was done by checking the designtime logs and incrementally adding targets, until there were no more warnings.
<Target Name="GetProjectDirectories" />
<Target Name="GetClCommandLines" />
<Target Name="GetGeneratedFiles" />
<Target Name="GetAssemblyReferences" />
<Target Name="GetWinMDReferences" />
<Target Name="GetComReferences" />
<Target Name="GetSDKReferences" />
<Target Name="GetProjectReferences" />
<Target Name="GetForeignReferences" />
<Target Name="GetResolvedReferences" />
<Target Name="GetResolvedSDKReferences" />
<Target Name="GetProjectReferencesInfo" />
<Target Name="GetResolvedLinkLibs" />

Setting the product version for a WiX project via MSBuild on a Jekins server. What's wrong here?

I'm trying to pass a dynamically determined version of a product to a WiX project via a call to an MSBuild project from Jenkins.
For starters, I don't think the problem is actually in Jenkins, since I can't get what I'm trying to do to work when running MSBuild from the VS2013 command prompt with a version hardcoded into our MSBuild project file.
The MSBuild project file is set to run from Jenkins with the following parameters: /p:Configuration=Release /p:Platform="x86" /p:AllowUnsafeBlocks=true
The MSBuild project looks as follows:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildCommunityTasksPath>$(MSBuildProjectDirectory)\.build</MSBuildCommunityTasksPath>
</PropertyGroup>
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.targets"/>
<!-- Version Number -->
<PropertyGroup>
<Major>0</Major>
<Minor>9</Minor>
<Build>1</Build>
<!--Jenkins sets BUILD_NUMBER -->
<Revision>$(BUILD_NUMBER)</Revision>
</PropertyGroup>
<PropertyGroup>
<OutputPath>$(MSBuildProjectDirectory)\_LatestBuild\</OutputPath>
</PropertyGroup>
<Target Name="Version">
<Message Text="Version: $(Version)"/>
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(MSBuildProjectDirectory)\GlobalAssemblyInfo.cs"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyConfiguration="$(Configuration)"
Condition="$(Revision) != '' " />
<AssemblyInfo CodeLanguage="CS"
OutputFile="$(MSBuildProjectDirectory)\GlobalAssemblyInfo.cs"
AssemblyVersion="$(Major).$(Minor).*"
AssemblyConfiguration="$(Configuration)"
Condition="$(Revision) == '' " />
</Target>
<Target Name="AfterBuild">
<ItemGroup>
<ZipSourceFiles Include="$(OutputPath)\Installer\**\*.*" Exclude="$(OutputPath)\Installer\**\*.zip;$(OutputPath)\Installer\**\*.wixpdb" />
<ZipFile Include="ProductName v$(Major).$(Minor).$(Build).$(Revision).zip"/>
</ItemGroup>
<Message Text="Zip: $(OutputPath)\*.*"/>
<Zip Files="#(ZipSourceFiles)" WorkingDirectory="$(OutputPath)\Installer\" ZipFileName="#(ZipFile)" ParallelCompression="false" />
<Message Text="Copying archive..."/>
<Copy SourceFiles="#(ZipFile)" DestinationFolder="\\serverName\Projects\Active\ProductName\Builds" />
</Target>
<!-- Projects to Build -->
<ItemGroup>
<ProjectFiles Include="$(MSBuildProjectDirectory)\**\*.sln">
<Properties>Configuration=$(Configuration)</Properties>
</ProjectFiles>
</ItemGroup>
<Target Name="Compile" DependsOnTargets="Version">
<MSBuild Projects="#(ProjectFiles)" />
</Target>
<Target Name="BuildInstaller">
<MSBuild Projects="$(MSBuildProjectDirectory)\ProductName.Installation\ProductName.Installation.wixproj" Properties="ProductVersion=$(Major).$(Minor).$(Build).$(Revision)" />
</Target>
<Target Name="Build">
<CallTarget Targets="Compile;BuildInstaller;AfterBuild" />
</Target>
</Project>
The WiX project file is as follows:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProjectGuid>e5232ce4-4412-4e41-9157-8ab1a36960b0</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>DicksonWare.Installation</OutputName>
<OutputType>Package</OutputType>
<ProductVersion Condition=" '$(ProductVersion)' == '' ">2.0.0.0</ProductVersion>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' AND '$(MSBuildExtensionsPath32)' != '' ">$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
<WixTargetsPath Condition=" '$(WixTargetsPath)' == '' ">$(MSBuildExtensionsPath)\Microsoft\WiX\v3.x\Wix.targets</WixTargetsPath>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>..\_LatestBuild\Installer\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>Debug;BuildVersion=$(ProductVersion)</DefineConstants>
<SuppressIces>ICE69</SuppressIces>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>..\_LatestBuild\Installer\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>BuildVersion=$(ProductVersion)</DefineConstants>
<SuppressIces>ICE69;ICE61</SuppressIces>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixUIExtension">
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
<Name>WixUIExtension</Name>
</WixExtension>
</ItemGroup>
<Import Project="$(WixTargetsPath)" />
<!--
To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Wix.targets.
-->
<!--
<Target Name="AfterBuild">
</Target>
-->
</Project>
Then in the .wxs file to set the version I simply do Version="$(var.BuildVersion)"
However, when Jenkins calls MSBuild on the build project or I manually type msbuild build.proj /p:Configuration=Release /p:Platform="x86" /p:AllowUnsafeBlocks=true /p:BUILD_NUMBER=23 and then run the .msi file created, the version given in the .msi file is always the 2.0.0.0 default I've set in the .wixproj file.
I'm pretty sure it's not the actual call to MSBuild that's the problem, since replacing the BuildInstaller task with <MSBuild Projects="$(MSBuildProjectDirectory)\ProductName.Installation\ProductName.Installation.wixproj" Properties="ProductVersion=1.2.3.4" /> still has the same problem.
From what I've been able to find by searching around, what I'm doing should be fine. But it's obviously not. Any ideas on what I'm missing or doing wrong?
Thanks.
Take a look at:
http://iswix.codeplex.com/SourceControl/latest#main/Source/Installer/IsWiX/IsWiX.wixproj
<PropertyGroup>
<!-- If MSIProductVersion not passed in, try to get it fom TFBuild Environments-->
<MSIProductVersion Condition=" '$(MSIProductVersion)' == '' ">$([System.Text.RegularExpressions.Regex]::Match($(TF_BUILD_BUILDNUMBER), "\d+.\d+.\d+.\d+"))</MSIProductVersion>
<!-- If we still don't have a value, default to 1.0.0 for developer builds -->
<MSIProductVersion Condition=" '$(MSIProductVersion)' == '' ">1.0.0</MSIProductVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<DefineConstants>WiXProductVersion=$(MSIProductVersion)</DefineConstants>
</PropertyGroup>
http://iswix.codeplex.com/SourceControl/latest#main/Source/Installer/IsWiX/IsWiX.wxs
<Product Id="*" Name="IsWiX" Language="1033" Version="$(var.WiXProductVersion)"
Manufacturer="ISWIX LLC" UpgradeCode="$(var.UpgradeCode)">
Pass /p:MSIProductVerison=1.2.3.4 when you call MSBuild. Alternatively you can set the environment variable TF_BUILD_NUMBER as this is how TFS does it these days.

If Statement in Visual Studio Project

I have this in my Visual Studio Project
<Target Name="BeforeBuild">
<Message Text="Compiling TypeScript files" />
<Message Text="Executing tsc$(TypeScriptSourceMap) #(TypeScriptCompile ->'"%(fullpath)"', ' ')" />
<Exec Command="tsc$(TypeScriptSourceMap) #(TypeScriptCompile ->'"%(fullpath)"', ' ')" IgnoreExitCode="true" />
</Target>
I want to execute the stuff within the <Target Name="BeforeBuild"></Target> if the Configuration is Debug and the Platform is AnyCPU. Is this possible and if so how is it done?
Try the following
<Target Name="BeforeBuild" Condition="'$(Configuration)' == 'Debug' And '$(Platform)' == 'AnyCPU'">
Slightly more fancy version
<Target Name="BeforeBuild" Condition="'$(Configuration)|$(Platform)' == 'Debug|AnyCPU'">

Include MajorVersion etc in filename (OutputName) when building MSI file (wix project)

In my Defines.wxi I have:
<?define MajorVersion="1" ?>
<?define MinorVersion="08" ?>
<?define BuildVersion="11" ?>
In my MyProject.Setup.wixproj I have:
<OutputName>MyProject</OutputName>
<OutputType>Package</OutputType>
Is it possible to include the version variables in the filename somehow, so that my file can be named MyProject.1.08.11.msi?
This didn't work (no such variable is defined):
<OutputName>MyProject-$(MajorVersion)</OutputName>
<OutputType>Package</OutputType>
This didn't work (no such variable is defined):
<Target Name="AfterBuild" Condition="'$(Configuration)' == 'Release'">
<Copy SourceFiles="$(OutputPath)$(OutputName).msi" DestinationFiles="C:\$(OutputName)-$(MajorVersion).msi" />
</Target>
It seems very clear to me that $(MajorVersion) is not the correct way of fetching the definition from the Defines.wxi file. What is?
Update
I tried to put this in MyProject.Setup.wixproj:
<InstallerMajorVersion>7</InstallerMajorVersion>
<InstallerMinorVersion>7</InstallerMinorVersion>
<InstallerBuildNumber>7</InstallerBuildNumber>
...
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>PrebuildPath=..\..\obj\prebuild\web\;InstallerMajorVersion=$(InstallerMajorVersion);InstallerMinorVersion=$(InstallerMinorVersion);InstallerBuildNumber=$(InstallerBuildNumber)</DefineConstants>
</PropertyGroup>
And this in Defines.wxi:
<?define MajorVersion="$(var.InstallerMajorVersion)" ?>
<?define MinorVersion="$(var.InstallerMinorVersion)" ?>
<?define BuildVersion="$(var.InstallerBuildNumber)" ?>
<?define Revision="0" ?>
<?define VersionNumber="$(var.InstallerMajorVersion).$(var.InstallerMinorVersion).$(var.InstallerBuildNumber)" ?>
Didn't work either. Got these error messages:
The Product/#Version attribute's value, '..', is not a valid version.
Legal version values should look like 'x.x.x.x' where x is an integer
from 0 to 65534.
The Product/#Version attribute was not found; it is
required.
This is what I ended up with, and it works!
Setup.Version.proj
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<InstallerMajorVersion>55</InstallerMajorVersion>
<InstallerMinorVersion>66</InstallerMinorVersion>
<InstallerBuildVersion>$(BuildNumber)</InstallerBuildVersion>
<InstallerBuildVersion Condition="$(InstallerBuildVersion) == ''">0</InstallerBuildVersion>
</PropertyGroup>
</Project>
MyProject.Setup.wixproj
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="Setup.Version.proj" />
<PropertyGroup>
<OutputName>MyProject_$(InstallerMajorVersion)_$(InstallerMinorVersion)_$(InstallerBuildVersion)</OutputName>
<OutputType>Package</OutputType>
<DefineConstants>InstallerMajorVersion=$(InstallerMajorVersion);InstallerMinorVersion=$(InstallerMinorVersion);InstallerBuildVersion=$(InstallerBuildVersion)</DefineConstants>
...
Defines.wxi
<?define MajorVersion="$(var.InstallerMajorVersion)" ?>
<?define MinorVersion="$(var.InstallerMinorVersion)" ?>
<?define BuildVersion="$(var.InstallerBuildVersion)" ?>
This common task should be simplified in future versions of WiX!
This solution combines #Wimmel's and this post. It draws the version from a target .exe, and otherwise does not store version numbers in files; it doesn't rename the output file in post-build. But, it is necessary to update the property ProjectDefineConstants, from which the candle arguments are derived (in wix.targets). Otherwise, updating only the TargetPath property does not change the inputs to candle.exe.
*.wixproj:
<Import Project="$(WixTargetsPath)" />
<Target Name="BeforeBuild">
<!-- Read the version from the to-be-installed .exe -->
<GetAssemblyIdentity AssemblyFiles="path.to.primary.exe">
<Output TaskParameter="Assemblies" ItemName="AsmInfo" />
</GetAssemblyIdentity>
<!-- Create the MSBuild property $(VersionNumber) -->
<CreateProperty Value="%(AsmInfo.Version)">
<Output TaskParameter="Value" PropertyName="VersionNumber" />
</CreateProperty>
<!-- Create the WiX preprocessor variable $(var.VersionNumber) -->
<CreateProperty Value="$(DefineConstants);VersionNumber=$(VersionNumber)">
<Output TaskParameter="Value" PropertyName="DefineConstants" />
</CreateProperty>
<!-- Update the MSBuild properties $(TargetName), etc. -->
<CreateProperty Value="$(SolutionName)-$(Platform)-$(VersionNumber)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetPdbExt)">
<Output TaskParameter="Value" PropertyName="TargetPdbName" />
</CreateProperty>
<CreateProperty Value="$(TargetDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
<CreateProperty Value="$(TargetPdbDir)$(TargetPdbName)">
<Output TaskParameter="Value" PropertyName="TargetPdbPath" />
</CreateProperty>
<!-- Update the MSBuild property from which candle.exe args are derived -->
<CreateProperty Value="
Configuration=$(ConfigurationName);
OutDir=$(OutDir);
Platform=$(PlatformName);
ProjectDir=$(ProjectDir);
ProjectExt=$(ProjectExt);
ProjectFileName=$(ProjectFileName);
ProjectName=$(ProjectName);
ProjectPath=$(ProjectPath);
TargetDir=$(TargetDir);
TargetExt=$(TargetExt);
TargetFileName=$(TargetFileName);
TargetName=$(TargetName);
TargetPath=$(TargetPath);
">
<Output TaskParameter="Value" PropertyName="ProjectDefineConstants" />
</CreateProperty>
</Target>
*.wxs
<Product Id="*" Version="$(var.VersionNumber)" ... >
...
</Product>
In your .wixproj file. Add the following section just before the </Project> tag.
<!-- rename the output msi with Version number -->
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="[Path of the main assembly with the assembly version number you want to use]">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion"/>
</GetAssemblyIdentity>
<Copy SourceFiles=".\bin\$(Configuration)\$(OutputName).msi" DestinationFiles=".\bin\$(Configuration)\$(OutputName)_%(AssemblyVersion.Version).msi" />
<Delete Files=".\bin\$(Configuration)\$(OutputName).msi" />
</Target>
This works for me.
It is not possible to read the .wxi file from the .wixproj file. So you have to use another way to specify the version. I can give an example where I read the version from a assembly included in the installer, and use that version to rename the msi;
Open the .wixproj file in an editor and add a ReadVersion target:
<Target Name="ReadVersion">
<GetAssemblyIdentity AssemblyFiles="bin\program.exe">
<Output TaskParameter="Assemblies" ItemName="MyAssemblyIdentities" />
</GetAssemblyIdentity>
<Message Text="AssemblyVersion = %(MyAssemblyIdentities.Version)" />
<CreateProperty Value="$(TargetName) %(MyAssemblyIdentities.Version)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(OutDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
</Target>
This reads the version from bin\program.exe, displays it for debugging purposes, and changes the TargetName, TargetFileName and TargetPath.
After the line containing <Import Project="$(WixTargetsPath)" />, add the following to inject this target into the build process:
<PropertyGroup>
<BuildDependsOn>ReadVersion;$(BuildDependsOn)</BuildDependsOn>
</PropertyGroup>
One way would be to define the variables in your MSBuild script, and have it update Defines.wxi at build time, as in this example.
In your MSBuild script, you could define the version properties as follows:
<PropertyGroup>
<MajorVersion>1</MajorVersion>
<MinorVersion>08</MinorVersion>
<BuildVersion>11</BuildVersion>
<WixConfigPath>.\Defines.wxi</WixConfigPath>
<_VariableDefinitions>
<Root>
<VariableDefinition Name="MajorVersion" NewValue="$(MajorVersion)" />
<VariableDefinition Name="MinorVersion" NewValue="$(MinorVersion)" />
<VariableDefinition Name="BuildVersion" NewValue="$(BuildVersion)" />
</Root>
</_VariableDefinitions>
</PropertyGroup>
<Target Name="UpdateWixVars">
<WixVarSubstitution SourceFile="$(WixConfigPath)" VariableDefinitions="$(_VariableDefinitions)"/>
</Target>
Then running the UpdateWixVars target will update the version numbers in Defines.wxi with the version numbers specified in your MSBuild project.
Note that I could not find an actual compiled dll with this custom build task, so I had to create it by:
Download the source from here. Build it and name the file Tranxition.BuildTasks.dll.
Add the reference to the build task like so:
<UsingTask TaskName="WixVarSubstitution"
AssemblyFile="$(MSBuildExtensionsPath)\Tranxition\Tranxition.BuildTasks.dll"/>
You can accomplish this seamlessly by implementing these two answers:
First, update your project's AfterBuild task to rename the WIX-built MSI: https://stackoverflow.com/a/10716409/374198
Then, update your WIX project to automatically use the verison number of the assembly you're packaging: https://stackoverflow.com/a/641094/374198
The other answers are much too complex!
PS: If you want to drop the fourth digit, following semantic versioning, you can do it like this:
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="..\Path\To\MyProject\bin\$(Platform)\$(Configuration)\MyProject.dll">
<Output TaskParameter="Assemblies" ItemName="AssemblyInfo" />
</GetAssemblyIdentity>
<PropertyGroup>
<In>%(AssemblyInfo.Version)</In>
<Pattern>^(\d+.\d+.\d+)</Pattern>
<AssemblyVersion>$([System.Text.RegularExpressions.Regex]::Match($(In), $(Pattern)))</AssemblyVersion>
</PropertyGroup>
<Move SourceFiles="bin\$(Platform)\$(Configuration)\MyProject.msi" DestinationFiles="bin\$(Platform)\$(Configuration)\CodeGenerator-$(AssemblyVersion).$(Platform).msi" />
</Target>
This will create an MSI named, for example, MyProject-1.2.3.x64.msi. See this answer for more.
This is a full example of a wixproj file where you can set a version number in the UI and use that to modify the output msi file name.
In Visual Studio (e.g. 2015):
define a version number in "Define preprocessor variables" in the
project properties window. I've entered VersionNumber=1.14 for this
example;
unload your project in the solution explorer;
right click your project in the solution explorer and select edit yourproject.wixproj file;
add code in the Target Name="BeforeBuild" element as shown below.
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" InitialTargets="EnsureWixToolsetInstalled" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">x86</Platform>
<ProductVersion>3.10</ProductVersion>
<ProjectGuid>PROJECT-GUID</ProjectGuid>
<SchemaVersion>2.0</SchemaVersion>
<OutputName>my project</OutputName>
<OutputType>Package</OutputType>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<!-- These constants can be set in the UI -->
<DefineConstants>VersionNumber=1.14</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
<OutputPath>bin\$(Configuration)\</OutputPath>
<IntermediateOutputPath>obj\$(Configuration)\</IntermediateOutputPath>
<DefineConstants>VersionNumber=1.14</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="Product.wxs" />
</ItemGroup>
<ItemGroup>
<WixExtension Include="WixUIExtension">
<HintPath>$(WixExtDir)\WixUIExtension.dll</HintPath>
<Name>WixUIExtension</Name>
</WixExtension>
</ItemGroup>
<Import Project="$(WixTargetsPath)" Condition=" '$(WixTargetsPath)' != '' " />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets" Condition=" '$(WixTargetsPath)' == '' AND Exists('$(MSBuildExtensionsPath32)\Microsoft\WiX\v3.x\Wix.targets') " />
<Target Name="BeforeBuild">
<!-- This extracts the version number from a setting in the UI -->
<!-- This method comes from: geekswithblogs.net/alexhildyard/archive/2013/03/09/passing-data-between-msbuild-and-wix.aspx -->
<ItemGroup>
<DefineConstantsKVPairs Include="$(DefineConstants)" />
</ItemGroup>
<!-- Evaluate each key/value pair with task batching, then make a conditional assignment -->
<PropertyGroup>
<VersionNumber Condition="$([System.String]::new('%(DefineConstantsKVPairs.Identity)').Contains('VersionNumber='))">$([System.String]::new('%(DefineConstantsKVPairs.Identity)').Split('=')[1])</VersionNumber>
</PropertyGroup>
<CreateProperty Value="$(OutputName)-$(VersionNumber)">
<Output TaskParameter="Value" PropertyName="TargetName" />
</CreateProperty>
<CreateProperty Value="$(TargetName)$(TargetExt)">
<Output TaskParameter="Value" PropertyName="TargetFileName" />
</CreateProperty>
<CreateProperty Value="$(TargetDir)$(TargetFileName)">
<Output TaskParameter="Value" PropertyName="TargetPath" />
</CreateProperty>
</Target>
</Project>

Order of AfterBuild tasks in Visual Studio projects ...?

I have defined several AfterBuild - Tasks in my Visual Studio project with different conditions:
<Target Name="AfterBuild" Condition="'$(Configuration)'=='FinalBuilder'">
<Message Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
<Target Name="AfterBuild" Condition="'$(MvcBuildViews)'=='true'">
<Message Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
</Target>
But only the last one is executed if the condition match. If I choose the FinalBuilder-Configuration, the AfterBuild tasks is ignored and not executed. If I change the order of the Targets in the project files (Condition="'$(Configuration)'=='FinalBuilder'" as last one), the AfterBuild for FinalBuilder-Configuration is executed but the one for MvcBuildViews is ignored.
Is the order of the target important? Is only the last AfterBuild task taken into account? Or how can I define different AfterBuild tasks with different Conditions?
Thanks
Konrad
The only second one is executed because it was redefined. See MSDN (Declaring targets in the project file chapter).
You should use only one AfterBuild target in your project file like this:
<Target Name="AfterBuild" >
<Message Condition="'$(MvcBuildViews)'=='true'" Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
<Message Condition="'$(Configuration)'=='FinalBuilder'" Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
EDIT:
Or use CallTarget task:
<Target Name="AfterBuild" >
<CallTarget Condition="'$(MvcBuildViews)'=='true'" Targets="MvcBuildTarget" />
<CallTarget Condition="'$(Configuration)'=='FinalBuilder'" Targets="FinalBuilderTarget" />
</Target>
<Target Name="MvcBuildTarget">
<Message Importance="high" Text="--- AfterBuild for MvcBuildViews ---" />
</Target>
<Target Name="FinalBuilderTarget" >
<Message Importance="high" Text="--- AfterBuild for FinalBuilder ---" />
</Target>
If you really need to run multiple AfterBuild tasks (this may be the case for example if you need different Input and Output sets for each task) you can use DependsOnTarget to simply make AfterBuild depend upon all of them:
<Target Name="AfterBuild1"
Inputs="stuff"
Outputs="stuff">
<Message Text="Running first after build task." Importance="high" />
<Exec Command="stuff" />
</Target>
<Target Name="AfterBuild2"
Inputs="other stuff"
Outputs="other stuff">
<Message Text="Running other after build task." Importance="high" />
<Exec Command="stuff" />
</Target>
<Target Name="AfterBuild" DependsOnTargets="AfterBuild1;AfterBuild2" />
If you need to constrain their order, just make AfterBuild2 depend on AfterBuild1 with DependsOnTargets="AfterBuild1".

Resources