Question

I'm using heat.exe to harvest Debug (or Release) directory of my visual studio project using this script in post-build event:

call "C:\Program Files (x86)\WiX Toolset v3.8\bin\heat.exe" dir "$(TargetDir)." -var var.WixDemo.TargetDir -dr INSTALLFOLDER -cg Binaries -ag -scom -sreg -sfrag -srd -o "$(SolutionDir)WixSetup\$(ProjectName).Binaries.wxs"

I would get a single installer with two features:

  1. project A

  2. project B

The problem is that this two projects refer the same dll (xyz.dll), so, both harvest processes create a component for this file with same Id.


Edit

This is my output file after harvesting release dir.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLFOLDER">
            <Directory Id="dir325E6775ACBC3561D5CA2F51CFCCC7E8" Name="WixDemoFeature1">
                <Component Id="cmp22AE0957A805CEDAC8D05EF04AE18D5E" Guid="*">
                    <File Id="fil9EDCA091AB8E6440E9D83A815255C794" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\xyz.dll" />
                </Component>
                <Component Id="cmpFCBDB8083076AA83E1B67540ADFECC9D" Guid="*">
                    <File Id="filAA36C3D42B58D430C889BE47087CA911" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\WixDemoFeature1.exe" />
                </Component>
                <Component Id="cmp8BCDCA87324BD346D955DDB4318E80BD" Guid="*">
                    <File Id="fil69700DBD0795710A0AE5DC17574128D5" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\WixDemoFeature1.exe.config" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="WixDemoFeature1Binaries">
            <ComponentRef Id="cmp22AE0957A805CEDAC8D05EF04AE18D5E" />
            <ComponentRef Id="cmpE0CAA29B7BB706D56252E85F7C1DDD0A" />
            <ComponentRef Id="cmp8BCDCA87324BD346D955DDB4318E80BD" />
        </ComponentGroup>
    </Fragment>
</Wix>

and

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLFOLDER">
            <Directory Id="dir325E6775ACBC3561D5CA2F51CFCCC7E8" Name="WixDemoFeature2">
                <Component Id="cmp22AE0957A805CEDAC8D05EF04AE18D5E" Guid="*">
                    <File Id="fil9EDCA091AB8E6440E9D83A815255C794" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\xyz.dll" />
                </Component>
                <Component Id="cmpE0CAA29B7BB706D56252E85F7C1DDD0A" Guid="*">
                    <File Id="fil3A3B754D2B216E86025902C3A826545D" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\WixDemoFeature2.exe" />
                </Component>
                <Component Id="cmpFB159630D6E1604557E20776A46EB6B3" Guid="*">
                    <File Id="fil060CF132A168E46D3C4C1C24CA1AEFF4" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\WixDemoFeature2.exe.config" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="WixDemoFeature2Binaries">
            <ComponentRef Id="cmp22AE0957A805CEDAC8D05EF04AE18D5E" />
            <ComponentRef Id="cmpFCBDB8083076AA83E1B67540ADFECC9D" />
            <ComponentRef Id="cmpFB159630D6E1604557E20776A46EB6B3" />
        </ComponentGroup>
    </Fragment>
</Wix>

These two xml have same ID for component "xyz.dll" and so i can't use both "WixDemoFeature1Binaries" and "WixDemoFeature2Binaries" in my Product.wxs.

I would like to get these instead:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLFOLDER">
            <Directory Id="dir325E6775ACBC3561D5CA2F51CFCCC7E8" Name="WixDemoFeature1">
                <Component Id="cmp_WixDemoFeature1.xyz.dll" Guid="*">
                    <File Id="fil_WixDemoFeature1.xyz.dll" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\xyz.dll" />
                </Component>
                <Component Id="cmp_WixDemoFeature1.WixDemoFeature1.exe" Guid="*">
                    <File Id="fil_WixDemoFeature1.WixDemoFeature1.exe" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\WixDemoFeature1.exe" />
                </Component>
                <Component Id="cmp_WixDemoFeature1.WixDemoFeature1.exe.config" Guid="*">
                    <File Id="fil_WixDemoFeature1.WixDemoFeature1.exe.config" KeyPath="yes" Source="$(var.WixDemoFeature1.TargetDir)\WixDemoFeature1.exe.config" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="WixDemoFeature1Binaries">
            <ComponentRef Id="cmp_WixDemoFeature1.xyz.dll" />
            <ComponentRef Id="cmp_WixDemoFeature1.WixDemoFeature1.exe" />
            <ComponentRef Id="cmp_WixDemoFeature1.WixDemoFeature1.exe.config" />
        </ComponentGroup>
    </Fragment>
