web-dev-qa-db-ja.com

プログラムによるmsbuild 15プロジェクトのビルド

VS2017で作成した簡単なC#7クラスライブラリプロジェクトを構築しようとしています。

フレームワークアセンブリからのMSBuildは古くなっているので、ビジュアルスタジオ(C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin)内のMSBuildフォルダーからMicrosoft.BuildMicrosoft.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コンパイラを使用してビルドを呼び出すプログラム的な方法はありますか?

13
repka

私のチームにも同様のニーズがあり、いくつかのバージョンのVisual StudioをサポートするC#のビルダーライブラリを作成しました。 Project.Build関数を正しく機能させることができなかったため、MsBuild.exeを直接実行しました。

私がそれを構築した方法:

NuGet からMicrosoft.Build.Frameworkを使用します

Buildという1つのターゲットを持つ新しいProjectオブジェクトを作成します

適切なToolsVersionを定義する

Visual Studioのバージョンに応じたプロジェクト:

  • 2010、2012 => 4.0
  • 2013 => 12.0
  • 2015 => 14.0
  • 2017 => 15.0

タイプMsBuildの新しいタスクを追加する

ビルドする必要があるすべてのプロジェクトを含むProjectsプロパティ

ToolsVersionを定義する

プロジェクトと同じ値を持つ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から結果の概要を逆シリアル化し、それを使用して、ビルドが失敗したかどうか、どのエラーや警告が発生したかなどを判断します。

4
Vilmir

最新のMSBuild.exeへのパスが必要なだけの場合は、NugetのMicrosoft.Build.Utilities.Coreを使用し、次のコードを使用します。

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

これは、ビルドツールがインストールされている場合でも、完全なVisual Studioインストールの場合でも機能します。

4
arni

私のプロジェクトでもまったく同じ問題がありました。
私の場合、SSDTプロジェクトをプログラムで構築したいのですが、他の種類のプロジェクトも試しました。

興味深いことに、VS2017のビルド26228.04(これはリリースビルドでした)では完全に正常に動作し、ビルド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>_要素全体を自分の_<configuration>_要素にコピーします_.config_プログラムでMSBuildを使用するプロジェクトのファイル。
    MSBuildアセンブリバージョンをリダイレクトしないと、MSBuildからかなり奇妙なエラーメッセージが表示されます。これは、MSBuildバージョンによって異なります。
    しかし、言うべきことは確かです:アセンブリバインディングリダイレクトがないと、確実に機能しません。

  4. SSDTプロジェクトをビルドする場合のもう1つのステップ:
    _.sqlproj_ファイルの次の2行を

    _<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はプログラムで使用するのが少し "難しい"(私の意見では、天気予報のように安定している場合がある)ため、私のアプローチはあなたのケースでは機能しない可能性があります。

3
haindl

これは、msbuildのプレリリースバージョン( 15.5.0-preview-000072-0942130 )ですでに解決されています。MsBuildの問題 を参照してください=)#2369:msbuild nugetパッケージがvs2017 csprojファイルを開くことができません 。したがって、将来的には、これ以上ハッキングする必要はありません。

2
Andre