Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Harvesting multiple directories in WiX

Tags:

wix

wix3.5

heat

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?

like image 249
pheobas Avatar asked Jun 14 '13 12:06

pheobas


2 Answers

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>
like image 102
Tom Blodget Avatar answered Oct 13 '22 11:10

Tom Blodget


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.

like image 23
Syed Ali Avatar answered Oct 13 '22 11:10

Syed Ali