</Wix>

and

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="INSTALLFOLDER">
            <Directory Id="dir325E6775ACBC3561D5CA2F51CFCCC7E8" Name="WixDemoFeature2">
                <Component Id="cmp_WixDemoFeature2.xyz.dll" Guid="*">
                    <File Id="fil_WixDemoFeature2.xyz.dll" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\xyz.dll" />
                </Component>
                <Component Id="cmp_WixDemoFeature2.WixDemoFeature2.exe" Guid="*">
                    <File Id="fil_WixDemoFeature2.WixDemoFeature2.exe" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\WixDemoFeature2.exe" />
                </Component>
                <Component Id="cmp_WixDemoFeature2.WixDemoFeature2.exe.config" Guid="*">
                    <File Id="fil_WixDemoFeature2.WixDemoFeature2.exe.config" KeyPath="yes" Source="$(var.WixDemoFeature2.TargetDir)\WixDemoFeature2.exe.config" />
                </Component>
            </Directory>
        </DirectoryRef>
    </Fragment>
    <Fragment>
        <ComponentGroup Id="WixDemoFeature2Binaries">
            <ComponentRef Id="cmp_WixDemoFeature2.xyz.dll" />
            <ComponentRef Id="cmp_WixDemoFeature2.WixDemoFeature2.exe" />
            <ComponentRef Id="cmp_WixDemoFeature2.WixDemoFeature2.exe.config" />
        </ComponentGroup>
    </Fragment>
</Wix>

How can I change this behavior?

Was it helpful?

Solution

The target dir is designed for debugging on the developer machine, not for gathering files to package.

Be that as it may, you have a few options, including:

  • Heat accepts an XSL transform to apply before it emits the .wxs file. With XSL, you can filter out the Component and ComponentRef elements you don't want.
  • Use a project reference for Project A and Project B in WiXSetup and harvest their outputs. You'd then have to harvest xyz.dll separately or hand-write a Component for it. Manually create two Features and reference the xyz Component in both. (I'm assuming the WiXSetup is a Visual Studio project created from the WiX Setup Project template.)
  • Use MSBuild's Copy task to create your package layout and harvest that. The Copy task can create hardlinks instead of actually copying. (Almost all types of Visual Studio projects are MSBuild projects.)

A simplistic XSLT for the updated question:

<?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:output method="xml" indent="yes"/>

  <!-- Preserving the order of attributes only for aesthetic reasons -->

  <xsl:template match="//wix:Component">
    <xsl:variable name="source" select="wix:File/@Source" />
    <xsl:variable
      name="baseId"
      select="concat(substring-before(substring-after($source, '$(var.'),'.TargetDir)'), 
              concat('.', substring-after($source,'\')))" />
    <Component>
      <xsl:attribute name="Id">
        <xsl:value-of select="concat('cmp_', $baseId)"/>
      </xsl:attribute>
      <xsl:attribute name="Guid">*</xsl:attribute>
      <File>
        <xsl:attribute name="Id">
          <xsl:value-of select="concat('fil_', $baseId)"/>
        </xsl:attribute>
        <xsl:attribute name="KeyPath">yes</xsl:attribute>
        <xsl:attribute name="Source">
          <xsl:value-of select="wix:File/@Source"/>
        </xsl:attribute>
      </File>
    </Component>
  </xsl:template>

  <xsl:template match="//wix:ComponentRef">
    <!-- ComponentRef nodes are interleaved with text() nodes. Want positions 2, 4, 6, ... -->
    <xsl:variable name="index" select="(position() - 1) div 2" /> 
    <xsl:variable name="source" select="//wix:Component[$index]/wix:File/@Source" />
    <xsl:variable
      name="baseId"
      select="concat(substring-before(substring-after($source, '$(var.'),'.TargetDir)'), 
              concat('.', substring-after($source,'\')))" />
    <ComponentRef>
      <xsl:attribute name="Id">
        <xsl:value-of select="concat('cmp_', $baseId)"/>
      </xsl:attribute>
    </ComponentRef>
  </xsl:template>

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top