什么 API 来获取或评估 MSBuild.exe.config 中定义的全局变量?

What API to get or evaluate the global variables defined in MSBuild.exe.config?

我正在编写一个应用程序来评估(但不是构建)*.csproj 和其他项目文件。

该应用程序模拟 MSBuild 的功能。它使用 Microsoft.Build.dll 实例公开的(记录的)public APIs,这些实例安装在 Visual Studio 文件夹中,例如:

C:\Program Files (x86)\Microsoft Visual Studio17\Enterprise\MSBuild.0\Bin\
C:\Program Files (x86)\Microsoft Visual Studio19\Enterprise\MSBuild\Current\Bin\

在这些目录中有一个 MSBuild.exe.config 文件,其中包含一个 msbuildToolsets 部分,如下所示:

<msbuildToolsets default="Current">
  <toolset toolsVersion="Current">
    <property name="MSBuildToolsPath" value="$([MSBuild]::GetCurrentToolsDirectory())" />
    <property name="MSBuildToolsPath32" value="$([MSBuild]::GetToolsDirectory32())" />
    <property name="MSBuildToolsPath64" value="$([MSBuild]::GetToolsDirectory64())" />
    <property name="MSBuildSDKsPath" value="$([MSBuild]::GetMSBuildSDKsPath())" />
    <property name="FrameworkSDKRoot" value="$(Registry:HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\NETFXSDK.8@InstallationFolder)" />
    <property name="MSBuildRuntimeVersion" value="4.0.30319" />
    <property name="MSBuildFrameworkToolsPath" value="$(SystemRoot)\Microsoft.NET\Framework\v$(MSBuildRuntimeVersion)\" />
    ... etc ...

这些条目定义了评估 *.csproj 时设置的全局变量的值。

the dotnet/msbuild source code 我看到了:

在调用 MSBuild 时正确设置这些全局变量至关重要,例如在将 ProjectCollection.LoadProject 方法调用到 return 一个 Project 实例时(否则项目将无法加载).

设置这些变量的 API 是通过 globalProperties 参数将它们传递给 ProjectCollection -- 但我不知道如何获取或评估这些变量的值我应该设置这些属性。

我的问题是——我如何读取这些变量(假定上面列出的 classes 是“内部的”)?有 public API 可以做到这一点吗?

如有必要,我可以 运行 我的应用程序在 MSBuild bin 文件夹中,and/or 复制 MSBuild.exe.config 文件。

使用方法:

  • 设置MSBUILD_EXE_PATH环境变量
  • 实例化一个BuildParameters
  • 调用其GetToolset方法
  • 参数在工具集Properties

目录路径(例如 Enterprise)可能因机器而异。 为了更加稳健,您可以实施 运行 时间探测来发现它,而不是对其进行硬编码。

        private readonly string _toolsPath;
        private readonly string _toolsVersion;

        public EvaluateCondition(string solutionPath)
        {
            var toolsets = new Dictionary<string, string>
            {
                {"2.0", @"C:\WINDOWS\Microsoft.Net\Framework\v2.0.50727"},
                {"3.5", @"C:\WINDOWS\Microsoft.Net\Framework\v3.5"},
                {"4.0", @"C:\WINDOWS\Microsoft.Net\Framework\v4.0.30319"},
                {"15.0", @"C:\Program Files (x86)\Microsoft Visual Studio17\Enterprise\MSBuild.0\Bin"},
                {"Current" /*"16.0"*/, @"C:\Program Files (x86)\Microsoft Visual Studio19\Enterprise\MSBuild\Current\Bin"}
            };

            // specify which version of the toolset we want to use
            // this is a compile-time definition but could have been a function parameter
#if X2017
            _toolsVersion = "15.0";
#endif
#if X2019
            _toolsVersion = "Current";
#endif

            var toolsPath = toolsets[_toolsVersion];
            _toolsPath = toolsPath; 
            
            // Use BuildParameters to load the settings which are defined in the MSBuild.exe.config and which would be loaded
            // if we specified ToolsetDefinitionLocations.ConfigurationFile as a ProjectCollection constructor parameter.
            Environment.SetEnvironmentVariable("MSBUILD_EXE_PATH", CombineFullPath(toolsPath, "MSBuild.exe"));
            var buildParameters = new BuildParameters();
            var toolset = buildParameters.GetToolset(_toolsVersion);
            var toolsetProperties = toolset.Properties;

            GlobalProperties = new Dictionary<string, string>();
            foreach (var kvp in toolsetProperties)
            {
                if (kvp.Key != kvp.Value.Name)
                {
                    throw new ArgumentException();
                }

                GlobalProperties.Add(kvp.Key, kvp.Value.EvaluatedValue);
            }

            Environment.SetEnvironmentVariable("MSBuildExtensionsPath", GlobalProperties["MSBuildExtensionsPath"]);
            Environment.SetEnvironmentVariable("MSBuildSDKsPath", GlobalProperties["MSBuildSDKsPath"]);

            // QED: pass the global variables to the ProjectCollection
            ProjectCollection = new ProjectCollection(GlobalProperties);

            foreach (var kvp in toolsets)
            {
                // this sets $(MSBuildToolsPath)
                ProjectCollection.AddToolset(new Toolset(kvp.Key, kvp.Value, ProjectCollection, string.Empty));
            }
        }