Как запросить файл MSBUILD для получения списка поддерживаемых целей?
Есть ли способ спросить msbuild, какие цели сборки обеспечивали поддержку файла msbuild? Если нет способа сделать это в командной строке? Может быть, это можно сделать программно?
Нет ли способа сделать это, кроме синтаксического анализа msbuild XML?
5 ответов
Обновлен для.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
Адаптировано из некоторого кода C#, найденного в 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 не работает, потому что оно выводит только цели, определенные в этом файле проекта, а не импортирует. Конечно, код 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
Однако есть одна загвоздка.
Когда вы загружаете такие сборки, они втыкаются в 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, чтобы напечатать их цели, а затем мы просто включаем это 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;
}
Если вы строите из командной строки, вы можете сгенерировать файл метапроекта, установив переменную среды перед сборкой. Этот файл детализирует все, на что будет нацелен файл решения, а также излагает их целевой порядок.
Диагностический файл не очень удобен для чтения, но содержит всю необходимую информацию в формате 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
В качестве реального примера я создал файл SLN, начинающийся с проекта C#, который называется mixed_proj
так что у меня есть mixed_proj.sln
, mixed_proj.csproj
Затем два проекта C++, которые я добавил, ConsoleApplication1
а также DLL1
,
Я установил порядок зависимости сборки где ConsoleApplication1
строит только после DLL1
Вот командная строка для его сборки:
call "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Enterprise\Common7\Tools\VsDevCmd.bat"
set MSBuildEmitSolution=1
call MSBuild miced_proj.sln /t:rebuild
Вот сгенерированное содержимое файла метапроекта (слишком большой для вставки)