I'm trying to implement automatic versioning so that when I build my Bootstrapper, both the MyApp and Bootstrapper versions are automatically incremented and synchronized. Otherwise you end up with duplicate bootstrap entries in the add/remove programs for each installation attempt with matching versions.
When I build the bootstrapper, an AfterBuild target uses GetAssemblyIdentity to get the automatically generated version of MyApp successfully, and the bootstrapper output file is properly named with the incremented version number e.g. MyApp.1.0.5123.1352.exe
But, when I install this Bootstrapper, the version that shows in add/remove programs is always 1.0.0.0.
I've tried using bindings in the Bootstrapper Bundle Version field but I can't get it to read the MyApp version.
Using !(bind.packageVersion.MyApp) results in never incrementing 1.0.0.0.
Using !(bind.assemblyName.MyApp) results in build errors (Unresolved bind-time variable).
Using !(bind.FileVersion.filAAF013CA9B89B07C81031AE69884BF11) results in build errors (Unresolved bind-time variable). <-- Makes sense as the FileID exists in the Setup project, and this is the Bootstrapper project.
What can I do to get my Bootstrapper Bundle to have the same version
as the MyApp it is packaging?
=====MyApp\AssemblyInfo.cs=====
[assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyFileVersion("1.0.*")]
=====Setup.wixproj=====
<Target Name="BeforeBuild">
<PropertyGroup>
<LinkerBaseInputPaths>..\MyApp\bin\$(Configuration)\</LinkerBaseInputPaths>
</PropertyGroup>
<HeatDirectory OutputFile="MyApp_Files.wxs" Directory="..\MyApp\bin\$(Configuration)\" DirectoryRefId="INSTALLLOCATION" ComponentGroupName="MyApp_Project" SuppressCom="true" SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" AutoGenerateGuids="false" GenerateGuidsNow="true" ToolPath="$(WixToolPath)" Condition="'%(ProjectReference.PackageThisProject)'=='True'" />
</Target>
=====Bootstrapper.wixproj=====
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="..\MyApp\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<Copy SourceFiles=".\bin\$(Configuration)\$(OutputName).exe" DestinationFiles=".\bin\$(Configuration)\MyApp.%(AssemblyVersion.Version).exe" />
<Delete Files=".\bin\$(Configuration)\$(OutputName).exe" />
</Target>
=====Bootstrapper\Bundle.wxs=====
<Bundle Name="$(var.ProductName) Bootstrapper v!(bind.packageVersion.MyApp)"
Version="!(bind.packageVersion.MyApp)"
This is what I would do:
In my assembly I use auto versioning: AssemblyVersion("1.0.*")
In the setup project reference the version using binding to the assembly: !(bind.fileVersion.MyAssembly.dll) (MyAssembly.dll is the File/#Id of the assembly)
In the bundle using binding to the setup version:
!(bind.packageVersion.Setup) (Setup is the project name as is referenced in the bundle project.)
Got it!!! I didn't realize you can declare Variables aka DefineConstants in a BeforeBuild Target.
Below is an example BeforeBuild Target that generates a variable BuildVersion
Bundle.wxs:
<Bundle Name="$(var.ProductName) Bootstrapper v$(var.BuildVersion)"
Version="$(var.BuildVersion)"
Bootstrapper.wixproj:
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="..\MyApp\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<PropertyGroup>
<DefineConstants>BuildVersion=%(AssemblyVersion.Version)</DefineConstants>
</PropertyGroup>
</Target>
<!-- This is extra, not needed: Renames output file with version number -->
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="..\MyApp\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<Copy SourceFiles=".\bin\$(Configuration)\$(OutputName).exe" DestinationFiles=".\bin\$(Configuration)\MyApp.%(AssemblyVersion.Version).exe" />
<Delete Files=".\bin\$(Configuration)\$(OutputName).exe" />
</Target>
Related
I'm deploying a MsBuild task using nuget, that generates a .dll before each build.
I fail to get the generated dll referenced in the References node in the Visual Studio project of consumers.
I'm also using MSBuild to build the .nupkg file. Generating and compiling works just fine, I deploy the following target in the build/directory
<Project>
<!-- this will automatically run before the 'Build' target -->
<Target Name="GenerateAndBuild" BeforeTargets="Build">
<!--the deployed task that generates the code-->
<Utils.CreateUtilResourceTask/>
<ItemGroup>
<CompileGeneratedSources Include="GeneratedClass.cs" />
</ItemGroup>
<MakeDir Directories="$(OutputPath)" Condition="!Exists('$(OutputPath)')" />
<Csc Sources="#(CompileGeneratedSources )" OutputAssembly="$(OutputPath)Util.dll" TargetType="library" EmitDebugInformation="true" />
<Delete Files="#(CompileGeneratedSources )" />
</Target>
</Project>
That generates the util.dll in the project output folder but I fail to get it referenced in the consuming project.
I thought that this would work in the .csproj file:
<PropertyGroup>
<TargetFrameworks>netstandard1.6;net46</TargetFrameworks>
<BuildOutputTargetFolder>tasks</BuildOutputTargetFolder>
<VersionPrefix>0.1.1</VersionPrefix>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<PackageOutputPath>$(MSBuildThisFileDirectory)</PackageOutputPath>
<PackageId>BuildUtil</PackageId>
<!-- that does not add a refenrece in consuming projects -->
<references>
<reference file="$(OutputPath)Util.dll"/>
</references>
<files>
<file src="$(OutputPath)Util.dll"/>
</files>
</PropertyGroup>
Perhaps someone has a hint on that?
You need to declare the assembly reference in an ItemGroup, not a PropertyGroup. For example:
<ItemGroup>
<Reference Include="$(OutputPath)Util.dll" />
</ItemGroup>
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>
I am using Wix 3.9 and have it building 3 MSI files and then a Bundle that includes each of the MSI files in succession. I have upgraded the primary Installer project to automatically include version number in the MSI file name using the techniques described here and here. My BeforeBuild task in my Wix project is as follows (from second link)
<!-- Set Output file name to include version-->
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="$(SolutionDir)WindowsClient\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersions" />
</GetAssemblyIdentity>
<CreateProperty Value="$(OutputName)%(AssemblyVersions.Version)">
<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>`
But this is causing my bundle to fail at the following line.
<MsiPackage Id="MyMsiFile" Name="$(var.WixInstaller.TargetFileName)" SourceFile="$(var.WixInstaller.TargetDir)"/>
It fails saying it cannot find the file MyMsiFile.msi, which is true because it is now named MyMsiFile1.0.1.msi. How can I reference the MSI file with the dynamic name in the Bundle file?
Here's one way…
Modify the project that generates the MyMsiFile MSI file to also generate a MyMsiFile.wxs that defines a PackageGroup containing that package with its current name.
Then for bundle projects that need it, add the MyMsiFile.wxs as a linked file and in their bundle chain, reference the MSI with a PackageGroupRef.
This method keeps detailed knowledge of the MSI package in the MSI project and away from the bundle projects.
I ended up avoiding the question all together. I changed my msi project to copy the msi and attach the version number to the copy. This way I get one msi file with the version number and one without for bundling into more complete installers. I removed the BeforeBuild code posted above and replaced it with this
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(SolutionDir)WindowsClient\bin\$(Configuration)\MyApp.exe">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersions" />
</GetAssemblyIdentity>
<Copy SourceFiles=".\bin\$(Configuration)\$(OutputName).msi" DestinationFiles=".\bin\$(Configuration)\$(OutputName)%(AssemblyVersions.Version).msi" />
</Target>
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>
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