Wix bootstrapper - Set version number in Bundle - wix

I've got a Wix installer that uses a bootstrapper to launch my msi file. I've done this by calling a batch file as a post build event in my wix project. This then calls candle and light manually and passes various variables into the Bundle.wxs file. This all works and generates the exe which calls my msi file..
However, I now want to pass the msi BuildVersion into the bundle file. In the wxs file that creates the msi I am using the BuildVersion that I have setup in the BeforeBuild section, using the BuildVersion=%(AssemblyVersion.Version).
I cannot access this variable no matter what I try, in order to pass it to my build_bootstrapper.bat file. I can however pass in hardcoded values. I am currently setting up my own AssemblyVersionNumber enviornment variable as you can see below in the AfterBuild section:
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">$(BuildVersion)</AssemblyVersionNumber>
but it is empty by the time it gets to my script file (even though it's populated if hardcoded). I've tried everything.
Does anybody have any ideas of how I can get the %(AssemblyVersion.Version); to my command file from the post build step?
Thanks in advance
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\..\App\AppThing\bin\Release\AppThing.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version);</DefineConstants>
</PropertyGroup>
</Target>
<Target Name="AfterBuild">
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version);</DefineConstants>
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">$(BuildVersion)</AssemblyVersionNumber>
</PropertyGroup>
</Target>
<PropertyGroup>
<PreBuildEvent>$(ProjectDir)scripts\copy_services.bat $(SolutionDir) $(ProjectDir)</PreBuildEvent>
</PropertyGroup>
<Target Name="AfterClean">
<Message Text="Cleaning wix files, TargetDir is: $(TargetDir)" Importance="High" ContinueOnError="true" />
<CreateItem Include="$(TargetDir)\**\*.*">
<Output TaskParameter="Include" ItemName="BinFilesDir" />
</CreateItem>
<Delete Files="#(BinFilesDir)" />
</Target>
<PropertyGroup>
<PostBuildEvent>$(ProjectDir)scripts\build_bootstrapper.bat $(ProjectDir) $(ConfigurationName) $(AssemblyVersionNumber)</PostBuildEvent>
</PropertyGroup>

$(BuildVersion) isn't set to anything.
You're setting define constants to "BuildVersion=%(AssemblyVersion.Version)" but never actually defining a MSBuild property called "BuildVersion" so the value of $(BuildVersion) is "".
Use %(AssemblyVersion.Version).
<AssemblyVersionNumber Condition="'$(AssemblyVersionNumber)' == ''">%(AssemblyVersion.Version)</AssemblyVersionNumber>

Related

MSBuild Update property in csproj

