HeatDirectory to harvest only root level files - wix

Is there a way to direct HeatDirectory task in my wixproj to harvest only files at the root level and not to dive into any sub-directories? For sure I can handle it running XSL transform on the output file, but I would prefer a cleaner way. If there is no way then I'd appreciate if anyone has a clean example of XSL transform snippet to handle it (will save me time at least). Thanks a lot :)

XSLT seems to be the most suitable way. A couple of other solutions are mentioned here: How to exclude files in Wix toolset, like making a copy of files to harvest or using external opensource app.
Also Wix has commercial expansion pack with more powerful replacement of heat.exe, which can exclude files and folder, use masks: https://www.firegiant.com/wix/wep-documentation/harvesting/harvestfolder/

Since it looks like there is no integrated way to handle it through the HeatDirectory task input parameters, then I will post the XSLT snipped I wrote which does that work for me. Whoever uses it needs to modify as per need accordingly:
<xsl:key name="SubDirComponentKeys" match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory/wix:Directory//wix:Component" use="#Id" />
<xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory/wix:Directory" />
<xsl:template match="//wix:ComponentRef[contains(key('SubDirComponentKeys',#Id)/wix:File/#Source, '\')]" />

Related

Embed a conditional XML file in a dll

I want to create a config class for a dll that reads from an embedded resource. I've created multiple xml files with the configuration for all our different environments. I have a separate configuration for each environment DEV, QA, PROD with matches the prefix on the xml file.
.Configuration/DEV.config.xml
.Configuration/QA.config.xml
.Configuration/PROD.config.xml
etc
We've never used MSBuild before and are confused because there seems to be about 3-4 different ways of using it (pre/post build events, xml scripting, custom tasks etc). All i'm trying to do is rename the respective file to config.xml and embed it in the dll, so that the config class can read it out when it's run.
I've seen similar questions that simply embed a file but none that embed and rename at the same time. I assume that any sort of embedding would need to be done as a pre-build event.
Normally I would have tried a few bits and bobs but I have no idea where to start with this, if anyone could point me in the right direction I would be extremely grateful.
Update:
so with some help from jlew I should be able to do something like this
<ItemGroup>
<EmbeddedResource Include="Configuration\$(Configuration).config.xml" >
<LogicalName>config.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
What you probably want to do (without having seen your code) is to not rename the input file, but direct MSBuild to use a "logical name" for the resource which is different than the file.
<ItemGroup>
<EmbeddedResource Include="Dev.config.xml">
<LogicalName>MyRenamedConfig.config.xml</LogicalName>
</EmbeddedResource>
</ItemGroup>
If you are using csc.exe directly, you can do something similar with:
csc ... /resource:Dev.config.xml,MyRenamedConfig.config.xml

Batch rename with MSBuild

