WiX Compile Time Directory Harvesting And Variables - wix

I'm a bit confused about the interaction of these tools at the moment and I need a little helping ironing out my appraoch (or being told I'm being daft).
I have a VS2010 solution with two WiX projects. I have a a .WXI file in a solution folder that contains a number of global variables that I would like to use in multiple projects (more will be added in the future).
I include the .WXI file in my main Product.wxs project using the standard syntax like this:
<?include ..\Shared\Common.wxi?>
This is working great and all the variables are available. However as part of this build process I also need to run heat.exe in order to generate a large fragment automatically on each rebuild.
I use a command like this:
"Heat.exe" dir %sourcedir% -sfrag -sreg -suid -gg -ke -template fragment -out %wixfile% -dr INSTALLFOLDER -cg MyInstall -var var.MyFileSource
In my included variables file I have this:
<?define MyFileSource= "$(var.SolutionDir)..\..\..\..\mydir\bindist" ?>
However I get an error like this:
Undefined preprocessor variable '$(var.MyFileSource)'.
As you would expect as the fragment file does not contain a line that references the file Common.wxi but the problem is this fragment file is going to be replaced each build prior to compilation so it's completely inpractical to hand edit this fragment in order to add it each time (not to mention impossible anyway).
My question is am I approaching this completely incorrectly? If not, what is the best way to get this working correctly?
What is confusing is that there is the -var switch to create a variable reference in the fragment file but no way to then enable the variable referencing from an include file (unless I've missed that somewhere in the docs).

Instead of placing the variables in a separate wxi file and include it in all possible places, you can supply candle.exe with the variables you need. You can do this either from command line (using -dVarName=VarValue syntax), or with any of the supported build tools, NAnt or MSBuild.
I can see the following advantages:
the WiX authoring are not polluted with <?include?> stuff here and there
you don't have to mess the heat harvested authoring
variables defined once from the command line are available in any wxs file of your project

I also have such an include-file that centralizes my configuration during the build. As I also use variables in the files produced by heat this is very convenient, as I have to define them just once.
To be able to use these variables also in the heat-output you can transform the output created by heat using a xslt-transformation. Just add -t myTransformFile.xslt to the command-line of heat, so the output will be transformed according to the stylesheet defined in myTransformFile.xslt.
In the myTransformFile.xslt-file just add the following, which will basically copy everything and also add the Include-directive:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns="http://schemas.microsoft.com/wix/2006/wi"
exclude-result-prefixes="wix">
<xsl:template match="wix:Wix">
<xsl:copy>
<!-- The following enters the directive for adding the Common.wxi include file to the dynamically generated file -->
<xsl:processing-instruction name="include">$(sys.CURRENTDIR)..\Shared\Common.wxi</xsl:processing-instruction>
<xsl:apply-templates select="#*" />
<xsl:apply-templates />
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You have to adjust the path to your Common.wxi of course so that it can be found at build-time.
Edit: forgot a part of the XSLT-file that copied also other elements.

Related

Insert a source file in programlisting with xinclude in docbook

I've the following file tree
mainfolder
|-assembly.xml
|
|-chapters
| |-introduction.xml
|
|-source
|-example01
|-main.cpp
assembly.xml is the assembly file of my docbook. It contains a reference to introduction.xml.
This is the assembly:
<?xml version="1.0" encoding="UTF-8"?>
<assembly version="5.1"
xmlns="http://docbook.org/ns/docbook">
<resources xml:base="chapters/">
<resource href="introduction.xml" xml:id="introduction" />
</resources>
<structure xml:id="main-book">
<info>
<description>Book chapters</description>
</info>
<output renderas="book"/>
<module resourceref="introduction"/>
</structure>
</assembly>
introduction.xml is docbook chapter. I want to insert in it an example including the main.cpp.
<?xml version="1.0" encoding="UTF-8"?>
<chapter version="5.1"
xmlns="http://docbook.org/ns/docbook"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xila="http://www.w3.org/2001/XInclude/local-attributes"
xmlns:xi="http://www.w3.org/2001/XInclude"
xmlns:trans="http://docbook.org/ns/transclusion"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:m="http://www.w3.org/1998/Math/MathML"
xmlns:html="http://www.w3.org/1999/xhtml"
xmlns:db="http://docbook.org/ns/docbook">
<info>
<title>Introduction</title>
</info>
<section>
<info>
<title>Chapter title</title>
</info>
<para>
You can see an example:
</para>
<example>
<title>main example</title>
<programlisting language="c++">
<xi:include href="./../source/example01/maiin.cpp" parse="text" />
</programlisting>
</example>
</section>
</chapter>
Anyway, when I build the book (I'm using personal edition of XMLMind), I don't see anything. In the PDF the example is printed, but instead of the source code inside the main.cpp I see
<xi:include></xi:include>
What I'm doing wrong? How can I include the source code from the file?
I've reproduced your situation in a different ways and got things working in a both cases.
You didn't mention the exact steps you make to build a book using XMLMind XML Editor (XXE for short), but the following steps certainly worked well.
None 1: I've used XXE Professional Edition, but as I know the difference between versions is only in putting random character within text if generating PDF from Personal Edition.
Note 2: XXE says that <description>Book chapters</description> element is not allowed within you context. Despite that fact, the assembly can be generated successfully.
1. Getting things working within XXE
Open an assembly file in XXE.
Select Convert > Convert document > Convert to PDF.
Select a path to save pdf file and press OK button.
2. Getting things working manually in command line
Use your exact files and DIR structure (I included my own .c file sample for my own test).
Use XXE assembly processor: http://www.xmlmind.com/xmleditor/assembly.shtml - it has the same codebase that XXE used for working with assemblies from XXE GUI.
Get docbook book file from assembly:assembly-1_0_2_01/bin/assembly -v assembly.xml docbook_book.xml In my test I've got the .c sample file included - that proves that assembly utility works well with XInclude.
Get .pdf manually: fop -c <config_file> -xsl <path_to_docbook_ns_stylesheets>/fo/docbook.xsl -xml docbook_book.xml -pdf docbook_book.pdf
I've got the final result with .c sample included in both cases.

Harvesting multiple directories in WiX

I'm trying to build an installer that includes a number of features and I'm using heat to harvest a directory of files for each feature.
My source directory structure looks something like this:
HarvestDir
\FeatureA
\FeatureImpl.dll
\FeatureImpl2.dll
\FeatureB
\FeatureImpl.dll
\FeatureImpl2.dll
So I execute heat.exe for each feature to create a fragment for each feature but I get basically identical fragments e.g.
[...] Source="SourceDir\FeatureImpl.dll"
[...] Source="SourceDir\FeatureImpl2.dll"
What I really want is something like this:
[...] Source="SourceDir\FeatureA\FeatureImpl.dll"
[...] Source="SourceDir\FeatureA\FeatureImpl2.dll"
and
[...] Source="SourceDir\FeatureB\FeatureImpl.dll"
[...] Source="SourceDir\FeatureB\FeatureImpl2.dll"
I could use -var to specify a separate variable to represent the source location for each feature, but then I'd have to pass values for these variables into the wixproj (and I'm going to have ~10 features).
So, is there any way I can include a relative path in my harvested fragment?
Rather than using heat on each Feature* folder, you could just harvest the whole feature layout folder that you have created and have heat transform the results. I'm assuming that each subfolder is a feature. It's a simple matter of creating a ComponentGroup for each one that references all the components under it.
You'd then use the component groups like this:
<Feature Id="FeatureA">
<ComponentGroupRef Id="FeatureA"/>
</Feature>
<Feature Id="FeatureB">
<ComponentGroupRef Id="FeatureB"/>
</Feature>
Heat command (I actually use the HarvestDirectory target in WiX 3.7):
Heat.exe dir FeatureLayout -dr FeatureLayoutDir -srd -ag -sfrag -t FeatureLayout.xslt -out obj\Debug\__dir.wxs
FeatureLayout.xslt:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="2.0"
xmlns:wix="http://schemas.microsoft.com/wix/2006/wi"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
exclude-result-prefixes="wix"
>
<xsl:output method="xml" indent="yes"/>
<xsl:template match="wix:Wix">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
<xsl:for-each select="wix:Fragment/wix:DirectoryRef/wix:Directory">
<wix:Fragment>
<wix:ComponentGroup Id="{#Name}">
<xsl:for-each select="descendant::wix:Component">
<wix:ComponentRef Id="{#Id}"/>
</xsl:for-each>
</wix:ComponentGroup>
</wix:Fragment>
</xsl:for-each>
</xsl:copy>
</xsl:template>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
You can either assign your source paths in .wixproj in DefineConstants or you can assign them in a WIX include file but either way you will have to use "var" option to specify the variable being used for your sources.
If you want to use DefineConstant in wixproj then you will have to do something like
<Target Name="BeforeBuild">
<PropertyGroup>
<DefineConstants>BINFOLDER=..\PATH\TO\SOURCE\$(Configuration)</DefineConstants>
</PropertyGroup>
<HeatDirectory OutputFile="output.wxs" Directory="..\PATH\TO\SOURCE\$(Configuration)" DirectoryRefId="INSTALLFOLDER" ComponentGroupName="COMPONENTGROUPNAME" ToolPath="$(WixToolPath)" PreprocessorVariable="var.BINFOLDER" />
</Target>
In Heat task make sure you add any additional attributes that you might need. In case you have added a reference to your source project in the .wixproj then you may directly use the project reference variables in the "PreprocessorVariable" attribute. For example instead of var.BINFOLDER use var.MyProject.TargetDir.
If you want to use a WIX include file (.wxi) then declare your variables in an include file for example Variables.wxi:
<?xml version="1.0" encoding="UTF-8"?>
<Include>
<?define PATH1="PATH\TO\SOURCE"?>
<?define PATH2="$(var.PATH1)\$(Configuration)"?>
</Include>
Now you will need to pass in PATH1 and PATH2 using the -var in the heat command line. Once you have your .wxs file you will then need to include the Variables.wxi file in that using
<?include Variables.wxi ?>
in your .wxs. The problem with this way is that you will have to either add this include directive manually each time you generate your WIX source files or you will have to inject it using XSL Transformation.
hit properties for the setup project, this is the command i'm using:
"%WIX%\bin\heat.exe" dir "$(SolutionDir)\Export\Release" -suid -dr bin -srd -cg ExportComponentGroup -var var.sourcePath -ag -sreg -out "$(SolutionDir)\SetupProject\AppExportDir.wxs"
the only thing else you need to define in the build tab is:
check the check box for "define 'debug'preprocessor variable" and enter.
sourcePath=%SystemDrive%\App\Main\Export\Release\
this please make sure that you use the flags that you want like suid or -dr
for more information about the flags you have see here:
http://wix.sourceforge.net/manual-wix3/heat.htm

Wix Include file (.wxi) throws exception

I am a beginner in Wix and we are trying to migrate from Installshield to Wix. However I am stuck with an error which I am unable to resolve. I have done my share of research online before posting this message and I am hoping to get some help from you experts in case someone had a similar problem and would be kind enough to point out the silly mistake I am making here.
Here is my Wix include file: properties.wxi
<Include>
<?define Language="1033"?>
<?define Manufacturer="ABC Inc"?>
<?define Name="TRIAL-MSI"?>
<?define UpgradeCode="....GUID...."?>
<?define Version="09.00.0021"?>
<?define Comments="Contact: team#abc.com"?>
<?define Description="TRIAL Application"?>
</Include>
And I am calling it in my code as follows:
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<?include properties.wxi ?>
<Product Id="*"
Name="${var.Name}"
Language="${var.Language}"
Manufacturer="${var.Manufacturer}"
UpgradeCode="${var.UpgradeCode}"
Version="${var.Version}" >
<Package Comments="${var.Contact}"
Description="${var.Description}"
InstallerVersion="200"
Keywords="Installer,MSI,Database"
Languages="${var.Language}"
Manufacturer="${var.Manufacturer}"
Compressed="yes"
Platform="x86" />
I am compiling my script on the command line:
candle -arch x86 -I properties.wxi trial.wxs
I keep getting errors as follows:
error CNDL0048 : The document element name 'Include' is invalid. A Windows Installer XML source file must use 'Wix' as the document element name.Source trace:
And I guess because Candle did not accept the include file, it throws exception for:
error CNDL0008 : The Product/#Language attribute's value, '${var.Language}', is not a legal integer value.
Could someone please help me with this? Any help is greatly appreciated.
I had this error when migrating wxs files to wxi
Setting the files Build Action property in Visual studio from Compile to Content fixed it.
The -I flag to candle is used to specify a directory to search for include files:
usage: candle.exe [-?] [-nologo] [-out outputFile] sourceFile
[sourceFile ...] [#responseFile]
-I add to include search path
The Wix preprocessor will automatically look in the directory of the current source file for include files so there is no reason to specify the include file on the command line. Your command line should only include the Wix source files:
candle -arch x86 trial.wxs
UPDATE:
Wix variables are inserted using $(var.VARIABLENAME). You have all of your variables surrounded with curly braces instead of parentheses.

Better Ways to Maintain Continuous Integration?

I find that I am always tuning and tweaking our CI setup as we add new projects. While there is NO question that the benefits are awesome for existing code that seldom changes, new projects or volitile ones seem to require more work as I have to configure each project to be "intergrated" as well as maintain an ever-growing CCNET.config file. Is there a better strategy short of building an utility to manage adding and modifying a CI setup?
I do a few things to try keep it under control:
1) Split the config file into two. I have one file that mostly stays the same and contains a set of constants e.g.
<?xml version="1.0" encoding="utf-8"?>
<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<!-- Constant definition used by the projecct config to prevent changes being required for each iteration -->
<cb:define branch="branch name for source control"/>
<cb:define ciserver="Server name in here"/>
<cb:define devenv="Path to DEVENV"/>
<cb:define nunit="Path to NUNIT"/>
<cb:define cruisecontrol="Cruisecontrol Path"/>
<!-- Include file to the standard CI project definitions. This file is kept under source control -->
<cb:include href="config\CCProjects.config"/>
</cruisecontrol>
The use of constants allows you to make a single change and have it propagate through each task in the config file.
See docs
2) Keep the file with the projects in under source control. The project file gets updated as part of the SVN checkout. This helps track changes that get made and let you rollback without too much hassle.
Maybe it has got to the point where CC.Net is working against you rather than for you. I've heard good things about the ease of configuration of other CI servers, like Hudson, but it may not be a good fit with your build environment.
1/ Split your config file as you want
For example, I have a constants section, and each project is an include, so I can update each project quite independently and use constants across projects.
ccnet.config
<cruisecontrol xmlns:cb="urn:ccnet.config.builder">
<!-- Shared constants -->
<cb:define WorkingFolderBase="D:\dev\ContinuousIntegration\WC" />
<cb:define ArtifactFolderBase="D:\dev\ContinuousIntegration\Artifact" />
<cb:define ConfigFolder="projects" />
<cb:define SvnBasePath="http://myserver.com/svn" />
<cb:define SvnUsername="Myusername" />
<cb:define SvnPassword="MyPassword" />
<!-- MyProject1 -->
<cb:include href="projects/MyProject1.config"/>
<!-- MyProject2 -->
<cb:include href="projects/MyProject2.config"/>
</cruisecontrol>
MyProject1.config
<project name="MyProject1" queue="Q1" queuePriority="1">
<artifactDirectory>$(ArtifactFolderBase)\MyProject1</artifactDirectory>
<workingDirectory>$(WorkingFolderBase)\MyProject1</workingDirectory>
<!-- SVN implementation -->
<sourcecontrol type="svn" username="$(SvnUsername)" password="$(SvnPassword)">
<trunkUrl>$(SvnBasePath)/MyProject1/trunk/</trunkUrl>
<workingDirectory>$(WorkingFolderBase)\MyProject1</workingDirectory>
</sourcecontrol>
[...]
</project>
2/ Use version control on CC.NET (I recommand on the whole installation) or on config files.
3/ Keep it simple! have all actions executed by a batch file (Compile applciation, compile tests, run tests, get coverage, static analyser, generate reports ...).

