Microsoft.Build.Evaluation 的正确用法是什么(快进到 2021 年)?

时间:2021-02-11 00:03:46

标签: c# msbuild

因此原始问题 What's the correct usage of Microsoft.Build.Evaluation? 具有以下可接受的答案:

project = new Project(projectPath, new Dictionary<string, string>(), "12.0", new ProjectCollection());

这在 2021 年不适用于 NuGet 包 Microsoft.Build 16.8.0。

我想像这样评估一个非 SDK 风格的项目:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
...
  <Import Project="..\..\..\Tools\MSBuild\Dayforce.targets" />
  <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

请注意:

尝试 1

new Project(projectPath, new Dictionary<string, string>(), "16.0", new ProjectCollection());

结果:

Microsoft.Build.Exceptions.InvalidProjectFileException: The tools version "16.0" is unrecognized. Available tools versions are "Current".

尝试 2

new Project(projectPath, new Dictionary<string, string>(), "Current", new ProjectCollection());

结果:

Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project "C:\work\CSTool\CSTool\bin\Debug\netcoreapp3.1\Current\Microsoft.Common.props" was not found. Confirm that the expression in the Import declaration "C:\work\CSTool\CSTool\bin\Debug\netcoreapp3.1\Current\Microsoft.Common.props" is correct, and that the file exists on disk.

尝试 3

new Project(projFilePath, new Dictionary<string, string>
{
    ["MSBuildExtensionsPath"] = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild",
}, "Current", new ProjectCollection());

结果:

Microsoft.Build.Exceptions.InvalidProjectFileException: The imported project "C:\work\CSTool\CSTool\bin\Debug\netcoreapp3.1\Microsoft.CSharp.targets" was not found. Confirm that the expression in the Import declaration "C:\work\CSTool\CSTool\bin\Debug\netcoreapp3.1\Microsoft.CSharp.targets" is correct, and that the file exists on disk. 

我们正在取得进展,Microsoft.Common.props 似乎已被导入,现在我们在最后一次导入时失败 - Microsoft.CSharp.targets

尝试 4

new Project(projFilePath, new Dictionary<string, string>
{
    ["MSBuildExtensionsPath"] = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild",
    ["MSBuildBinPath"] = @"C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin",
}, "Current", new ProjectCollection());

但结果是:

System.ArgumentException: The "MSBuildBinPath" property name is reserved.

那么,我错过了什么?

我设法做我想做的事。但是,没有一个 Microsoft.Build NuGet 包按我的预期工作。我检查了所有已发布的版本。

对我有用的是参考在 VS 2019 安装目录中找到的 Microsoft.Build Dll。这是我的项目文件:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.1</TargetFramework>
  </PropertyGroup>

  ...

  <ItemGroup>
    <Reference Include="Microsoft.Build">
      <HintPath>..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Build.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Build.Framework">
      <HintPath>..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Build.Framework.dll</HintPath>
    </Reference>
    <Reference Include="Microsoft.Build.Utilities.Core">
      <HintPath>..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\MSBuild\Current\Bin\Microsoft.Build.Utilities.Core.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

这是有效的代码:

var project = new Project(projFilePath);
foreach (var compileItem in project.AllEvaluatedItems.Cast<ProjectItem>().Where(item => item.ItemType == "Compile"))
{
    var filePath = compileItem.EvaluatedInclude;
    ...
}

我检查了 msbuild github repository - 它也不使用 NuGet 包。相反,它包含所有相关库的源代码并只构建它们。这些 dll 也像 VS dll 一样工作。

那么,NuGet 包是怎么回事?我不明白。打开https://github.com/dotnet/msbuild/issues/6147

2 个答案:

答案 0 :(得分:0)

您需要引用 Microsoft.Build.Utilities.Core NuGet 包并将 MSBUILD_EXE_PATH 环境变量设置为 MSBuild.exe 或 MSBuild.dll

https://web.archive.org/web/20210419051516/https://blog.rsuter.com/missing-sdk-when-using-the-microsoft-build-package-in-net-core/

在此之后只需创建一个项目:

var project = new Project("Path\To\Your\Project");

答案 1 :(得分:0)

有几种情况。

  • 您想使用安装在 C:\Program Files\dotnet 下的 .NET Core SDK。在这种情况下,以下代码适用于我:
var msbuildDllPath = Path.Combine(sdkPath, "msbuild.dll");
Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", msbuildDllPath);
Environment.SetEnvironmentVariable("MSBuildExtensionsPath32", sdkPath);
Environment.SetEnvironmentVariable("MSBuildExtensionsPath64", sdkPath);
Environment.SetEnvironmentVariable("MSBuildExtensionsPath", sdkPath);

var project = new Project(projectPath, null, "Current", new ProjectCollection(ToolsetDefinitionLocations.Local));

这是我能找到的唯一适用于这种情况的组合。代码本身的 TargetFrameworknet5.0

  • 您想在 PATH 中使用与 Visual Studio 关联的构建工具,但可能是 2017 或 2019 或 2017,同时安装了 2019。这是一个真正的 PITA,解决方案并不漂亮。以下是对我有用的方法:
private static (string, string) GetMostRecentMSBuildExePath()
{
    var flavors = new[]
    {
        "Professional",
        "Enterprise",
        "Community",
        "BuildTools",
    };

    string found = null;
    int maxVersion = 2017;
    foreach (var drive in DriveInfo.GetDrives().Where(o => o.DriveType == DriveType.Fixed))
    {
        foreach (var dir in Directory.GetDirectories(drive.Name + "Program Files (x86)\\Microsoft Visual Studio"))
        {
            if (!int.TryParse(dir.Substring(dir.Length - 4), out var version) || version <= maxVersion)
            {
                continue;
            }
            var msbuildExePath = flavors.Select(flavor => dir + "\\" + flavor + "\\MSBuild\\Current\\bin\\msbuild.exe").FirstOrDefault(File.Exists);
            if (msbuildExePath != null)
            {
                maxVersion = version;
                found = msbuildExePath;
            }
        }
    }

    string vsVersion = null;
    if (found != null)
    {
        foreach (var dir in Directory.GetDirectories(found + @"\..\..\..\Microsoft\VisualStudio", "v*"))
        {
            if (Directory.Exists(dir + "\\WebApplications"))
            {
                var index = dir.LastIndexOf("\\");
                vsVersion = dir.Substring(index + 2);
                break;
            }
        }
    }
    return (found, vsVersion);
}

...
// Check if VSINSTALLDIR implicates 2017.
// If not - we are good.
// If yes - search if there is VS 2019 in any of the well known locations.
// If yes - point at it, if no - leave as is.

string toolsVersion = null;
var vsInstallDir = Environment.GetEnvironmentVariable("VSINSTALLDIR");
if (vsInstallDir?.Contains("\\2017\\") == true)
{
    // Bummer. Check if 2019 or up is installed.
    var (msBuildExePath, vsVersion) = GetMostRecentMSBuildExePath();
    if (msBuildExePath == null)
    {
        // Only VS 2017
        toolsVersion = "15.0";
        Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", Path.Combine(vsInstallDir, "MSBuild\\15.0\\bin\\msbuild.exe"));
    }
    else
    {
        toolsVersion = "Current";
        Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", msBuildExePath);
        Environment.SetEnvironmentVariable("VisualStudioVersion", vsVersion);
    }
}

var project = new Project(projectFilePath, null, toolsVersion);

在这种情况下,代码的 TargetFrameworknet472。我还假设 VS 安装在标准位置,尽管在任何驱动器上。