以编程方式构建msbuild 15项目

时间:2017-03-28 19:38:35

标签: c# msbuild visual-studio-2017 c#-7.0

我正在尝试构建一个使用VS2017创建的简单C#7类库项目。

来自框架程序集的MSBuild已过时,因此我在visual studio(Microsoft.Build)中的MSBuild文件夹中引用了Microsoft.Build.EngineMicrosoft.Build.FrameworkC:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin

但是,当我这样做时:

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编译器调用构建的编程方法?

5 个答案:

答案 0 :(得分:4)

我的团队有类似的需求,我为C#编写了一个Builder库,支持多个版本的Visual Studio。 我无法使Project.Build功能正常工作,所以我直接执行了MsBuild.exe。

我是如何构建的:

使用NuGet

中的Microsoft.Build.Framework

使用名为Build

的1个目标创建一个新的Project对象

定义正确的ToolsVersion

根据Visual Studio版本的项目:

  • 2010,2012 =&gt; 4.0
  • 2013 =&gt; 12.0
  • 2015 =&gt; 14.0
  • 2017 =&gt; 15.0

添加MsBuild

类型的新任务

使用包含我需要构建的所有项目的Projects属性

定义ToolsVersion

MsBuild任务的

具有与Project

相同的值

将项目序列化为临时文件

根据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 Logger构建序列化项目 - 您可以使用MsBuildExtensionPack

提供的项目

阅读结果摘要

从Xml反序列化结果摘要并使用它来确定构建是否失败,发生了哪些错误和警告等。

答案 1 :(得分:4)

如果您只需要最新MSBuild.exe的路径,请使用Nuget中的 Microsoft.Build.Utilities.Core 并使用以下代码:

ToolLocationHelper.GetPathToBuildToolsFile("msbuild.exe", ToolLocationHelper.CurrentToolsVersion);

无论是安装Build Tools还是完整的Visual Studio安装都可以。

答案 2 :(得分:3)

我的项目中存在完全相同的问题 在我的情况下,我想以编程方式构建一个SSDT项目,但我也尝试了其他项目类型。

有趣的是,它在VS2017的构建版本26228.04(这是Release版本)中工作得非常好,并且在构建版本26228.09中停止工作。
昨天发布的26228.10已经发布,所以我决定再给它一次。

令人惊讶的是,以下内容对我有用:

  1. 使用Visual Studio Installer更新到最新的VS2017版本26228.10。
  2. 我从来没有得到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...
    
  3. 确保已将所有MSBuild装配绑定重定向复制到.config文件。
    如果您还没有这样做,请打开文件C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MSBuild.exe.config并将整个<runtime>元素复制到使用MSBuild的项目中您自己的<configuration>文件的.config元素编程。
    如果您不重定向MSBuild程序集版本,您将从MSBuild获得一些非常奇怪的错误消息,这些消息将根据您的MSBuild版本而有所不同。
    但它肯定会说:如果没有装配绑定重定向,它肯定不会起作用。

  4. 如果您想构建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>
    
  5. 我不是百分百肯定为什么,但这些步骤对我来说非常合适! 但是因为MSBuild以编程方式使用有点“难”(在我看来它有时和天气预报一样稳定),所以我的方法可能不适用于你的情况。

答案 3 :(得分:2)

这已在msbuild的PreRelease版本(15.5.0-preview-000072-0942130)中解决,请参阅MsBuild问题#2369: msbuild nuget package unable to open vs2017 csproj files。 因此,将来不需要进一步的黑客攻击

答案 4 :(得分:0)

现在更容易了,因为微软已经发布了他们自己的软件包。

设置包引用 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);
}

根据上面的文档链接,对 Microsoft.Build 程序集的调用需要以与 MSBuildLocator.RegisterDefaults() 不同的方法发生。