//
// 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;
namespace CrowIDE{
public class SolutionProject {
public string ProjectHostGuid;
public string ProjectName;
public string RelativePath;
public string ProjectGuid;
}
///
/// .sln loaded into class.
///
public class Solution
{
///
/// Solution name
///
public String name;
public string DisplayName {
get { return name; }
}
///
/// File path from where solution was loaded.
///
[XmlIgnore]
public String path;
///
/// Solution name for debugger.
///
[ExcludeFromCodeCoverage]
public override string ToString()
{
return "Solution, name = " + name;
}
///
/// Gets solution path
///
///
public String SolutionFolder
{
get { return Path.GetDirectoryName (path); }
}
public IList Projects {
get {
List tmp = new List ();
foreach (SolutionProject p in projects) {
string pp = Path.Combine (SolutionFolder, p.RelativePath.Replace('\\','/'));
tmp.Add (new Project (pp, this));
}
return tmp;
}
}
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();
}
///
/// Creates new solution.
///
public Solution() { }
///
/// 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(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;
SolutionProject 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();
SolutionProject child = s.projects.Where(x => args[0] == x.ProjectGuid).FirstOrDefault();
SolutionProject parent = s.projects.Where(x => args[1] == x.ProjectGuid).FirstOrDefault();
// parent.nodes.Add(child);
// child.parent = parent;
return "";
}));
return "";
}
));
return s;
} //LoadSolution
} //class Solution
}