CruiseControl.net, msbuild, /p:OutputPath and CCNetArtifactDirectory

I'm trying to setup CruiseControl.net at the moment. So far it works nice, but I have a Problem with the MSBuild Task.
According to the Documentation, it passes CCNetArtifactDirectory to MSBuild. But how do I use it?
I tried this:
<buildArgs>
/noconsolelogger /p:OutputPath=$(CCNetArtifactDirectory)\test
</buildArgs>
But that does not work. In fact, it kills the service with this error:
ThoughtWorks.CruiseControl.Core.Config.Preprocessor.EvaluationException: Reference to unknown symbol CCNetArtifactDirectory
Documentation is rather sparse, and google und mainly offers modifying the .sln Project file, which is what I want to avoid in order to be able to manually build this project later - I would really prefer /p:OutputPath.
The CCNetArtifactDirectory is passed to the MSBuild by default, so you dont need to worry about it. MSBuild will place the build output in the "bin location" relevant to the working directory that you have specified.
<executable>c:\WINDOWS\Microsoft.NET\Framework\v3.5\MSBuild.exe</executable>
<workingDirectory>C:\data\projects\FooSolution\</workingDirectory>
<projectFile>FooSolution.sln</projectFile>
<buildArgs>/noconsolelogger /p:Configuration=Debug </buildArgs>
So in the above example your build output will be put in C:\data\projects\FooSolution[ProjectName]\bin\Debug. Should you want to output to a different location you may want to look at of the tag in CCNET.
<publishers>
<xmllogger />
<buildpublisher>
<sourceDir>C:\data\projects\FooSolution\FooProject\bin\Debug</sourceDir>
<publishDir>C:\published\FooSolution\</publishDir>
<useLabelSubDirectory>false</useLabelSubDirectory>
</buildpublisher>
</publishers>
This will allow you to publish your output to a different location.
You can use the artifact directory variable inside the MSBuild script itself. Here's an example of how I'm running FxCop right now from my CC.Net MSBuild script (this script is what CC.Net points to - there is also a "Build" target in the script that includes an MSBuild task against the SLN to do the actual compilation):
<Exec
Command='FxCopCmd.exe /project:"$(MSBuildProjectDirectory)\FXCopRules.FxCop" /out:"$(CCNetArtifactDirectory)\ProjectName.FxCop.xml"'
WorkingDirectory="C:\Program Files\Microsoft FxCop 1.35"
ContinueOnError="true"
IgnoreExitCode="true"
/>
Parameters like CCNetArtifactDirectory are passed to external programs using environment variables. They are available in the external program but they aren't inside CCNET configuration. This often leads to confusion.
You can use a preprocessor constant instead:
<cb:define project.artifactDirectory="C:\foo">
<project>
<!-- [...] -->
<artifactDirectory>$(project.artifactDirectory)</artifactDirectory>
<!-- [...] -->
<tasks>
<!-- [...] -->
<msbuild>
<!-- [...] -->
<buildArgs>/noconsolelogger /p:OutputPath=$(project.artifactDirectory)\test</buildArgs>
<!-- [...] -->
</msbuild>
<!-- [...] -->
</tasks>
<!-- [...] -->
</project>