Есть ли способ спросить msbuild, какие цели сборки предоставляются для поддержки файла msbuild? Если в командной строке нет способа сделать это? Может быть, это можно сделать программно?
Нет ли способа сделать это, кроме разбора XML файла msbuild?
Есть ли способ спросить msbuild, какие цели сборки предоставляются для поддержки файла msbuild? Если в командной строке нет способа сделать это? Может быть, это можно сделать программно?
Нет ли способа сделать это, кроме разбора XML файла msbuild?
Обновлено для .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);
}
}
}
Разумеется, 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);
}
}
}
Мне понравилась идея использования 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... и все это может зависеть от настолько сложных условий, что невозможно сказать, что произойдет, не выполняя его;)
Я предлагаю вам использовать 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...
Вот фрагмент кода, чтобы получить все цели в порядке их выполнения.
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;
}