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

问题描述 投票:23回答:6

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

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

msbuild
6个回答
11
投票

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

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

17
投票

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

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

Yeah. Really. That's the whole thing.

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

New-Object Microsoft.Build.Evaluation.Project $Project | 
    Select -Expand Targets |
    Format-Table Name, DependsOnTargets -Wrap

There is a catch, however.

当您加载这样的构建时,只要您将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()
    }
}

而现在你甚至不需要手动格式表...

附录:

显然,您可以使用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
投票

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

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

0
投票

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

它不是一个读取友好的诊断文件,但确实拥有您可能需要的所有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   

举一个真实的例子,我创建了一个SLN文件,从一个名为mixed_proj的C#项目开始,所以我有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是生成的元项目文件内容(太大了,无法粘贴)

© www.soinside.com 2019 - 2024. All rights reserved.