How can I define a project variable with candle in WIX? - msbuild

I want to take an existing project variable and extend it but I want it to be global so that I don't have to include it in each wxs file.
The one I want to extend is:
$(var.MyProject.TargetDir)
I want to turn to make
var.MyProject.TargetDir.Help=$(var.MyProject.TargetDir)Help\
How can I do this globablly?
I know I can do this:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?define var.MyProject.TargetDir.Help=$(var.MyProject.TargetDir)Help\ ?>
but then I have to do that in each file or I have to create a wxi file and include that in each file. I want this to be predefined before the build. How can I do this?

You can define preprocessor variables with Candle as so:
candle.exe -dMyVariable=whatever.
This is not a hard answer to find with google but my problem was that I was trying to do:
candle.exe -dMyProject.TargetDir.Help=$(var.MyProject.TargetDir)Help\
So I was a little bit careless here forgetting that $(var.MyProject.TargetDir) is also created by candle and I can't just reference that before it's created.
You would have to manually find the path of the file as so:
candle.exe -dMyProject.TargetDir.Help=$(SolutionDir)MyProject\bin\$(Configuration)\Help\
Also just a quick note, your variable that you create does not get prefixed with `var.'

Related

Why is it called response file

MBuild can use response files to save and run commands. But why is it called response file? What is it responding to?
(Also in an MsBuild file the task elements are called Target. What is 'target' refering too?)
A target represents a collection of things you want to do. In an msbuild file, it is represented by an xml element that can have various child xml elements called tasks.
Conceptually it looks like this:
<Target Name="Foo">
<Task />
<AnotherTask />
</Target>
The target you want to execute can be passed in as a command line parameter to msbuild. There are other ways to execute the target of your choice, but you will need to read the docs for that:
https://learn.microsoft.com/en-us/visualstudio/msbuild/msbuild?view=vs-2019
In other build systems, a target can be called a goal.
Note:
Some build systems use a very rigid convention, where files have to be in certain places. MSBuild is not like that. It relies on configuration, where you can configure it any way you like. The only convention's really are the xml syntax and schema that you have to follow.
As for the response file name. Who knows and who cares anyways? It's just an extra place to put more command line parameters. I don't rely on it, and neither should you. If you know what you are doing you can stick all that stuff in a proper msbuild xml file and just invoke msbuild to kick off a build.

Setting an MSBuild property to a file version

I'm installing a toolset and I use the $(Registry:Path#Value) syntax to read the path to the compiler executable and set a property containing this value. However, in order to properly set up the search paths that the executable needs, I need to read the file version of this executable. I've seen examples of using a Target to do this and putting a Task in the target that invokes GetAssemblyVersion, but I want something more like a property function, because I want to initialize this before a build has even been run. The user should be able to go into the Project Settings and see the value I've determined as part of their include path. So what I'd really like is something like this:
<Project>
<PropertyGroup>
<CompilerExe>$(Registry:Path#Value)</CompilerExe>
<CompilerVersion>$(GetFileVersion:$(CompilerExe))</CompilerVersion>
</PropertyGroup>
</Project>
Obviously the syntax above doesn't work, but is there any way I can do something like this?

WiX: How to mix hand-generated and auto-generated .wxs content?

I have a project that has a large number of files. Between versions of our software, new files get added and some get removed. Therefore, in automating our build process, I would like to have heat auto-generate a .wxs file (let's call it files.wxs).
But then there are certain hand-generated items, like the <product> element with its associated version and <environment> tags for environment variables that we need to set. These never change (except for the version number which increments). Right now, I have put all of that in a file named product.wxs.
How can I best combine them into one .msi? Do I need to create a <component> element inside the <product> element for each of the fragments that were auto-generated in the files.wxs file? If so, that kind of defeats the purpose of auto-generating that file. I'm hoping there is another way.
Help! Thanks, in advance.
The installer that I work on has both manual and heat generated code how ours handle it is:
A script builds the destination file structure i.e. all the files that will be installed are copied to a temp directory mimicking the structure they will have on destination machine
Heat.exe is then executed on that directory structure outputting to a file.
We then apply some transforms as we have multiple features in our installer (though if your installer is just one feature you could use a singular ComponentGroup created by Heat.exe), the transform groups the components into ComponentGroups based off the directory structure.
The manual files only reference the ComponentGroups.
If a file is added or removed the work is done on the script (if necessary, parts of script just scoop whole directories). When the installer is built a new component is automatically generated for that file and it's added to the appropriate group by the transform. No work is needed as that group is already referenced in the manual files.

How to pass parameter to candle in command line and let it overwrite the value in the target .wxs

I'm working on a MSBUILD script to dynamically inject a number of parameters to a wix project for several builds, and I understand I can use -d switch in candle to supply additional params.
However I'm getting several warnings similar to "The variable 'xxx' with value 'yyy' was previously declared with value 'zzz'", This is understandable as in the .wxs I already have these values defined for a default build, the build will then carry on using the values from .wxs after the warnings.
So the question is..is it possbile to force candle to overwrite these parameters which are already in .wxs..
Thanks in advance.
Preprocessor variables can only be defined once so you need something like:
<?ifndef Variable ?>
<?define Variable="default" ?>
<?endif?>
to protect against redefinition. This is the same as with the C/C++ preprocessor that the WiX toolset was modeled after.

Tfs2010 Build Number and Assembly File Versions & MSBuild targets

I read John Robbins' article TFS 2010 Build Number and Assembly File Versions: Completely In Sync with Only MSBuild 4.0, and I'm wondering about the best way to go about integrating this.
The download for the article has two files, one is a targets file and one is a proj file.
The targets file has a number of tasks to scrape out a build number based on the Tfs build number (the same one used for the builds) and write that number out to some location (call it BuildNumberFile) for consumption by other proj files.
The proj file is very simple. It just imports the aforementioned targets file, and then declares a target with name "All" while also declaring DefaultTargets on the Project element to be All as well.
<Project ToolsVersion="4.0" DefaultTargets="All" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<!-- The two required properties so the Wintellect.TFSBuildNumber tasks knows your major and minor values.-->
<TFSMajorBuildNumber>3</TFSMajorBuildNumber>
<TFSMinorBuildNumber>1</TFSMinorBuildNumber>
</PropertyGroup>
<Import Project="Wintellect.TFSBuildNumber.targets"/>
<!-- Just ask for the version information files you need. These are here to show all the diffent ones in
Wintellect.TFSBuildNumber.Targets. You can change the names -->
<Target Name="All"
DependsOnTargets="WriteSharedCSharpAssemblyVersionFile;
WriteSharedVBAssemblyVersionFile;
WriteSharedCPPCLIAssemblyVersionFile;
WriteSharedCPPAssemblyVersionFile;
WriteSharedWiXAssemblyVersionFile;
WriteSharedTextAssemblyVersionFile;"/>
</Project>
I have two questions about this:
I'm still learning MSBuild. If the name of the target isn't specified elsewhere in the targets, is the target executed? How do I ensure that this target is run?
Are the csproj files supposed to declare an Include item for the location where BuildNumberFile is, even though it doesn't exist until compiletime?
Do ItemGroups and Include have a DependsOnTargets or something that allows them make sure the file exists before they build?
Are the entire contents of the csproj file using this supposed to be wrapped in a target that expresses DependsOnTargets for BuildNumberFile?
Thanks!
I think I've got this figured out, but two people promoted my question so I'll answer it here:
You can ensure that a target is run by expressing a dependency on it from another target. Microsoft.Common.targets exposes two targets--BeforeBuild and AfterBuild--expressly for the purpose of being overridden for customizability. I found the easiest way to do this was <Target Name="BeforeBuild" DependsOnTargets="WriteSharedCSharpAssemblyVersionFile" /> where WriteSharedCSharpAssemblyVersionFile is the target declared in the download from the link in the original post. Also, if you're new to MSBuild, this BeforeBuild target must be declared after the Microsoft.CSharp.targets is imported, but the default csproj template guides you in doing this.
The WriteSharedCSharpAssemblyVersionFile target should indeed write the file to some central location, since when building a solution, all targets are executed only once. All projects should reference the file from that location even if it doesn't exist, since by the time compilation happens (or more importantly, by the time references are resolved), the BeforeBuild target will have run and the file will be in place.
In my structure, I have these versioning files in a folder directly underneath the branch root folder. Furthermore, since the file being built is generated, I have it build to the output directory. It seems a little strange to be referencing things from the output, but it preserves the invariant of having all build products in one place so that the output directory can be blown away as a means of performing a clean.
In MSBuild items constitute inputs into the system (usually files) so it's weird to think of them depending on targets. After some learning this question doesn't make a lot of sense. In any case, the answer is no.
The entire contents of the file should indeed not be all in one target--all that is required is to import the Wintellect.TFSBuildNumber.targets file at the beginning of your csproj file, and declare BeforeBuild's dependency on WriteSharedCSharpAssemblyVersionFile at the end.
Hope this helps!