-
-
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!