2

Windows サービス VDPROJ を WiX に移行する作業を行っています。

HEAT を使用して、Windows サービス プロジェクトからの出力をフラグメントに収集することができました。現在、カスタム アクションが適切に機能するように、Heat によって生成されたファイルから生成された GUID の一部を、メインの Product.wxs で参照される既知の文字列に手動で変更しています。

WiX プロジェクトを継続的なビルド サーバーに統合する必要があるため、手動の介入に頼るのではなく、ビルドごとにプログラムでこれを行う必要があります。

私が調査できることから、HEAT の出力で XSLT 変換を使用して必要なことを達成できますが、XSLT 変換を機能させるのに苦労しています。

XSLT 変換を使用せずに生成されたフラグメントのセクションを次に示します。

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>

HEAT prebuild コマンドを次のように変更しました。

"$(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 を作成して、2 つのことを達成しました。

  • 「ComponentIdINeedToReplace」のすべての出現箇所を既知の文字列に置き換えます (2 つ存在します)
  • "FileIdINeedToReplace" の単一出現を既知の文字列に置き換えます

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>

必要なものを実現するために XSLT を変更するにはどうすればよいですか?

4

1 に答える 1

10

あなたの XSLT には多くの問題があります。1 つ目は名前空間に関するものです。Wix XML ファイルでは、すべての要素が名前空間「http://schemas.microsoft.com/wix/2006/wi」内にありますが、XSLT にはその名前空間についての言及がありません。名前付き要素の場合、名前空間にない要素にのみ一致します。これは、wix ファイルには当てはまりません。

対応する名前空間宣言を XSLT に追加するために必要なもの

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

次に、名前付き要素を参照する場所で、この宣言されたプレフィックスを前に付けます。たとえば、テンプレート マッチングファイルは次のようになります。

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

さて、このテンプレート内にも問題があります。最初に行うことは属性を出力することですが、この時点では実際にはFile要素をコピーしていないため、おそらくエラーが発生するでしょう。属性を追加するものは何でもあります。したがって、テンプレートはおそらく次のようにする必要があります

<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要素に子要素がない場合、この場合はおそらく不要です)

前のテンプレートにも問題があります

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

ここでは「ノード」ではなく「ノード()」を使用するつもりだったと思います。「ノード」自体は文字通り「ノード」と呼ばれる要素を探しますが、その要素はありません。しかし、主な問題は@IdvIdToReplaceを比較する場所です。この場合、実際に変数と比較したいときに、vIdToReplaceという要素を探しています。正しい構文は$vIdToReplace です

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

ちょっと待って!XSLT 1.0 を使用している場合は、エラーが発生します。XSLT 1.0 では、このようなテンプレート マッチングで変数を使用することはできません。XSLT 2.0 でのみ機能します。ただし、できることは、長い式をテンプレート マッチに貼り付けるだけです。

<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要素を検索するキーを定義することもできます。

<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要素が存在するかどうかを確認できます。

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

この場合の完全な XSLT は次のとおりです。

<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 に適用すると、以下が出力されます。

  <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>
于 2013-10-08T22:29:07.290 に答える