Adding A Version To Post-Build Event In WiX - visual-studio

I'm new to WiX and having some trouble achieving what I think should be really simple. I'm using WiX v3.8 in Visual Studio 2013. Overall what I'm trying to accomplish is having one place where I can change the version of the installer and this will be propagated throughout the WiX project.
In the project properties of the WiX project => Build tab => "Define preprocessor variables" textbox I have:
ProjectVersion=3.6.7.0
However, where i run into problems is in the Post-Build Events when this fails:
copy !(TargetPath) "C:\Development\Release Builds\MyProject\$(TargetName) $(var.ProjectVersion)$(TargetExt)"
I've been scouring the internet, but unable to find a solution to my problem. Maybe I just don't know what to ask?
My question is: How can I make this post-build event work? What am I doing wrong? All I want to do is be able to do is easily change the ProjectVersion variable or another such variable in the post-build event.

This isn't exactly what you are asking for, but maybe it can help you achieve what you want?
I do it slightly different than your approach. I read the version from my 'main' assembly bundled with the wix installer, renames the msi filename to contain the version string, and sign it afterwards in a post-build event.
Resources:
https://stackoverflow.com/a/19371257/767926
https://stackoverflow.com/a/12323770/767926
To rename the msi to contain the version in the filename (wixproj):
<Target Name="BeforeBuild">
<GetAssemblyIdentity AssemblyFiles="$(SolutionDir)'HARDCODED PATH'\bin\$(Configuration)\'HARDCODED NAME OF ASSEMBLY'">
<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>
To sign the msi after the renaming (wixproj):
<PropertyGroup>
<PostBuildEvent>"C:\Program Files\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe" sign /sha1 'CERTIFICATEHASH' /v /t http://timestamp.verisign.com/scripts/timstamp.dll /d "DESCRIPTION" "$(ProjectDir)\bin\$(ConfigurationName)\'HARCODED PARTIAL MSI NAME'#(AssemblyVersions->'%(Version)').msi"</PostBuildEvent>
</PropertyGroup>
It's important to manually add/edit this post-build event in the wixproj file(use the editor), if you use the GUI it will mess up:
#(AssemblyVersions->'%(Version)')
Also, if you would like to sign your MSI's, make sure you add a description for the MSI, otherwise the UAC prompt will show a temporary filename. Resource: http://kentie.net/article/wixtipstricks/

Related

How to see MsBuild output in Visual Studio in real time

