-
-
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 don't understand this.
Your solution is genious but it does not take into consideration projects' dependencies estableshed in project file. It could be easily corrected. Do you want me to send this fix?
Your solution is genious but it does not take into consideration projects' dependencies estableshed in project file. It could be easily corrected. Do you want me to send this fix?
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!
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
use env var for caching results from ProjectsMap and similar... for a quick optimization