I just joined a team that has no CI process in place (not even an overnight build) and some sketchy development practices. There's desire to change that, so I've now been tasked with creating an overnight build. I've followed along with this series of articles to: create a master solution that contains all our projects (some web apps, a web service, some Windows services, and couple off tools that compile to command line executables); created an MSBuild script to automatically build, package, and deploy our products; and created a .cmd file to do it all in one click. Here's a task that I'm trying to accomplish now as part of all this:
The team currently has a practice of keeping the web.config and app.config files outside of source control, and to put into source control files called web.template.config and app.template.config. The intention is that the developer will copy the .template.config file to .config in order to get all of the standard configuration values, and then be able to edit the values in the .config file to whatever he needs for local development/testing. For obvious reasons, I would like to automate the process of renaming the .template.config file to .config. What would be the best way to do this?
Is it possible to do this in the build script itself, without having to stipulate within the script every individual file that needs to be renamed (which would require maintenance to the script any time a new project is added to the solution)? Or might I have to write some batch file that I simply run from the script?
Furthermore, is there a better development solution that I can suggest that will make this entire process unnecessary?
After a lot of reading about Item Groups, Targets, and the Copy task, I've figured out how to do what I need.
<ItemGroup>
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include="..\**\web.template.config">
<NewFilename>web.config</NewFilename>
</FilesToCopy>
<FilesToCopy Include"..\Hibernate\hibernate.cfg.template.xml">
<NewFilename>hibernate.cfg.xml</NewFilename>
</FilesToCopy>
</ItemGroup>
<Target Name="CopyFiles"
Inputs="#(FilesToCopy)"
Outputs="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')">
<Message Text="Copying *.template.config files to *.config"/>
<Copy SourceFiles="#(FilesToCopy)"
DestinationFiles="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFilename)')"/>
I create an item group that contains the files that I want to copy. The ** operator tells it to recurse through the entire directory tree to find every file with the specified name. I then add a piece of metadata to each of those files called "NewFilename". This is what I will be renaming each file to.
This snippet adds every file in the directory structure named app.template.config and specifies that I will be naming the new file app.config:
<FilesToCopy Include="..\**\app.template.config">
<NewFilename>app.config</NewFilename>
</FilesToCopy>
I then create a target to copy all of the files. This target was initially very simple, only calling the Copy task in order to always copy and overwrite the files. I pass the FilesToCopy item group as the source of the copy operation. I use transforms in order to specify the output filenames, as well as my NewFilename metadata and the well-known item metadata.
The following snippet will e.g. transform the file c:\Project\Subdir\app.template.config to c:\Project\Subdir\app.config and copy the former to the latter:
<Target Name="CopyFiles">
<Copy SourceFiles="#(FilesToCopy)"
DestinationFiles="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')"/>
</Target>
But then I noticed that a developer might not appreciate having his customized web.config file being over-written every time the script is run. However, the developer probably should get his local file over-written if the repository's web.template.config has been modified, and now has new values in it that the code needs. I tried doing this a number of different ways--setting the Copy attribute "SkipUnchangedFiles" to true, using the "Exist()" function--to no avail.
The solution to this was building incrementally. This ensures that files will only be over-written if the app.template.config is newer. I pass the names of the files as the target input, and I specify the new file names as the target output:
<Target Name="CopyFiles"
Input="#(FilesToCopy)"
Output="#(FilesToCopy->'%(RootDir)%(Directory)%(NewFileName)')">
...
</Target>
This has the target check to see if the current output is up-to-date with respect to the input. If it isn't, i.e. the particular .template.config file has more recent changes than its corresponding .config file, then it will copy the web.template.config over the existing web.config. Otherwise, it will leave the developer's web.config file alone and unmodified. If none of the specified files needs to be copied, then the target is skipped altogether. Immediately after a clean repository clone, every file will be copied.
The above turned out be a satisfying solution, as I've only started using MSBuild and I'm surprised by its powerful capabilities. The only thing I don't like about it is that I had to repeat the exact same transform in two places. I hate duplicating any kind of code, but I couldn't figure out how to avoid this. If anyone has a tip, it'd be greatly appreciated. Also, while I think the development practice that necessitates this totally sucks, this does help in mitigating that suck factor.
Short answer:
Yes, you can (and should) automate this. You should be able to use MSBuild Move task to rename files.
Long answer:
It is great that there is a desire to change from a manual process to an automatic one. There are usually very few real reasons not to automate. Your build script will act as living documentation of how build and deployment actually works. In my humble opinion, a good build script is worth a lot more than static documentation (although I am not saying you should not have documentation - they are not mutually exclusive after all). Let's address your questions individually.
What would be the best way to do this?
I don't have a full understanding of what configuration you are storing in those files, but I suspect a lot of that configuration can be shared across the development team.
I would suggest raising the following questions:
Which of the settings are developer-specific?
Is there any way to standardise local developer machines so that settings could be shared?
Is it possible to do this in the build script itself, without having to stipulate within the script every individual file that needs to be renamed?
Yes, have a look at MSBuild Move task. You should be able to use it to rename files.
...which would require maintenance to the script any time a new project is added to the solution?
This is inevitable - your build scripts must evolve together with your solution. Accept this as a fact and include in your estimates time to make changes to your build scripts.
Furthermore, is there a better development solution that I can suggest that will make this entire process unnecessary?
I am not aware of all the requirements, so it is hard to recommend something very specific. I can say suggest this:
Create a shared build script for your solution
Automate manual tasks as much as possible (within reason)
If you are struggling to automate something - it could be an indicator of an area that needs to be rethought/redesigned
Make sure your team mates understand how the build works and are able to make changes to it themselves - don't "own" the build and become a bottleneck
Bear in mind that going from no build script to full automation is not an overnight process. Be patient and first focus on automating areas that are causing the most pain.
If I have misinterpreted any of your questions, please let me know and I will update the answer.

Using WiX heat.exe with a static components

I am using heat.exe available in WiX3.5 to get the list of components and componentref for the files I want to install. However, for the file which should be installed as windows service, I dont want it to be generated by heat.exe as I need to add <ServiceInstall> etc.
I dont want to put the <serviceinstall> in xslt as it would make it very ugly code. Right now, I delete the exe before running heat.exe and then copy it back, but this is also not ideal. Is there a better way of doing this?
I would say that tweaking the heat output with XSLT templates is a kind of recommended approach. Although heat is not that rich in features to consider cases like you mentioned, its ability to apply XSLT to the output covers all possible needs.
Why do you think the code will be ugly? Do you reference the XSLT syntax here or the idea to tweak wxs file in general? Anyway, this piece of code won't change that much, I guess. And once you code it thoroughly and debug it, it won't take much of your time in future.

Reusing WIX components to speed up candle/light

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

Best way to create a wix fragment file based on User-defined directories to be used in MSBUILD

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).