我需要在构建完成后重新签署我的程序集(并且我已经对它做了一些其他的事情),所以我开始添加一个名为 <Exec>
的 C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\sn.exe
任务。这必须适用于其他开发人员/环境,所以我希望我可以从该文件夹中复制 sn.exe
和 sn.exe.config
并将其存储在我们的代码存储库中,以便我始终可以从已知位置调用它的通用版本。sn.exe
在 sdk 目录之外单独崩溃,所以我想知道如何在不知道它将位于什么路径下的情况下引用它。不同的人有不同的环境(x86 与 x64,不同的安装目录,不同的版本),所以我希望能够轻松引用该工具的最新版本(或者任何版本)。似乎是一个足够简单的工具,也许还有另一种方法可以使用另一个工具/命令/msbuild 任务来签署程序集?任何帮助,将不胜感激。
最佳答案
要以适合大多数人的方式在 msbuild 脚本中正确引用 sn
或 sqlmetal
(我所追求的)等工具,您必须考虑操作环境和框架实现的不同方面。主要有两种情况:Microsoft Windows 和 Microsoft 的框架实现,然后是其他所有内容(我指的是 Mono/unix)。最后列出了支持我能想到的情况的正确方法的示例。
微软
在 Windows 中找到 sn
或其他类似工具所在位置的正确方法是从 GetFrameworkSdkPath task 开始,如 already mentioned 。
但是,正如问题所暗示的那样,无法直接确定 sn
或其他工具所在的 FrameworkSdkPath 中的确切位置。引用的答案表明 FrameworkSdkPath 下唯一可能用于工具驻留的文件夹是 bin
和 bin/NETFX 4.0 Tools
。但是,其他值也是可能的(Visual Studio 2013 Preview 使用 bin/NETFX 4.5.1 Tools
)。因此,搜索 sn
的唯一正确方法是使用 glob 表达式或递归搜索它。我无法弄清楚如何使用 MSBuild 进行 glob 扩展,并且内置的 MSBuild 任务似乎不支持在 FrameworkSdkPath 下搜索特定实用程序。但是cmd的 WHERE
有这个功能,可以用来做搜索。结果类似于以下 msbuild 代码:
<Target Name="GetSNPath" BeforeTargets="AfterBuild">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
</GetFrameworkSdkPath>
<Exec Command="WHERE /r "$(WindowsSdkPath.TrimEnd('\\'))" sn > sn-path.txt" />
<ReadLinesFromFile File="sn-path.txt">
<Output TaskParameter="Lines" PropertyName="SNPath"/>
</ReadLinesFromFile>
<Delete Files="sn-path.txt" />
<PropertyGroup>
<SNPath>$([System.Text.RegularExpressions.Regex]::Replace('$(SNPath)', ';.*', ''))</SNPath>
</PropertyGroup>
</Target>
(请参阅 Property Functions 以了解为什么我可以在这里使用
String.TrimEnd
。WHERE
不喜欢尾部斜杠。编辑:我添加了使用属性函数来访问 Regex.Replace()
以删除除 SNPath
属性中第一个找到的路径之外的所有路径。我 friend 的一台机器的 WHERE
调用将为某些命令输出多个结果,并破坏了对 fon 工具进行 <Exec/>
的任何尝试。此更改确保仅找到一个结果并且 <Exec/>
s 实际上成功。)现在您可以使用
sn
调用 <Exec Command=""$(SNPath)"" />
。便携的
不出所料,在 Windows 以外的任何操作系统上解析
sn
的路径都要简单得多。在 Mac OSX 和任何 Linux 发行版上,我在 PATH 中找到了 sn
。在这种情况下,使用 GetFrameworkSdkPath
无济于事;实际上,这似乎返回了一个找不到 sn
的路径,至少对于我在使用 xbuild 时测试的旧版 mono-2.10 而言:FrameworkSdkPath
是/Library/Frameworks/Mono.framework/Versions/2.10.5/lib/mono/2.0
,而/usr/bin/sn
是/Library/Frameworks/Mono.framework/Commands/sn
的符号链接(symbolic link)。 FrameworkSdkPath
是 /usr/lib64/mono/2.0
,sn
是 /usr/bin/sn
(这是一个 shell 脚本,使用 /usr/lib64/mono/4.0/sn.exe
调用 mono
)。 因此,我们需要做的就是尝试执行
sn
。任何将他们的 sn
实现放置在非标准位置的 unix 用户都知道要适本地更新 PATH,因此构建脚本无需搜索它。此外,WHERE
在 unix 中不存在。因此,在unix的情况下,我们希望将第一个<Exec/>
调用替换为仅在unix上输出sn
并在Windows上运行时仍进行完整搜索的东西。为了区分类 Unix 和 Windows 环境,我们使用了一个技巧,它利用了 unix shell 的 true
命令和 cmd 标签语法的快捷方式。作为一个简短的示例,以下脚本将在 unix shellout 中输出 I’m unix!
,在 Windows shellout 中输出 I’m Windows :-/
。:; echo 'I’m unix!'; exit $?
echo I’m Windows :-/
利用这一点,我们生成的
GetSNPath
任务如下所示:<Target Name="GetSNPath" BeforeTargets="AfterBuild">
<GetFrameworkSdkPath>
<Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
</GetFrameworkSdkPath>
<Exec Command=":; echo sn > sn-path.txt; exit $?
WHERE /r "$(WindowsSdkPath.TrimEnd('\\'))" sn > sn-path.txt" />
<ReadLinesFromFile File="sn-path.txt">
<Output TaskParameter="Lines" PropertyName="SNPath"/>
</ReadLinesFromFile>
<Delete Files="sn-path.txt" />
<PropertyGroup>
<SNPath>$([System.Text.RegularExpressions.Regex]::Replace('$(SNPath)', ';.*', ''))</SNPath>
</PropertyGroup>
</Target>
结果是一种可移植的方法,用于查找调用
sn
所需的字符串。最后一个解决方案让您可以同时支持 Microsoft 及其 msbuild 以及使用 xbuild 的所有其他平台。它还克服了将 bin\NETFX 4.0 Tools
硬编码到 .csproj 文件的问题,以同时支持 Microsoft 工具的 future 和当前版本。关于c# - 我应该如何在 msbuild 脚本中引用 sn.exe?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/12781870/