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

mar*_*ark 4 c# msbuild

所以原来的问题Microsoft.Build.Evaluation 的正确用法是什么?有以下可接受的答案:

project = new Project(projectPath, new Dictionary<string, string>(), "12.0", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

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

我想评估一个非 SDK 风格的项目,如下所示:

project = new Project(projectPath, new Dictionary<string, string>(), "12.0", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

请注意:

尝试1

new Project(projectPath, new Dictionary<string, string>(), "16.0", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

结果是:

<?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>
Run Code Online (Sandbox Code Playgroud)

尝试2

new Project(projectPath, new Dictionary<string, string>(), "Current", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

结果是:

new Project(projectPath, new Dictionary<string, string>(), "16.0", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

尝试3

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

结果是:

Microsoft.Build.Exceptions.InvalidProjectFileException: The tools version "16.0" is unrecognized. Available tools versions are "Current".
Run Code Online (Sandbox Code Playgroud)

我们正在取得进展,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());
Run Code Online (Sandbox Code Playgroud)

但结果是:

new Project(projectPath, new Dictionary<string, string>(), "Current", new ProjectCollection());
Run Code Online (Sandbox Code Playgroud)

那么,我错过了什么?

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

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

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.
Run Code Online (Sandbox Code Playgroud)

这是有效的代码:

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

我检查了msbuild github 存储库- 它也不使用 NuGet 包。相反,它包含所有相关库的源代码并只是构建它们。这些 dll 的工作方式也与 VS dll 的工作方式相同。

那么,NuGet 包有什么用呢?我不明白这一点。打开https://github.com/dotnet/msbuild/issues/6147

小智 5

关于加载项目文件存在一个未解决的问题,该文件解释了 net5 和 net6 的当前 caviats。https://github.com/dotnet/msbuild/issues/3434#issuecomment-774280523

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>net6.0</TargetFramework>
    </PropertyGroup>

    <ItemGroup>
        <PackageReference Include="Microsoft.Build" Version="17.3.2" ExcludeAssets="runtime" />
        <PackageReference Include="Microsoft.Build.Locator" Version="1.5.5" />
        <PackageReference Include="Microsoft.Build.Utilities.Core" Version="17.4.0" ExcludeAssets="runtime" />
    </ItemGroup>

</Project>
Run Code Online (Sandbox Code Playgroud)
using Microsoft.Build.Evaluation;
using Microsoft.Build.Locator;

class Program
{
   const string ProjectPath = @"C:\tmp\project.csproj";

   private static void Main(string[] args)
   {
      MSBuildLocator.RegisterDefaults();
      LoadProject();
   }

   private static void LoadProject()
   {
      var buildEngine = new ProjectCollection();
      var project = buildEngine.LoadProject(ProjectPath);
      Console.WriteLine($"OutputType: {project.GetPropertyValue("OutputType")}");
      Console.WriteLine($"TargetFramework: {project.GetPropertyValue("TargetFramework")}");
   }
Run Code Online (Sandbox Code Playgroud)

请注意,MSBuild API 的首次使用必须在单独的方法中进行,否则 JIT 将在注册 MSBuildLocator 之前尝试解析 Microsoft.Build.dll。

在net6中似乎需要“MSBuildLocator.RegisterDefaults()”,但在net5中不需要。

我没有像其他人提到的那样设置MSBUILD_EXE_PATH