以编程方式构建 msbuild 15 项目
Building msbuild 15 project programmatically
我正在尝试构建一个使用 VS2017 创建的简单 C# 7 class 库项目。
来自框架程序集的 MSBuild 已过时,因此我从 visual studio (C:\Program Files (x86)\Microsoft Visual Studio17\Community\MSBuild.0\Bin
) 的 MSBuild 文件夹中引用 Microsoft.Build
、Microsoft.Build.Engine
和 Microsoft.Build.Framework
.
不过,当我这样做时:
using (var collection = new ProjectCollection())
{
var proj = collection.LoadProject(@"c:\projects\Sample\Sample.csproj"); // <-- exception
proj.Build(new[] {new ConsoleLogger()});
}
我得到 InvalidProjectFileException: The tools version "15.0" is unrecognized. Available tools versions are "4.0", "2.0".
是否有使用最新构建工具和 C# 7 编译器调用构建的编程方式?
我的团队也有类似的需求,我为 C# 编写了一个支持多个版本 Visual Studio 的 Builder 库。
我无法使Project.Build功能正常运行,所以我直接执行MsBuild.exe。
我是如何构建它的:
使用 NuGet
中的 Microsoft.Build.Framework
创建一个包含 1 个名为 Build 的目标的新项目对象
定义正确的工具版本
根据 Visual Studio 版本的项目:
- 2010, 2012 => 4.0
- 2013 => 12.0
- 2015 => 14.0
- 2017 => 15.0
添加 MsBuild 类型的新任务
With Projects 属性 包含我需要构建的所有项目
定义工具版本
与项目具有相同值的 MsBuild 任务
将项目序列化为临时文件
根据ToolsVersion
找到合适的MsBuild.exe
4.0、12.0、14.0
在注册表中找到:
Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{msBuildVersion}")
15.0
注册表中没有了,需要使用Nuget包Microsoft.VisualStudio.Setup.Configuration.Interop
var query = new SetupConfiguration();
var query2 = (ISetupConfiguration2)query;
var e = query2.EnumAllInstances();
var helper = (ISetupHelper)query;
int fetched;
var instances = new ISetupInstance[1];
do
{
e.Next(1, instances, out fetched);
if (fetched > 0)
{
var instance = instances[0];
var instance2 = (ISetupInstance2)instance;
var state = instance2.GetState();
// Skip non-complete instance, I guess?
// Skip non-local instance, I guess?
// Skip unregistered products?
if (state != InstanceState.Complete
|| (state & InstanceState.Local) != InstanceState.Local
|| (state & InstanceState.Registered) != InstanceState.Registered)
{
continue;
}
var msBuildComponent =
instance2.GetPackages()
.FirstOrDefault(
p =>
p.GetId()
.Equals("Microsoft.Component.MSBuild",
StringComparison.InvariantCultureIgnoreCase));
if (msBuildComponent == null)
{
continue;
}
var instanceRootDirectory = instance2.GetInstallationPath();
var msbuildPathInInstance = Path.Combine(instanceRootDirectory, "MSBuild", msBuildVersion, "Bin", "msbuild.exe");
if (File.Exists(msbuildPathInInstance))
{
return msbuildPathInInstance;
}
}
} while (fetched > 0);
执行MsBuild.exe
并使用自定义 XML 记录器构建序列化项目 - 您可以使用 MsBuildExtensionPack
提供的记录器
阅读结果摘要
反序列化 Xml 的结果摘要,并用它来确定构建是否失败,发生了哪些错误和警告等。
我在我的项目中遇到了完全相同的问题。
就我而言,我想以编程方式构建一个 SSDT 项目,但我也尝试了其他项目类型。
有趣的是,它在 VS2017 的版本 26228.04(发布版本)中运行良好,在版本 26228.09 中停止工作。
昨天发布了 build 26228.10,所以我决定再试一次。
令人惊讶的是,以下对我有用:
- 使用 Visual Studio 安装程序更新到最新的 VS2017 build 26228.10。
我一直无法让 collection.LoadProject(...)
在不出现奇怪错误的情况下工作,但您可以使用此代码进行构建:
BuildResult result = null;
using (var pc = new ProjectCollection())
result = BuildManager.DefaultBuildManager.Build(
new BuildParameters(pc) { Loggers = new[] { new ConsoleLogger() } },
// Change this path to your .sln file instead of the .csproj.
// (It won't work with the .csproj.)
new BuildRequestData(@"c:\projects\Sample.sln",
// Change the parameters as you need them,
// e.g. if you want to just Build the Debug (not Rebuild the Release).
new Dictionary<string, string>
{
{ "Configuration", "Release" },
{ "Platform", "Any CPU" }
}, null, new[] { "Rebuild" }, null));
if (result.OverallResult == BuildResultCode.Failure)
// Something bad happened...
确保您已将所有 MSBuild 程序集绑定重定向复制到您的 .config
文件。
如果您还没有这样做,请打开文件 C:\Program Files (x86)\Microsoft Visual Studio17\Enterprise\MSBuild.0\Bin\MSBuild.exe.config
并将整个 <runtime>
元素复制到使用 MSBuild 的项目中您自己的 .config
文件的 <configuration>
元素以编程方式。
如果您不重定向 MSBuild 程序集版本,您会从 MSBuild 收到一些非常奇怪的错误消息,这些消息会因您的 MSBuild 版本而异。
但是可以肯定地说:没有程序集绑定重定向肯定行不通。
如果要构建 SSDT 项目,还需要一个步骤:
将 .sqlproj
文件中的以下两行更改为
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
至
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion>
<VisualStudioVersion Condition="'$(SSDTExists)' == ''">15.0</VisualStudioVersion>
我不是 100% 确定为什么,但这些步骤对我来说非常有效!
但是因为 MSBuild 有点 "difficult" 以编程方式使用(在我看来它有时像天气预报一样稳定),我的方法很可能不适用于您的情况。
如果您只需要最新 MSBuild.exe 的路径,请使用 Nuget 中的 Microsoft.Build.Utilities.Core 并使用以下代码:
ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion);
无论是只安装构建工具还是完整 Visual Studio 安装,这都有效。
这已在 PreRelease 版本中解决 (15.5.0-preview-000072-0942130) of msbuild, see MsBuild issue #2369: msbuild nuget package unable to open vs2017 csproj files。
所以将来不需要进一步的黑客攻击
现在更容易了,因为 Microsoft 已经发布了他们自己的软件包。
设置包引用as described here。请注意除 Locator 包之外的所有包上的 ExcludeAssets="runtime"
:
<PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
<PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10.0" ExcludeAssets="runtime" />
public Init()
{
MSBuildLocator.RegisterDefaults();
var project = LoadProject(@"C:\Path\To\Project.csproj");
}
public Project LoadProject(string csprojPath)
{
var context = new ProjectCollection();
return context.LoadProject(csprojPath);
}
根据上面的文档 link,对 Microsoft.Build 程序集的调用需要以不同于 MSBuildLocator.RegisterDefaults()
.
的方法进行
我正在尝试构建一个使用 VS2017 创建的简单 C# 7 class 库项目。
来自框架程序集的 MSBuild 已过时,因此我从 visual studio (C:\Program Files (x86)\Microsoft Visual Studio17\Community\MSBuild.0\Bin
) 的 MSBuild 文件夹中引用 Microsoft.Build
、Microsoft.Build.Engine
和 Microsoft.Build.Framework
.
不过,当我这样做时:
using (var collection = new ProjectCollection())
{
var proj = collection.LoadProject(@"c:\projects\Sample\Sample.csproj"); // <-- exception
proj.Build(new[] {new ConsoleLogger()});
}
我得到 InvalidProjectFileException: The tools version "15.0" is unrecognized. Available tools versions are "4.0", "2.0".
是否有使用最新构建工具和 C# 7 编译器调用构建的编程方式?
我的团队也有类似的需求,我为 C# 编写了一个支持多个版本 Visual Studio 的 Builder 库。 我无法使Project.Build功能正常运行,所以我直接执行MsBuild.exe。
我是如何构建它的:
使用 NuGet
中的 Microsoft.Build.Framework创建一个包含 1 个名为 Build 的目标的新项目对象
定义正确的工具版本
根据 Visual Studio 版本的项目:
- 2010, 2012 => 4.0
- 2013 => 12.0
- 2015 => 14.0
- 2017 => 15.0
添加 MsBuild 类型的新任务
With Projects 属性 包含我需要构建的所有项目
定义工具版本
与项目具有相同值的 MsBuild 任务
将项目序列化为临时文件
根据ToolsVersion
找到合适的MsBuild.exe4.0、12.0、14.0
在注册表中找到:
Registry.LocalMachine.OpenSubKey($@"SOFTWARE\Microsoft\MSBuild\ToolsVersions\{msBuildVersion}")
15.0
注册表中没有了,需要使用Nuget包Microsoft.VisualStudio.Setup.Configuration.Interop
var query = new SetupConfiguration();
var query2 = (ISetupConfiguration2)query;
var e = query2.EnumAllInstances();
var helper = (ISetupHelper)query;
int fetched;
var instances = new ISetupInstance[1];
do
{
e.Next(1, instances, out fetched);
if (fetched > 0)
{
var instance = instances[0];
var instance2 = (ISetupInstance2)instance;
var state = instance2.GetState();
// Skip non-complete instance, I guess?
// Skip non-local instance, I guess?
// Skip unregistered products?
if (state != InstanceState.Complete
|| (state & InstanceState.Local) != InstanceState.Local
|| (state & InstanceState.Registered) != InstanceState.Registered)
{
continue;
}
var msBuildComponent =
instance2.GetPackages()
.FirstOrDefault(
p =>
p.GetId()
.Equals("Microsoft.Component.MSBuild",
StringComparison.InvariantCultureIgnoreCase));
if (msBuildComponent == null)
{
continue;
}
var instanceRootDirectory = instance2.GetInstallationPath();
var msbuildPathInInstance = Path.Combine(instanceRootDirectory, "MSBuild", msBuildVersion, "Bin", "msbuild.exe");
if (File.Exists(msbuildPathInInstance))
{
return msbuildPathInInstance;
}
}
} while (fetched > 0);
执行MsBuild.exe
并使用自定义 XML 记录器构建序列化项目 - 您可以使用 MsBuildExtensionPack
提供的记录器阅读结果摘要
反序列化 Xml 的结果摘要,并用它来确定构建是否失败,发生了哪些错误和警告等。
我在我的项目中遇到了完全相同的问题。
就我而言,我想以编程方式构建一个 SSDT 项目,但我也尝试了其他项目类型。
有趣的是,它在 VS2017 的版本 26228.04(发布版本)中运行良好,在版本 26228.09 中停止工作。
昨天发布了 build 26228.10,所以我决定再试一次。
令人惊讶的是,以下对我有用:
- 使用 Visual Studio 安装程序更新到最新的 VS2017 build 26228.10。
我一直无法让
collection.LoadProject(...)
在不出现奇怪错误的情况下工作,但您可以使用此代码进行构建:BuildResult result = null; using (var pc = new ProjectCollection()) result = BuildManager.DefaultBuildManager.Build( new BuildParameters(pc) { Loggers = new[] { new ConsoleLogger() } }, // Change this path to your .sln file instead of the .csproj. // (It won't work with the .csproj.) new BuildRequestData(@"c:\projects\Sample.sln", // Change the parameters as you need them, // e.g. if you want to just Build the Debug (not Rebuild the Release). new Dictionary<string, string> { { "Configuration", "Release" }, { "Platform", "Any CPU" } }, null, new[] { "Rebuild" }, null)); if (result.OverallResult == BuildResultCode.Failure) // Something bad happened...
确保您已将所有 MSBuild 程序集绑定重定向复制到您的
.config
文件。
如果您还没有这样做,请打开文件C:\Program Files (x86)\Microsoft Visual Studio17\Enterprise\MSBuild.0\Bin\MSBuild.exe.config
并将整个<runtime>
元素复制到使用 MSBuild 的项目中您自己的.config
文件的<configuration>
元素以编程方式。
如果您不重定向 MSBuild 程序集版本,您会从 MSBuild 收到一些非常奇怪的错误消息,这些消息会因您的 MSBuild 版本而异。
但是可以肯定地说:没有程序集绑定重定向肯定行不通。如果要构建 SSDT 项目,还需要一个步骤:
将.sqlproj
文件中的以下两行更改为<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">11.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(SSDTExists)' == ''">11.0</VisualStudioVersion>
至
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">15.0</VisualStudioVersion> <VisualStudioVersion Condition="'$(SSDTExists)' == ''">15.0</VisualStudioVersion>
我不是 100% 确定为什么,但这些步骤对我来说非常有效!
但是因为 MSBuild 有点 "difficult" 以编程方式使用(在我看来它有时像天气预报一样稳定),我的方法很可能不适用于您的情况。
如果您只需要最新 MSBuild.exe 的路径,请使用 Nuget 中的 Microsoft.Build.Utilities.Core 并使用以下代码:
ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion);
无论是只安装构建工具还是完整 Visual Studio 安装,这都有效。
这已在 PreRelease 版本中解决 (15.5.0-preview-000072-0942130) of msbuild, see MsBuild issue #2369: msbuild nuget package unable to open vs2017 csproj files。 所以将来不需要进一步的黑客攻击
现在更容易了,因为 Microsoft 已经发布了他们自己的软件包。
设置包引用as described here。请注意除 Locator 包之外的所有包上的 ExcludeAssets="runtime"
:
<PackageReference Include="Microsoft.Build.Locator" Version="1.4.1" />
<PackageReference Include="Microsoft.Build" Version="16.10.0" ExcludeAssets="runtime" />
<PackageReference Include="Microsoft.Build.Utilities.Core" Version="16.10.0" ExcludeAssets="runtime" />
public Init()
{
MSBuildLocator.RegisterDefaults();
var project = LoadProject(@"C:\Path\To\Project.csproj");
}
public Project LoadProject(string csprojPath)
{
var context = new ProjectCollection();
return context.LoadProject(csprojPath);
}
根据上面的文档 link,对 Microsoft.Build 程序集的调用需要以不同于 MSBuildLocator.RegisterDefaults()
.