如何查询MSBUILD文件以获取支持的目标列表?

如何查询MSBUILD文件以获取支持的目标列表?,msbuild,Msbuild,有没有办法询问msbuild哪些生成目标提供了msbuild文件支持?如果在命令提示符下无法执行此操作?这可能是通过编程实现的吗 除了解析msbuild XML之外,没有其他方法可以做到这一点吗?当然,MS提供了api来实现这一点,而无需自己解析XML。查找microsoft.build.buildengine 改编自一些C#。。。它通常值得探索。需要引用microsoft.build.engine dll才能编译此文件。用您的值替换下面的框架版本和路径。虽然列表可能比您预期的要长,但这在样例项

有没有办法询问msbuild哪些生成目标提供了msbuild文件支持?如果在命令提示符下无法执行此操作?这可能是通过编程实现的吗


除了解析msbuild XML之外,没有其他方法可以做到这一点吗?

当然,MS提供了api来实现这一点,而无需自己解析XML。查找microsoft.build.buildengine

改编自一些C#。。。它通常值得探索。需要引用microsoft.build.engine dll才能编译此文件。用您的值替换下面的框架版本和路径。虽然列表可能比您预期的要长,但这在样例项目文件中起到了作用

using System;
using Microsoft.Build.BuildEngine;
class MyTargets
{        
  static void Main(string[] args)
  {
    Engine.GlobalEngine.BinPath = @"C:\Windows\Microsoft.NET\Framework\v2.0.NNNNN";
    Project project = new Project();
    project.Load(@"c:\path\to\my\project.proj");
    foreach (Target target in project.Targets)
    {
      Console.WriteLine("{0}", target.Name);
    }
  }
}

更新了.NET Framework 4,因为上述内容已被弃用。导入microsoft.build.dll,代码如下:

using System;
using Microsoft.Build.Evaluation;
class MyTargets
{
  static void Main(string[] args)
  {
    Project project = new Project(args[0]);
    foreach (string target in project.Targets.Keys)
    {
      Console.WriteLine("{0}", target);
    }
  }
}

我建议您使用PowerShell:

