I'm working on migrating a Windows Service VDPROJ to WiX.
I was able to use HEAT to harvest output from my Windows Service project into a fragment. Currently, in order for my custom actions to work properly, I manually change some of the generated GUIDs from the Heat-generated file into known strings that are referenced in the main Product.wxs.
I need to do this programmatically on every build instead of relying on manual intervention since I need to integrate the WiX project into our continuous build server.
From what I could research, I can use an XSLT transform on the output of HEAT to achieve what I need, but I'm having a hard time making my XSLT transform work.
Here is a section of the generated fragment without using the XSLT transform
Fragments\Windows.Service.Content.wxs
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
[...]
<Fragment>
<ComponentGroup Id="Windows.Service.Binaries">
<ComponentRef Id="ComponentIdINeedToReplace" />
[...]
</ComponentGroup>
</Fragment>
[...]
<Fragment>
<ComponentGroup Id="CG.WinSvcContent">
<Component Id="ComponentIdINeedToReplace" Directory="TARGETDIR" Guid="{SOMEGUID}">
<File Id="FileIdINeedToReplace" Source="$(var.Windows.Service.TargetDir)\Windows.Service.exe" />
</Component>
[...]
</ComponentGroup>
</Fragment>
[...]
</Wix>
I modified the HEAT prebuild command to:
"$(WIX)bin\heat.exe" project "$(ProjectDir)\..\Windows.Service\Windows.Service.csproj" -gg -pog Binaries -pog Symbols -pog Content -cg CG.WinSvcContent -directoryid "TARGETDIR" -t "$(ProjectDir)Resources\XsltTransform.xslt" -out "$(ProjectDir)Fragments\Windows.Service.Content.wxs"
and wrote the following XSLT to achieve two things:
- Replace all occurrences of "ComponentIdINeedToReplace" to a known string (there's two)
- Replace single ocurrence of "FileIdINeedToReplace" to a known string
Resources\XsltTransform.xslt
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable
name="vIdToReplace"
select="//ComponentGroup[@Id='CG.WinSvcContent']/Component/File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]/../@Id" />
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()" />
</xsl:copy>
</xsl:template>
<xsl:template match="node[@Id=vIdToReplace]">
<xsl:copy-of select="@*[name()!='Id']"/>
<xsl:attribute name="Id">C_Windows_Service_exe</xsl:attribute>
</xsl:template>
<xsl:template
match="//ComponentGroup[@Id='CG.WinSvcContent']/Component/File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">
<xsl:copy-of select="@*[name()!='Id']"/>
<xsl:attribute name="Id">Windows_Service_exe</xsl:attribute>
</xsl:template>
</xsl:stylesheet>
How can I modify my XSLT to achieve what I need?