0

I am generating a .wxs file using Wix Toolset v4, but it's producing an invalid output. It's trivial to fix manually, but I'd like to fix it automatically if possible.

It produces XML of this form:

<!-- Starting XML -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
    <Fragment>
        <DirectoryRef />
    </Fragment>
    <Fragment>
        <ComponentGroup>
            <Component>
                <File />
                <RegistryValue />
                <TypeLib>
                    <!-- Child Interface nodes. -->
                </TypeLib>
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

And I would like to transform it, using an XSLT stylesheet applying during the call to heat.exe into this:

<!-- Desired XML -->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
    <Fragment>
        <DirectoryRef />
    </Fragment>
    <Fragment>
        <ComponentGroup>
            <Component>
                <File>
                    <TypeLib Language="0">
                        <!-- Child Interface nodes. -->
                    </TypeLib>
                </File>
            </Component>
        </ComponentGroup>
    </Fragment>
</Wix>

Which accomplishes three tasks:

  1. Removes the RegistryValue node.
  2. Adds the Language="0" attribute to the TypeLib node.
  3. Moves the TypeLib node, a sibling of the File node, into the File node as a child.

I have a stylesheet that accomplishes 1 and 2, but I have no idea how to do 2 and 3 at the same time:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:wix="http://wixtoolset.org/schemas/v4/wxs">

    <xsl:output method="xml" indent="yes" />

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

    <!-- Remove the RegistryValue attribute. -->
    <xsl:template match="wix:RegistryValue" />

    <!-- Add the missing Language="0" attribute to the TypeLib component. -->
    <xsl:template match="wix:TypeLib" name="add-language">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="Language">0</xsl:attribute>
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>

    <!-- Select the file node and make the typelib node a child. -->
    <xsl:template match="wix:File">
        <xsl:copy>
            <xsl:apply-templates select="@*" />
            <xsl:call-template name="add-language" />
            <!--<xsl:copy-of select="following-sibling::wix:TypeLib[1]" />-->
            <xsl:apply-templates select="node()" />
        </xsl:copy>
    </xsl:template>
    <xsl:template match="wix:TypeLib[preceding-sibling::*[1][self::wix:File]]" />
</xsl:stylesheet>
Kira
  • 3
  • 1

2 Answers2

0

Here is one way you could look at it:

XSLT 1.0

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wix="http://wixtoolset.org/schemas/v4/wxs">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>

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

<!-- Remove the RegistryValue attribute. -->
<xsl:template match="wix:RegistryValue" />

<!-- Add the missing Language="0" attribute to the TypeLib component. -->
<xsl:template match="wix:TypeLib" mode="moved">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:attribute name="Language">0</xsl:attribute>
        <xsl:apply-templates select="node()"/>
    </xsl:copy>
</xsl:template>

<!-- move  TypeLib into File -->
<xsl:template match="wix:File">
    <xsl:copy>
        <xsl:apply-templates select="@*"/>
        <xsl:apply-templates select="../wix:TypeLib" mode="moved"/>
    </xsl:copy>
</xsl:template>

<!-- remove TypeLib in original place -->
<xsl:template match="wix:TypeLib"/>

</xsl:stylesheet>
michael.hor257k
  • 113,275
  • 6
  • 33
  • 51
0

There's a few ways to do something like this, a typical "tweaking" stylesheet (a stylesheet that copies its input, with a few modifications). Here's how I'd do it:

  • Use an identity template, obviously.

  • When inserting an element, write a template that matches the element's parent, and insert the new element as a new child.

  • When moving an element, write a template that matches the new parent, and insert it as a child (as in the previous bullet point), and also write a template that matches the old parent element, and elide it from that element's children by applying templates to the child nodes except for the element being moved.

  • When an element is simply being deleted and not moved somewhere else, you can just write an empty template that matches the element being deleted (as you've done). But here since I already had a template to handle the Component element and elide its TypeLib, I chose to just skip over the RegistryValue element in the same way.

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0"
  xmlns:wix="http://wixtoolset.org/schemas/v4/wxs">

    <xsl:output method="xml" indent="yes" />

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

    <!-- Add the missing Language="0" attribute to the TypeLib element -->
    <xsl:template match="wix:TypeLib">
        <xsl:copy>
            <xsl:apply-templates select="@*"/>
            <xsl:attribute name="Language">0</xsl:attribute>
            <xsl:apply-templates/>
        </xsl:copy>
    </xsl:template>

    <!-- Match a File element and insert its following sibling TypeLib as a child. -->
    <xsl:template match="wix:File">
        <xsl:copy>
            <xsl:apply-templates select="
                @* |
                child::node() | 
                following-sibling::wix:TypeLib
            "/>
        </xsl:copy>
    </xsl:template>
    
    <!-- Match the Component element and elide the TypeLib -->
    <xsl:template match="wix:Component">
        <xsl:copy>
            <xsl:apply-templates select="
                @* | 
                child::node()[not(self::wix:TypeLib | self::wix:RegistryValue)]
            "/>
        </xsl:copy>
    </xsl:template>
    
</xsl:stylesheet>
Conal Tuohy
  • 2,561
  • 1
  • 8
  • 15