-
-
Save ytkachov/ba4e43975d49f59e2d36836cca10b903 to your computer and use it in GitHub Desktop.
Variant 3: based on idea 'Map of projects'. Illustrates solution-wide PRE/POST 'events' for build-operations from Visual Studio IDE (i.e. primary from VS IDE and msbuild.exe as optional)
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"?> | |
<!-- | |
Illustrates solution-wide PRE/POST 'events' for build-operations from Visual Studio IDE (i.e. primary from VS IDE and msbuild.exe as optional) | |
Alternative for IVsUpdateSolutionEvents2/IVsUpdateSolutionEvents etc. | |
- | |
Variant 3: based on idea 'Map of projects'. | |
Problems for this variant: | |
* Unavailable projects - for example, if user clicked on 'Unload Project' in Solution Explorer. | |
However, it's trivial and not related as main bug.. This should be resolved with 'Reload Project'. | |
- | |
entry.reg@gmail.com | |
--> | |
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> | |
<!-- Build section --> | |
<Target Name="_Build" BeforeTargets="Build" DependsOnTargets="ProjectsMap"> | |
<CallTarget Targets="_BuildPRE" Condition="$(ScopeDetectFirst)" /> | |
<CallTarget Targets="_BuildPOST" Condition="$(ScopeDetectLast)" /> | |
</Target> | |
<Target Name="_BuildPRE"> | |
<!-- ... --> | |
<Exec Command="calc.exe" ContinueOnError="true" /> | |
</Target> | |
<Target Name="_BuildPOST"> | |
<!-- ... --> | |
<Exec Command="notepad.exe" ContinueOnError="true" /> | |
</Target> | |
<!-- Rebuild section --> | |
<Target Name="_Rebuild" BeforeTargets="Rebuild" DependsOnTargets="ProjectsMap"> | |
<CallTarget Targets="_RebuildPRE" Condition="$(ScopeDetectFirst)" /> | |
<CallTarget Targets="_RebuildPOST" Condition="$(ScopeDetectLast)" /> | |
</Target> | |
<Target Name="_RebuildPRE"> | |
<!-- ... --> | |
</Target> | |
<Target Name="_RebuildPOST"> | |
<!-- ... --> | |
</Target> | |
<!-- Clean section --> | |
<Target Name="_Clean" BeforeTargets="Clean" DependsOnTargets="ProjectsMap"> | |
<!-- it should be with the reverse order for Clean targets --> | |
<CallTarget Targets="_CleanPRE" Condition="$(ScopeDetectLast)" /> | |
<CallTarget Targets="_CleanPOST" Condition="$(ScopeDetectFirst)" /> | |
</Target> | |
<Target Name="_CleanPRE"> | |
<!-- ... --> | |
</Target> | |
<Target Name="_CleanPOST"> | |
<!-- ... --> | |
</Target> | |
<!-- helpers --> | |
<Target Name="ProjectsMap"> | |
<ProjectBuildOrder Sln="$(SolutionPath)" OnlyFirst="false"> | |
<Output PropertyName="ProjectBuildOrderString" TaskParameter="Result" /> | |
</ProjectBuildOrder> | |
<ScopeDetect Map="$(ProjectBuildOrderString)" Current="$(ProjectGuid)" IsFirst="true" > | |
<Output PropertyName="ScopeDetectFirst" TaskParameter="Result" /> | |
</ScopeDetect> | |
<ScopeDetect Map="$(ProjectBuildOrderString)" Current="$(ProjectGuid)" IsFirst="false" > | |
<Output PropertyName="ScopeDetectLast" TaskParameter="Result" /> | |
</ScopeDetect> | |
</Target> | |
<UsingTask | |
TaskName="ProjectBuildOrder" | |
TaskFactory="CodeTaskFactory" | |
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll"> | |
<ParameterGroup> | |
<Sln ParameterType="System.String" Required="true" /> | |
<OnlyFirst ParameterType="System.Boolean" Required="true" /> | |
<Result ParameterType="System.String" Output="true" /> | |
</ParameterGroup> | |
<Task> | |
<Reference Include="System.Xml"/> | |
<Using Namespace="System" /> | |
<Using Namespace="System.Collections" /> | |
<Using Namespace="System.Collections.Generic" /> | |
<Using Namespace="System.IO" /> | |
<Using Namespace="System.Text" /> | |
<Using Namespace="System.Text.RegularExpressions" /> | |
<Using Namespace="System.Xml" /> | |
<Code Type="Fragment" Language="cs"> | |
<![CDATA[ | |
string solutionfolder = Path.GetDirectoryName(Sln); | |
// Patterns from Microsoft.Build.BuildEngine.Shared.SolutionParser ! | |
Regex rProject = new Regex("^Project\\(\"(?<TypeGuid>.*)\"\\)\\s*=\\s*\"(?<Name>.*)\"\\s*,\\s*\"(?<Path>.*)\"\\s*,\\s*\"(?<Guid>.*)\"$"); | |
Regex rProperty = new Regex("^(?<PName>[^=]*)\\s*=\\s*(?<PValue>[^=]*)$"); | |
//Projects with dependencies | |
Dictionary<string, List<string>> projects = new Dictionary<string, List<string>>(); | |
using (StreamReader reader = new StreamReader(Sln, Encoding.Default)) | |
{ | |
string line; | |
while ((line = reader.ReadLine()) != null) | |
{ | |
line = line.Trim(); | |
if (!line.StartsWith("Project(", StringComparison.Ordinal)) | |
{ | |
continue; | |
} | |
Match m = rProject.Match(line); | |
if (!m.Success) | |
{ | |
throw new Exception("incorrect line"); | |
} | |
if (String.Equals("{2150E333-8FDC-42A3-9474-1A3956D46DE8}", m.Groups["TypeGuid"].Value.Trim(), StringComparison.OrdinalIgnoreCase)) | |
{ | |
continue; //SolutionFolder | |
} | |
if (String.Equals("{D954291E-2A0B-460D-934E-DC6B0785DB48}", m.Groups["TypeGuid"].Value.Trim(), StringComparison.OrdinalIgnoreCase)) | |
{ | |
continue; // SharedCodeProject | |
} | |
string pGuid = m.Groups["Guid"].Value.Trim(); | |
projects[pGuid] = new List<string>(); | |
while ((line = reader.ReadLine()) != null && (line != "EndProject")) | |
{ | |
line = line.Trim(); | |
if (line.StartsWith("ProjectSection(ProjectDependencies)", StringComparison.Ordinal)) | |
{ | |
for (line = reader.ReadLine(); line != null; line = reader.ReadLine()) | |
{ | |
line = line.Trim(); | |
if (line.StartsWith("EndProjectSection", StringComparison.Ordinal)) | |
{ | |
break; | |
} | |
projects[pGuid].Add(rProperty.Match(line).Groups["PName"].Value.Trim()); | |
} | |
} | |
} | |
// find dependent projects in project file itself | |
string prjpath = Path.Combine(solutionfolder, m.Groups["Path"].Value.Trim()); | |
List<string> refprjs = new List<string>(); | |
XmlDocument prj = new XmlDocument(); | |
XmlReaderSettings readerSettings = new XmlReaderSettings(); | |
//readerSettings.DtdProcessing = DtdProcessing.Ignore; | |
// Load the .etp project file thru the XML reader | |
using (XmlReader xmlReader = XmlReader.Create(prjpath, readerSettings)) | |
{ | |
prj.Load(xmlReader); | |
} | |
XmlNamespaceManager ns = new XmlNamespaceManager(prj.NameTable); | |
ns.AddNamespace("p", "http://schemas.microsoft.com/developer/msbuild/2003"); | |
XmlNodeList refprojects = prj.DocumentElement.SelectNodes(@"descendant::p:ProjectReference/p:Project", ns); | |
foreach (XmlNode referenceNode in refprojects) | |
refprjs.Add(referenceNode.InnerText.ToUpper()); | |
projects[pGuid].AddRange(refprjs); | |
} | |
} | |
// Build order | |
Queue<string> steps = new Queue<string>(); // it should be with the reverse order for Clean targets | |
Func<string, bool> h = null; | |
h = delegate (string id) | |
{ | |
if (projects.ContainsKey(id)) | |
{ | |
projects[id].ForEach(dep => h(dep)); | |
if (!steps.Contains(id)) | |
{ | |
steps.Enqueue(id); | |
} | |
} | |
return true; | |
}; | |
foreach (KeyValuePair<string, List<string>> project in projects) | |
{ | |
h(project.Key); | |
if (!steps.Contains(project.Key)) | |
{ | |
steps.Enqueue(project.Key); | |
} | |
} | |
Result = (OnlyFirst) ? steps.Peek() : string.Join(";", steps.ToArray()); | |
]]> | |
</Code> | |
</Task> | |
</UsingTask> | |
<UsingTask | |
TaskName="ScopeDetect" | |
TaskFactory="CodeTaskFactory" | |
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v$(MSBuildToolsVersion).dll"> | |
<ParameterGroup> | |
<Map ParameterType="System.String" Required="true" /> | |
<Current ParameterType="System.String" Required="true" /> | |
<IsFirst ParameterType="System.Boolean" Required="true" /> | |
<Result ParameterType="System.Boolean" Output="true" /> | |
</ParameterGroup> | |
<Task> | |
<Using Namespace="System" /> | |
<Code Type="Fragment" Language="cs"> | |
<![CDATA[ | |
if(String.IsNullOrEmpty(Map) || String.IsNullOrEmpty(Current)) | |
{ | |
throw new Exception("'Map' or 'Current' arg is empty"); | |
} | |
string[] items = Map.Split(';'); | |
if(IsFirst && items[0] == Current) | |
{ | |
Result = true; | |
return _Success; | |
} | |
if(!IsFirst && items[items.Length - 1] == Current) | |
{ | |
Result = true; | |
return _Success; | |
} | |
Result = false; | |
]]> | |
</Code> | |
</Task> | |
</UsingTask> | |
<!-- remap --> | |
<Target Name="Build" DependsOnTargets="_Build" /> | |
<Target Name="BuildSelection" DependsOnTargets="_Build" /> | |
<Target Name="BuildOnlyProject" DependsOnTargets="_Build" /> | |
<Target Name="BuildCtx" DependsOnTargets="_Build" /> | |
<Target Name="Rebuild" DependsOnTargets="_Rebuild" /> | |
<Target Name="RebuildSelection" DependsOnTargets="_Rebuild" /> | |
<Target Name="RebuildOnlyProject" DependsOnTargets="_Rebuild" /> | |
<Target Name="RebuildCtx" DependsOnTargets="_Rebuild" /> | |
<Target Name="Clean" DependsOnTargets="_Clean" /> | |
<Target Name="CleanSelection" DependsOnTargets="_Clean" /> | |
<Target Name="CleanOnlyProject" DependsOnTargets="_Clean" /> | |
<Target Name="CleanCtx" DependsOnTargets="_Clean" /> | |
<Target Name="Deploy" DependsOnTargets="_Deploy" /> | |
<Target Name="DeploySelection" DependsOnTargets="_Deploy" /> | |
<Target Name="DeployCtx" DependsOnTargets="_Deploy" /> | |
</Project> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment