Создание программного проекта msbuild 15 программно

Я пытаюсь создать простой проект библиотеки классов С# 7, созданный с помощью VS2017.

MSBuild из сборки сборки устарел, поэтому я ссылаюсь на Microsoft.Build, Microsoft.Build.Engine и Microsoft.Build.Framework из папки MSBuild в visual studio (C:\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". InvalidProjectFileException: The tools version "15.0" is unrecognized. Available tools versions are "4.0", "2.0".

Есть ли программный способ для вызова сборки с использованием новейших инструментов сборки и компилятора С# 7?

Ответ 1

У меня были аналогичные потребности для моей команды, и я написал библиотеку Builder для С#, которая поддерживает несколько версий Visual Studio. Я не мог заставить функцию Project.Build работать правильно, поэтому я пошел на выполнение MsBuild.exe напрямую.

Как я его построил:

Используйте Microsoft.Build.Framework из NuGet

Создайте новый объект проекта с 1 целью, называемой Build

Определите правильный инструмент ToolsVersion

Из проекта в соответствии с версией Visual Studio:

  • 2010, 2012 => 4.0
  • 2013 => 12.0
  • 2015 => 14.0
  • 2017 => 15,0

Добавьте новую задачу типа MsBuild

Свойство Projects, содержащее весь проект, мне нужно построить

Определить ToolsVersion

задачи MsBuild с тем же значением, что и для проекта

Сериализовать проект во временный файл

Найти MsBuild.exe в соответствии с ToolsVersion

4,0, 12,0, 14,0

Найдено в реестре:

Registry.LocalMachine.OpenSubKey([email protected]"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 и используйте его, чтобы определить, произошла ли сбой сборки или нет, какие ошибки и предупреждения произошли и т.д.

Ответ 2

Если вам нужен только путь к последнему файлу MSBuild.exe, используйте Microsoft.Build.Utilities.Core от Nuget и используйте следующий код:

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

Это работает независимо от того, установлены ли только средства сборки или полная установка Visual Studio.

Ответ 3

У меня были точно такие же проблемы в моем проекте.
В моем случае я хочу программно разработать проект SSDT, но я также пробовал другие типы проектов.

Интересно, что он отлично работал в сборке 26228.04 VS2017 (которая была сборкой Release) и перестала работать в сборке 26228.09.
Вчера был выпущен сборник 26228.10, поэтому я решил отправить его еще раз.

Удивительно, но для меня это работало:

  1. Обновите последнюю версию VS2017 build 26228.10 с помощью установщика Visual Studio.
  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:
    Измените следующие две строки в файле .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 немного "сложно" использовать программно (по моему мнению, он иногда стабилен, как прогноз погоды), скорее всего, мой подход не будет работать в вашем случае.