【问题标题】:How should I reference sn.exe in msbuild script?我应该如何在 msbuild 脚本中引用 sn.exe?
【发布时间】:2012-10-08 12:35:07
【问题描述】:

我需要在构建完成后重新签署我的程序集(并且我已经对其进行了一些其他操作),因此我首先添加了一个名为 C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\sn.exe<Exec> 任务。这必须适用于其他开发人员/环境,所以我希望我可以从该文件夹中复制 sn.exesn.exe.config 并将其存储在我们的代码存储库中,这样我就可以始终从已知位置调用它的通用版本。

sn.exe 在 sdk 目录之外单独崩溃,所以我想知道如何在不知道它在什么路径下的情况下引用它。不同的人有不同的环境(x86 vs x64,不同的安装目录,不同的版本),所以我希望能够轻松引用该工具的最新版本(或者可能是任何版本)。似乎是一个足够简单的工具,也许还有另一种方法可以使用另一个工具/命令/msbuild 任务对程序集进行签名?任何帮助将不胜感激。

【问题讨论】:

  • sn.exe 有一个与之关联的配置文件。如果您不向 exe 提供任何参数,我注意到它也会崩溃
  • 我尝试将 sn.exe.config 连同它一起复制,但似乎也不起作用。甚至不让我运行sn.exe -h 以获取帮助信息而不会崩溃。
  • 您可以尝试下载Dependency Walker 看看是否缺少任何依赖项

标签: c# .net msbuild code-signing assembly-signing


【解决方案1】:

要以适合大多数人的方式在 msbuild 脚本中正确引用 snsqlmetal(我所追求的)等工具,您必须考虑操作环境和框架的不同方面执行。主要有两种情况:Microsoft Windows 和 Microsoft 对框架的实现,然后是其他一切(我指的是 Mono/unix)。最后列出了支持我能想到的情况的正确方法的示例。

微软

找到sn 或其他类似工具在Windows 中的位置的正确方法是以GetFrameworkSdkPath task 开头,如already mentioned

但是,正如问题所暗示的那样,无法直接确定 sn 或其他工具在 FrameworkSdkPath 中的确切位置。引用的答案表明,FrameworkSdkPath 下唯一可能的文件夹是工具所在的 binbin/NETFX 4.0 Tools。但是,其他值也是可能的(Visual Studio 2013 Preview 使用 bin/NETFX 4.5.1 Tools)。因此,搜索 sn 的唯一正确方法是使用 glob 表达式或递归搜索它。我无法弄清楚如何使用 MSBuild 进行全局扩展,并且内置的 MSBuild 任务似乎不支持在 FrameworkSdkPath 下搜索特定实用程序。但是,cmd 的WHERE 有这个功能,可以用来做搜索。结果类似于以下 msbuild 代码:

<Target Name="GetSNPath" BeforeTargets="AfterBuild">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
  </GetFrameworkSdkPath>
  <Exec Command="WHERE /r &quot;$(WindowsSdkPath.TrimEnd('\\'))&quot; sn &gt; 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.TrimEndWHERE 不喜欢尾部斜杠。编辑:我添加了使用属性函数来访问 Regex.Replace() 以删除除第一个找到的路径之外的所有路径在SNPath 属性中。我朋友的一台机器的WHERE 调用将为某些命令输出多个结果,并破坏了对喜欢的工具&lt;Exec/&gt; 的任何尝试。此更改确保只找到一个结果并且&lt;Exec/&gt;s实际上成功了。)

现在您可以使用&lt;Exec Command="&amp;quot;$(SNPath)&amp;quot;" /&gt; 调用sn

便携

不出所料,在 Windows 以外的任何操作系统上解析到 sn 的路径都要简单得多。在 Mac OSX 和任何 Linux 发行版上,我在 PATH 中找到 sn。在这种情况下使用 GetFrameworkSdkPath 并没有帮助;事实上,这似乎返回了一条无法找到sn 的路径,至少对于我在使用 xbuild 时测试的 mono-2.10 的旧版本而言:

  • 在 Mac OSX 上,FrameworkSdkPath/Library/Frameworks/Mono.framework/Versions/2.10.5/lib/mono/2.0/usr/bin/sn 是指向 /Library/Frameworks/Mono.framework/Commands/sn 的符号链接。
  • 在某个 Linux 安装中,FrameworkSdkPath/usr/lib64/mono/2.0sn/usr/bin/sn(这是一个使用 mono 调用 /usr/lib64/mono/4.0/sn.exe 的 shell 脚本)。

因此,我们需要做的就是尝试执行sn。任何将他们的sn 实现放在非标准位置的 unix 用户都已经知道要适当地更新 PATH,因此构建脚本不需要搜索它。此外,WHERE 在 unix 中不存在。因此,在 unix 案例中,我们希望将第一个 &lt;Exec/&gt; 调用替换为在 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 &gt; sn-path.txt; exit $?
WHERE /r &quot;$(WindowsSdkPath.TrimEnd('\\'))&quot; sn &gt; 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 工具。

