为了在msbuild脚本中正确引用像sn
或sqlmetal
这样的工具(我所追求的),以便为大多数人提供有效的工作方式,您必须考虑操作环境和框架实现的不同方面。有两个主要情况:Microsoft Windows和Microsoft的框架实现,以及其他所有内容(我的意思是Mono / unix)。支持我能想到的各种情况的正确方法示例列在结尾处。
Microsoft
在Windows中找到sn
或其他类似工具的正确方法是从GetFrameworkSdkPath任务开始,如已经提到的那样。
然而,正如问题所述,无法直接确定
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>
(参见
属性函数,了解为什么我可以在这里使用
String.TrimEnd
。
WHERE
不喜欢尾随斜杠。编辑:我添加了使用属性函数访问
Regex.Replace()
以删除
SNPath
属性中除第一个找到的路径之外的所有内容。我的朋友的机器上的
WHERE
调用将为某些命令输出多个结果,并破坏任何尝试执行
<Exec/>
的工具。此更改确保仅找到一个结果,并且
<Exec/>
实际成功。)
现在,您可以使用<Exec Command=""$(SNPath)"" />
调用sn
。
便携式
毫不意外,在Windows以外的任何操作系统上,解决sn
的路径要简单得多。在Mac OSX和任何Linux发行版上,我可以在PATH中找到sn
。在这种情况下,使用GetFrameworkSdkPath
并没有帮助;事实上,对于我在使用xbuild时测试的旧版本mono-2.10而言,它似乎返回了一个无法找到sn
的路径:
- 在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.0
,而sn
是/usr/bin/sn
(它是一个shell脚本,用mono
调用/usr/lib64/mono/4.0/sn.exe
)。
因此,我们需要尝试执行
sn
。任何将其
sn
实现放置在非标准位置的Unix用户都知道要适当更新PATH,因此构建脚本无需搜索它。此外,在Unix中不存在
WHERE
。因此,在Unix情况下,我们想要用某些输出仅在Unix上输出
sn
的东西来替换第一个
<Exec/>
调用,并在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 工具的问题。
sn.exe -h
命令获取帮助信息,否则就会崩溃。 - Ocelot20