如何在Powershell中运行MSBuild而不生成msbuild.exe进程?

12

我考虑通过直接调用MSBuild程序集(而不是查找MSBuild安装路径并启动msbuild.exe作为子进程)来在Powershell脚本中运行MSBuild。

有没有人这样做过?运行构建的最简单、最直接的方法是什么?您想指出哪种技术的利弊呢?(我特别关注可能因在与脚本相同的进程/应用程序域中运行msbuild而引起的任何问题。)

目前我的想法大致如下:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
[void][Microsoft.Build.BuildEngine.Engine]::GlobalEngine.BuildProjectFile("path/main.proj")
5个回答

14

最简单的嵌入式构建调用(可工作并生成输出)是:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Engine, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
$engine = New-Object Microsoft.Build.BuildEngine.Engine
$engine.RegisterLogger((New-Object Microsoft.Build.BuildEngine.ConsoleLogger))
$engine.BuildProjectFile('fullPath\some.proj')

然而,事实证明在PowerShell (V1) 中直接嵌入MSBuild存在问题:

'MSBUILD : warning MSB4056: The MSBuild engine must be called on
a single-threaded-apartment. Current threading model is "MTA".
Proceeding, but some tasks may not function correctly.'

为什么在一个受管理的环境中工作时,我们仍然要在2009年支付COM税呢?

我的结论是,在PowerShell(V1)中嵌入MSBuild不是一个好主意。为了参考,我也包括了我最终使用的基于进程的方法:

[void][System.Reflection.Assembly]::Load('Microsoft.Build.Utilities.v3.5, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a')
$msbuild = [Microsoft.Build.Utilities.ToolLocationHelper]::GetPathToDotNetFrameworkFile("msbuild.exe", "VersionLatest")
&$msbuild fullPath\some.proj

5

我非常建议您查看PSake

让我引用该页面的一部分:

请记住,psake是围绕PowerShell的语法糖。因此,您可以在psake中执行任何可以在PowerShell中执行的操作。这意味着您可以运行MSBuild、NAnt或其他脚本。没有必要完全替换您当前的构建系统。您可以使用psake来自动化和扩展它!

psake会自动将适当版本的.NET Framework添加到其路径中。因此,您可以访问$env:windir\Microsoft.NET\Framework\$version\中安装的MSBuild、csc.exe、vbc.exe或任何其他工具,而无需使用完全限定路径。


3
请不要陷入“msbuild 不好用,应该使用基于 PowerShell 的构建工具”这个哲学辩论中,因为那不是我的问题。 - Milan Gardian
你在谈论什么哲学辩论?你读了这个页面吗?它提供了一种简单的方法来包装MSBuild。 - EBGreen
是的,我确实阅读了该页面。它在描述这种类似于rake的“无XML括号税”构建工具。我之前使用过rake,但现在已转为使用msbuild,不会再返回去了 :-)psake调用msbuild的机制是将其添加到'path'中 - 即外部进程。这正好是我不想要的。 - Milan Gardian
那就好了。只是想确认这不是一种条件反射。 - EBGreen
对不起,我说话太过刻薄了。我只是感到沮丧,因为我找不到一个合适的描述来使用最新的(3.5)BuildEngine.Engine类(谷歌,SO,MSDN)。MSDN信息已经过时了;它使用的方法在Engine类的3.5实现中已经被弃用了(我使用Reflector进行了调查)。 - Milan Gardian
我建议直接联系詹姆斯看看他能否提供帮助。他在这方面似乎非常有经验。我自己没有直接使用过这个类。 - EBGreen

3

一种不同且可能更易用的方法是创建一个msbuild cmdlet。MsBuild有一个很好的API,并且有许多示例说明如何从编译语言(如C# / VB)中使用它。构建一个cmdlet非常容易,可以为您的powershell脚本提供更好的语法。


3
好的,我的问题是 - 你有没有提供“如何使用它的许多示例”的链接或者自己提供一些? - Milan Gardian

3

谢谢你的建议,Anthony。但是将代码从纯 PowerShell 移动到 cmdlet 只是性能/便利优化。我对直接使用 Microsoft.Build.BuildEngine 的步骤/代码以及注意事项很感兴趣。我可以轻松地在 ps1 和 cmdlet 之间移动实际实现。 - Milan Gardian
我有许多不同地方的msbuild脚本,我希望能够从一个中心位置运行某些东西。如果您最终得到了一个可行的答案,我很乐意看到一篇博客文章或其他类似内容。 - Anthony Potts
请参阅https://dev59.com/hkXRa4cB1Zd3GeqPpSrg。 - Ruben Bartelink

0

从前,我一直在尝试运行增强的MSBuild构建过程(例如,更积极地跳过某些构建部分)。

有两个选择:

  1. 通过加载其DLL在自己的进程中托管MSBuild。
  2. 以常规方式生成MSBuild.exe,然后注入其中(例如,记录器提供了一个不错的方法)。

我都实现了一遍,但必须放弃第一种方法,因为它不够灵活。

例如,MSBuild在其.config文件中有大量的程序集绑定重定向。

Visual Studio进程(devenv.exe),它也使用其API托管MSBuild,最终将这些内容复制到其devenv.exe.config中。我的.exe.config也必须具有这些内容,但它会让你坚持特定的MSBuild版本。当然,你必须修改配置。实际上,这在PS中不是一个选项,所以我怀疑你是否能得到一个真正稳定的解决方案。


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