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

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.

Related

Looking for a simple way to include all files in a project's BIN folder in my MSI

I have many files (hundreds) in my project's output BIN folder. I simply need to have an installer to include the files in the bin folder in an MSI installer.
In my WIX installer project, I have the following target to use the harvest tool and generate a list of all files in a bin folder, later on, I reference them in my WIX definitions:
<Target Name="GenerateHeat">
<HeatDirectory Directory="..\MyApp\bin\Debug"
PreprocessorVariable="var.HeatPath" OutputFile="HarvestedFiles.wxs"
ComponentGroupName="HarvestedFiles" DirectoryRefId="FOLDER1"
AutogenerateGuids="true" ToolPath="$(WixToolPath)"
SuppressFragments="true" SuppressRegistry="true" SuppressRootDirectory="true" />
</Target>
Is there any way to simply include all files in a bin folder and include them in the MSI without generating the intermediate file list? I prefer to specify the BIN folder name and WIX includes them in a <ComponentGroup>, so I can reference it in my <Product>
Update and clarification
This question is not about how MSI works. This is about how WIX can copy the content of a folder into an MSI without specifying every single file name in a <Component> and <File> sandwich.
Is there any way to simply include all files in a bin folder and
include them in the MSI without generating the intermediate file list?
This is not possible with built-in functionality of the free version of WiX. As Stein Åsmul points out, the commercial branch of WiX might have something like that.
If commercial WiX is not an option and you are ready to invest significant time in C# development, using mostly undocumented API, you could write a WiX compiler extension that adds entries to the File and Component tables based on a given source directory path. It could also generate component groups, that can be referenced elsewhere.
I have done exactly this in the past but it certainly wasn't a trivial task. One should also have very good knowledge of component rules and MSI in general before doing things like generating component GUIDs. You will find some pseudo code below. Before going down this route, it would be worth looking around if someone else has created an open-source WiX extension like that.
This is the kind of authoring that could be achieved with such a compiler extension:
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:ex="MyExtensionUri">
<Product ... >
<Feature Id="ProductFeature" Title="MyFeature" Level="1">
<ComponentGroupRef Id="ProductComponents" />
</Feature>
</Product>
<Fragment>
<ex:ComponentGroupFromFiles Id="ProductComponents"
Directory="INSTALLFOLDER"
Source="MyApp\bin"
Wildcard="*"/>
</Fragment>
</Wix>
Here is some pseudo code for a compiler extension. This is mainly intended to serve as keywords for exploring the WiX source "Compiler.cs".
Override Wix.CompilerExtension.ParseElement() to parse the attributes of your extension element.
Create component group and reference it by the product:
Wix.Row compGroupRow = Core.CreateRow(sourceLineNumbers, "WixComponentGroup");
compGroupRow[0] = myComponentGroupId;
Core.CreateWixGroupRow( sourceLineNumbers, Wix.ComplexReferenceParentType.Product, Core.ActiveSection.Id, Wix.ComplexReferenceChildType.ComponentGroup, myComponentGroupId );
For each component/file:
// Add record to the Component table
Wix.Row compRow = Core.CreateRow( sourceLineNumbers, "Component" );
// TODO: Assign data to compRow[0..5] according to MSI "Component" table documentation
// Add this component to the component group.
Core.CreateComplexReference( sourceLineNumbers, Wix.ComplexReferenceParentType.ComponentGroup, myComponentGroupId, "", Wix.ComplexReferenceChildType.Component, myComponentId, false );
// Add record to the File table.
Wix.Row fileRow = Core.CreateRow( sourceLineNumbers, "File" );
// TODO: Assign data to fileRow[0..2] and [6] according to MSI "File" table documentation. Columns 3, 4, 5, 7 are written by the WiX binder at a later time! Set them to null (if nullable) or 0.
// Create required metadata for WiX
Wix.WixFileRow wixFileRow = (Wix.WixFileRow) Core.CreateRow(sourceLineNumbers, "WixFile");
// TODO: Assign wixFileRow.File, wixFileRow.Directory, wixFileRow.DiskId, wixFileRow.Source
// Set wixFileRow.Attributes to 1 if you have generated a short file name.
// Add reference to the Media table
Core.CreateWixSimpleReferenceRow( sourceLineNumbers, "Media", diskId );
Useful utilities for generating Component / File table column data:
Core.GenerateIdentifier()
Core.GenerateShortName()
How to add components to a merge module? This is left as an exercise for the reader. 😉 Just find the code in WiX's "Compiler.cs".
FireGiant: I believe the commercial branch of WiX (FireGiant), has a module for this. HeatWave Harvesting - or something like that. It is part of the WiX expansion pack if I am not mistaken. It allows advanced harvesting to create MSI files and perhaps other formats that I am not aware of. I know next to nothing about the module apart from that. Oh, it also supports COM extraction from 64-bit COM files - which is not working in heat.exe at the moment.
Tallow: Back in the days of WiX2 there was a tool called Tallow that was developed by someone whose name I can not recall. It generated WiX markup to install files from a given input folder and even kept component GUIDs in sync (as far as I recall). I saw some copies of it on github.com, but the repository I downloaded had several malware hits, so no link.
Paraffin: A tool I have never used is Paraffin - supposedly a better Tallow - https://github.com/Wintellect/Paraffin. I can't say much about it since I have not tried it. Give it a quick look? I am not sure if it is maintained at all to be honest, but it sure beats rolling your own solution from scratch.
That would be against the whole design concept of Windows Installer.
Windows Installer manages the health/installation status of Components. It does this across all versions of all products that are installed. To make this workable, there are
"Components Rules." To make the rules manageable over component lifecycles, a component should be atomic: one file (or multi-file .NET assembly) and/or registry key.
(If you have a choice, you don't have to use Windows Installer if you don't appreciate its design.)
Also, you should consider whether, how and where whatever is in your bin folder should be installed. Its basic intent is for debugging in place. It may contain thing that 1) you aren't licensed to redistribute, 2) you are licensed to redistribute only in specific ways, 3) might already be part of the target systems, 4) might be best redistributed with their original installer from the vendor 5) you don't actually want to deliver to users, …. For these concerns, you can either narrow down the harvesting or filter out specific results.
Tip: If you want to modularize your installation, you could build sets of components into different MSI files and use WiX's bootstrapper to install them together.
Unless your talking about a web application / website (think node.js directory structure) I recommend against this.
For the some package manager authored all these thousands of files for me story (^^^) then use Heat.
For all the other normal scenarios I created IsWiX. It helps me reduce the complexity and friction of authoring/maintaining installers while keeping me in control of what does/doesn't ship and when something does or doesn't get installed (features).
https://iswix.com/2007/06/20/dealing-with-very-large-number-of-files/
https://github.com/iswix-llc/iswix-tutorials