I have been trying to update the ApplicationVersion property in my csproj file.witch works fine; i have added a Target that runs an custom task to extract the AssemblyFileVersion from my assemblyinfo.cs; this works there is no doubt about that.
But then when i want to use my updated ApplicationVersion to determan where to put my newly build files, i get the default value set in the property.
<PropertyGroup>
...
<ApplicationVersion>1.0.0.0</ApplicationVersion>
...
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>..\media-converter-BUILD\debug\$(ApplicationVersion)\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<DocumentationFile>..\media-converter-BUILD\debug\$(ApplicationVersion)\MediaConverter.XML</DocumentationFile>
</PropertyGroup>
My Targets
<UsingTask AssemblyFile="GetAssemblyFileVersion.dll" TaskName="GetAssemblyFileVersion.GetAssemblyFileVersion" />
<Target Name="MainAfterCompile">
<CallTarget Targets="AfterCompile" />
<CallTarget Targets="VerifyParam" />
</Target>
<Target Name="AfterCompile">
<GetAssemblyFileVersion strFilePathAssemblyInfo="Properties\AssemblyInfo.cs">
<Output TaskParameter="strAssemblyFileVersion" PropertyName="ApplicationVersionModded" />
</GetAssemblyFileVersion>
<PropertyGroup>
<ApplicationVersion>$(ApplicationVersionModded)</ApplicationVersion>
</PropertyGroup>
</Target>
<Target Name="VerifyParam">
<Message Text="New $(ApplicationVersionModded)" Importance="high"/>
<Message Text="Old Updated $(ApplicationVersion)" Importance="high"/>
</Target>
the GetAssemblyFileVersion.dll i more or less stole from some post i found on the internet, just can't find it again, so i can't add a link, sorry.
My theory on why it does not work is that the transforms and parameters in PropertyGroups are rendered before both InitailTagets and DefaultTargets is run. And there for will my plan never work
but if anyone knows of a way to make it work, i will be grateful to here it
My theory on why it does not work is that the transforms and parameters in PropertyGroups are rendered before both InitailTagets and DefaultTargets is run indeed, that's how the evaluation order works: msbuild evaluates global properties in the first pass of the file, you define OutputPath, that is used by the Microsoft.Common.CurrentVersion.targets file to derive OutDir/BaseIntermediateOutputPath/.... Then in another pass your targets run and update the version number, but there isn't another pass which evaluates the global OutputPath property again.
You can however override the value of OutputPath and derived paths in a Target, and it will take effect, you just have to take care of running it early in the build so that other targets use the updated version. This does the trick:
<Target Name="GetApplicationVersion">
<GetAssemblyFileVersion strFilePathAssemblyInfo="Properties\AssemblyInfo.cs">
<Output TaskParameter="strAssemblyFileVersion" PropertyName="ApplicationVersion" />
</GetAssemblyFileVersion>
</Target>
<Target Name="SetOutputPaths" DependsOnTargets="GetApplicationVersion"
BeforeTargets="PrepareForBuild">
<PropertyGroup>
<OutputPath>bin\$(Configuration)\$(ApplicationVersion)\</OutputPath>
<OutDir>$(OutputPath)</OutDir>
</PropertyGroup>
<Message Text="Set OutDir to $(OutDir)" Importance="high" />
</Target>
Another way to deal with this is doing things the other way around: define the application version as a global msbuild property, then use it to define OutputPath and to update the number in AssemblyVersion.cs before it is compiled.

How to pass parameter to Wix patch?

We are creating patch using Wix. It is given below
<Family DiskId="5000"
MediaSrcProp="Sample"
Name="Sample"
SequenceStart="5000">
<UpgradeImage SourceFile="Z:\MyViewName\Latest_UnCompressed\EmailTrans.msi" Id="Latest">
<TargetImage SourceFile="Z:\MyViewName\Prev_Uncompressed\EmailTrans.msi" Order="2" Id="Previous" IgnoreMissingFiles="no"/>
</UpgradeImage>
</Family>
I do not want to use <UpgradeImage SourceFile="Z:\MyViewName , Because this may change often.
I am using Msbuild target like below to build it
<Target Name="CreateUncompressFolder">
<RemoveDir Condition="Exists('$(OldUncompressedMsiPath)')" Directories="$(OldUncompressedMsiPath)" />
<MakeDir Condition="!Exists('$(OldUncompressedMsiPath)')" Directories="$(OldUncompressedMsiPath)" />
<RemoveDir Condition="Exists('$(NewUncompressedMsiPath)')" Directories="$(NewUncompressedMsiPath)" />
<MakeDir Condition="!Exists('$(NewUncompressedMsiPath)')" Directories="$(NewUncompressedMsiPath)" />
</Target>
<Target Name="UnCompressMsi" DependsOnTargets="CreateUncompressFolder">
<Exec Command="msiexec.exe /a "$(NewMsiPath)" /qb TARGETDIR="$(NewUncompressedMsiPath)""/>
<Exec Command="msiexec.exe /a "$(OldMsiPath)" /qb TARGETDIR="$(OldUncompressedMsiPath)""/>
</Target>
<Target Name="BuildMsp">
<Exec Command="candle.exe "$(PatchWxsName)""/>
<Exec Command="light.exe "$(WixObj)" -out "$(PCPName)""/>
<Exec Command="msimsp.exe -s "$(PCPName)" -p "$(MspName)" -l "Patch.log" "/>
</Target>
Is it possible to pass Z:\MyViewName as parameter via Msbuild ?
You need to use the DefineConstants and have msbuild pass in parameters to your .wixproj as parameters. In Automating WiX with MSBuild, you'll see he's passing in PRODUCTVERSION as a MSBUILD parameter to the wixproj and he can then reference the value of PRODUCTVERSION using $(var.PRODUCTVERSION) in his WXS file.
Another approach I've seen done, which is pretty much the same in the link i mentioned is to instead add the XML element DefineConstants as a property to the PropertyGroup in the .wixproj file instead of creating the property on the fly in the BeforeBuild target.
E.x.:
<Project>
<PropertyGroup>
<Configuration/>
<Platform/>
<DefineConstants>
BuildVersion=$(ProductVersion);
WixVarName=$(MSBuildPropertyName);
WixVarName1=$(MSBuildPropertyName1);
WixVarName2=$(MSBuildPropertyName2);
</DefineConstants>
<OutputPath/>
<OutputName/>

