Created
June 4, 2015 15:25
-
-
Save 3F/a77129e3978841241927 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> | |
<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> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
@ytkachov
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
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