// // Solution.cs // //code taken in project https://sourceforge.net/projects/syncproj/ // no licence info was included, I took the liberty to modify it. // Author: // tarmopikaro // 2018 Jean-Philippe Bruyère //MIT-licenced using Microsoft.Win32; using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Xml.Serialization; using Crow; namespace CrowIDE{ public class SolutionProject { public string ProjectHostGuid; public string ProjectName; public string RelativePath; public string ProjectGuid; } /// /// .sln loaded into class. /// public class Solution: IValueChange { #region IValueChange implementation public event EventHandler ValueChanged; public virtual void NotifyValueChanged(string MemberName, object _value) { ValueChanged.Raise(this, new ValueChangeEventArgs(MemberName, _value)); } #endregion ProjectItem selectedItem = null; object selectedItemElement = null; ObservableList openedItems = new ObservableList(); public ObservableList OpenedItems { get { return openedItems; } set { if (openedItems == value) return; openedItems = value; NotifyValueChanged ("OpenedItems", openedItems); } } public ProjectItem SelectedItem { get { return selectedItem; } set { if (selectedItem == value) return; selectedItem = value; NotifyValueChanged ("SelectedItem", selectedItem); } } public object SelectedItemElement { get { return selectedItemElement; } set { if (selectedItemElement == value) return; selectedItemElement = value; NotifyValueChanged ("SelectedItemElement", selectedItemElement); } } public string DisplayName { get { return name; } } /// /// Gets solution path /// public String SolutionFolder { get { return Path.GetDirectoryName (path); } } // public System.CodeDom.Compiler.CompilerErrorCollection CompilationErrors { // get { // System.CodeDom.Compiler.CompilerErrorCollection tmp = Projects.SelectMany // (p => p.CompilationResults.Errors); // return tmp; // } // } public List CompilerErrors { get { int errCount = 0; for (int i = 0; i < Projects.Count; i++) { if (Projects [i].CompilationResults != null) errCount += Projects [i].CompilationResults.Errors.Count; } System.CodeDom.Compiler.CompilerError[] tmp = new System.CodeDom.Compiler.CompilerError[errCount]; int ptr = 0; for (int i = 0; i < Projects.Count; i++) { if (Projects [i].CompilationResults == null) continue; Projects [i].CompilationResults.Errors.CopyTo (tmp,ptr); ptr += Projects [i].CompilationResults.Errors.Count; } return new List(tmp); } } public void UpdateErrorList () { NotifyValueChanged ("CompilerErrors", CompilerErrors); } void onSelectedItemChanged (object sender, SelectionChangeEventArgs e){ ProjectItem pi = e.NewValue as ProjectItem; if (pi == null) return; if (openedItems.Contains (pi)) return; openedItems.AddElement (pi); } public void OnCloseTab (object sender, MouseButtonEventArgs e){ openedItems.RemoveElement ((sender as GraphicObject).DataSource as ProjectItem); } public void CloseItem (ProjectItem pi) { openedItems.RemoveElement (pi); } /// /// Solution name /// String name; /// /// File path from where solution was loaded. /// [XmlIgnore] String path; /// /// Solution name for debugger. /// [ExcludeFromCodeCoverage] public override string ToString() { return "Solution: " + name; } #region Solution properties double slnVer; // 11.00 - vs2010, 12.00 - vs2015 /// /// Visual studio version information used for generation, for example 2010, 2012, 2015 and so on... /// public int fileFormatVersion; /// /// null for old visual studio's /// public String VisualStudioVersion; /// /// null for old visual studio's /// public String MinimumVisualStudioVersion; /// /// List of project included into solution. /// public List Projects = new List(); /// /// List of configuration list, in form "{Configuration}|{Platform}", for example "Release|Win32". /// To extract individual platforms / configuration list, use following functions. /// public List configurations = new List(); /// /// Extracts platfroms supported by solution /// public IEnumerable getPlatforms() { return configurations.Select(x => x.Split('|')[1]).Distinct(); } /// /// Extracts configuration names supported by solution /// public IEnumerable getConfigurations() { return configurations.Select(x => x.Split('|')[0]).Distinct(); } #endregion public Configuration UserConfig; public Project StartupProject { get { return Projects?.FirstOrDefault (p => p.ProjectGuid == UserConfig.Get ("StartupProject")); } set { if (value == StartupProject) return; if (value == null) UserConfig.Set ("StartupProject", ""); else { UserConfig.Set ("StartupProject", value.ProjectGuid); value.NotifyValueChanged("IsStartupProject", true); } NotifyValueChanged ("StartupProject", StartupProject); } } #region CTOR /// /// Creates new solution. /// public Solution() { } #endregion #region Loading from SLN file /// /// Loads visual studio .sln solution /// /// The file specified in path was not found. static public Solution LoadSolution(string path) { Solution s = new Solution(); s.path = path; String slnTxt = File.ReadAllText(path); // // Extra line feed is used by Visual studio, cmake does not generate extra line feed. // s.slnVer = Double.Parse(Regex.Match(slnTxt, "[\r\n]?Microsoft Visual Studio Solution File, Format Version ([0-9.]+)", RegexOptions.Multiline).Groups[1].Value, CultureInfo.InvariantCulture); int vsNumber = Int32.Parse(Regex.Match(slnTxt, "^\\# Visual Studio (Express )?([0-9]+)", RegexOptions.Multiline).Groups[2].Value); if (vsNumber > 2000) s.fileFormatVersion = vsNumber; else s.fileFormatVersion = vsNumber - 14 + 2015; // Visual Studio 14 => vs2015, formula might not be applicable for future vs versions. foreach (String line in new String[] { "VisualStudioVersion", "MinimumVisualStudioVersion" }) { var m = Regex.Match(slnTxt, "^" + line + " = ([0-9.]+)", RegexOptions.Multiline); String v = null; if (m.Success) v = m.Groups[1].Value; s.GetType().GetField(line).SetValue(s, v); } Regex reProjects = new Regex( "Project\\(\"(?{[A-F0-9-]+})\"\\) = \"(?.*?)\", \"(?.*?)\", \"(?{[A-F0-9-]+})\"[\r\n]*(?.*?)EndProject[\r\n]+", RegexOptions.Singleline); reProjects.Replace(slnTxt, new MatchEvaluator(m => { SolutionProject p = new SolutionProject (); foreach (String g in reProjects.GetGroupNames()) { if (g == "0") //"0" - RegEx special kind of group continue; // // ProjectHostGuid, ProjectName, RelativePath, ProjectGuid fields/properties are set here. // String v = m.Groups[g].ToString(); if (g != "dependencies") { FieldInfo fi = p.GetType().GetField(g); if (fi != null) { fi.SetValue(p, v); } else { p.GetType().GetProperty(g).SetValue(p, v); } continue; } if (v == "") // No dependencies set continue; String depsv = new Regex("ProjectSection\\(ProjectDependencies\\)[^\r\n]*?[\r\n]+" + "(.*?)" + "EndProjectSection", RegexOptions.Singleline).Match(v).Groups[1].Value; // // key is always equal to it's value. // http://stackoverflow.com/questions/5629981/question-about-visual-studio-sln-file-format // var ProjectDependencies = new Regex("\\s*?({[A-F0-9-]+}) = ({[A-F0-9-]+})[\r\n]+", RegexOptions.Multiline).Matches(depsv).Cast().Select(x => x.Groups[1].Value).ToList(); } //foreach s.Projects.Add(new Project (s, p)); return ""; } ) ); new Regex("GlobalSection\\(SolutionConfigurationPlatforms\\).*?[\r\n]+(.*?)EndGlobalSection[\r\n]+", RegexOptions.Singleline).Replace(slnTxt, new MatchEvaluator(m2 => { s.configurations = new Regex("\\s*(.*)\\s+=").Matches(m2.Groups[1].ToString()).Cast().Select(x => x.Groups[1].Value).ToList(); return ""; } )); new Regex("GlobalSection\\(ProjectConfigurationPlatforms\\).*?[\r\n]+(.*?)EndGlobalSection[\r\n]+", RegexOptions.Singleline).Replace(slnTxt, new MatchEvaluator(m2 => { foreach (Match m3 in new Regex("\\s*({[A-F0-9-]+})\\.(.*?)\\.(.*?)\\s+=\\s+(.*?)[\r\n]+").Matches(m2.Groups[1].ToString())) { String guid = m3.Groups[1].Value; String solutionConfig = m3.Groups[2].Value; String action = m3.Groups[3].Value; String projectConfig = m3.Groups[4].Value; Project p = s.Projects.Where(x => x.ProjectGuid == guid).FirstOrDefault(); if (p == null) continue; int iConfigIndex = s.configurations.IndexOf(solutionConfig); if (iConfigIndex == -1) continue; // while (p.slnConfigurations.Count < s.configurations.Count) // { // p.slnConfigurations.Add(null); // p.slnBuildProject.Add(false); // } // // if (action == "ActiveCfg") // { // p.slnConfigurations[iConfigIndex] = projectConfig; // } // else // { // if (action.StartsWith("Build")) // { // p.slnBuildProject[iConfigIndex] = true; // } // else // { // if (action.StartsWith("Deploy")) // { // if (p.slnDeployProject == null) p.slnDeployProject = new List(); // // while (p.slnDeployProject.Count < s.configurations.Count) // p.slnDeployProject.Add(null); // // p.slnDeployProject[iConfigIndex] = true; // } // } // } //if-esle } return ""; } )); // // Initializes parent-child relationship. // new Regex("GlobalSection\\(NestedProjects\\).*?[\r\n]+(.*?)EndGlobalSection[\r\n]+", RegexOptions.Singleline).Replace(slnTxt, new MatchEvaluator(m4 => { String v = m4.Groups[1].Value; new Regex("\\s*?({[A-F0-9-]+}) = ({[A-F0-9-]+})[\r\n]+", RegexOptions.Multiline).Replace(v, new MatchEvaluator(m5 => { String[] args = m5.Groups.Cast().Skip(1).Select(x => x.Value).ToArray(); Project child = s.Projects.Where(x => args[0] == x.ProjectGuid).FirstOrDefault(); Project parent = s.Projects.Where(x => args[1] == x.ProjectGuid).FirstOrDefault(); parent.dependantProjects.Add(child); child.ParentProject = parent; return ""; })); return ""; } )); s.UserConfig = new Configuration (s.path + ".user"); return s; } //LoadSolution #endregion } }