0

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

该应用程序模拟 MSBuild 所做的事情。它使用Microsoft.Build.dll实例公开的(记录在案的)公共 API,这些 API 安装在 Visual Studio 文件夹中,例如:

C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\
C:\Program Files (x86)\Microsoft Visual Studio\2019\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\4.8@InstallationFolder)" />
    <property name="MSBuildRuntimeVersion" value="4.0.30319" />
    <property name="MSBuildFrameworkToolsPath" value="$(SystemRoot)\Microsoft.NET\Framework\v$(MSBuildRuntimeVersion)\" />
    ... etc ...

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

dotnet/msbuild 源代码中,我看到:

  • 在类中(在命名空间中)GetMSBuildSDKsPath()定义了一个类似的函数,它是一个类。IntrinsicFunctionsMicrosoft.Build.Evaluationinternal static
  • 这个函数的实现委托给BuildEnvironmentHelper(在Microsoft.Build.Shared命名空间中),它也是一个internal类。

在调用 MSBuild 时正确设置这些全局变量至关重要,例如在调用ProjectCollection.LoadProject方法以返回Project实例时(否则项目将不会加载)。

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

我的问题是——我怎样才能读取这些变量(假设上面列出的类是“内部”)?是否有一个公共 API 可以做到这一点?

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

4

1 回答 1

0

使用方法:

  • 设置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 Studio\2017\Enterprise\MSBuild\15.0\Bin"},
                {"Current" /*"16.0"*/, @"C:\Program Files (x86)\Microsoft Visual Studio\2019\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));
            }
        }
于 2021-08-14T11:05:40.460 回答