Get NuGet package folder in MSBuild

I want to call executable tools like NUnit which I manage via NuGet in MSBuild:
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="$(PackagesDirectory)\NUnit.2.5.10.11092\tools"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The problem is that the folder of a NuGet packages is containing the version number of the package. For instance nunit-console.exe is in the folder packages\NUnit.2.5.10.11092\tools. If I update the NUnit package this path will change and I have to update my MSBuild script. That isn't acceptable.
MSBuild doesn't allow Wildcards in directories, so this isn't working:
ToolPath="$(PackagesDirectory)\NUnit.*\tools"
How can I call tools in MSBuild without having to update my build script whenever I update a NuGet package?
You can use MSBuild Transforms to get the relative directory of a specific tool:
<ItemGroup>
<NunitPackage Include="$(PackagesDirectory)\NUnit.*\tools\nunit-console.exe"/>
</ItemGroup>
<Target Name="Test">
<CreateItem Include="$(BuildCompileDirectory)\*.Tests.*dll">
<Output TaskParameter="Include" ItemName="TestAssemblies" />
</CreateItem>
<NUnit
Assemblies="#(TestAssemblies)"
ToolPath="#(NunitPackage->'%(relativedir)')"
WorkingDirectory="$(BuildCompileDirectory)"
OutputXmlFile="$(BuildDirectory)\$(SolutionName).Tests.xml" />
</Target>
The comment of Mike Rosoft links to Patrik Svensson his blog post and it helped me as follows:
Add GeneratePathProperty="true" to PackageReference of the NuGet package you want to have the location of.
Use it as $(PkgPackage_Name) whereby the dots are replaced by an underscore. Note the Pkg prefix.
This example forces Nswag to use the 32 bit dotnet.exe by overwriting the existing NSwagExe_Net60 property. This was necessary on an x86 project.
<PropertyGroup>
<NSwagExe_Net60>"$(MSBuildProgramFiles32)\dotnet\dotnet.exe" "$(PkgNSwag_MSBuild)\tools\Net60\dotnet-nswag.dll"</NSwagExe_Net60>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NSwag.MSBuild" Version="13.16.1" GeneratePathProperty="true">
</ItemGroup>

MSBUILD Unable to force PackageLocation for Package target

