Мне понравилась идея использования 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 ... и все эти вещи могут зависеть от таких сложных условий, что невозможно сказать, что произойдет, не выполняя их на самом деле ;)