Wix Heat-使用XSLT用已知的字符串替换自动生成的GUID [英] Wix Heat - Replace an autogenerated GUID with known string using XSLT

查看:110
本文介绍了Wix Heat-使用XSLT用已知的字符串替换自动生成的GUID的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将Windows Service VDPROJ迁移到WiX.

I'm working on migrating a Windows Service VDPROJ to WiX.

我能够使用HEAT将Windows Service项目中的输出收获成一个片段.当前,为了使我的自定义操作正常工作,我将一些由Heat生成的文件生成的GUID手动更改为在Product.wxs主目录中引用的已知字符串.

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.

由于需要将WiX项目集成到我们的连续构建服务器中,因此我需要在每个构建中都以编程方式执行此操作,而不是依靠手动干预.

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.

根据我的研究,我可以对HEAT的输出使用XSLT转换来实现所需的功能,但是我很难使我的XSLT转换正常工作.

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.

这是生成的片段的一部分,不使用XSLT转换

Here is a section of the generated fragment without using the XSLT transform

<?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>

我将HEAT prebuild命令修改为:

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"

并编写了以下XSLT以实现两件事:

and wrote the following XSLT to achieve two things:

  • 将所有出现的"ComponentIdINeedToReplace"替换为已知字符串(有两个)
  • 将"FileIdINeedToReplace"的一次出现替换为已知字符串
<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>

如何修改XSLT以实现所需?

How can I modify my XSLT to achieve what I need?

推荐答案

恐怕您的XSLT存在许多问题.第一个与名称空间有关.在您的wix XML文件中,所有元素都在名称空间" http://schemas.microsoft.com/wix/2006/wi ",但是XSLT中没有提及该命名空间,这意味着您尝试匹配命名元素的地方,它将仅匹配NO命名空间中的元素,而wix文件不是这种情况.

You've got a number of problems with your XSLT, I am afraid. The first one is to do with namespaces. In your wix XML file, all the elements are within the namespace "http://schemas.microsoft.com/wix/2006/wi", but there is no mention of that namespace in the XSLT, meaning where you try to match a named element, it will only match ones which are in NO namespace, which is not the case for your wix file.

您需要为XSLT添加相应的名称空间声明,就像这样

What you need to add a corresponding namespace declaration to you XSLT, like so

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wi="http://schemas.microsoft.com/wix/2006/wi">

然后,在其中引用命名元素的地方,使用此声明的前缀为它们添加前缀.例如,与模板匹配的文件将如下所示:

Then, where you reference named elements, prefix them with this declared prefix. For example, your template matching File would look like this

<xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">

现在,您在此模板中也遇到了问题,因为您要做的第一件事是输出属性,但是此时您实际上尚未复制 File 元素,因此您可能会收到一个错误消息,因为它没有任何要向其中添加属性的内容.因此,模板可能需要像这样

Now, you've also got a problem within this template, because the first thing you do is output an attribute, but you've not actually copied the File element at this point, so you will probably get an error as it won't have anything to add the attributes to. The template, therefore, probably needs to look like this

<xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">
   <xsl:copy>
      <xsl:attribute name="Id">Windows_Service_exe</xsl:attribute>
      <xsl:copy-of select="@*[name()!='Id']"/>
      <xsl:apply-templates />
   </xsl:copy>
</xsl:template>

(我在这里添加了 xsl:apply-templates ,但是在这种情况下,如果 File 元素从来没有子元素,则可能没有必要)

(I've added in an xsl:apply-templates here, but it is probably unnecessary in this case, if File elements don't ever have child elements)

以前的模板也有问题

<xsl:template match="node[@Id=vIdToReplace]">

我猜您在这里打算使用"node()"而不是"node",因为"node"本身实际上是在寻找一个没有"node"元素.但是主要的问题是将 @Id vIdToReplace 进行比较.在这种情况下,当您确实想将其与变量进行比较时,它将查找名为 vIdToReplace 的元素.正确的语法是 $ vIdToReplace

I am guessing you meant to use "node()" and not "node" here, as "node" itself would literally look for an element called "node" of which there are none. But the main problem is where you compare @Id with vIdToReplace. In this case, it is looking for an element called vIdToReplace, when you really want to compare it with your variable. The correct syntax is $vIdToReplace

<xsl:template match="node()[@Id=$vIdToReplace]">

但是等等!如果您使用的是XSLT 1.0,则会收到错误消息.您不能在XSLT 1.0中像这样在模板匹配中使用变量.它仅在XSLT 2.0中有效.不过,您可以做的就是将您的长表达式粘贴到模板匹配项中:

But wait! If you are using XSLT 1.0, you will get an error. You cannot use variables in template matching like this in XSLT 1.0. It only works in XSLT 2.0. What you could do though, is simply paste in your long expression into the template match:

<xsl:template match="node()[@Id=//wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]/@Id]">

但这看起来很笨拙.另外,您可以定义一个键来查找包含要替换ID的 Component 元素:

But this looks rather ungainly. Alternatively, you could define a key to look up the Component element containing the id you wish to replace:

<xsl:key name="vIdToReplace"
         match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]"
         use="@Id"/>

然后您可以在模板匹配中使用此键,以检查当前@Id是否存在 Component 元素

Then you can use this key in the template match, to check if the Component elements exists for the current @Id

<xsl:template match="node()[key('vIdToReplace', @Id)]">

这里是完整的XSLT

Here is the full XSLT in this case

<xsl:stylesheet version="1.0" 
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:wi="http://schemas.microsoft.com/wix/2006/wi">
  <xsl:key name="vIdToReplace"
  match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component[wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]]"
  use="@Id"/>

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

  <xsl:template match="node()[key('vIdToReplace', @Id)]">
    <xsl:copy>
      <xsl:attribute name="Id">C_Windows_Service_exe</xsl:attribute>
      <xsl:copy-of select="@*[name()!='Id']"/>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="wi:ComponentGroup[@Id='CG.WinSvcContent']/wi:Component/wi:File[contains(@Source,'Windows.Service.exe') and not(contains(@Source,'config'))]">
     <xsl:copy>
        <xsl:attribute name="Id">Windows_Service_exe</xsl:attribute>
        <xsl:copy-of select="@*[name()!='Id']"/>
        <xsl:apply-templates />
     </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

应用于XML时,将输出以下内容

When applied to your XML, the following is output

  <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
  [...]
    <Fragment>
      <ComponentGroup Id="Windows.Service.Binaries">
        <ComponentRef Id="C_Windows_Service_exe"/>
        [...]
      </ComponentGroup>
    </Fragment>
    [...]
    <Fragment>
      <ComponentGroup Id="CG.WinSvcContent">
        <Component Id="C_Windows_Service_exe" Directory="TARGETDIR" Guid="{SOMEGUID}">
          <File Id="Windows_Service_exe" Source="$(var.Windows.Service.TargetDir)\Windows.Service.exe"/>
        </Component>
        [...]
      </ComponentGroup>
    </Fragment>
    [...]
  </Wix>

这篇关于Wix Heat-使用XSLT用已知的字符串替换自动生成的GUID的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