Wix generate single component id for entire tree

I am someone with little to no experience with wix and I am trying to support Windows also for the component I am responsible for. I am trying to create merge module for a set of files that my product generates. These files exist in numerous sub directories. I was wondering how I can create a single component ID for all the files in the entire tree. I am not worried about minor upgrades as that is something I am not going to be doing. I am trying to avoid generating numerous GUIDs for each of the file.
Also is there any way I can change the name of the root directory I want the files to be installed. Currently, in our build system the files I want to install end up in a directory name "install". In the wxs file generated by heat it comes up as install. I was wondering if I could change it to the actual product name instead of "install".
Use one file per component - this avoids all sorts of problems (except .NET assemblies spanning multiple files). See the following thread: One file per component or several files per component?
Wix is a great framework for creating installers, but it has a steep learning curve. I strongly recommend you read a few sections of this great, online tutorial: https://www.firegiant.com/wix/tutorial/
If you are a "sample based tinkerer", you can find an even quicker, sample based tour in this article: http://www.codeproject.com/Tips/105638/A-quick-introduction-Create-an-MSI-installer-with
Wix is hands-on. Just focus on the samples, and focus on getting the components created and a major upgrade set up:
How to implement WiX installer upgrade? (modern, convenience way)
How to get WiX major upgrade working? (legacy way - more flexible, less convenient)
http://wixtoolset.org/documentation/manual/v3/howtos/updates/major_upgrade.html
Once you got that running the rest of the details fall into place by reading the documentation for whatever feature you need. Using Visual Studio / Votive with intellisense ensures that you can learn as you go with features such as shortcuts, ini files, xml files, dialogs, etc...
Another top tip is to use dark.exe (part of the Wix toolkit) to decompile existing MSI files. This yields Wix XML with code you can copy and paste into your own Wix files. I use other MSI tools to compile such MSI files, and then copy the sections I need into my Wix file - just to speed up the process of creating the Wix XML. Studying the decompiled XML is very educational - a real time saver.
UPDATE, May 2021: Some more links:
WiX Quick Start - Very long version
WiX Quick Start - Short version
If all the files are going to the same destination folder, then you can create one single COMPONENT with all the FILE within it. There is nothing stopping you to do that. You can then just create one GUID for that component. Also read these answers which talks about the advantages vs disadvantages of one component vs multiple components before you implement it: Answer1 Answer2. To Summarize:
You will have trouble with minor upgrades/repairs. If a component is
being updated, only the file designated as the KEYPATH is checked to see if
it is out of date: if it is up to date, all the others are ignored.
You'll also have difficulty if you want to add or remove files from each
component. Once released, a component is immutable (in terms of what files
are in it). The only way to update it without breaking component rules would
be to effectively remove and install the a new version of the MSI.
Understanding the component rules is key in Windows Installer and one file
per component makes the component rules easier to work with, which is why it
is the recommendation of many people here.
LINK
The root directory name can be changed by modifying the "Name" property for the DIRECTORY element.

How to create a Wix patch in combination with Heat?

