0
votes

After the WiX Toolset Heat tool harvests components from the source directory it generates a wxs file with e.g. the following content.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="TARGETDIR">
            <Directory Id="dirGeneratedID1" Name="MySourceDirName">
                <Component Id="cmpGeneratedID1" Guid="*">
                    <File Id="filGeneratedID1" KeyPath="yes" Source="$(var.SourceRootDir)\File1.dll" />
                </Component>
                <Component Id="cmpGeneratedID2" Guid="*">
                    <File Id="filGeneratedID2" KeyPath="yes" Source="$(var.SourceRootDir)\File2.dll" />
                </Component>
                <Directory Id="dirGeneratedID2" Name="MyNestedDirName">
                    <Component Id="cmpGeneratedID3" Guid="*">
                        <File Id="filGeneratedID3" KeyPath="yes" Source="$(var.SourceRootDir)\MyNestedDirName\File3.dll" />
                    </Component>
                </Directory>
            </Directory>
        </DirectoryRef>
    </Fragment>
</Wix>

My task is to change the value of the Name attribute for parent Directory element only. That is to change the /Wix/Fragment/DirectoryRef/Directory[@Name='MySourceDirName'] element's Name attribute value from 'MySourceDirName' to let's say 'MY_RENAMED_SOURCE_DIR_NAME', so as a result I get the following.

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="TARGETDIR">
            <Directory Id="dirGeneratedID1" Name="MY_RENAMED_SOURCE_DIR_NAME">
                <Component Id="cmpGeneratedID1" Guid="*">
                    <File Id="filGeneratedID1" KeyPath="yes" Source="$(var.SourceRootDir)\File1.dll" />
                </Component>
                <Component Id="cmpGeneratedID2" Guid="*">
                    <File Id="filGeneratedID2" KeyPath="yes" Source="$(var.SourceRootDir)\File2.dll" />
                </Component>
                <Directory Id="dirGeneratedID2" Name="MyNestedDirName">
                    <Component Id="cmpGeneratedID3" Guid="*">
                        <File Id="filGeneratedID3" KeyPath="yes" Source="$(var.SourceRootDir)\MyNestedDirName\File3.dll" />
                    </Component>
                </Directory>
            </Directory>
        </DirectoryRef>
    </Fragment>
</Wix>

Since it's Heat tool that generates the wxs file and the source directory name was 'MySourceDirName' then the Directory elements Name attribute was generated to be equal to 'MySourceDirName', which later needs to be XSL transformed to my desired new folder name 'MY_RENAMED_SOURCE_DIR_NAME'.

I use the following XSLT but it doesn't work and I get error "The DirectoryRef element contains an unexpected attribute 'Name'."

<?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"
                exclude-result-prefixes="wix">

    <xsl:output omit-xml-declaration="no" indent="yes" />
    <xsl:strip-space elements="*"/>

    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory">

      <xsl:attribute name="Name">
        <xsl:choose>
          <xsl:when test=". = 'MySourceDirName'">
            <xsl:text>MY_RENAMED_SOURCE_DIR_NAME</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="." />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>

    </xsl:template>
</xsl:stylesheet

And the resulting wxs file after XSLT becomes a wrong and not complete file:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    <Fragment>
        <DirectoryRef Id="SciemetricDIR" Name="" />
    </Fragment>
</Wix>

If I change my XSLT and add xsl:copy to restore the content as the following (here I show only the affecting rule of the whole XSLT content)

    <xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory">

      <xsl:copy>
        <xsl:apply-templates select="@*|node()" />
      </xsl:copy>

      <xsl:attribute name="Name">
        <xsl:choose>
          <xsl:when test=". = 'MySourceDirName'">
            <xsl:text>MY_RENAMED_SOURCE_DIR_NAME</xsl:text>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="." />
          </xsl:otherwise>
        </xsl:choose>
      </xsl:attribute>

    </xsl:template>

Then I get this error: "Error applying transform C:\path-to-my-transform-file\massageAfterHeatHarvesting.xsl to harvested WiX: Attribute and namespace nodes cannot be added to the parent element after a text, comment, pi, or sub-element node has already been added. MySoulutionName heat.exe 0 "

What is wrong in my XSLT? Why does it touch the DirectoryRef parent element if I clearly target the Directory child element underneath it? Any support is much appreciated. Thanks in advance.

1

1 Answers

1
votes

The XSLT processor comes across the Directory element, and then, as specified, creates a Name attribute and nothing else (it stops).

Try changing

<xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory">

to

<xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory/@Name">

In fact, it's nicer to move the conditional to the match attribute as a predicate:

<xsl:template match="/wix:Wix/wix:Fragment/wix:DirectoryRef/wix:Directory/@Name[.='MySourceDirName']">
  <xsl:attribute name="Name">MY_RENAMED_SOURCE_DIR_NAME</xsl:attribute>
</xsl:template>