Wix,Heat and Wxi File - wix

I am using heat.exe to genrate file listing, I need to replace File/#Source="SourceDir"
so I am passing -var and directory name , but those variable are defined in my .wxi file
How can I include .wxi file in the heat generated wxs file . as this file will be generated each time i make a build.

Why replace SourceDir?
You can just pass in additional base folders to light with the -b switch and for all references of SourceDir, WiX will look in the base folders you've specified. Makes it easy to move things around between machines and only have to update a parameter in your build system, rather than editing an include file.

An update for wix 3.7, when you use HeatDirectory task in your wixproj instead of running heat.exe, you can PreprocessorVariable to set the SourceDir.
<Target Name="BeforeBuild">
<HeatDirectory Directory="..\distribution"
PreprocessorVariable="myVar" <--- your variable name
OutputFile="HeatGeneratedFileList.wxs"
ComponentGroupName="HeatGenerated"
DirectoryRefId="INSTALLFOLDER"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)"
SuppressFragments="true"
SuppressRegistry="true"/>
</Target>
More detail:
http://wix.sourceforge.net/manual-wix3/msbuild_task_reference_heatdirectory.htm
WIX HeatDirectory Task - Setting the preprocessorVariable

Related

Specify build directory name

I need project to be built into folder:
\bin\Debug 1.0.3.4
Where 1.0.3.4 is a current building assembly version specified in assembly: AssemblyVersion attribute.
I tried using different variables like $(AssemblyVersion), GetAssemblyIdentity task but had no luck.. I'm not so good at using MSBuild.
A quick and dirty solution to this is to, after the build, use GetAssemblyIdentity to extract the version info and then move the $(TargetFile) to the appropriate directory.
This assumes you don't have any dependencies going on. Otherwise, you'll need to modify the $(TargetPath), $(TargetName), and $(TargetExt) or go with the "proper" way of doing it - modifying $(OutputPath) dynamically.
<Target Name="AfterBuild" AfterTargets="Build">
<GetAssemblyIdentity AssemblyFiles="$(TargetPath)">
<Output TaskParameter="Assemblies" ItemName="AssemblyIdentity"/>
</GetAssemblyIdentity>
<PropertyGroup>
<Version>#(AssemblyIdentity->'%(Version)')</Version>
</PropertyGroup>
<Copy SourceFiles="$(TargetPath)" DestinationFolder="$(OutDir)$(Version)" />
</Target>
The proper way would be to modify $(OutDir) aka $(OutputPath), but that would involve things like modifying the AssemblyVersion.cs file (or more complexly, a copy of it injected automatically into the build chain).
You could also instead parse the version from the AssemblyVersion.cs file.

WiX harvest directories with heat

I have a large project with numerous sub-directories I need to harvest files from. I have this entry in my product.wxs file:
<Feature Id="DefaultFeature" Title="Main Feature" Level="1">
<ComponentGroupRef Id="APP_DATA" />
</Feature>
At build time I run a bat file with a number of heat commands to harvest this directory, among others :
"%WIX%\bin\heat" dir ".\image\App_Data" -cg APP_DATA -gg -scom -dr
App_Data -gl -sfrag -srd -out ".\App_Data.wxs"
This generates a file App_Data.wxs in the same folder as the Product.wxs file. But when the build runs I get the following error :
Product.wxs(97): error LGHT0094: Unresolved reference to symbol
'WixComponentGroup:APP_DATA' in section
'Product:{FAD0EA15-49BC-4EF8-A440-87070E4FAC7A}'
I appears that the WiX project cannot find the "APP_DATA" component group in the file App_Data.wxs. How do I get it to do that? I would assume there is an entry in the ProjectName.wixproj file but I have not found what you do.
I have been searching the internet and found a thousand ways to set this up but none go into enough detail about how you get the WiX project to see files generated by heat during the build. I saw this post.
Which talks about editing the Target Name="BeforeBuild" section of the wixproj file. Is that the only way to get this to work?
You need to add App_Data.wxs to your wixproj just like you'd add a code-generated cs file to a csproj.
For example, if the App_Data.wxs is in the same folder as your wixproj, you'd add the following Compile element to your ItemGroup element in the wixproj.
ProTip: If you are using a SCM that defaults to read-only you'll need to check it out every time or set to always writable.
<ItemGroup>
<Compile Include="App_Data.wxs" />
</ItemGroup>

MSBuild: Ignore targets that don't exist