Select-Xml `
    -XPath //b:Target `
    -Path path-to-build-file `
    -Namespace @{ b = 'http://schemas.microsoft.com/developer/msbuild/2003' } |
    Select-Object -ExpandProperty Node |
    Format-Table -Property Name, DependsOnTargets -AutoSize
XPath查询将查找所有
Target
元素,并以表格格式显示目标名称和依赖项。下面是从Microsoft.Web.Publishing.targets中选择前10个目标的示例:

PS C:\Program Files (x86)\MSBuild\Microsoft\VisualStudio\v11.0\Web> Select-Xml `
    -XPath //b:Target `
    -Path Microsoft.Web.Publishing.targets `
    -Namespace @{ b = 'http://schemas.microsoft.com/developer/msbuild/2003' } |
    Select-Object -ExpandProperty Node |
    Sort-Object -Property Name |
    Select-Object -First 10 |
    Format-Table -Property Name, DependsOnTargets -AutoSize

Name                                    DependsOnTargets                       
----                                    ----------------                       
_CheckPublishToolsUpToDate                                                     
_CheckRemoteFx45                                                               
_CleanWPPIfNeedTo                                                              
_DetectDbDacFxProvider                                                         
_WPPCopyWebApplication                  $(_WPPCopyWebApplicationDependsOn)     
AddContentPathToSourceManifest          $(AddContentPathToSourceManifestDepe...
AddDeclareParametersItems               $(AddDeclareParametersItemsDependsOn)  
AddDeclareParametersItemsForContentPath $(AddDeclareParametersItemsForConten...
AddDeclareParametersItemsForIis6        $(AddDeclareParametersItemsForIis6De...
AddDeclareParametersItemsForIis7        $(AddDeclareParametersItemsForIis7De...

我喜欢使用PowerShell的想法,但纯XML解决方案不起作用,因为它只输出该项目文件中定义的目标,而不输出导入。当然,每个人继续引用的C代码都是死的简单的,并且.NET 4.5是两条线(首先要考虑的只是添加到你的配置文件中):

是 啊真正地这就是全部。 由于输出非常详细,您可能希望限制您正在查看的内容:

New-Object Microsoft.Build.Evaluation.Project $Project | 
    Select -Expand Targets |
    Format-Table Name, DependsOnTargets -Wrap
然而,有一个陷阱。 当您这样加载构建时,只要您保持PowerShell窗口打开,并且在卸载它们之前无法重新打开它们,它们就会停留在
GlobalProjectCollection
中。要卸载它们,请执行以下操作:

[Microsoft.Build.Evaluation.ProjectCollection]::GlobalProjectCollection.UnloadAllProjects()
考虑到这一点,在一个可以接受部分和相对路径甚至管道项目文件作为输入的函数中,包装以下内容可能是值得的:

Add-Type -As Microsoft.Build
Update-TypeData -DefaultDisplayPropertySet Name, DependsOnTargets -TypeName Microsoft.Build.Execution.ProjectTargetInstance

function Get-Target {
    param(
        # Path to project file (supports pipeline input and wildcards)
        [Parameter(ValueFromPipelineByPropertyName=$true, ValueFromPipeline=$true, Position=1)]
        [Alias("PSPath")]
        [String]$Project,

        # Filter targets by name. Supports wildcards
        [Parameter(Position=2)]
        [String]$Name = "*"

    )
    begin {
        # People do funny things with parameters
        # Lets make sure they didn't pass a Project file as the name ;)
        if(-not $Project -and $Name -ne "*") {
            $Project = Resolve-Path $Name
            if($Project) { $Name = "*" }
        }
        if(-not $Project) {
            $Project = Get-Item *.*proj
        }
    }
    process {
        Write-Host "Project: $_ Target: $Name"
        Resolve-Path $Project | % {
            # Unroll the ReadOnlyDictionary to get the values so we can filter ...
            (New-Object Microsoft.Build.Evaluation.Project "$_").Targets.Values.GetEnumerator()
        } | Where { $_.Name -like $Name }
    }
    end {
        [microsoft.build.evaluation.projectcollection]::globalprojectcollection.UnloadAllProjects()
    }
}
现在你们甚至不需要手动格式化表格

增编: 显然,您可以使用更新类型数据向输出中添加任何您想要的内容,例如,如果您想要查看条件,或者可能是在目标之前或之后

您甚至可以提取嵌套信息。例如,您可以将上面的
updatetypedata
调用替换为以下两个:

Update-TypeData -MemberName CallTargets -MemberType ScriptProperty -Value {
    $this.Children | ? Name -eq "CallTarget" | %{ $_.Parameters["Targets"] } 
} -TypeName Microsoft.Build.Execution.ProjectTargetInstance

Update-TypeData -DefaultDisplayPropertySet Name, DependsOnTargets, CallTargets -TypeName Microsoft.Build.Execution.ProjectTargetInstance
您可以看到,第一个添加了计算的CallTargets属性,该属性枚举直接子级并查找CallTarget任务以打印其目标,然后我们只在
DefaultDisplayPropertySet
中包含该属性


注意:在构建任何特定目标时,查看将要执行的每个目标需要很多逻辑(为此,我们必须递归地处理依赖的目标,并且我们还需要查找在其前目标或后目标中有此目标的任何目标(也是递归的),那是在我们开始真正可以调用目标的任务之前,比如CallTargets和MSBuild…所有这些事情都可能取决于非常复杂的条件,如果不实际执行它,就不可能知道会发生什么;)

以下是代码片段,用于按执行顺序获取所有目标

    static void Main(string[] args)
    {
        Project project = new Project(@"build.core.xml");
        var orderedTargets = GetAllTargetsInOrderOfExecution(project.Targets, project.Targets["FinalTargetInTheDependencyChain"]).ToList();
        File.WriteAllText(@"orderedTargets.txt", orderedTargets.Select(x => x.Name).Aggregate((a, b) => a + "\r\n" + b));
    }

    /// <summary>
    /// Gets all targets in the order of their execution by traversing through the dependent targets recursively
    /// </summary>
    /// <param name="allTargetsInfo"></param>
    /// <param name="target"></param>
    /// <returns></returns>
    public static List<ProjectTargetInstance> GetAllTargetsInOrderOfExecution(IDictionary<string, ProjectTargetInstance> allTargetsInfo, ProjectTargetInstance target)
    {
        var orderedTargets = new List<ProjectTargetInstance>();

        var dependentTargets =
            target
            .DependsOnTargets
            .Split(';')
            .Where(allTargetsInfo.ContainsKey)
            .Select(x => allTargetsInfo[x])
            .ToList();

        foreach (var dependentTarget in dependentTargets)
        {
            orderedTargets = orderedTargets.Union(GetAllTargetsInOrderOfExecution(allTargetsInfo, dependentTarget)).ToList();
        }

        orderedTargets.Add(target);

        return orderedTargets;
    }
static void Main(字符串[]args)
{
项目=新项目(@“build.core.xml”);
var orderedTargets=GetAllTargetSinOrderoExecution(project.Targets,project.Targets[“FinalTargetHedependencyChain]”)。ToList();
File.writealText(@“orderedTargets.txt”,orderedTargets.Select(x=>x.Name).Aggregate((a,b)=>a+“\r\n”+b));
}
/// 
///通过递归遍历依赖目标,按执行顺序获取所有目标
/// 
/// 
/// 
/// 
公共静态列表GetAllTargetSinoOrderOfExecution(IDictionary allTargetsInfo,ProjectTargetInstance目标)
{
var orderedTargets=新列表();
变量依赖目标=
目标
.dependson目标
.Split(“;”)
.其中(allTargetsInfo.Containeskey)
.选择(x=>allTargetsInfo[x])
.ToList();
foreach(dependentTargets中的变量dependentTarget)
{
orderedTargets=orderedTargets.Union(GetAllTargetsInOrderOfExecution(allTargetsInfo,dependentTarget)).ToList();
}
orderedTargets.Add(目标);
退货订单目标;
}

如果从命令行生成,则可以通过在生成之前设置环境变量来生成元项目文件。该文件将详细说明解决方案文件将针对的所有内容,并概述其目标顺序

它不是一个真正的可读性好的诊断文件,但它包含了您可能需要的XML格式的所有信息

例如,从CMD和典型的VS2017安装;用您使用的任何构建工具加载程序替换第1行

call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
set MSBuildEmitSolution=1
call MSBuild SolutionFile.sln /t:rebuild   

一个真实的例子,我用一个名为<代码>混合的Projj>代码的C项目开始了一个SLN文件,所以我得到了<代码> MixEdProj.SLN ,<代码> MixEdProj.CSProj> <代码>,然后添加了两个C++项目, 我已将生成依赖项顺序设置为where

ConsoleApplication1
仅在
DLL1

以下是用于构建它的命令行:

call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
set MSBuildEmitSolution=1
call MSBuild miced_proj.sln /t:rebuild 

生成的元项目文件内容(太大,无法粘贴到此处)

我最近使用XML解析来检查/修改MSBuild项目文件。很抱歉,没有答案…非常好!在直接子节点
CallTarget
中为值列表添加列有多困难?一些依赖项也存在。问题是它是基于文件的,不会导入包含项,因此一个实际的项目构建文件,您将只得到目标的一小部分。您的解决方案确实提供了一个更完整的报告…但是:您显示了
DependsOnTargets
,但是您也应该
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
set MSBuildEmitSolution=1
call MSBuild miced_proj.sln /t:rebuild