I'm developer on a big system (>100 Projects in Solution, >100 000 LOC, > 10 Services, ...) and did the installation of this system in the past with wix and it worked fine. Now I need a way to patch (Minor Upgrade) parts of the system and run into several issues.
My Current Wix Setup is as following:
I have VS2010 and Wix3.6 Toolset and TFS2012 to Build the whole thing and get an installer
I'm using a Setup Library Project Type per Service
I'm using exactly one Setup Project to bundle things together and get one installer for the whole system.
It's not possible to change this setup.
The Setup Library Projects are set up as following:
I use the heat-directory msbuild task to generate the components and files and I'm using preprocessor variables to modify the file paths.
I need to modify the file paths because it must be possible to build an installer on the local developer system and to build the installer on the tfs build system which is different in folder structures.
The TFS uses always the same directory to compile subsequent versions of the software and moves the output after successful compilation to a unique folder structure.
Now I need a patch.
I created the Patch.wxs and called candle and light for it. I called torch to get the difference file. And finally want to create the patch with pyro.
Everything worked fine with a simple testproject, but on the big system
Pyro has the problem that it can't find the files to install.
Through my setup (see above), I must use preprocessor variables and have a full qualified path in my wix output (for example: C:\builds\myproduct\prodct.exe as file source). After moving the TFS output to another location this path is not valid anymore. I tried to use -bt and -bu switches for pyro, but this does only work for relative paths or for named bindpaths.
Now I wanted to change my wix project setup to use named bindpaths rather than preprocessor variables, but it seems that this is not possible.
heat can only use preprocessor variables or wixvariables but it seems not to be possible to use bindpath variables. heat provides a switch -wixvar which should create binder variables instead of preprocessor variables but I does exactly nothing.
Now I tried do use no wix and no preprocessor variables in heat and tell light per -bu -bt switches where to find the files. But if I do not set a preprocessor variable the resulting files look like Sources\product.exe. I can't get rid of this Sources. I know that I can transform all the xml with xslt and remove the Sources but thats a workaround which I would only implement if no other solution is possible. This would also mean that there is a problem in the wix toolchain.
It looks like pyro does only support bindpath variables and heat does only support preprocessor and wix variables. This seems to be really crazy, because how should they work together?
How can I create a patch if I use lit, light, candle, heat, torch and pyro and if the original build paths have changed (which is very common on a build system) and the file paths are created with heat and therefore be fixed or preprocessor or wix variables?
As you've found heat wasn't designed to be used in the patching scenario. It was only in recent versions of the WiX toolset that the generated GUIDs got to a point where there was even a chance that heat could successfully build output that would be patchable. Still need to do work there to make patching where heat is used work well.
Ultimately, I believe the answer is to simplify the "original source" problem. It is challenging to get all the bindpaths set up correctly and that makes patching, which is a hard problem, even harder. We've kicked around a few ideas but nothing has come together yet.
You could always use admin image based patching. It's slower but can be easier to get the "original source" and "target" laid out. That path does lose filtering though.
Basically, we need to do a bit more work in patching scenarios to make it much easier.
PS: "Source" in the path for a File/#Source attribute is an alias for the "default bindpath". You can use bindpaths there.

How can you _create_ a text file in Wix?

Is there any way to create a new text file and write to it using Wix? I have come across elements that will allow me to work with Xml files and Ini files, but nothing for a plain text file.
The root of the problem stems from the fact that we're using a third party library that reads from its own custom configuration file, which really ties my hands as to what the text file can look like. The configuration file is similar to an Ini file, minus the "sections." And I've noticed that Wix handles Ini files by always placing them in the system folder, which won't work for our needs.
The data that needs to be written to the configuration file is gathered at run-time, so there's no opportunity to simply lay down a pre-configured file.
I would be willing to accept a Wix extension to accomplish the same result if one exists, but haven't come across one yet.
There is no built-in feature in Windows Installer for this. You'll have to write code to do it and invoke that code with a custom action.
The IniFile element can write .ini files anywhere; use the #Directory attribute to specify which directory it should go in. If the library ignores [section] lines, you can use anything as the #Section attribute value.
You might want to have a look at this project. It contains the collection of WiX extensions and custom actions, and I suppose it also has CA to read and write text files. Try it out - it is claimed to be tested and proved by using in enterprise installations.

WiX Includes vs Fragments

What's the difference between a WiX include (.wxi file) and a WiX fragment (.wxs file)?
What are the use cases for each?
Which should be used and why?
The file extension is a convenience, the content of the file is what really matters. The distinction makes it easier to manage the difference between the actual installation (wxs files) and the properties required to create the installation (wxi files).
For example, your wxs files specify the product and its contents (which files you install) whereas you would use your wxi files to specify product version, upgrade codes, common paths, etc.
The WiX documentation provides some clear information.
Include files (.wxi)
Source files (.wxs)
Personally, I only use .wxi files to include common <?define?>s. I organize the rest of my product into many different logical groupings of Fragments. Treat Fragments like functions in your typical procedural language. Group like stuff together and don't make them too long.
One thing to be aware of is that adding a wxs file to your project implies that MSBuild will do an incremental build when you update the wxs file. Including a wxi file does not do this.
But on the other hand, MSBuild is not doing automatic builds when any of the files you're including into your msi have been updated, so you're probably not doing incremental builds anyway.