-
-
Save 3F/a77129e3978841241927 to your computer and use it in GitHub Desktop.
<?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> | |
<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" /> | |
<Code Type="Fragment" Language="cs"> | |
<![CDATA[ | |
// 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 | |
} | |
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()); | |
} | |
} | |
} | |
} | |
} | |
// 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) | |
{ | |
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> |
I forked this gist and and made relevant chages in it but do not know how to make pull request for it. Thank you for your excellent work.
hmm, I don't use Gist so often (my today's sandbox
repo on github is better way for this). More probably your changes will be just avaialble from /forks page o_O ie. to integrate this manually. Still lot of inconceivable behaviors from this service by my view (at least they finally added notifications for the past ~5 years <_<).
But if you want to contribute for related MvsSln, you need of course to push your committed changes as PR into: https://github.com/3F/MvsSln
Thank you for your excellent work.
You're welcome. Glad that was really helpful :)
I recommend you also consider to use an independent toolset like vssbe & mvssln
https://github.com/3F
@ytkachov
Why not :)
btw, this is old draft that was finally implemented in https://github.com/3F/vsSolutionBuildEvent for that period.
However, part of this solution now is core of LProjectDependencies's logic in the new MvsSln: https://github.com/3F/MvsSln
Here's map example:
I mean, I remember some fixes for this, but anyway, your PR is welcomed for mentioned project too.
Thanks!