I'm trying to build a deployment package for my web service from msbuild just like you can do in Visual Studio by right-clicking on the project file.
The package gets created fine and is put in the /obj/Release/Packages folder under my source directory for the project file.
You should be able to specify where that package is created by setting the PackageLocation property in a PropertyGroup inside the project file. However, that is not working for me. It still puts the package in /obj/Release/Packages under the source directory.
Here is the snippet from my project file:
<Import Project="$(SrcTreeRoot)\Build\TaskInit.Tasks" />
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<Import Project="$(SrcTreeRoot)\Build\TaskOverrides.Tasks" />
<PropertyGroup>
<Platform>Any Cpu</Platform>
<Configuration>Dev</Configuration>
<PackageLocation>$(PackageOutputDir)\MatrixOnCdsService\MatrixOnCdsService.zip</PackageLocation>
<PackageAsSingleFile>True</PackageAsSingleFile>
<EnablePackageProcessLoggingAndAssert>True</EnablePackageProcessLoggingAndAssert>
<!--OutDir>$(PackageOutputDir)\MatrixOnCdsService\</OutDir-->
</PropertyGroup>
We are also using a MasterBuild.proj that has the following sections:
<PackageProject Include="..\Source\AnalysisSuite\MatrixOnCdsService\MatrixOnCdsService.csproj"/>
...
<Target Name="Package">
<MSBuild Projects="#(PackageProject)" Targets="Package" Properties="Platform=$(Platform);
Configuration=$(Configuration);
DeployOnBuild=true;
DeployTarget=Package;
PackageLocation=$(PackageLocation);"/>
</Target>
TaskInit.tasks is our own custom import file that contains the PackageOutputDir property that we use to tell the project where to put the package.
My question is why is the package still being placed in the /obj/Release/Packages folder even after specifying the PackageLocation?
Stick the property group you have in a target:
<Target Name="SetValues">
<PropertyGroup>
<Platform>Any Cpu</Platform>
<Configuration>Dev</Configuration>
<PackageLocation>$(PackageOutputDir)\MatrixOnCdsService\MatrixOnCdsService.zip</PackageLocation>
<PackageAsSingleFile>True</PackageAsSingleFile>
<EnablePackageProcessLoggingAndAssert>True</EnablePackageProcessLoggingAndAssert>
<!--OutDir>$(PackageOutputDir)\MatrixOnCdsService\</OutDir-->
</PropertyGroup>
</Target>
then add this as a DependsOnTarget for your Package Target and i think you will have your values passed.
e.g. <Target Name="Package" DependsOnTargets="SetValues">
You could do the following in your MasterBuild.proj.
<Target Name="Package">
<ConvertToAbsolutePath Paths="$(PackageOutputDir)">
<Output TaskParameter="AbsolutePaths" PropertyName="Source_Dir_Abs"/>
</ConvertToAbsolutePath>
<MSBuild Projects="#(PackageProject)" Targets="Package"
properties="Platform=$(Platform);
Configuration=$(Configuration);
DeployOnBuild=false;
DeployTarget=Package;
PackageLocation=$(Source_Dir_Abs)\$(PackageProjectName).zip;
PackageAsSingleFile=true;
ExcludeFilesFromDeployment=Web.config;
_PackageTempDir=$(PackageOutputDir)\temp;">
</MSBuild>
</Target>
Where you are calling msbuild you will need to add a property that will be used in $(PackageProjectName) by doing the following:
msbuild.exe /property:PackageProjectName=$project

How can I change AssemblyProduct, AssemblyTitle using MSBuild?

