如何在Wix工具集中排除文件

22

在收集heat.exe文件时,我想要从输入文件夹中排除扩展名为.exe的文件,因为它会首先获取文件夹中的所有文件。

以下是我的代码。

    %WIX_PATH%\Heat.exe" dir "%input_folder%" -cg SourceProjectComponents 
    -dr INSTALLLOCATION -scom -sreg -srd -var var.BasePath -gg -sfrag 
    -var var.BasePath -out "%output_folder%\Output.wxs

PS:input_folder包含多个.dll和.exe文件,因此无法单独获取每个文件。

提前致谢。

5个回答

39
你需要使用XSLT转换。以下内容应该对你有所帮助;只需在heat的命令行中包含-t <XSLT文件路径>即可。
这个XSLT会输出一个新的XML文件,其中包含输入的所有XML节点,除非任何节点是带有.exe <File>元素的<Component>元素。

RemoveExeComponentsTransform.xslt

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

    version="1.0" 
    exclude-result-prefixes="xsl wix"
>

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

    <xsl:strip-space elements="*" />

    <!--
    Find all <Component> elements with <File> elements with Source="" attributes ending in ".exe" and tag it with the "ExeToRemove" key.

    <Component Id="cmpSYYKP6B1M7WSD5KLEQ7PZW4YLOPYG61L" Directory="INSTALLDIR" Guid="*">
        <File Id="filKUS7ZRMJ0AOKDU6ATYY6IRUSR2ECPDFO" KeyPath="yes" Source="!(wix.StagingAreaPath)\ProofOfPEqualsNP.exe" />
    </Component>

    Because WiX's Heat.exe only supports XSLT 1.0 and not XSLT 2.0 we cannot use `ends-with( haystack, needle )` (e.g. `ends-with( wix:File/@Source, '.exe' )`...
    ...but we can use this longer `substring` expression instead (see https://github.com/wixtoolset/issues/issues/5609 )
    -->
    <xsl:key
        name="ExeToRemove"
        match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 3 ) = '.exe' ]"
        use="@Id"
    /> <!-- Get the last 4 characters of a string using `substring( s, len(s) - 3 )`, it uses -3 and not -4 because XSLT uses 1-based indexes, not 0-based indexes. -->

    <!-- We can also remove .pdb files too, for example: -->
    <xsl:key
        name="PdbToRemove"
        match="wix:Component[ substring( wix:File/@Source, string-length( wix:File/@Source ) - 3 ) = '.pdb' ]"
        use="@Id"
    />

    <!-- By default, copy all elements and nodes into the output... -->
    <xsl:template match="@*|node()">
        <xsl:copy>
            <xsl:apply-templates select="@*|node()" />
        </xsl:copy>
    </xsl:template>

    <!-- ...but if the element has the "ExeToRemove" key then don't render anything (i.e. removing it from the output) -->
    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'ExeToRemove', @Id ) ]" />

    <xsl:template match="*[ self::wix:Component or self::wix:ComponentRef ][ key( 'PdbToRemove', @Id ) ]" />

</xsl:stylesheet>

30
使用 XSLT 转换进行过滤的方法非常困难且不直观,以至于它让人联想到“物质崇拜编程”。 - ceztko
5
我修改了这个答案,使用了XSLT 1.0的 ends-with 风格匹配,而不是使用 contains(..., '.exe'),因为否则 app.config 文件(例如 Foo.Bar.exe.config)将被删除,这可能不是你想要的,因为这将导致绑定重定向被删除,例如(这发生在我身上,花费了很长时间才弄清楚为什么!) - Dai

9
如果您正在使用 WiX 工具集 4.0 版本:
在您设置正确的命名空间之前,xsl 过滤器将无法工作(xmlns:wix="http://wixtoolset.org/schemas/v4/wxs")。我曾经升级从 3.11 到 4.0 的情况中,花费了我数小时才发现为什么过滤器根本不起作用。这种情况既出现在 VS 解决方案中,也出现在命令行(heat.exe)版本中。
希望这可以帮助到某些人。

谢谢你的回答,我过去一个小时一直为这个问题感到非常沮丧,结果原来就是这个问题。真是让人抓狂,就像 WiX 的其他方面一样。 - undefined

7
我曾经遇到过同样的问题,需要将大量文件包含在项目的WXS文件中。因此,我编写了一个开源命令行应用程序,可以生成目录结构、文件和组件的XML文件,并通过.wixignore文件忽略文件夹、扩展名、文件等内容。
您可以在这里查看它的IT技术:https://github.com/aykanatm/WixXmlGenerator

4
@Michal Hainc,依我之见,heat.exe不太友好。请让我为您翻译此句话。 - Murat Aykanat
16
就像所有的Wix一样。我仍然不明白它做了什么,而这些东西通过编写一个程序就可以轻松完成100倍。 - Steve Smith

5
这可能非常明显,但最简单的方法是在wix中使用预构建事件创建一个只包含所需文件的目录,然后对该目录运行heat.exe。

是的。在看过所有其他建议之后,这是最简单和最直接的解决方案。 - Sven Döring
是的,谢谢你提醒。在所有更复杂的解决方案中,我甚至没有考虑过这个。 - Oliver

5

我喜欢WiX,但是heat.exe工具比制作它更难使用。你可以编写自己的替代程序。你只需要枚举目录中的文件,然后输出一些XML。以下是一些开始的代码,跳过了.pdb文件:

foreach (string file in Directory.EnumerateFiles(directoryName))
{
    string extension = Path.GetExtension(file) ?? "";
    if (extension.Equals(".pdb", OrdinalIgnoreCase)) continue;
    string relativePath = GetRelativePath(wixProjectDirectoryName, file);
    string guid = Guid.NewGuid().ToString("D").ToUpperInvariant();
    stringBuilder.AppendLine($"<Component Id=\"Comp{fileName}\" Guid=\"{guid}\">");
    stringBuilder.AppendLine($"  <File Source=\"{relativePath}\" />");
    stringBuilder.AppendLine("</Component>");
}

public static string GetRelativePath(string baseDirectoryName, string fileFullPath)
{
    string[] absDirs = baseDirectoryName.Split('\\');
    string[] relDirs = fileFullPath.Split('\\');

    int len = absDirs.Length < relDirs.Length
        ? absDirs.Length
        : relDirs.Length;

    int lastCommonRoot = -1;
    int index;

    for (index = 0; index < len; index++)
    {
        if (absDirs[index] == relDirs[index]) lastCommonRoot = index;
        else break;
    }

    if (lastCommonRoot == -1)
        throw new ArgumentException("No common base");

    var relativePath = new StringBuilder();

    for (index = lastCommonRoot + 1; index < absDirs.Length; index++)
    {
        if (absDirs[index].Length > 0) relativePath.Append("..\\");
    }

    for (index = lastCommonRoot + 1; index < relDirs.Length - 1; index++)
        relativePath.Append(relDirs[index] + "\\");
    
    relativePath.Append(relDirs[relDirs.Length - 1]);

    return relativePath.ToString();
}

我喜欢这个答案,因为作为初学者,我遇到了一些 XSL 的学习曲线和限制问题,最终我选择了这个方案。如果能提供一个链接来说明如何在 Wix 中包含 C# 自定义操作,那就更有帮助了!就像这个链接一样! https://dev59.com/pLHma4cB1Zd3GeqPFg0N - Jody Sowald
1
如果您想在安装程序构建之前使用此功能,可以将其包含在wixproj文件中,如下所示: https://dev59.com/fabja4cB1Zd3GeqPaxfh - Jody Sowald

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接