SolutionFolder LoadSolution (Solution sol, string fileName, MSBuildFileFormat format, IProgressMonitor monitor)
{
string headerComment;
string version = GetSlnFileVersion (fileName, out headerComment);
ListDictionary globals = null;
SolutionFolder folder = null;
SlnData data = null;
List<Section> projectSections = null;
List<string> lines = null;
FileFormat projectFormat = Services.ProjectService.FileFormats.GetFileFormat (format);
monitor.BeginTask (GettextCatalog.GetString ("Loading solution: {0}", fileName), 1);
//Parse the .sln file
using (StreamReader reader = new StreamReader(fileName)) {
sol.FileName = fileName;
sol.ConvertToFormat (projectFormat, false);
folder = sol.RootFolder;
sol.Version = "0.1"; //FIXME:
data = new SlnData ();
folder.ExtendedProperties [typeof (SlnFileFormat)] = data;
data.VersionString = version;
data.HeaderComment = headerComment;
string s = null;
projectSections = new List<Section> ();
lines = new List<string> ();
globals = new ListDictionary ();
//Parse
while (reader.Peek () >= 0) {
s = GetNextLine (reader, lines).Trim ();
if (String.Compare (s, "Global", StringComparison.OrdinalIgnoreCase) == 0) {
ParseGlobal (reader, lines, globals);
continue;
}
if (s.StartsWith ("Project", StringComparison.Ordinal)) {
Section sec = new Section ();
projectSections.Add (sec);
sec.Start = lines.Count - 1;
int e = ReadUntil ("EndProject", reader, lines);
sec.Count = (e < 0) ? 1 : (e - sec.Start + 1);
continue;
}
if (s.StartsWith ("VisualStudioVersion = ", StringComparison.Ordinal)) {
Version v;
if (Version.TryParse (s.Substring ("VisualStudioVersion = ".Length), out v))
data.VisualStudioVersion = v;
else
monitor.Log.WriteLine ("Ignoring unparseable VisualStudioVersion value in sln file");
}
if (s.StartsWith ("MinimumVisualStudioVersion = ", StringComparison.Ordinal)) {
Version v;
if (Version.TryParse (s.Substring ("MinimumVisualStudioVersion = ".Length), out v))
data.MinimumVisualStudioVersion = v;
else
monitor.Log.WriteLine ("Ignoring unparseable MinimumVisualStudioVersion value in sln file");
}
}
}
monitor.BeginTask("Loading projects ..", projectSections.Count + 1);
Dictionary<string, SolutionItem> items = new Dictionary<string, SolutionItem> ();
List<SolutionItem> sortedList = new List<SolutionItem> ();
foreach (Section sec in projectSections) {
monitor.Step (1);
Match match = ProjectRegex.Match (lines [sec.Start]);
if (!match.Success) {
LoggingService.LogDebug (GettextCatalog.GetString (
"Invalid Project definition on line number #{0} in file '{1}'. Ignoring.",
sec.Start + 1,
fileName));
continue;
}
try {
// Valid guid?
new Guid (match.Groups [1].Value);
} catch (FormatException) {
//Use default guid as projectGuid
LoggingService.LogDebug (GettextCatalog.GetString (
"Invalid Project type guid '{0}' on line #{1}. Ignoring.",
match.Groups [1].Value,
sec.Start + 1));
continue;
}
string projTypeGuid = match.Groups [1].Value.ToUpper ();
string projectName = match.Groups [2].Value;
string projectPath = match.Groups [3].Value;
string projectGuid = match.Groups [4].Value.ToUpper ();
List<string> projLines;
if (projTypeGuid == MSBuildProjectService.FolderTypeGuid) {
//Solution folder
SolutionFolder sfolder = new SolutionFolder ();
sfolder.Name = projectName;
MSBuildProjectService.InitializeItemHandler (sfolder);
MSBuildProjectService.SetId (sfolder, projectGuid);
projLines = lines.GetRange (sec.Start + 1, sec.Count - 2);
DeserializeSolutionItem (sol, sfolder, projLines);
foreach (string f in ReadFolderFiles (projLines))
sfolder.Files.Add (MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (fileName), f));
SlnData slnData = new SlnData ();
slnData.Extra = projLines.ToArray ();
sfolder.ExtendedProperties [typeof (SlnFileFormat)] = slnData;
items.Add (projectGuid, sfolder);
sortedList.Add (sfolder);
continue;
}
if (projectPath.StartsWith("http://")) {
monitor.ReportWarning (GettextCatalog.GetString (
"{0}({1}): Projects with non-local source (http://...) not supported. '{2}'.",
fileName, sec.Start + 1, projectPath));
data.UnknownProjects.AddRange (lines.GetRange (sec.Start, sec.Count));
continue;
}
string path = MSBuildProjectService.FromMSBuildPath (Path.GetDirectoryName (fileName), projectPath);
if (String.IsNullOrEmpty (path)) {
monitor.ReportWarning (GettextCatalog.GetString (
"Invalid project path found in {0} : {1}", fileName, projectPath));
LoggingService.LogWarning (GettextCatalog.GetString (
"Invalid project path found in {0} : {1}", fileName, projectPath));
continue;
}
projectPath = Path.GetFullPath (path);
SolutionEntityItem item = null;
try {
if (sol.IsSolutionItemEnabled (projectPath)) {
item = ProjectExtensionUtil.LoadSolutionItem (monitor, projectPath, delegate {
return MSBuildProjectService.LoadItem (monitor, projectPath, format, projTypeGuid, projectGuid);
});
if (item == null) {
throw new UnknownSolutionItemTypeException (projTypeGuid);
}
} else {
var uitem = new UnknownSolutionItem () {
FileName = projectPath,
UnloadedEntry = true
};
var h = new MSBuildHandler (projTypeGuid, projectGuid) {
Item = uitem,
};
uitem.SetItemHandler (h);
item = uitem;
}
} catch (Exception e) {
// If we get a TargetInvocationException from using Activator.CreateInstance we
// need to unwrap the real exception
while (e is TargetInvocationException)
e = ((TargetInvocationException) e).InnerException;
if (e is UnknownSolutionItemTypeException) {
var name = ((UnknownSolutionItemTypeException)e).TypeName;
var relPath = new FilePath (path).ToRelative (sol.BaseDirectory);
if (!string.IsNullOrEmpty (name)) {
var guids = name.Split (';');
var projectInfo = MSBuildProjectService.GetUnknownProjectTypeInfo (guids);
if (projectInfo != null) {
LoggingService.LogWarning (string.Format ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ()));
monitor.ReportWarning (GettextCatalog.GetString ("Could not load {0} project '{1}'. {2}", projectInfo.Name, relPath, projectInfo.GetInstructions ()));
} else {
LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type '{1}'", relPath, name));
monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type '{1}'", relPath, name));
}
} else {
LoggingService.LogWarning (string.Format ("Could not load project '{0}' with unknown item type", relPath));
monitor.ReportWarning (GettextCatalog.GetString ("Could not load project '{0}' with unknown item type", relPath));
}
} else if (e is UserException) {
var ex = (UserException) e;
LoggingService.LogError ("{0}: {1}", ex.Message, ex.Details);
monitor.ReportError (string.Format ("{0}{1}{1}{2}", ex.Message, Environment.NewLine, ex.Details), null);
} else {
LoggingService.LogError (string.Format ("Error while trying to load the project {0}", projectPath), e);
monitor.ReportWarning (GettextCatalog.GetString (
"Error while trying to load the project '{0}': {1}", projectPath, e.Message));
}
var uitem = new UnknownSolutionItem () {
FileName = projectPath,
LoadError = e.Message,
};
var h = new MSBuildHandler (projTypeGuid, projectGuid) {
Item = uitem,
};
uitem.SetItemHandler (h);
item = uitem;
}
MSBuildHandler handler = (MSBuildHandler) item.ItemHandler;
projLines = lines.GetRange (sec.Start + 1, sec.Count - 2);
DataItem it = GetSolutionItemData (projLines);
handler.UnresolvedProjectDependencies = ReadSolutionItemDependencies (projLines);
handler.SlnProjectContent = projLines.ToArray ();
handler.ReadSlnData (it);
if (!items.ContainsKey (projectGuid)) {
items.Add (projectGuid, item);
sortedList.Add (item);
data.ItemsByGuid [projectGuid] = item;
} else {
monitor.ReportError (GettextCatalog.GetString ("Invalid solution file. There are two projects with the same GUID. The project {0} will be ignored.", projectPath), null);
}
}
monitor.EndTask ();
if (globals != null && globals.Contains ("NestedProjects")) {
LoadNestedProjects (globals ["NestedProjects"] as Section, lines, items, monitor);
globals.Remove ("NestedProjects");
}
// Resolve project dependencies
foreach (var it in items.OfType<SolutionEntityItem> ()) {
MSBuildHandler handler = (MSBuildHandler) it.ItemHandler;
if (handler.UnresolvedProjectDependencies != null) {
foreach (var id in handler.UnresolvedProjectDependencies.ToArray ()) {
SolutionItem dep;
if (items.TryGetValue (id, out dep) && dep is SolutionEntityItem) {
handler.UnresolvedProjectDependencies.Remove (id);
it.ItemDependencies.Add ((SolutionEntityItem)dep);
}
}
if (handler.UnresolvedProjectDependencies.Count == 0)
handler.UnresolvedProjectDependencies = null;
}
}
//Add top level folders and projects to the main folder
foreach (SolutionItem ce in sortedList) {
if (ce.ParentFolder == null)
folder.Items.Add (ce);
}
//FIXME: This can be just SolutionConfiguration also!
if (globals != null) {
if (globals.Contains ("SolutionConfigurationPlatforms")) {
LoadSolutionConfigurations (globals ["SolutionConfigurationPlatforms"] as Section, lines,
sol, monitor);
globals.Remove ("SolutionConfigurationPlatforms");
}
if (globals.Contains ("ProjectConfigurationPlatforms")) {
LoadProjectConfigurationMappings (globals ["ProjectConfigurationPlatforms"] as Section, lines,
sol, monitor);
globals.Remove ("ProjectConfigurationPlatforms");
}
if (globals.Contains ("MonoDevelopProperties")) {
LoadMonoDevelopProperties (globals ["MonoDevelopProperties"] as Section, lines, sol, monitor);
globals.Remove ("MonoDevelopProperties");
}
ArrayList toRemove = new ArrayList ();
foreach (DictionaryEntry e in globals) {
string name = (string) e.Key;
if (name.StartsWith ("MonoDevelopProperties.")) {
int i = name.IndexOf ('.');
LoadMonoDevelopConfigurationProperties (name.Substring (i+1), (Section)e.Value, lines, sol, monitor);
toRemove.Add (e.Key);
}
}
foreach (object key in toRemove)
globals.Remove (key);
}
//Save the global sections that we dont use
List<string> globalLines = new List<string> ();
foreach (Section sec in globals.Values)
globalLines.InsertRange (globalLines.Count, lines.GetRange (sec.Start, sec.Count));
data.GlobalExtra = globalLines;
monitor.EndTask ();
// When reloading a project, keep the solution data and item id
sol.SolutionItemAdded += delegate(object sender, SolutionItemChangeEventArgs e) {
if (e.Reloading) {
ItemSlnData.TransferData (e.ReplacedItem, e.SolutionItem);
var ih = e.SolutionItem.ItemHandler as MSBuildHandler;
if (ih != null)
ih.ItemId = e.ReplacedItem.ItemId;
}
};
return folder;
}