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

时间:2009-01-14 01:43:28

标签: msbuild

有没有办法问msbuild什么构建目标提供msbuild文件支持?如果在命令提示符下无法执行此操作?可能是以编程方式完成的吗?

除了解析msbuild XML之外,还有办法吗?

6 个答案:

答案 0 :(得分:18)

当然,MS提供了api,无需自己解析xml。查找microsoft.build.buildengine

改编自一些C#code found on msdn ...通常值得探索。需要引用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);
    }
  }
}

答案 1 :(得分:17)

我喜欢使用PowerShell的想法,但纯XML解决方案不起作用,因为它只输出 项目文件中定义的目标,而不是导入。当然,每个人一直指的C#代码都很简单,而.Net 4.5则是两行(首先你应该考虑添加到你的个人资料中):

Add-Type -As Microsoft.Build
New-Object Microsoft.Build.Evaluation.Project $Project | Select -Expand Targets 

呀。真。这就是整个事情。

由于输出非常详细,您可能希望限制您正在查看的内容:

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()
    }
}

现在你甚至不需要手动设置Format-Table ...

附录:

显然,您可以使用Update-TypeData向输出添加任何内容,例如,如果您想查看条件,或者可能是BeforeTargets或AfterTargets ......

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

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中。

注意:在构建任何特定目标时,要查看将要执行的每个目标需要很多逻辑(为此,我们必须递归处理DependsOnTargets,我们还需要在他们的 BeforeTargets或AfterTargets(也是递归)中查找具有此目标的任何目标,并且之前是我们可以进入实际上只能调用目标的任务,例如CallTargets MSBuild ...所有这些都可以依赖于如此复杂的条件,以至于不可能知道发生了什么在没有实际执行的情况下发生;)

答案 2 :(得分:10)

针对.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);
    }
  }
}

答案 3 :(得分:2)

我建议您使用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...

答案 4 :(得分:0)

以下是按执行顺序获取所有目标的代码段。

    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;
    }

答案 5 :(得分:0)

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

它实际上不是易读的诊断文件,但确实具有您可能需要的XML格式的所有信息。

例如来自CMD和典型的VS2017安装;将第1行替换为您使用的任何build-tool-loader

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

举一个真实的例子,我已经创建了一个名为mixed_proj的C#项目开始的SLN文件,所以我得到了mixed_proj.slnmixed_proj.csproj和两个C ++项目添加了ConsoleApplication1DLL1

我已将构建依赖关系顺序设置为ConsoleApplication1仅在DLL1之后构建的位置

以下是构建它的命令行:

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

Here是所生成的metaproject文件内容(太大,无法在此处粘贴