Solution1.sln contains two projects:
ProjectA.csproj
ProjectB.csproj
ProjectB has a custom target called "Foo". I want to run:
msbuild Solution1.sln /t:Foo
This will fail because ProjectA doesn't define the "Foo" target.
Is there a way to make the solution ignore the missing target? (E.g., do nothing if the target doesn't exist for a specific project) without modifying the SLN or project files?
There is a two-part solution if you don't want to edit the solution or project files and you're happy for it to work from MSBuild command-line but not from Visual Studio.
Firstly, the error you get when you run:
MSBuild Solution1.sln /t:Foo
Is not that ProjectA does not contain a Foo target but that the solution itself does not contain a Foo target. As #Jaykul suggests, setting the MSBuildEmitSolution environment variable will reveal the default targets contained within the solution metaproj.
Using the metaproj as inspiration you can introduce a new file "before.Solution1.sln.targets" next to the solution file (the file name pattern is important) with contents like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo">
<MSBuild Projects="#(ProjectReference)" Targets="Foo" BuildInParallel="True" Properties="CurrentSolutionConfigurationContents=$(CurrentSolutionConfigurationContents); SolutionDir=$(SolutionDir); SolutionExt=$(SolutionExt); SolutionFileName=$(SolutionFileName); SolutionName=$(SolutionName); SolutionPath=$(SolutionPath)" SkipNonexistentProjects="%(ProjectReference.SkipNonexistentProjects)" />
</Target>
</Project>
The MSBuild element is mostly just copied from the solution metaproj's Publish target. Adjust the target name and any other details to suit your scenario.
With this file in place, you'll now get the error that ProjectA does not contain the Foo target. ProjectB may or may not build anyway depending on inter-project dependencies.
So, secondly, to solve this problem we need to give every project an empty Foo target which is then overridden in projects that actually already contain one.
We do this by introducing another file, eg "EmptyFoo.targets" (name not important) that looks like this:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Target Name="Foo" />
</Project>
And then we get every project to automatically import this targets file either by running MSBuild with an extra property, eg:
MSBuild Solution1.sln /t:Foo /p:CustomBeforeMicrosoftCommonTargets=c:\full_path_to\EmptyFoo.targets
Or include the CustomerBeforeMicrosoftCommonTargets property in the Properties attribute on the MSBuild element in the first targets file where you could optionally specify the full path relative to the $(SolutionDir) property.
However, if you're willing to run Foo in conjunction with any of the default solution targets (ie Build, Rebuild, Clean, or Publish) you could take some inspiration for how the Web Publishing Pipeline in MSBuild uses the DeployOnBuild property to call the Publish target on Web projects in a solution containing other project types that don't support publishing.
More info on the before.Solution1.sln.targets file here:
http://sedodream.com/2010/10/22/MSBuildExtendingTheSolutionBuild.aspx
You can target those by project name, like /t:project:target (might need quotes, I can't remember).
You can find all the generated targets by setting the environment variable MSBuildEmitSolution = 1 ... which causes msbuild to save to disk the temp .metaproj file which it generates for your solution. That file has all those targets defined in it, just open it up and take a look ;)
Maybe not the best answer but a reasonable hack.
msbuild ProjectA.csproj
msbuild ProjectB.csproj /t:Foo
When msbuild building solution - msbuild emits only limited set of targets into it's .metaproj file, and afaik - you can't build custom target through building sln file, you have to use original project1.csproj or custom build script.
Just for reference:
Use ContinueOnError when using MSBuildTask or -p:ContinueOnError=ErrorAndContinue when using (dotnet) msbuild
It may be in limited scenarios helpful: For example you have a list of .csproj files and want attach metadata only to specific project file items then you could write something like this:
<Target Name="UniqueTargetName" Condition="'$(PackAsExecutable)' == 'Package' Or '$(PackAsExecutable)' == 'Publish'" Outputs="#(_Hello)">
<ItemGroup>
<_Hello Include="$(MSBuildProjectFullPath)" />
</ItemGroup>
</Target>
<Target Name="BuildEachTargetFramework" DependsOnTargets="_GetTargetFrameworksOutput;AssignProjectConfiguration;_SplitProjectReferencesByFileExistence"
Condition="$(ExecutableProjectFullPath) != ''">
<Message Text="[$(MSBuildThisFilename)] Target BuildEachTargetFramework %(_MSBuildProjectReferenceExistent.Identity)" Importance="high" />
<MSBuild
Projects="%(ProjectReferenceWithConfiguration.Identity)"
Targets="UniqueTargetName"
ContinueOnError="true">
<Output TaskParameter="TargetOutputs" ItemName="_Hallo2" />
</MSBuild>
<Message Text="[$(MSBuildThisFilename)] ########### HELLO %(_Hallo2.Identity)" Importance="high" />
</Target>

heatdirectory directory dynamic

I want to add a HeatDirecotry task to my wixproj in Visual Studio, but I need the Directory attribute - the source path - will be a variable - a preprocessor variable, so I can supply it dynamically.
Can anyone tell how can I do that?
Thanks!!!
Try the following
Unload your project and then add the below code in .wixproj file
<Target Name="BeforeBuild">
<HeatDirectory DirectoryRefId="INSTALLFOLDER"
OutputFile="Source.wxs"
Directory="C:\Users\aaa\Desktop\ComponentsFiles"
SuppressRootDirectory="true"
ToolPath="$(WixToolPath)" AutogenerateGuids="true"
ComponentGroupName="SourceComponentGroup"
PreprocessorVariable="var.SourcePath">
</HeatDirectory>
</Target>
and then reload the project. Add the following file for the preprocessor variable inside the buils tag of the project properties
SourcePath=C:\Users\aaa\Desktop\ComponentsFiles

How do I move a bunch of files using a Move MSBuild task and a wildcard?

I have a folder with files that have names starting with App_Web_ and ending with .dll. I don't know what's in between those parts and I don't know the number of files. I need MSBuild to move those files into another folder.
So I composed this:
<Move
SourceFiles="c:\source\App_Web_*.dll"
DestinationFolder="c:\target"
/>
but when the target runs I get the following output:
error MSB3680: The source file "c:\source\App_Web_*.dll" does not exist.
The files are definitely there.
What am I doing wrong? How do I have the files moved?
You cannot use regular expression directly in task parameters. You need to create an item containing list of files to move and pass its content to the task:
<ItemGroup>
<FilesToMove Include="c:\source\App_Web_*.dll"/>
</ItemGroup>
MSBuild will expand regular expression before passing it to the task executor. So later in some target you may invoke Move task:
<Target Name="Build">
<Move
SourceFiles="#(FilesToMove)"
DestinationFolder="C:\target"
/>
</Target>