使用 Microsoft.Build.Evaluation 解析实际参考路径

Resolve actual Reference path using Microsoft.Build.Evaluation

我正在使用小型 C# 控制台应用程序中的 Microsoft.Build.Evaluation 工具对 csproj 文件进行一些内省和分析。我想找到参考项的实际位置,使用与 MSBuild 本身相同的启发式方法,即描述的位置 here. I'm heading towards auto conversion of build artifacts into packages, similar to what's outlined on the JetBrains blog here

我能找到的唯一示例希望 HintPath 是正确的,例如 this project,而且我知道有一些 HintPath 当前不正确,我不想相信它们。这个项目非常接近我正在尝试做的事情,增加了我想使用真实解析行为来查找依赖项的复杂性。

我的 csproj 有一个 Microsoft.Build.Evaluation.Project 对象的实例,但我看不到任何可用的方法可以为我解决问题。我想我所希望的是一个用于 Reference 或 ProjectItem 的神奇 Resolve() 方法,有点像 this method.

我可能可以通过将自己的搜索限制在该构建系统使用的一组有限输出路径来找到替代方案,但如果可以的话,我想连接到 MSBuild。

引用解析是 MSBuild 中最棘手的部分之一。如何定位程序集的逻辑是在一组标准任务中实现的: ResolveAssemblyReference, ResolveNativeReference,等等。逻辑是如何工作的非常复杂,你可以通过查看这些任务的可能参数的数量来了解。

但是,您无需知道查找引用文件位置的确切逻辑。有称为 "ResolveAssemblyReferences"、"ResolveProjectReferences" 的标准目标和一些更专门用于本机引用、COM 引用的其他目标。这些目标作为正常构建的一部分执行。如果你只是单独执行那些目标,你可以找出 return 值,这正是你需要的。 IDE 使用相同的机制来获取引用的位置,用于 Intellisense、内省等

以下是您可以在代码中完成的方法:

using Microsoft.Build.BuildEngine;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using System;
using System.Collections.Generic;

class Program
{
    static int Main(string[] args)
    {
        if (args.Length < 1)
        {
            Console.WriteLine("Usage: GetReferences.exe <projectFileName>");
            return -1;
        }

        string projectFileName = args[0];

        ConsoleLogger logger = new ConsoleLogger(LoggerVerbosity.Normal);
        BuildManager manager = BuildManager.DefaultBuildManager;

        ProjectInstance projectInstance = new ProjectInstance(projectFileName);
        var result = manager.Build(
            new BuildParameters()
            {
                DetailedSummary = true,
                Loggers = new List<ILogger>() { logger }
            },
            new BuildRequestData(projectInstance, new string[] 
            { 
                "ResolveProjectReferences",
                "ResolveAssemblyReferences"
            }));

        PrintResultItems(result, "ResolveProjectReferences");
        PrintResultItems(result, "ResolveAssemblyReferences");

        return 0;
    }

    private static void PrintResultItems(BuildResult result, string targetName)
    {
        var buildResult = result.ResultsByTarget[targetName];
        var buildResultItems = buildResult.Items;

        if (buildResultItems.Length == 0)
        {
            Console.WriteLine("No refereces detected in target {0}.", targetName);
            return;
        }

        foreach (var item in buildResultItems)
        {
            Console.WriteLine("{0} reference: {1}", targetName, item.ItemSpec);
        }
    }
}

注意,调用引擎是为了调用项目中的特定目标。您的项目通常不会生成,但某些目标可能会被先决条件目标调用。

只需编译它并打印所有依赖项的子集。如果您对项目使用 COM 引用或本机依赖项,​​则可能存在更多依赖项。修改样本也应该很容易得到这些。