Как запросить файл MSBUILD для списка поддерживаемых целей?
Есть ли способ спросить msbuild, какие цели сборки предоставляются для поддержки файла msbuild? Если в командной строке нет способа сделать это? Может быть, это можно сделать программно?
Нет ли способа сделать это, кроме разбора XML файла msbuild?
Ответы
Ответ 1
Обновлено для .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);
}
}
}
Ответ 2
Разумеется, MS предоставляет api для этого без синтаксического разбора xml. Найдите microsoft.build.buildengine
Адаптировано из некоторого кода С# найденного в msdn... обычно это стоит изучить. Нужно ссылаться на dll microsoft.build.engine для компиляции. Замените версию каркаса и путь ниже вашими значениями. Это работало над образцовым файлом проекта, хотя список может быть длиннее, чем вы ожидаете.
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);
}
}
}
Ответ 3
Мне понравилась идея использования PowerShell, но чистое XML-решение не работает, поскольку оно выводит цели, определенные в этом файле проекта, а не импорт. Конечно, код С#, о котором все говорят, мертв просто, и с .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
Однако есть уловка.
Когда вы загружаете сборки вроде этого, они вставляются в GlobalProjectCollection
, пока вы оставляете окно PowerShell открытым, и вы не можете повторно открывать их, пока вы их не выгрузите. Чтобы разгрузить их:
[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 для печати своих целей, а затем мы просто включаем, что ind DefaultDisplayPropertySet
.
ПРИМЕЧАНИЕ: Для этого потребуется много логики, чтобы увидеть каждую цель, которая будет выполняться при создании какой-либо конкретной цели (для этого нам нужно рекурсивно обрабатывать DependsOnTargets, и нам также нужно будет искать любые цели, у которых есть эта цель, в их BeforeTargets или AfterTargets (также рекурсивно), и что перед тем, как мы перейдем к задачам, которые могут фактически вызвать цели, такие как CallTargets и MSBuild... и все это может зависеть от настолько сложных условий, что невозможно сказать, что произойдет, не выполняя его;)
Ответ 4
Я предлагаю вам использовать 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
и отобразит целевое имя и зависимости в формате таблицы. Вот пример, который выбирает 10 первых целей из Microsoft.Web.Publishing.targets
:
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...
Ответ 5
Вот фрагмент кода, чтобы получить все цели в порядке их выполнения.
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;
}