I have an MSBuild script which compiles my existing solution but I'd like to change some properties of one of the projects within the solution at compile-time, including but not limited to AssemblyProduct and AssemblyTitle.
Here's a snippet of my build script:
<Target Name="Compile" >
<MSBuild Projects="..\MySolution.sln"
Properties="Configuration=MyReleaseConfig;Platform=x86" />
</Target>
I've got one main executable and several DLLs that are compiled. I am aware of the MSBuild Extension Pack and I suspect it might help me to get to where I need to be, although I'm not sure how to proceed.
Can I selectively change AssemblyInfo properties at build time?
You're on the right track with the MSBuild Extension Pack.
I find the easiest way to conditionally generate the assembly details at build time is to add an "AssemblyVersion" target directly to my .csproj file(s) that require an updated AssemblyInfo file. You can add the target directly to each csproj file that requires an updated AssemblyInfo file, or as I prefer to do it, create a custom targets file with the AssemblyVersion target and have each csproj file include your custom targets file.
Either way you likely want to use the MSBuild Extension Pack or the MSBuild Community Tasks to use their respective AssemblyInfo task.
Here's some code from our build scripts:
<!-- Import the AssemblyInfo task -->
<Import Project="$(MSBuildCommunityTasksPath)\MSBuild.Community.Tasks.Targets"/>
<!-- Overriding the Microsoft.CSharp.targets target dependency chain -->
<!-- Call our custom AssemblyVersion target before build, even from VS -->
<PropertyGroup>
<BuildDependsOn>
AssemblyVersion;
$(BuildDependsOn)
</BuildDependsOn>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="$(MSBuildProjectDirectory)\Properties\AssemblyInfo.cs"/>
</ItemGroup>
<Target Name="AssemblyVersion"
Inputs="#(AssemblyVersionFiles)"
Outputs="UpdatedAssemblyVersionFiles">
<Attrib Files="%(AssemblyVersionFiles.FullPath)"
Normal="true"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="%(AssemblyVersionFiles.FullPath)"
AssemblyCompany="$(CompanyName)"
AssemblyCopyright="Copyright $(CompanyName), All rights reserved."
AssemblyVersion="$(Version)"
AssemblyFileVersion="$(Version)">
<Output TaskParameter="OutputFile"
ItemName="UpdatedAssemblyVersionFiles"/>
</AssemblyInfo>
</Target>
Sneal's answer was very helpful, but I'd like to show what I actually ended up doing. Instead of editing csproj files (there are several) I instead added tasks to my build script. Here's a snippet:
<PropertyGroup>
<ProductName>MyApp</ProductName>
<CompanyName>MyCompany</CompanyName>
<Major>1</Major>
<Minor>0</Minor>
<Build>0</Build>
<Revision>0</Revision>
</PropertyGroup>
<ItemGroup>
<AssemblyVersionFiles Include="..\MyMainProject\Properties\AssemblyInfo.cs"/>
</ItemGroup>
<Target Name="AssemblyVersionMAIN" Inputs="#(AssemblyVersionFiles)" Outputs="UpdatedAssemblyVersionFiles">
<Attrib Files="%(AssemblyVersionFiles.FullPath)" Normal="true"/>
<AssemblyInfo
CodeLanguage="CS"
OutputFile="%(AssemblyVersionFiles.FullPath)"
AssemblyProduct="$(ProductName)"
AssemblyTitle="$(ProductName)"
AssemblyCompany="$(CompanyName)"
AssemblyCopyright="© $(CompanyName) 2010"
AssemblyVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyFileVersion="$(Major).$(Minor).$(Build).$(Revision)"
AssemblyInformationalVersion="$(Major).$(Minor).$(Build).$(Revision)">
<Output TaskParameter="OutputFile" ItemName="UpdatedAssemblyVersionFiles"/>
</AssemblyInfo>
</Target>
<Target Name="Compile" DependsOnTargets="AssemblyVersionMAIN">
<MSBuild Projects="..\MySolution.sln"
Properties="Configuration=Release;Platform=x86;Optimize=true" />
</Target>
Then, I can override my variables from the command line, or a batch script, like so:
set MAJ=1
set MIN=2
set BLD=3
set REV=4
msbuild buildScript.xml /t:Compile /p:Major=%MAJ% /p:Minor=%MIN% /p:Build=%BLD% /p:Revision=%REV%
<Target Name="SetVersion">
<ItemGroup>
<AssemblyInfoFiles Include="$(TargetDir)\**\AssemblyInfo.cs"/>
</ItemGroup>
<Message Text="change the Version number for:"/>
<Message Text="%(AssemblyInfoFiles.FullPath)"/>
<MSbuild.ExtensionPack.Framework.AssemblyInfo
AssemblyInfoFiles="#(AssemblyInfoFiles)"
AssemblyTitle="newTitle"
AssemblyMajorVersion="2"
AssemblyMinorVersion="0"/>
</Target>