【讨论】:

  • 绝对比我原来的答案更彻底。 +1。
【解决方案2】:

原来有一个名为“GetFrameworkSdkPath”的任务将获取 Windows SDK 位置。从那里,我必须测试 sn.exe 是否直接存在于bin 文件夹中,或者它是否在bin\NETFX 4.0 Tools\ 中。目前看来可靠。

<PropertyGroup>
  <SNExePath>NotSet</SNExePath>
</PropertyGroup>

<!-- Sometimes theres nothing in the WindowsSdkPath dir and there's stuff in a deeper folder called 'NETFX 4.0 Tools'. -->
<Target Name="GetSNPath" BeforeTargets="AfterBuild">
  <GetFrameworkSdkPath>
    <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
  </GetFrameworkSdkPath>
  <PropertyGroup>
    <SNExePath>$(WindowsSdkPath)bin\sn.exe</SNExePath>
  </PropertyGroup>
  <PropertyGroup>
    <SNExePath Condition="!Exists($(SNExePath))">$(WindowsSdkPath)bin\NETFX 4.0 Tools\sn.exe</SNExePath>
  </PropertyGroup>
</Target>  

<Target Name="AfterBuild">
  <Exec Command="$(SNExePath) -R $(TargetPath) $(SignatureFile)" />
</Target>

【讨论】:

    【解决方案3】:

    我目前使用的方法是使用属性函数在框架 SDK 路径下搜索 SN.exe - ala:

    <GetFrameworkSDKPath>
      <Output TaskParameter="Path" PropertyName="DotNetFrameworkDir"/>
    </GetFrameworkSDKPath>
    
    <PropertyGroup>
      <SNPath>$([System.IO.Directory]::GetFiles("$(DotNetFrameworkDir)", "sn.exe", SearchOption.AllDirectories)[0])</SNPath>
    </PropertyGroup>
    

    到目前为止,我只在 Visual Studio 2013 上亲自对此进行了测试,但文档暗示它应该可以在 Visual Studio 2010 上运行。

    【讨论】:

      【解决方案4】:

      您可以在每台引用可执行文件的开发机器上创建一个环境变量,MSBuild 允许您将其作为属性引用。

      因此,通过系统属性的高级选项卡创建环境变量。我通常只创建一个系统环境变量,而不是一个针对当前用户的环境变量。您必须重新启动 Visual Studio 才能将其拾取。

      然后,在 MSBuild 中引用它:

      <Exec Command="$(SnExe)">
      

      其中SnExe 是您定义的环境变量。

      【讨论】:

      • 这不是很便携,需要机器级别的更改才能运行构建。此处的目标是允许以与机器无关的方式从 .NET sdk 中查找工具。
      【解决方案5】:

      $(SDK40ToolsPath) 变量在类似情况下对我有用。这样就不需要知道安装了哪个特定版本的工具,例如:

        <PropertyGroup>
          <XsdExePath>$(SDK40ToolsPath)xsd.exe</XsdExePath>
        </PropertyGroup>
        <Target Name="BeforeBuild">
          <ItemGroup>
            <xsd Include="Objects.xsd" />
          </ItemGroup>
          <Exec Command="&quot;$(XsdExePath)&quot; @(xsd) /c /namespace:Blah.Objects" />
        </Target>
      

      【讨论】:

        【解决方案6】:

        .NET Framwork 工具(如 sn.exe)的路径实际上是使用 GetFramworkSDKPath(如上所述)和(几乎)TargetFrameworkVersion(在项目文件中指定)的组合。您基本上需要从 TargetFrameworkVersion 中删除初始的“v”并将这些部分连接在一起以形成工具的路径,例如

        <Target Name="GetSNPath" BeforeTargets="AfterBuild">
          <GetFrameworkSdkPath>
            <Output TaskParameter="Path" PropertyName="WindowsSdkPath" />
          </GetFrameworkSdkPath>
          <PropertyGroup>
          
           <FrameworkVersion>$([System.Text.RegularExpressions.Regex]::Replace('$(TargetFrameworkVersilon)', 'v', ''))</FrameworkVersion>
            <NETFXVersion>NETFX $(FrameworkVersion) Tools</NETFXVersion>
            <SNPath>$(WindowsSdkPath)bin\$(NETFXVersion)\sn.exe</SNPath>
          </PropertyGroup>
        </Target>
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-10-10
          • 1970-01-01
          • 2021-11-12
          • 1970-01-01
          • 2014-10-03
          • 2018-05-26
          • 1970-01-01
          相关资源
          最近更新 更多