I've added some additional targets to a .csproj file in order to carry out some additional tasks after the project build is completed.
Nothing appears in the Visual Studio output window until all targets have completed. I want to be able to see messages that occur as the targets are being processed.
If I use the MSBuild Task Explorer (a VS extension), I can see that the messages can be picked up by a Visual Studio window as they are generated, so am I just missing a setting somewhere?
I've also tried replacing the Exec tasks with SmartExec from the MSBuild Extensions package.
Here is a snippet from my .csproj project file:
<Target Name="PostBuildActions" AfterTargets="Build">
<!--Get the version number from the assembly info -->
<GetAssemblyIdentity AssemblyFiles="$(ProjectDir)$(OutputPath)$(TargetFileName)">
<Output TaskParameter="Assemblies" ItemName="ToolboxVersion" />
</GetAssemblyIdentity>
<CreateProperty Value="$(ProjectDir)$(OutputPath.TrimEnd('\'))">
<Output TaskParameter="Value" PropertyName="ToolboxTarget" />
</CreateProperty>
<!-- Run the Simulink Widget Generator tool -->
<CreateProperty Value=""$(SolutionDir)SimulinkWidgetGenerator\bin\$(Configuration)\SimulinkWidgetGenerator.exe" -v %(ToolboxVersion.Version) -d "$(ToolboxTarget)"">
<Output TaskParameter="Value" PropertyName="WidgetGenCommand" />
</CreateProperty>
<Message Text="Running Simulink Widget Generator:" Importance="High" />
<Message Text="$(WidgetGenCommand)" Importance="High" />
<Exec Command="$(WidgetGenCommand)" ConsoleToMSBuild="true" />
<!-- Invoke Matlab -->
<CreateProperty Value="try, PackageToolbox, catch ex, disp(getReport(ex)), exit(-1), end, exit(0);">
<Output TaskParameter="Value" PropertyName="MatlabScript" />
</CreateProperty>
<CreateProperty Value=""$(MATLAB_INSTALL_DIR)\bin\matlab.exe" -automation -wait -log -sd "$(ToolboxTarget)" -r "$(MatlabScript)"">
<Output TaskParameter="Value" PropertyName="MatlabCommand" />
</CreateProperty>
<Message Text="Invoking Matlab: " Importance="High" />
<Message Text="$(MatlabCommand)" Importance="High" />
<Exec Command="$(MatlabCommand)" ConsoleToMSBuild="true" />
In Visual Studio, you can config your MSBuild verbosity in Tools –> Options –> Projects and Solutions –> Build and Run.
From here:
Verbosity set to Quiet – shows either success or the build failure. 1 line displayed below for successful build.
Verbosity set to Minimal – shows the command line for the build. 2 lines displayed for successful rebuild.
Verbosity set to Normal. Shows the output from the MSBuild Targets. 25 lines displayed for successful rebuild.
Verbosity set to Detailed. Much more comments shown from MSBuild. 395 lines displayed for successful build.
And lastly, Verbosity set to Diagnostic, shows you everything. 1097 lines displayed for successful build.
For this issue, I recommend you use msbuild command like msbuild
xxx.csproj by developer command prompt to see the targets being processed.
So am I just missing a setting somewhere?
No, indeed you're right and for now, the output in Visual studio seems to not support for real-time display after my test.
Details to describe this situation:
As we know, there has two ways to build vs project:
1. Build in Visual Studio 2. Msbuild.exe.
Build process in VS(#1) actually calls the Msbuild tool(#2) to work.
Msbuild tool will display the target to a console window in real-time.
And in VS, the output of its build seems like a Non-real-time
log, which will display after the build process ends.If we add a Time-consuming operation like yours, it won't display until the command ends.
I've done a test for this, create a simple test.csproj, and add a script like this:
<Target Name="WaitingToDoSth" AfterTargets="Test1">
<Exec Command="$(ProjectDir)DoSth.exe"/>
</Target>
This DoSth.exe has a Thread.sleep(3000) in it. In VS, the output won't display anything until the DoSth.exe executes successfully and the entire build process ends.
When using msbuild xxx.csproj command in developer command prompt for VS2017, the display can be real-time and we can see messages that occur as the targets are being processed.
If my answer is helpful, please give a feedback. Thank you.
The key to seeing MSBuild output in realtime is to use the MSBuild project SDK. Many thanks to rainersigwald that posted this solution on GitHub:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace>_5451</RootNamespace>
</PropertyGroup>
<Target Name="LogStuffInRealTime" BeforeTargets="CoreCompile">
<Exec Command="ping 127.0.0.1" YieldDuringToolExecution="True" ConsoleToMSBuild="true" StandardOutputImportance="high">
<Output TaskParameter="ConsoleOutput" ItemName="OutputOfExec" />
</Exec>
</Target>
</Project>

Post-Build event msbuild. Rename file at end of successful build

I don't know MSbuild scripting and don't have time to learn it right now. I need a method at the end of successful build to rename a dacpac file to include the version that is currently being built.
Example: (TfsDropLocation)\filename.dapac to (TfsDropLocation\filename.1.0.0.0.dacpac)
Is there a way to do this?
Is there a way to do this?
The answer is yes. If you don't mind editing the Visual Studio project file, then there is a simple solution that allows you to use a macro which looks like this:#(VersionNumber):
To accomplish this, unload your project. Then at the very end of the project, just before the end-tag, place below scripts:
<PropertyGroup>
<PostBuildEventDependsOn>
$(PostBuildEventDependsOn);
PostBuildMacros;
</PostBuildEventDependsOn>
</PropertyGroup>
<Target Name="PostBuildMacros">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="Targets" />
</GetAssemblyIdentity>
<ItemGroup>
<VersionNumber Include="#(Targets->'%(Version)')"/>
</ItemGroup>
</Target>
Now as promised, the assembly version is available to your post build event with this macro. So you add rename the file name by copy task in the post-build event with below command line:
copy /Y "(TfsDropLocation)\filename.dapac" "(TfsDropLocation)\filename.#(VersionNumber).dapac"
If you do not want to keep the previous filename.dapac, you can add a del command in the post-build event:
del "(TfsDropLocation)\filename.dapac"
Note: Do not ignore double quotation marks in the post-build event command line.
Then you can check you output and windows explorer, I used the file dll to test, you can check my test result:

Is it possible to get Product Version in post build event?

I wish to copy MyDir (in my $ProjectDir) to $OutDir{ProductVersion} upon completion of a build.
Using the following configuration in csproj file I am able to get the File Version.
<Target Name="AfterBuild">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyVersion" />
</GetAssemblyIdentity>
<Exec Command="robocopy $(ProjectDir)MyDir $(OutDir)/%(AssemblyVersion.Version) /E" IgnoreExitCode="true" />
</Target>
This however, retrieves the FileVersion and not the ProductVersion. Is there any way I can obtain the ProductVersion in post build event?
You're looking for Read AssemblyFileVersion from AssemblyInfo post-compile. You're going to need a custom task for this, since GetAssemblyIdentity doesn't return the productversion.
The linked question has the answer for AssemblyFileVersion, it shouldn't be too hard to adapt it to make it return the ProductVersion.

How to turn off caching of build definitions in Visual studio

In project file I import my own target file
<Import Project="Build\CopyDependencies.target" />
and later I call target from that target file
<CallTarget Targets="CopyDependencies" UseResultsCache="false" />
If I edit CopyDependencies.target file I have to reload whole solution and only then changes to CopyDependencies.target take effect. I believe it is some sort of build definitions caching in Visual Studio? If it is, maybe it can be turned off?
Thanks #KazR
Here is a smaller Solution that you can insert into your .csproj file
<Target Name="AfterBuild">
<PropertyGroup>
<TempProjectFile>Build.$([System.Guid]::NewGuid()).proj</TempProjectFile>
</PropertyGroup>
<Copy SourceFiles="Build.proj" DestinationFiles="$(TempProjectFile)" />
<MSBuild Projects="$(TempProjectFile)" />
<ItemGroup>
<TempProjectFiles Include="Build.????????-????-????-????-????????????.proj"/>
</ItemGroup>
<Delete Files="#(TempProjectFiles)" />
</Target>
Problem solved
I don't know how you would disable the VS cache, however I may have a workaround that would allow you to edit the build target without having to reload the solution.
You could use the MSBuild task in your proj file to call a wrapper target that copies your CopyDependencies.target file to CopyDependencies.[RandomNumber].target, then invokes your CopyDependencies target in the newly created file, and finally deletes it.
This would force VS to reload the target on each invocation as the filename is different.
Here's an example:
myProject.proj
Add this to the AfterBuild target:
<MSBuild Projects="Wrapper.target" Targets="MyWrappedTarget" UnloadProjectsOnCompletion="true"/>
Wrapper.target
Here we have the target that will - at build time - copy the real target file and invoke the desired build target within it (I've used an inline c# task which is only available in MSBuild 4.0):
<UsingTask TaskName="RandomNumber" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<ParameterGroup>
<Number ParameterType="System.Int32" Output="true"/>
</ParameterGroup>
<Task>
<Code Type="Fragment" Language="cs">
<!-- CDATA -->
Random rndGenerator = new Random();
Number = rndGenerator.Next(Int32.MaxValue);
<!-- CDATA -->
</Code>
</Task>
</UsingTask>
<Target Name="MyWrappedTarget">
<Message Text="MyWrappedTarget target called"/>
<RandomNumber>
<Output TaskParameter="Number" PropertyName="FileNumber"/>
</RandomNumber>
<PropertyGroup>
<CopiedTarget>inner.test.$(FileNumber).target</CopiedTarget>
</PropertyGroup>
<Copy SourceFiles="inner.test.target" DestinationFiles="$(CopiedTarget)"/>
<MSBuild Projects="$(CopiedTarget)" Targets="_innerTestTarget"/>
<Delete Files="$(CopiedTarget)"/>
</Target>
inner.test.target
This contains the real build target you want to execute, in this example it's a simple file copy.
<Target Name="_innerTestTarget">
<Message Text="This is a inner test text message"/>
<Copy SourceFiles="x.txt" DestinationFiles="x1.txt"/>
</Target>
This isn't production ready, but hopefully illustrates my point.
With this (slightly convoluted) process in place, you can change the inner.test.target file without having to reload the solution in VS.
Here's a solution that doesn't require any MSBuild scripting at all.
I noticed that unloading and reloading a project doesn't get around the cache, but closing and reopening the solution does. In addition, Visual Studio will prompt you to reload the solution if it notices the .sln file has changed. And finally, this superuser question explains how to touch a file in Windows.
Putting these together, I added a Visual Studio external tool to touch the current solution file. Here's how:
Select TOOLS > External Tools ...
Click the Add button to add a new tool.
Set properties as follows:
Title: Reload Solution
Command: cmd.exe
Arguments: /c copy "$(SolutionFileName)"+>nul
Initial directory: $(SolutionDir)
and turn on Use Output window
Click OK to close the External Tools window
Now if you have made changes to your MSBuild files, just select TOOLS > Reload Solution and all your build files will be reloaded.
I'm using Windows 7 64-bit and Visual Studio 2012 Express for Windows Desktop.
I have a different solution, not involving temporary files:
Include.targets file:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foobar">
<Copy SourceFiles="test.source" DestinationFiles="testFoobar.dest" />
</Target>
</Project>
Project file:
....
<Target Name="BeforeBuild">
<Exec Command="$(MSBuildToolsPath)\MSBuild.exe Include.targets /t:Foobar" ContinueOnError="false" />
</Target>
....
in this case VS does not recognize the MSBuild command, and does not cache the file.
happy coding!
Before running MSBuild I run this to clear the download cache:
call "%VS120COMNTOOLS%vsvars32.bat"
echo Clear download cache
gacutil -cdl

Publish ClickOnce from the command line

Is there a way to have Visual Studio 2008 execute the "Publish Now" button from the command line?
I've seen posts that suggest to use msbuild /target:publish to call it. That is OK, but MSBuild doesn't increment the revision number. I'm hoping for something like:
devenv mysolution.sln /publish
To increment build numbers, I am using MSBuild Extension pack inside my .csproj file as follows:
<Target Name="BeforeBuild" Condition=" '$(Configuration)|$(Platform)' == 'Release-VersionIncrement|AnyCPU' ">
<CallTarget Targets="CleanAppBinFolder" />
<MSBuild.ExtensionPack.VisualStudio.TfsSource TaskAction="Checkout" ItemCol="#(AssemblyInfoFiles)" WorkingDirectory="C:\inetpub\wwwroot\MySolution" ContinueOnError="true" />
<!-- Microsoft's task that goes over assembly files and increments revision number. -->
<MSBuild.ExtensionPack.Framework.AssemblyInfo Condition="'$(Optimize)'=='True' " AssemblyInfoFiles="#(AssemblyInfoFiles)" AssemblyRevisionType="AutoIncrement" AssemblyFileRevisionType="AutoIncrement">
<Output TaskParameter="MaxAssemblyVersion" PropertyName="MaxAssemblyVersion" />
</MSBuild.ExtensionPack.Framework.AssemblyInfo>
<Message Text="----current version---: '$(MaxAssemblyVersion)'" />
</Target>
This way, anytime the configuration is set to Release-VersionIncrement, the version number is changed. When this is done, I can use the following MSBuild command to publish it:
msbuild c:\projects\MyProject.csproj
/t:ResolveReferences;_CopyWebApplication
/p:Configuration=Release;BuildingProject=true;WebProjectOutputDir=c:\inetpub\wwwroot\OutputProject\MyProjectOutput;OutDir=c:\inetpub\wwwroot\OutputProject\MyProjectOutput
Note that this is for an ASP.NET 3.5 web application.

Resources