Created
December 16, 2018 15:35
-
-
Save valerysntx/c3ea8747ed4ec1aa94b7764cdfa1cd81 to your computer and use it in GitHub Desktop.
Scan Indirect Project Dependencies Recursively
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8"?> | |
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<!-- All the stuff normally found in the project, then in the AfterBuild event... --> | |
<Target Name="AfterBuild"> | |
<!-- Here's the call to the custom task to get the list of dependencies --> | |
<ScanIndirectDependencies StartFolder="$(MSBuildProjectDirectory)" | |
StartProjectReferences="@(ProjectReference)" | |
Configuration="$(Configuration)"> | |
<Output TaskParameter="IndirectDependencies" ItemName="IndirectDependenciesToCopy" /> | |
</ScanIndirectDependencies> | |
<!-- Only copy the file in if we won't stomp something already there --> | |
<Copy SourceFiles="%(IndirectDependenciesToCopy.FullPath)" | |
DestinationFolder="$(OutputPath)" | |
Condition="!Exists('$(OutputPath)\%(IndirectDependenciesToCopy.Filename)%(IndirectDependenciesToCopy.Extension)')" /> | |
</Target> | |
<!-- THE CUSTOM TASK! --> | |
<UsingTask TaskName="ScanIndirectDependencies" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v12.0.dll"> | |
<ParameterGroup> | |
<StartFolder Required="true" /> | |
<StartProjectReferences ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" /> | |
<Configuration Required="true" /> | |
<IndirectDependencies ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" /> | |
</ParameterGroup> | |
<Task> | |
<Reference Include="System.Xml"/> | |
<Using Namespace="Microsoft.Build.Framework" /> | |
<Using Namespace="Microsoft.Build.Utilities" /> | |
<Using Namespace="System" /> | |
<Using Namespace="System.Collections.Generic" /> | |
<Using Namespace="System.IO" /> | |
<Using Namespace="System.Linq" /> | |
<Using Namespace="System.Xml" /> | |
<Code Type="Fragment" Language="cs"> | |
<![CDATA[ | |
var projectReferences = new List<string>(); | |
var toScan = new List<string>(StartProjectReferences.Select(p => Path.GetFullPath(Path.Combine(StartFolder, p.ItemSpec)))); | |
var indirectDependencies = new List<string>(); | |
bool rescan; | |
do{ | |
rescan = false; | |
foreach(var projectReference in toScan.ToArray()) | |
{ | |
if(projectReferences.Contains(projectReference)) | |
{ | |
toScan.Remove(projectReference); | |
continue; | |
} | |
Log.LogMessage(MessageImportance.Low, "Scanning project reference for other project references: {0}", projectReference); | |
var doc = new XmlDocument(); | |
doc.Load(projectReference); | |
var nsmgr = new XmlNamespaceManager(doc.NameTable); | |
nsmgr.AddNamespace("msb", "http://schemas.microsoft.com/developer/msbuild/2003"); | |
var projectDirectory = Path.GetDirectoryName(projectReference); | |
// Find all project references we haven't already seen | |
var newReferences = doc | |
.SelectNodes("/msb:Project/msb:ItemGroup/msb:ProjectReference/@Include", nsmgr) | |
.Cast<XmlAttribute>() | |
.Select(a => Path.GetFullPath(Path.Combine(projectDirectory, a.Value))); | |
if(newReferences.Count() > 0) | |
{ | |
Log.LogMessage(MessageImportance.Low, "Found new referenced projects: {0}", String.Join(", ", newReferences)); | |
} | |
toScan.Remove(projectReference); | |
projectReferences.Add(projectReference); | |
// Add any new references to the list to scan and mark the flag | |
// so we run through the scanning loop again. | |
toScan.AddRange(newReferences); | |
rescan = true; | |
// Include the assembly that the project reference generates. | |
var outputLocation = Path.Combine(Path.Combine(projectDirectory, "bin"), Configuration); | |
var localAsm = Path.GetFullPath(Path.Combine(outputLocation, doc.SelectSingleNode("/msb:Project/msb:PropertyGroup/msb:AssemblyName", nsmgr).InnerText + ".dll")); | |
if(!indirectDependencies.Contains(localAsm) && File.Exists(localAsm)) | |
{ | |
Log.LogMessage(MessageImportance.Low, "Added project assembly: {0}", localAsm); | |
indirectDependencies.Add(localAsm); | |
} | |
// Include third-party assemblies referenced by file location. | |
var externalReferences = doc | |
.SelectNodes("/msb:Project/msb:ItemGroup/msb:Reference/msb:HintPath", nsmgr) | |
.Cast<XmlElement>() | |
.Select(a => Path.GetFullPath(Path.Combine(projectDirectory, a.InnerText.Trim()))) | |
.Where(e => !indirectDependencies.Contains(e)); | |
Log.LogMessage(MessageImportance.Low, "Found new indirect references: {0}", String.Join(", ", externalReferences)); | |
indirectDependencies.AddRange(externalReferences); | |
} | |
} while(rescan); | |
// Expand to include pdb and xml. | |
var xml = indirectDependencies.Select(f => Path.Combine(Path.GetDirectoryName(f), Path.GetFileNameWithoutExtension(f) + ".xml")).Where(f => File.Exists(f)).ToArray(); | |
var pdb = indirectDependencies.Select(f => Path.Combine(Path.GetDirectoryName(f), Path.GetFileNameWithoutExtension(f) + ".pdb")).Where(f => File.Exists(f)).ToArray(); | |
indirectDependencies.AddRange(xml); | |
indirectDependencies.AddRange(pdb); | |
Log.LogMessage("Located indirect references:\n{0}", String.Join(Environment.NewLine, indirectDependencies)); | |
// Finally, assign the output parameter. | |
IndirectDependencies = indirectDependencies.Select(i => new TaskItem(i)).ToArray(); | |
]]> | |
</Code> | |
</Task> | |
</UsingTask> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment