I am fairly new to WIX, so forgive me if I'm completly missing the boat here, but I was wondering if it was possible to reuse components (mwm,cab,etc) from within a wxs file without having light re-link them every time. The installer I'm working on has several executables, dlls and config files that tend to change between each install. These files amount to about 5 meg worth of installer. The part I want to reuse is the ~350 meg worth of image/map/database files that do not change very often that I don't want to necessarilly have to compile/link every time the installer is built.
I've tried creating a mwm file for the maps, but when I reference them within the wxs, they get linked via light into the main .msi file. I've tried specifing a non embedded CAB file to hold the maps:
<Media Id="1" Cabinet="media1.cab" EmbedCab="yes" />
<Media Id="2" Cabinet="NewRiver.cab" EmbedCab="no" CompressionLevel="none" />
...
<Merge Id="NewRiverDigMap" SourceFile="..\Output\NewRiverDigitalMaps.msm" Language="1033" DiskId="2" />
But every time light runs, the stand-alone CAB file gets regenerated - which takes a while.
I thought about just creating a ZIP file to deliver along with the msi and have the installer just kick off the zip extract, but that seems anti-wix to me. I'd like to have the files be removed when they are no longer needed.
Are there any other wix like operations that I'm missing? I've read about fragments, but that doesn't seem to be what I'm looking for.
Thanks,
David
Your intuition is absolutely leading you in the right direction. The feature you are looking for is called "cab-cache". You use it by adding the following to your light.exe command-line:
-reusecab -cc path\to\directory\for\cabinets
Note: Compiling (candle.exe) and linking (first half of light.exe) should happen very quickly. What is usually slow is the binding (second have of light.exe) because it actually touches all of the files and builds the cabinets. Cabinet building is the slowest part, so hopefully the cab-cache speeds things up sufficiently for you.
P.S.: If compiling is taking much time you can create ".wixlib" with lit.exe. I have more about .wixlibs here: http://robmensching.com/blog/posts/2008/10/10/What-are-.wixlibs-and-why-would-you-use-them
Related
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
We are using heat executable to generate an authoring file, where a unique component is created for each file. Is there a way to include multiple files under a single component.
Expected result:
<component Id="samplecomponent" Directory="INSTALLFOLDER" Guid="*">
<File Id="file1.txt" Source="$(var.Sourcedir)\file1.xml" />
<File Id="file2.txt" Source="$(var.Sourcedir)\file2.xml" />
<File Id="file3.txt" Source="$(var.Sourcedir)\file3.xml" />
</Component>
Paraffin 3.131 version works to help you create multiple files per component, but please read the disclaimers below (and the links in the comments above).
It should be noted that this feature has probably been deprecated from Paraffin for good reason (and never seen in WiX's own heat.exe as far as I know), since it tends to make minor upgrades and patching almost impossible to use. This has to do with the overall complexity of MSI - particularly with regards to upgrades. There could also be other reasons for Paraffin deprecating the feature, for example that it is too complex to maintain. I don't know.
I always prefer one file per component - it avoids all kinds of problems (for patching, upgrades, self-repair, etc...). Here are some technical details: Change my component GUID in wix?
However, Santhosh has problems with a package that is so large that its installation performance is too slow, even after having applied a number of tricks to speed it up (limit costing, use admin image, etc... - see link). My preferred alternative in such cases is to split the package into several MSI files and install them with a bootstrapper in sequence - for example using WiX's Burn bootstrapper. This is not always acceptable of course. Sometimes a single MSI is crucial.
Is there are manual cabinet file's preparing VS automatic cabinet file's creation during building wix project?
Or may be this is something else ?
MediaTemplate replaced Media and is available starting in WiX 3.8. Take a read at:
WiX and cabinetry
Basically the Media element took a lot of code to describe what to do when you had more then 1 CAB file. The MediaTemplate element is syntactical sugar that reduces this to one line.
Many smaller installers with just 1 CAB won't care one way or the other. But when you start to build large MSIs it'll help.
I've recently started learning and utilizing WiX, and my first true project with WiX is repackaging a custom configuration of Qt. It's been quite a challenge, as the Qt project is massive.
I've managed to smash my way very inelegantly through the process, but have recently reached a snag during linking. I've been receiving Light.exe error "LGHT0296", most likely because I was creating a CAB that was much greater than 2 GB. After trying for the highest compression level, and having that not make a difference, the only option left to me is to split the installation package into more than one CAB file (Side note: The error returned was extraordinarily helpful in telling me what courses of action to try).
Anyway, I've found myself a bit lost when it comes to creating multiple CAB files. I'm not entirely sure what I should do in this case, and I haven't been able to find any helpful documentation or examples where this splitting is done. What's the best way for me to go about doing this?
Thanks.
You just declare multiple media elements like this:
<Media Id='1' Cabinet='package1.cab' EmbedCab='no'/>
<Media Id='2' Cabinet='package2.cab' EmbedCab='no'/>
If you have enough space on your installation media and would rather eliminate the time and disk space that the installer uses to unpackage files, then you can also put unpackaged files in some folder relative to the MSI like this (you can even create an MSI that installs itself that way):
<Media Id='3' Layout="./somefolder" />
Finally, you chose in which media to put each file by adding a DiskId attribute like this:
<File Source="./somefile" DiskId="2" />
In the spirit of this question by Si here: WiX tricks and tips. I am trying to determine the best way to get create wix fragments based on a directories. File harvesting, so to speak. For example under the bin\release folder I could have many different folders plus files that I want to capture very easily in fragments. I have been doing this by typing them or using wixedit.
Please note I haven't tried anything just done the reasearch here:
A)I read a little bit on heat.(http://installing.blogspot.com/2006/04/heatexe-making-setup-easier.html) I am not sure though about running it from msbuild?
B)I found this blog article from Newagesolution which discusses using t4 script: http://blog.newagesolution.net/2008/06/how-to-use-msbuild-and-wix-to-msi.html
I would like to know what others are doing to solve this.
Regards, Brian
Ok, Daniel I definately agree with using heat, but I think Rob pointed me in the right direction for what I ended up using.
What I ended up using was HeatDirectory.(Please note, I do not plan on doing patching. If you want patching, you may want to look at another solution, but heat/heat directory can work for patching.) Unfortunately, Rob's answer was quite limited, so I wouldn't say that he really answered my question. Just pointed me in the right direction. Thanks to both Rob and Daniel for your help.
Therefore, I came back and answered this question myself.
Hurdles...
Hurdle 1
I ran into is there is nearly no documentation on the web for how to implement heat tasks/heat directory(Heat wrapped in msbuild) from within visual studio. Please note that the wrapper to run from msbuild appears to be pretty new and I disclaim to use at your own risk, etc. Because of the lack of documentation, I sent an email to the wix user list. I received help from Brian Rogers. Thanks Brian, this solved everything for me. Take a look at his great blog entry here for a better understand how heat is going to help you automate: http://icumove.spaces.live.com/blog/cns!FB93073C6534B681!461.entry
Hurdle 2
After working with heat, I found out that I was not using components the recommended way, but I had to piece together all the reasoning. I think many people will run into this in the beginning of using Wix if you don't have prior knowledge of windows installer. Therefore I recommend some reading:
Make sure you read component rules as well if you are a novice like I was with Windows Installer.Component rules from MS: http://msdn.microsoft.com/en-us/library/aa370561.aspx. Read these from Rob Mensching for a better understanding of components: http://robmensching.com/blog/posts/2003/10/4/Windows-Installer-Components-Introduction
http://robmensching.com/blog/posts/2003/10/18/Component-Rules-101
For Extra Reading on Windows installer check these rules from the windows installer team(Rule 16: Follow Component Rules):
http://blogs.msdn.com/windows_installer_team/archive/2006/05/12/595950.aspx
Hurdle 3
You must decide whether or not you want to do patching of your files or upgrade everytime you install a new version.
I fortunately do not need to do patching, therefore for every upgrade I will just uninstall the previous version like this: How to implement WiX installer upgrade?.
This simplifies things greatly. When patching you are going to need full control over things. Something that is more difficult when you want automation/automatic building for by many developers that do not know anything about wix other than wix builds msi fields. It seems that this will work with using heat, but I am uncertain.
Hurdle 4
For me it took a little time to figure out why the include file with my preprocessor variables was not working. I finally figured it out. You must include the file in your .wxs file like this:
See here for more details:
Include a text file content into a WiX script
http://wix.sourceforge.net/manual-wix2/preprocessor.htm
Hurdle 5
Since I am using major upgrades with mix and I wanted to hook in subversion into my product number. This I thought would be quite simple and it is if you know how to do it. I had trouble here and had to get help for this as well.
Hurdle 6
With heat updating the files based on a bin folder everything works quite well. Well almost automatic, if there are any conflicts in dll references, etc then files will not be included in the installer and therefore things might not run properly. You need to have a testing process in place so as to test your program after installing. This can be critical if you miss a dll. I would also recommended that you keep track of the program's used dlls and compare against the msi file.
Now for the solution using HeatDirectory(from the help of Brian Rogers(wix team)):
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == ''">Debug</Configuration>
<OutputName>msbuild.heatfile</OutputName>
<OutputType>Package</OutputType>
<WixToolPath>..\..\YourHeatDir</WixToolPath>
<WixToolPath>$(WixToolPath)</WixToolPath>
<Cultures>en-us</Cultures>
<LinkerBaseInputPaths>..\..\data\HeatDir</LinkerBaseInputPaths>
</PropertyGroup>
<ItemGroup>
<Compile Include="product.wxs" />
<Compile Include="TestDir.wxs" />
</ItemGroup>
<Import Project="$(WixToolPath)\Wix.targets" />
<UsingTask TaskName="HeatDirectory"
AssemblyFile="$(WixToolPath)WixUtilExtension.dll" />
<Target Name="BeforeBuild">
<HeatDirectory
Directory="..\..\data\HeatDir"
DirectoryRefId="DataDir"
OutputFile="TestDir.wxs"
AutogenerateGuids="true"
ToolPath="$(WixToolPath)" />
</Target>
This needs to be pasted into your project file and appropriate settings/pathing needs to be changed. What this will do is take all the files in the path you specify
and create a TestDir.wxs file which you then can reference by component group. There are several Heatdirectory options see you need to see the heat sourcecode for details.
Just for reference: I found this post on stackoverflow after I posted my question: How to add a whole directory or project output to WiX package
Also for reference I just saw this article here: Harvesting a .csproj with heat.exe in Visual Studio 2008 and WiX(v3). and again this can be done using heat tasks instead.
Both discuss using third party tools. One called Mallow and another called Paraffin. I think paraffin looks supported and can do pretty much what heat does.
With a few additions: See here for details: http://www.wintellect.com/cs/blogs/jrobbins/archive/2007/10/18/wix-hints-for-new-users-part-1-of-3.aspx
Anyways hope this helps others.
The new HeatTasks might work very well for you here. They can pull project output groups from other projects in Visual Studio. It does much better in the latest build than older builds, so be sure to pick up a weekly http://wixtoolset.org/releases/.
For what it's worth, we are, in fact, using heat.exe from msbuild. If you're as picky as I am, the output of heat is probably not exactly what you want, but this is easy to rectify with an XSL transformation (I believe heat.exe has a command-line parameter expressly for this purpose).