In msbuild task definiertes Element über C # auswerten
Ich versuche, ein gerichtetes Diagramm (Punktformat) von Zielen zu generieren, das mit einem MSBuild-Projektdateipfad als Eingabe aufgerufen wird. Dies ist dasselbe wiegrand-ui von ggtools.net (außer dass ich versuche, mit einem schreibgeschützten Bild zu beginnen).
Es gibt 3 Fälle, die ich behandeln möchte:
1) Ein Ziel hat das Attribut DependsOnTargets
2) Ein Ziel ruft MSBuild mit der Exec-Task auf
3) Ein Ziel ruft die MSBuild-Task auf und übergibt eine ItemGroup
Ich glaube, ich habe meistens # 1 und # 2 im Code unten gehandhabt. Ich kann jedoch nicht herausfinden, wie ich einen Verweis auf die ItemGroup erhalten kann.
MSBuild-Beispielprojekt:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTargets="Build">
<Target Name="Build" DependsOnTargets="PrepareEnvironmentForBuild;MapDrives">
<Exec Command="$(MSBuildBinPath)\msbuild /nologo /clp:Verbosity=quiet $(MSBuildThisFileFullPath) /t:TargetWithConfidentialSteps"/>
<ItemGroup>
<StepsToRunInParallel Include="$(MSBuildThisFileFullPath)">
<Properties>TargetToInvoke=ParallelTarget1</Properties>
</StepsToRunInParallel>
<StepsToRunInParallel Include="$(MSBuildThisFileFullPath)">
<Properties>TargetToInvoke=ParallelTarget2</Properties>
</StepsToRunInParallel>
</ItemGroup>
<MSBuild Projects="@(StepsToRunInParallel)" BuildInParallel="true" StopOnFirstFailure="true" Targets="InvokeInParallelWithinThisProject"/>
</Target>
<Target Name="InvokeInParallelWithinThisProject">
<MSBuild Projects="$(MSBuildThisFileFullPath)" Targets="$(TargetToInvoke)" StopOnFirstFailure="true"/>
</Target>
<Target Name="ParallelTarget1">
<Message Text="Hello from ParallelTarget1"/>
</Target>
<Target Name="ParallelTarget2">
<Message Text="Hello from ParallelTarget2"/>
</Target>
<Target Name="PrepareEnvironmentForBuild">
<Message Text="Hello from PrepareEnvironmentForBuild"/>
</Target>
<Target Name="MapDrives">
<Message Text="Hello from MapDrives"/>
</Target>
<Target Name="TargetWithConfidentialSteps">
<Message Text="Hush! Verbosity on the wrapper does not affect the Exec call." Importance="High"/>
</Target>
</Project>
Die Ziele können sich jedoch über mehrere Projektdateien erstrecken.
Was ich bisher geschafft habe (VS2010 + .NET 4):
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
namespace ConsoleApplication18
{
class Program
{
static String ProcessMSBuildFile(System.IO.FileInfo file, ProjectTaskInstance referringTask = null)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine("subgraph cluster_" + file.FullName.Replace(".", "_").Replace(@"\", "_").Replace(":", "_") + " {");
ProjectInstance currentProj = new ProjectInstance(file.FullName);
foreach (ProjectTargetInstance target in currentProj.Targets.Values)
{
if (String.IsNullOrWhiteSpace(target.DependsOnTargets))
{
sb.AppendLine(target.Name + ";");
}
else
{
for (int i = 0; i < target.DependsOnTargets.Split(';').Length; i++)
{
sb.AppendLine(target.Name + "->" + currentProj.ExpandString(target.DependsOnTargets.Split(';')[i]) + ";");
}
}
foreach (ProjectTaskInstance task in target.Tasks)
{
if (task.Name == "MSBuild")
{
foreach (String projectPath in task.Parameters["Projects"].Split(';').AsEnumerable<String>())
{
if (projectPath.StartsWith("$"))
{
if (currentProj.ExpandString(projectPath) != currentProj.FullPath)
{
sb.Append(ProcessMSBuildFile(new System.IO.FileInfo(currentProj.ExpandString(projectPath))));
}
}
else if (projectPath.StartsWith("@"))
{
sb.AppendLine("Could not handle : " + projectPath);
}
}
foreach (String msbuildTaskTarget in task.Parameters["Targets"].Split(';'))
{
if (referringTask != null)
{
sb.AppendLine(referringTask.Name + "->" + task.Name + ";");
}
else
{
sb.AppendLine(task.Name + ";");
}
}
}
else if (task.Name == "Exec")
{
// Get the Command. If it is MSBuild, we should process it further.
if (task.Parameters.ContainsKey("Command"))
{
String taskCommand = currentProj.ExpandString(task.Parameters["Command"]);
String executablePath = taskCommand.Substring(0, taskCommand.IndexOf(" "));
if (new System.IO.FileInfo(executablePath).Name.ToLower() == "msbuild")
{
String[] separator = { " " };
String projectPath = taskCommand.Split(separator, StringSplitOptions.RemoveEmptyEntries).Where<String>(arg => !arg.StartsWith("/")).ElementAt(1);
if (projectPath != currentProj.FullPath)
{
ProcessMSBuildFile(new System.IO.FileInfo(projectPath));
}
else
{
foreach (String targetToBeCalled in taskCommand.Split(separator, StringSplitOptions.RemoveEmptyEntries).Where(arg => (arg.ToLower().StartsWith("/t") || arg.ToLower().StartsWith("/target"))))
{
sb.Append(target.Name + "->" + targetToBeCalled.Replace("/target:", "").Replace("/t:", ""));
}
}
}
}
}
}
}
sb.AppendLine("}");
return sb.ToString();
}
static void Main(string[] args)
{
if (args.Length != 1)
{
System.Console.WriteLine("Please pass the path to an MSBuild project as argument.\nPress any key to quit.");
System.Console.Read();
return;
}
if (!System.IO.File.Exists(args[0]))
{
System.Console.WriteLine("Input MSBuild project file does not exist.\nPress any key to quit.");
System.Console.Read();
return;
}
StringBuilder sb = new StringBuilder();
sb.AppendLine("digraph Build {");
sb.Append(ProcessMSBuildFile(new System.IO.FileInfo(args[0])));
sb.AppendLine("}");
System.Console.WriteLine(sb);
}
}
}
Das Problem liegt im Abschnitt "Konnte nicht verarbeiten".
Was ich versuche zu generieren:
digraph Build {
subgraph cluster_C__temp_proj {
Build->PrepareEnvironmentForBuild;
Build->MapDrives;
Build->TargetWithConfidentialSteps;
Build->InvokeInParallelWithinThisProject;
InvokeInParallelWithinThisProject->ParallelTarget1;
InvokeInParallelWithinThisProject->ParallelTarget2;
InvokeInParallelWithinThisProject;
ParallelTarget1;
ParallelTarget2;
PrepareEnvironmentForBuild;
MapDrives;
TargetWithConfidentialSteps;
}
}
Kann mir jemand helfen, einen Verweis auf ItemGroup "StepsToRunInParallel" aus dem obigen Beispiel zu erhalten?
Ich dachte auch daran, einen Logger zu registrieren, der einfach aufzeichnet:
1) Projektdateiname zur Erzeugung von Clustern / Subgraphen
2) Zielname
Und dann verbrauchen Sie das Ereignis, ohne etwas auszuführen. Ideen auch in diese Richtung sind willkommen.