PageRenderTime 65ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/SODA/src/Main/Base/Project/Src/Project/Solution/Solution.cs

https://github.com/stephenong/TickZoomPublic
C# | 1246 lines | 1055 code | 123 blank | 68 comment | 196 complexity | c073090d37c487e5e622f5d7cbbc193a MD5 | raw file
Possible License(s): MIT, LGPL-2.1, CC-BY-SA-3.0, Apache-2.0, BSD-3-Clause, LGPL-2.0, GPL-2.0, GPL-3.0
  1. // <file>
  2. // <copyright see="prj:///doc/copyright.txt"/>
  3. // <license see="prj:///doc/license.txt"/>
  4. // <owner name="Daniel Grunwald" email="daniel@danielgrunwald.de"/>
  5. // <version>$Revision: 5505 $</version>
  6. // </file>
  7. using System;
  8. using System.Collections.Generic;
  9. using System.ComponentModel;
  10. using System.IO;
  11. using System.Linq;
  12. using System.Text;
  13. using System.Text.RegularExpressions;
  14. using ICSharpCode.Core;
  15. using ICSharpCode.SharpDevelop.Gui;
  16. using MSBuild = Microsoft.Build.BuildEngine;
  17. namespace ICSharpCode.SharpDevelop.Project
  18. {
  19. public interface IMSBuildEngineProvider
  20. {
  21. MSBuild.Engine BuildEngine {
  22. get;
  23. }
  24. }
  25. public class Solution : SolutionFolder, IDisposable, IMSBuildEngineProvider, IBuildable
  26. {
  27. public const int SolutionVersionVS2005 = 9;
  28. public const int SolutionVersionVS2008 = 10;
  29. [Obsolete("Use SolutionVersionVS2005 instead")]
  30. public const int SolutionVersionVS05 = 9;
  31. [Obsolete("Use SolutionVersionVS2008 instead")]
  32. public const int SolutionVersionVS08 = 10;
  33. /// <summary>contains &lt;GUID, (IProject/ISolutionFolder)&gt; pairs.</summary>
  34. Dictionary<string, ISolutionFolder> guidDictionary = new Dictionary<string, ISolutionFolder>();
  35. bool isLoading;
  36. string fileName = String.Empty;
  37. MSBuild.Engine buildEngine = MSBuildInternals.CreateEngine();
  38. public Solution()
  39. {
  40. preferences = new SolutionPreferences(this);
  41. }
  42. [Browsable(false)]
  43. public MSBuild.Engine BuildEngine {
  44. get { return buildEngine; }
  45. }
  46. #region Enumerate projects/folders
  47. public IProject FindProjectContainingFile(string fileName)
  48. {
  49. IProject currentProject = ProjectService.CurrentProject;
  50. if (currentProject != null && currentProject.IsFileInProject(fileName))
  51. return currentProject;
  52. // Try all project's in the solution.
  53. foreach (IProject project in Projects) {
  54. if (project.IsFileInProject(fileName)) {
  55. return project;
  56. }
  57. }
  58. return null;
  59. }
  60. [Browsable(false)]
  61. public IEnumerable<IProject> Projects {
  62. get {
  63. Stack<ISolutionFolder> stack = new Stack<ISolutionFolder>();
  64. foreach (ISolutionFolder solutionFolder in Folders) {
  65. stack.Push(solutionFolder);
  66. }
  67. while (stack.Count > 0) {
  68. ISolutionFolder currentFolder = stack.Pop();
  69. if (currentFolder is IProject) {
  70. yield return ((IProject)currentFolder);
  71. }
  72. if (currentFolder is ISolutionFolderContainer) {
  73. ISolutionFolderContainer currentContainer = (ISolutionFolderContainer)currentFolder;
  74. foreach (ISolutionFolder subFolder in currentContainer.Folders) {
  75. stack.Push(subFolder);
  76. }
  77. }
  78. }
  79. }
  80. }
  81. [Browsable(false)]
  82. public IEnumerable<ISolutionFolderContainer> SolutionFolderContainers {
  83. get {
  84. Stack<ISolutionFolder> stack = new Stack<ISolutionFolder>();
  85. foreach (ISolutionFolder solutionFolder in Folders) {
  86. stack.Push(solutionFolder);
  87. }
  88. while (stack.Count > 0) {
  89. ISolutionFolder currentFolder = stack.Pop();
  90. if (currentFolder is ISolutionFolderContainer) {
  91. ISolutionFolderContainer currentContainer = (ISolutionFolderContainer)currentFolder;
  92. yield return currentContainer;
  93. foreach (ISolutionFolder subFolder in currentContainer.Folders) {
  94. stack.Push(subFolder);
  95. }
  96. }
  97. }
  98. }
  99. }
  100. [Browsable(false)]
  101. public IEnumerable<ISolutionFolder> SolutionFolders {
  102. get {
  103. Stack<ISolutionFolder> stack = new Stack<ISolutionFolder>();
  104. foreach (ISolutionFolder solutionFolder in Folders) {
  105. stack.Push(solutionFolder);
  106. }
  107. while (stack.Count > 0) {
  108. ISolutionFolder currentFolder = stack.Pop();
  109. yield return currentFolder;
  110. if (currentFolder is ISolutionFolderContainer) {
  111. ISolutionFolderContainer currentContainer = (ISolutionFolderContainer)currentFolder;
  112. foreach (ISolutionFolder subFolder in currentContainer.Folders) {
  113. stack.Push(subFolder);
  114. }
  115. }
  116. }
  117. }
  118. }
  119. /// <summary>
  120. /// Returns the startup project. If no startup project is set in the solution preferences,
  121. /// returns any project that is startable.
  122. /// </summary>
  123. [Browsable(false)]
  124. public IProject StartupProject {
  125. get {
  126. if (!HasProjects) {
  127. return null;
  128. }
  129. IProject startupProject = preferences.StartupProject;
  130. if (startupProject != null)
  131. return startupProject;
  132. foreach (IProject project in Projects) {
  133. if (project.IsStartable) {
  134. return project;
  135. }
  136. }
  137. return null;
  138. }
  139. }
  140. public ISolutionFolder GetSolutionFolder(string guid)
  141. {
  142. foreach (ISolutionFolder solutionFolder in SolutionFolders) {
  143. if (solutionFolder.IdGuid == guid) {
  144. return solutionFolder;
  145. }
  146. }
  147. return null;
  148. }
  149. public SolutionFolder CreateFolder(string folderName)
  150. {
  151. return new SolutionFolder(folderName, folderName, "{" + Guid.NewGuid().ToString().ToUpperInvariant() + "}");
  152. }
  153. #endregion
  154. #region Properties
  155. [Browsable(false)]
  156. public bool HasProjects {
  157. get {
  158. return Projects.GetEnumerator().MoveNext();
  159. }
  160. }
  161. [Browsable(false)]
  162. public string FileName {
  163. get {
  164. return fileName;
  165. }
  166. set {
  167. fileName = value;
  168. }
  169. }
  170. [Browsable(false)]
  171. public string Directory {
  172. get {
  173. return Path.GetDirectoryName(fileName);
  174. }
  175. }
  176. SolutionPreferences preferences;
  177. [Browsable(false)]
  178. public SolutionPreferences Preferences {
  179. get {
  180. return preferences;
  181. }
  182. }
  183. /// <summary>Returns true if the solution is readonly.</summary>
  184. [Browsable(false)]
  185. public bool ReadOnly {
  186. get
  187. {
  188. try {
  189. FileAttributes attributes = File.GetAttributes(fileName);
  190. return ((FileAttributes.ReadOnly & attributes) == FileAttributes.ReadOnly);
  191. } catch (FileNotFoundException) {
  192. return false;
  193. } catch (DirectoryNotFoundException) {
  194. return true;
  195. }
  196. }
  197. }
  198. #endregion
  199. #region ISolutionFolderContainer implementations
  200. [Browsable(false)]
  201. public override Solution ParentSolution {
  202. get { return this; }
  203. }
  204. public override ProjectSection SolutionItems {
  205. get {
  206. foreach (SolutionFolder folder in Folders) {
  207. if (folder.Name == "Solution Items") {
  208. return folder.SolutionItems;
  209. }
  210. }
  211. SolutionFolder newFolder = CreateFolder("Solution Items");
  212. return newFolder.SolutionItems;
  213. }
  214. }
  215. internal void BeforeAddFolderToSolution(ISolutionFolder folder)
  216. {
  217. IProject project = folder as IProject;
  218. if (project != null && !isLoading) {
  219. // HACK: don't deal with configurations during loading
  220. if (this.GetConfigurationNames().Count == 0) {
  221. foreach (string config in project.ConfigurationNames) {
  222. foreach (string platform in project.PlatformNames)
  223. AddSolutionConfigurationPlatform(config, FixPlatformNameForSolution(platform), null, false, false);
  224. }
  225. }
  226. }
  227. }
  228. public override void AddFolder(ISolutionFolder folder)
  229. {
  230. base.AddFolder(folder);
  231. guidDictionary[folder.IdGuid] = folder;
  232. }
  233. internal void AfterAddFolderToSolution(ISolutionFolder folder)
  234. {
  235. IProject project = folder as IProject;
  236. if (project != null && !isLoading) {
  237. var projectConfigurations = project.ConfigurationNames;
  238. var solutionConfigurations = this.GetConfigurationNames();
  239. var projectPlatforms = project.PlatformNames;
  240. var solutionPlatforms = this.GetPlatformNames();
  241. foreach (string config in solutionConfigurations) {
  242. string projectConfig = config;
  243. if (!projectConfigurations.Contains(projectConfig))
  244. projectConfig = projectConfigurations.FirstOrDefault() ?? "Debug";
  245. foreach (string platform in solutionPlatforms) {
  246. string projectPlatform = FixPlatformNameForProject(platform);
  247. if (!projectPlatforms.Contains(projectPlatform))
  248. projectPlatform = projectPlatforms.FirstOrDefault() ?? "AnyCPU";
  249. CreateMatchingItem(config, platform, project, projectConfig + "|" + FixPlatformNameForSolution(projectPlatform));
  250. }
  251. }
  252. }
  253. }
  254. #endregion
  255. #region Save
  256. public void Save()
  257. {
  258. try {
  259. Save(fileName);
  260. return;
  261. } catch (IOException ex) {
  262. MessageService.ShowErrorFormatted("${res:SharpDevelop.Solution.CannotSave.IOException}", fileName, ex.Message);
  263. } catch (UnauthorizedAccessException ex) {
  264. FileAttributes attributes = File.GetAttributes(fileName);
  265. if ((FileAttributes.ReadOnly & attributes) == FileAttributes.ReadOnly) {
  266. MessageService.ShowErrorFormatted("${res:SharpDevelop.Solution.CannotSave.ReadOnly}", fileName);
  267. }
  268. else
  269. {
  270. MessageService.ShowErrorFormatted
  271. ("${res:SharpDevelop.Solution.CannotSave.UnauthorizedAccessException}", fileName, ex.Message);
  272. }
  273. }
  274. }
  275. public void Save(string fileName)
  276. {
  277. this.fileName = fileName;
  278. string outputDirectory = Path.GetDirectoryName(fileName);
  279. if (!System.IO.Directory.Exists(outputDirectory)) {
  280. System.IO.Directory.CreateDirectory(outputDirectory);
  281. }
  282. StringBuilder projectSection = new StringBuilder();
  283. StringBuilder nestedProjectsSection = new StringBuilder();
  284. Stack<ISolutionFolder> stack = new Stack<ISolutionFolder>(Folders.Count);
  285. // push folders in reverse order because it's a stack
  286. for (int i = Folders.Count - 1; i >= 0; i--) {
  287. stack.Push(Folders[i]);
  288. }
  289. while (stack.Count > 0) {
  290. ISolutionFolder currentFolder = stack.Pop();
  291. string relativeLocation;
  292. // The project file relative to the solution file.
  293. if (currentFolder is IProject) {
  294. currentFolder.Location = ((IProject)currentFolder).FileName;
  295. }
  296. if (Path.IsPathRooted(currentFolder.Location)) {
  297. relativeLocation = FileUtility.GetRelativePath(Path.GetDirectoryName(FileName), currentFolder.Location);
  298. } else {
  299. relativeLocation = currentFolder.Location;
  300. }
  301. projectSection.AppendFormat
  302. ("Project(\"{0}\") = \"{1}\", \"{2}\", \"{3}\"",
  303. new object [] {currentFolder.TypeGuid, currentFolder.Name, relativeLocation, currentFolder.IdGuid});
  304. projectSection.AppendLine();
  305. if (currentFolder is IProject) {
  306. IProject project = (IProject)currentFolder;
  307. // Web projects can have sections
  308. SaveProjectSections(project.ProjectSections, projectSection);
  309. } else if (currentFolder is SolutionFolder) {
  310. SolutionFolder folder = (SolutionFolder)currentFolder;
  311. SaveProjectSections(folder.Sections, projectSection);
  312. ISolutionFolder subFolder;
  313. for (int i = folder.Folders.Count - 1; i >= 0; i--) {
  314. //foreach (ISolutionFolder subFolder in folder.Folders) {
  315. subFolder = folder.Folders[i];
  316. stack.Push(subFolder);
  317. nestedProjectsSection.Append("\t\t");
  318. nestedProjectsSection.Append(subFolder.IdGuid);
  319. nestedProjectsSection.Append(" = ");
  320. nestedProjectsSection.Append(folder.IdGuid);
  321. nestedProjectsSection.Append(Environment.NewLine);
  322. }
  323. } else {
  324. LoggingService.Warn("Solution.Load(): unknown folder : " + currentFolder);
  325. }
  326. projectSection.Append("EndProject");
  327. projectSection.Append(Environment.NewLine);
  328. }
  329. StringBuilder globalSection = new StringBuilder();
  330. globalSection.Append("Global");
  331. globalSection.Append(Environment.NewLine);
  332. foreach (ProjectSection section in Sections) {
  333. globalSection.Append("\tGlobalSection(");
  334. globalSection.Append(section.Name);
  335. globalSection.Append(") = ");
  336. globalSection.Append(section.SectionType);
  337. globalSection.Append(Environment.NewLine);
  338. section.AppendSection(globalSection, "\t\t");
  339. globalSection.Append("\tEndGlobalSection");
  340. globalSection.Append(Environment.NewLine);
  341. }
  342. // we need to specify UTF8 because MSBuild needs the BOM
  343. using (StreamWriter sw = new StreamWriter(fileName, false, Encoding.UTF8)) {
  344. sw.WriteLine();
  345. int versionNumber = SolutionVersionVS2005;
  346. foreach (IProject p in this.Projects) {
  347. if (p.MinimumSolutionVersion > versionNumber)
  348. versionNumber = p.MinimumSolutionVersion;
  349. }
  350. sw.WriteLine("Microsoft Visual Studio Solution File, Format Version " + versionNumber + ".00");
  351. if (versionNumber == SolutionVersionVS2005) {
  352. sw.WriteLine("# Visual Studio 2005");
  353. } else if (versionNumber == SolutionVersionVS2008) {
  354. sw.WriteLine("# Visual Studio 2008");
  355. }
  356. sw.WriteLine("# SharpDevelop " + RevisionClass.FullVersion);
  357. sw.Write(projectSection.ToString());
  358. sw.Write(globalSection.ToString());
  359. if (nestedProjectsSection.Length > 0) {
  360. sw.WriteLine("\tGlobalSection(NestedProjects) = preSolution");
  361. sw.Write(nestedProjectsSection.ToString());
  362. sw.WriteLine("\tEndGlobalSection");
  363. }
  364. sw.WriteLine("EndGlobal");
  365. }
  366. }
  367. static void SaveProjectSections(IEnumerable<ProjectSection> sections, StringBuilder projectSection)
  368. {
  369. foreach (ProjectSection section in sections) {
  370. projectSection.Append("\tProjectSection(");
  371. projectSection.Append(section.Name);
  372. projectSection.Append(") = ");
  373. projectSection.Append(section.SectionType);
  374. projectSection.Append(Environment.NewLine);
  375. section.AppendSection(projectSection, "\t\t");
  376. projectSection.Append("\tEndProjectSection");
  377. projectSection.Append(Environment.NewLine);
  378. }
  379. }
  380. #endregion
  381. #region Read/SetupSolution
  382. static Regex versionPattern = new Regex("Microsoft Visual Studio Solution File, Format Version\\s+(?<Version>.*)", RegexOptions.Compiled);
  383. static Regex projectLinePattern = new Regex("Project\\(\"(?<ProjectGuid>.*)\"\\)\\s+=\\s+\"(?<Title>.*)\",\\s*\"(?<Location>.*)\",\\s*\"(?<Guid>.*)\"", RegexOptions.Compiled);
  384. static Regex globalSectionPattern = new Regex("\\s*GlobalSection\\((?<Name>.*)\\)\\s*=\\s*(?<Type>.*)", RegexOptions.Compiled);
  385. static string GetFirstNonCommentLine(TextReader sr)
  386. {
  387. string line = "";
  388. while ((line = sr.ReadLine()) != null) {
  389. line = line.Trim();
  390. if (line.Length > 0 && line[0] != '#')
  391. return line;
  392. }
  393. return "";
  394. }
  395. /// <summary>
  396. /// Reads the specified solution file. The project-location-guid information is written into the conversion class.
  397. /// </summary>
  398. /// <returns>The version number of the solution.</returns>
  399. public static string ReadSolutionInformation(string solutionFileName, Converter.PrjxToSolutionProject.Conversion conversion)
  400. {
  401. LoggingService.Debug("ReadSolutionInformation: " + solutionFileName);
  402. string solutionDirectory = Path.GetDirectoryName(solutionFileName);
  403. using (StreamReader sr = File.OpenText(solutionFileName)) {
  404. string line = GetFirstNonCommentLine(sr);
  405. Match match = versionPattern.Match(line);
  406. if (!match.Success) {
  407. return null;
  408. }
  409. string version = match.Result("${Version}");
  410. while ((line = sr.ReadLine()) != null) {
  411. match = projectLinePattern.Match(line);
  412. if (match.Success) {
  413. string projectGuid = match.Result("${ProjectGuid}");
  414. string title = match.Result("${Title}");
  415. string location = Path.Combine(solutionDirectory, match.Result("${Location}"));
  416. string guid = match.Result("${Guid}");
  417. LoggingService.Debug(guid + ": " + title);
  418. conversion.NameToGuid[title] = new Guid(guid);
  419. conversion.NameToPath[title] = location;
  420. conversion.GuidToPath[new Guid(guid)] = location;
  421. }
  422. }
  423. return version;
  424. }
  425. }
  426. static bool SetupSolution(Solution newSolution)
  427. {
  428. ProjectSection nestedProjectsSection = null;
  429. bool needsConversion = false;
  430. // read solution files using system encoding, but detect UTF8 if BOM is present
  431. using (StreamReader sr = new StreamReader(newSolution.FileName, Encoding.Default, true)) {
  432. string line = GetFirstNonCommentLine(sr);
  433. Match match = versionPattern.Match(line);
  434. if (!match.Success) {
  435. MessageService.ShowErrorFormatted("${res:SharpDevelop.Solution.InvalidSolutionFile}", newSolution.FileName);
  436. return false;
  437. }
  438. switch (match.Result("${Version}")) {
  439. case "7.00":
  440. needsConversion = true;
  441. if (!MessageService.AskQuestion("${res:SharpDevelop.Solution.ConvertSolutionVersion7}")) {
  442. return false;
  443. }
  444. break;
  445. case "8.00":
  446. needsConversion = true;
  447. if (!MessageService.AskQuestion("${res:SharpDevelop.Solution.ConvertSolutionVersion8}")) {
  448. return false;
  449. }
  450. break;
  451. case "9.00":
  452. case "10.00":
  453. break;
  454. default:
  455. MessageService.ShowErrorFormatted("${res:SharpDevelop.Solution.UnknownSolutionVersion}", match.Result("${Version}"));
  456. return false;
  457. }
  458. using (AsynchronousWaitDialog waitDialog = AsynchronousWaitDialog.ShowWaitDialog("Loading solution")) {
  459. nestedProjectsSection = SetupSolutionLoadSolutionProjects(newSolution, sr, waitDialog);
  460. }
  461. }
  462. // Create solution folder 'tree'.
  463. if (nestedProjectsSection != null) {
  464. foreach (SolutionItem item in nestedProjectsSection.Items) {
  465. string from = item.Name;
  466. string to = item.Location;
  467. if (newSolution.guidDictionary.ContainsKey(to) && newSolution.guidDictionary.ContainsKey(from)) {
  468. // ignore invalid entries
  469. ISolutionFolderContainer folder = newSolution.guidDictionary[to] as ISolutionFolderContainer;
  470. folder.AddFolder(newSolution.guidDictionary[from]);
  471. }
  472. }
  473. }
  474. if (!newSolution.ReadOnly && (newSolution.FixSolutionConfiguration(newSolution.Projects) || needsConversion)) {
  475. // save in new format
  476. newSolution.Save();
  477. }
  478. return true;
  479. }
  480. static ProjectSection SetupSolutionLoadSolutionProjects(Solution newSolution, StreamReader sr, IProgressMonitor progressMonitor)
  481. {
  482. string solutionDirectory = Path.GetDirectoryName(newSolution.FileName);
  483. ProjectSection nestedProjectsSection = null;
  484. while (true) {
  485. string line = sr.ReadLine();
  486. if (line == null) {
  487. break;
  488. }
  489. Match match = projectLinePattern.Match(line);
  490. if (match.Success) {
  491. string projectGuid = match.Result("${ProjectGuid}");
  492. string title = match.Result("${Title}");
  493. string location = match.Result("${Location}");
  494. string guid = match.Result("${Guid}");
  495. if (!FileUtility.IsUrl(location)) {
  496. location = FileUtility.NormalizePath(Path.Combine(solutionDirectory, location));
  497. }
  498. if (projectGuid == FolderGuid) {
  499. SolutionFolder newFolder = SolutionFolder.ReadFolder(sr, title, location, guid);
  500. newSolution.AddFolder(newFolder);
  501. } else {
  502. IProject newProject = LanguageBindingService.LoadProject(newSolution, location, title, projectGuid, progressMonitor);
  503. ReadProjectSections(sr, newProject.ProjectSections);
  504. newProject.IdGuid = guid;
  505. newSolution.AddFolder(newProject);
  506. }
  507. match = match.NextMatch();
  508. } else {
  509. match = globalSectionPattern.Match(line);
  510. if (match.Success) {
  511. ProjectSection newSection = ProjectSection.ReadGlobalSection(sr, match.Result("${Name}"), match.Result("${Type}"));
  512. // Don't put the NestedProjects section into the global sections list
  513. // because it's transformed to a tree representation and the tree representation
  514. // is transformed back to the NestedProjects section during save.
  515. if (newSection.Name == "NestedProjects") {
  516. nestedProjectsSection = newSection;
  517. } else {
  518. newSolution.Sections.Add(newSection);
  519. }
  520. }
  521. }
  522. }
  523. return nestedProjectsSection;
  524. }
  525. #endregion
  526. #region Configuration/Platform management
  527. #region Section management
  528. public ProjectSection GetSolutionConfigurationsSection()
  529. {
  530. foreach (ProjectSection sec in this.Sections) {
  531. if (sec.Name == "SolutionConfigurationPlatforms")
  532. return sec;
  533. }
  534. ProjectSection newSec = new ProjectSection("SolutionConfigurationPlatforms", "preSolution");
  535. this.Sections.Insert(0, newSec);
  536. // convert VS 2003 solution to VS 2005 (or later)
  537. foreach (ProjectSection sec in this.Sections) {
  538. if (sec.Name == "SolutionConfiguration") {
  539. this.Sections.Remove(sec);
  540. foreach (SolutionItem item in sec.Items) {
  541. // item.Name = item.Location
  542. // might be ConfigName.0 = Debug (VS.NET)
  543. // or Debug = Debug (VS.NET 03)
  544. newSec.Items.Add(new SolutionItem(item.Location + "|x86", item.Location + "|x86"));
  545. }
  546. break;
  547. }
  548. }
  549. return newSec;
  550. }
  551. public ProjectSection GetProjectConfigurationsSection()
  552. {
  553. foreach (ProjectSection sec in Sections) {
  554. if (sec.Name == "ProjectConfigurationPlatforms")
  555. return sec;
  556. }
  557. ProjectSection newSec = new ProjectSection("ProjectConfigurationPlatforms", "postSolution");
  558. Sections.Add(newSec);
  559. foreach (ProjectSection sec in this.Sections) {
  560. if (sec.Name == "ProjectConfiguration") {
  561. this.Sections.Remove(sec);
  562. foreach (SolutionItem item in sec.Items) {
  563. string name = item.Name;
  564. string location = item.Location;
  565. if (!name.Contains("|")) {
  566. int pos = name.LastIndexOf('.');
  567. if (pos > 0) {
  568. string firstpart = name.Substring(0, pos);
  569. string lastpart = name.Substring(pos);
  570. if (lastpart == ".0") {
  571. pos = firstpart.LastIndexOf('.');
  572. if (pos > 0) {
  573. lastpart = name.Substring(pos);
  574. firstpart = name.Substring(0, pos);
  575. }
  576. }
  577. name = firstpart + "|Any CPU" + lastpart;
  578. }
  579. pos = location.LastIndexOf('|');
  580. if (pos < 0) {
  581. location += "|Any CPU";
  582. } else {
  583. string platform = location.Substring(pos+1);
  584. bool found = false;
  585. foreach (IProject p in this.Projects) {
  586. if (p.PlatformNames.Contains(platform)) {
  587. found = true;
  588. break;
  589. }
  590. }
  591. if (!found) {
  592. location = location.Substring(0, pos) + "|Any CPU";
  593. }
  594. }
  595. }
  596. newSec.Items.Add(new SolutionItem(name, location));
  597. }
  598. break;
  599. }
  600. }
  601. return newSec;
  602. }
  603. public bool FixSolutionConfiguration(IEnumerable<IProject> projects)
  604. {
  605. ProjectSection solSec = GetSolutionConfigurationsSection();
  606. ProjectSection prjSec = GetProjectConfigurationsSection();
  607. bool changed = false;
  608. if (solSec.Items.Count == 0) {
  609. solSec.Items.Add(new SolutionItem("Debug|Any CPU", "Debug|Any CPU"));
  610. solSec.Items.Add(new SolutionItem("Release|Any CPU", "Release|Any CPU"));
  611. LoggingService.Warn("!! Inserted default SolutionConfigurationPlatforms !!");
  612. changed = true;
  613. }
  614. foreach (IProject project in projects) {
  615. string guid = project.IdGuid.ToUpperInvariant();
  616. foreach (SolutionItem configuration in solSec.Items) {
  617. string searchKey = guid + "." + configuration.Name + ".Build.0";
  618. if (!prjSec.Items.Exists(delegate (SolutionItem item) {
  619. return item.Name == searchKey;
  620. }))
  621. {
  622. prjSec.Items.Add(new SolutionItem(searchKey, configuration.Location));
  623. changed = true;
  624. }
  625. searchKey = guid + "." + configuration.Name + ".ActiveCfg";
  626. if (!prjSec.Items.Exists(delegate (SolutionItem item) {
  627. return item.Name == searchKey;
  628. }))
  629. {
  630. prjSec.Items.Add(new SolutionItem(searchKey, configuration.Location));
  631. changed = true;
  632. }
  633. }
  634. }
  635. // remove all configuration entries belonging to removed projects
  636. prjSec.Items.RemoveAll(
  637. item => {
  638. if (item.Name.Contains(".")) {
  639. string guid = item.Name.Substring(0, item.Name.IndexOf('.'));
  640. if (!this.Projects.Any(p => string.Equals(p.IdGuid, guid, StringComparison.OrdinalIgnoreCase))) {
  641. changed = true;
  642. return true;
  643. }
  644. }
  645. return false;
  646. });
  647. return changed;
  648. }
  649. #endregion
  650. /// <summary>
  651. /// Removes all configurations belonging to the specified project.
  652. /// Is used to remove a project from the solution.
  653. /// </summary>
  654. internal void RemoveProjectConfigurations(string projectGuid)
  655. {
  656. ProjectSection prjSec = GetProjectConfigurationsSection();
  657. prjSec.Items.RemoveAll(
  658. item => {
  659. if (item.Name.Contains(".")) {
  660. string guid = item.Name.Substring(0, item.Name.IndexOf('.'));
  661. if (string.Equals(projectGuid, guid, StringComparison.OrdinalIgnoreCase))
  662. return true; // remove configuration
  663. }
  664. return false;
  665. });
  666. }
  667. #region GetProjectConfigurationsSection/GetPlatformNames
  668. public IList<string> GetConfigurationNames()
  669. {
  670. List<string> configurationNames = new List<string>();
  671. foreach (SolutionItem item in GetSolutionConfigurationsSection().Items) {
  672. string name = AbstractProject.GetConfigurationNameFromKey(item.Name);
  673. if (!configurationNames.Contains(name))
  674. configurationNames.Add(name);
  675. }
  676. return configurationNames;
  677. }
  678. public IList<string> GetPlatformNames()
  679. {
  680. List<string> platformNames = new List<string>();
  681. foreach (SolutionItem item in GetSolutionConfigurationsSection().Items) {
  682. string name = AbstractProject.GetPlatformNameFromKey(item.Name);
  683. if (!platformNames.Contains(name))
  684. platformNames.Add(name);
  685. }
  686. return platformNames;
  687. }
  688. #endregion
  689. #region Solution - project configuration matching
  690. public void ApplySolutionConfigurationAndPlatformToProjects()
  691. {
  692. foreach (ProjectConfigurationPlatformMatching l in
  693. GetActiveConfigurationsAndPlatformsForProjects(preferences.ActiveConfiguration,
  694. preferences.ActivePlatform))
  695. {
  696. l.Project.ActiveConfiguration = l.Configuration;
  697. l.Project.ActivePlatform = FixPlatformNameForProject(l.Platform);
  698. }
  699. ProjectService.OnSolutionConfigurationChanged(new SolutionConfigurationEventArgs(this, preferences.ActiveConfiguration));
  700. }
  701. /// <summary>
  702. /// This is a special case in MSBuild we need to take care of.
  703. /// </summary>
  704. static string FixPlatformNameForProject(string platformName)
  705. {
  706. return MSBuildInternals.FixPlatformNameForProject(platformName);
  707. }
  708. /// <summary>
  709. /// This is a special case in MSBuild we need to take care of.
  710. /// Opposite of FixPlatformNameForProject
  711. /// </summary>
  712. static string FixPlatformNameForSolution(string platformName)
  713. {
  714. return MSBuildInternals.FixPlatformNameForSolution(platformName);
  715. }
  716. internal class ProjectConfigurationPlatformMatching
  717. {
  718. public readonly IProject Project;
  719. public string Configuration;
  720. public string Platform;
  721. public SolutionItem SolutionItem;
  722. public ProjectConfigurationPlatformMatching(IProject project, string configuration, string platform, SolutionItem solutionItem)
  723. {
  724. this.Project = project;
  725. this.Configuration = configuration;
  726. this.Platform = platform;
  727. this.SolutionItem = solutionItem;
  728. }
  729. public void SetSolutionConfigurationPlatform(ProjectSection section, string newConfiguration, string newPlatform)
  730. {
  731. if (this.SolutionItem == null)
  732. return;
  733. string oldName = this.SolutionItem.Name;
  734. this.SolutionItem.Name = this.Project.IdGuid + "." + newConfiguration + "|" + newPlatform + ".Build.0";
  735. string newName = this.SolutionItem.Name;
  736. if (StripBuild0(ref oldName) && StripBuild0(ref newName)) {
  737. oldName += ".ActiveCfg";
  738. newName += ".ActiveCfg";
  739. foreach (SolutionItem item in section.Items) {
  740. if (item.Name == oldName)
  741. item.Name = newName;
  742. }
  743. }
  744. }
  745. public void SetProjectConfigurationPlatform(ProjectSection section, string newConfiguration, string newPlatform)
  746. {
  747. this.Configuration = newConfiguration;
  748. this.Platform = newPlatform;
  749. if (this.SolutionItem == null)
  750. return;
  751. this.SolutionItem.Location = newConfiguration + "|" + newPlatform;
  752. string thisName = this.SolutionItem.Name;
  753. if (StripBuild0(ref thisName)) {
  754. thisName += ".ActiveCfg";
  755. foreach (SolutionItem item in section.Items) {
  756. if (item.Name == thisName)
  757. item.Location = this.SolutionItem.Location;
  758. }
  759. }
  760. }
  761. internal static bool StripBuild0(ref string s)
  762. {
  763. if (s.EndsWith(".Build.0")) {
  764. s = s.Substring(0, s.Length - ".Build.0".Length);
  765. return true;
  766. } else {
  767. return false;
  768. }
  769. }
  770. }
  771. internal List<ProjectConfigurationPlatformMatching>
  772. GetActiveConfigurationsAndPlatformsForProjects(string solutionConfiguration, string solutionPlatform)
  773. {
  774. List<ProjectConfigurationPlatformMatching> results = new List<ProjectConfigurationPlatformMatching>();
  775. ProjectSection prjSec = GetProjectConfigurationsSection();
  776. Dictionary<string, SolutionItem> dict = new Dictionary<string, SolutionItem>(StringComparer.OrdinalIgnoreCase);
  777. foreach (SolutionItem item in prjSec.Items) {
  778. dict[item.Name] = item;
  779. }
  780. string searchKeyPostFix = "." + solutionConfiguration + "|" + solutionPlatform + ".Build.0";
  781. foreach (IProject p in Projects) {
  782. string searchKey = p.IdGuid + searchKeyPostFix;
  783. SolutionItem solutionItem;
  784. if (dict.TryGetValue(searchKey, out solutionItem)) {
  785. string targetConfPlat = solutionItem.Location;
  786. if (targetConfPlat.IndexOf('|') > 0) {
  787. string conf = AbstractProject.GetConfigurationNameFromKey(targetConfPlat);
  788. string plat = AbstractProject.GetPlatformNameFromKey(targetConfPlat);
  789. results.Add(new ProjectConfigurationPlatformMatching(p, conf, plat, solutionItem));
  790. } else {
  791. results.Add(new ProjectConfigurationPlatformMatching(p, targetConfPlat, solutionPlatform, solutionItem));
  792. }
  793. } else {
  794. results.Add(new ProjectConfigurationPlatformMatching(p, solutionConfiguration, solutionPlatform, null));
  795. }
  796. }
  797. return results;
  798. }
  799. internal SolutionItem CreateMatchingItem(string solutionConfiguration, string solutionPlatform, IProject project, string initialLocation)
  800. {
  801. SolutionItem item = new SolutionItem(project.IdGuid + "." + solutionConfiguration + "|"
  802. + solutionPlatform + ".Build.0", initialLocation);
  803. GetProjectConfigurationsSection().Items.Add(item);
  804. GetProjectConfigurationsSection().Items.Add(new SolutionItem(project.IdGuid + "." + solutionConfiguration + "|"
  805. + solutionPlatform + ".ActiveCfg", initialLocation));
  806. return item;
  807. }
  808. #endregion
  809. #region Rename Solution Configuration/Platform
  810. public void RenameSolutionConfiguration(string oldName, string newName)
  811. {
  812. foreach (string platform in GetPlatformNames()) {
  813. foreach (ProjectConfigurationPlatformMatching m
  814. in GetActiveConfigurationsAndPlatformsForProjects(oldName, platform))
  815. {
  816. m.SetSolutionConfigurationPlatform(GetProjectConfigurationsSection(), newName, platform);
  817. }
  818. }
  819. foreach (SolutionItem item in GetSolutionConfigurationsSection().Items) {
  820. if (AbstractProject.GetConfigurationNameFromKey(item.Name) == oldName) {
  821. item.Name = newName + "|" + AbstractProject.GetPlatformNameFromKey(item.Name);
  822. item.Location = item.Name;
  823. }
  824. }
  825. }
  826. public void RenameSolutionPlatform(string oldName, string newName)
  827. {
  828. foreach (string configuration in GetConfigurationNames()) {
  829. foreach (ProjectConfigurationPlatformMatching m
  830. in GetActiveConfigurationsAndPlatformsForProjects(configuration, oldName))
  831. {
  832. m.SetSolutionConfigurationPlatform(GetProjectConfigurationsSection(), configuration, newName);
  833. }
  834. }
  835. foreach (SolutionItem item in GetSolutionConfigurationsSection().Items) {
  836. if (AbstractProject.GetPlatformNameFromKey(item.Name) == oldName) {
  837. item.Name = AbstractProject.GetConfigurationNameFromKey(item.Name) + "|" + newName;
  838. item.Location = item.Name;
  839. }
  840. }
  841. }
  842. #endregion
  843. #region Rename Project Configuration/Platform
  844. public bool RenameProjectConfiguration(IProject project, string oldName, string newName)
  845. {
  846. IProjectAllowChangeConfigurations pacc = project as IProjectAllowChangeConfigurations;
  847. if (pacc == null)
  848. return false;
  849. if (!pacc.RenameProjectConfiguration(oldName, newName))
  850. return false;
  851. // project configuration was renamed successfully, adjust solution:
  852. foreach (SolutionItem item in GetProjectConfigurationsSection().Items) {
  853. if (item.Name.ToLowerInvariant().StartsWith(project.IdGuid.ToLowerInvariant())) {
  854. if (AbstractProject.GetConfigurationNameFromKey(item.Location) == oldName) {
  855. item.Location = newName + "|" + AbstractProject.GetPlatformNameFromKey(item.Location);
  856. }
  857. }
  858. }
  859. return true;
  860. }
  861. public bool RenameProjectPlatform(IProject project, string oldName, string newName)
  862. {
  863. IProjectAllowChangeConfigurations pacc = project as IProjectAllowChangeConfigurations;
  864. if (pacc == null)
  865. return false;
  866. if (!pacc.RenameProjectPlatform(FixPlatformNameForProject(oldName), FixPlatformNameForProject(newName)))
  867. return false;
  868. // project configuration was renamed successfully, adjust solution:
  869. foreach (SolutionItem item in GetProjectConfigurationsSection().Items) {
  870. if (item.Name.ToLowerInvariant().StartsWith(project.IdGuid.ToLowerInvariant())) {
  871. if (AbstractProject.GetPlatformNameFromKey(item.Location) == oldName) {
  872. item.Location = AbstractProject.GetConfigurationNameFromKey(item.Location) + "|" + newName;
  873. }
  874. }
  875. }
  876. return true;
  877. }
  878. #endregion
  879. #region Add Solution Configuration/Platform
  880. /// <summary>
  881. /// Creates a new solution configuration.
  882. /// </summary>
  883. /// <param name="newName">Name of the new configuration</param>
  884. /// <param name="copyFrom">Name of existing configuration to copy values from, or null</param>
  885. /// <param name="createInProjects">true to also create the new configuration in all projects</param>
  886. public void AddSolutionConfiguration(string newName, string copyFrom, bool createInProjects)
  887. {
  888. foreach (string platform in this.GetPlatformNames()) {
  889. AddSolutionConfigurationPlatform(newName, platform, copyFrom, createInProjects, false);
  890. }
  891. }
  892. public void AddSolutionPlatform(string newName, string copyFrom, bool createInProjects)
  893. {
  894. foreach (string configuration in this.GetConfigurationNames()) {
  895. AddSolutionConfigurationPlatform(configuration, newName, copyFrom, createInProjects, true);
  896. }
  897. }
  898. void AddSolutionConfigurationPlatform(string newConfiguration, string newPlatform,
  899. string copyFrom, bool createInProjects, bool addPlatform)
  900. {
  901. List<ProjectConfigurationPlatformMatching> matchings;
  902. if (string.IsNullOrEmpty(copyFrom)) {
  903. matchings = new List<ProjectConfigurationPlatformMatching>();
  904. } else {
  905. if (addPlatform) {
  906. matchings = GetActiveConfigurationsAndPlatformsForProjects(newConfiguration, copyFrom);
  907. } else {
  908. matchings = GetActiveConfigurationsAndPlatformsForProjects(copyFrom, newPlatform);
  909. }
  910. }
  911. GetSolutionConfigurationsSection().Items.Add(new SolutionItem(newConfiguration + "|" + newPlatform,
  912. newConfiguration + "|" + newPlatform));
  913. foreach (IProject project in this.Projects) {
  914. // get old project configuration and platform
  915. string projectConfiguration, projectPlatform;
  916. ProjectConfigurationPlatformMatching matching = matchings.Find(
  917. delegate(ProjectConfigurationPlatformMatching m) { return m.Project == project; });
  918. if (matching != null) {
  919. projectConfiguration = matching.Configuration;
  920. projectPlatform = matching.Platform;
  921. } else {
  922. projectConfiguration = project.ConfigurationNames.First();
  923. projectPlatform = FixPlatformNameForSolution(project.PlatformNames.First());
  924. }
  925. if (createInProjects) {
  926. ICollection<string> existingInProject = addPlatform ? project.PlatformNames : project.ConfigurationNames;
  927. if (existingInProject.Contains(addPlatform ? newPlatform : newConfiguration)) {
  928. // target platform/configuration already exists, so reference it
  929. if (addPlatform) {
  930. projectPlatform = newPlatform;
  931. } else {
  932. projectConfiguration = newConfiguration;
  933. }
  934. } else {
  935. // create in project
  936. IProjectAllowChangeConfigurations pacc = project as IProjectAllowChangeConfigurations;
  937. if (pacc != null) {
  938. if (addPlatform) {
  939. if (pacc.AddProjectPlatform(FixPlatformNameForProject(newPlatform),
  940. FixPlatformNameForProject(projectPlatform))) {
  941. projectPlatform = newPlatform;
  942. }
  943. } else {
  944. if (pacc.AddProjectConfiguration(newConfiguration, projectConfiguration)) {
  945. projectConfiguration = newConfiguration;
  946. }
  947. }
  948. }
  949. }
  950. }
  951. // create item combining solution configuration with project configuration
  952. CreateMatchingItem(newConfiguration, newPlatform, project, projectConfiguration + "|" + projectPlatform);
  953. }
  954. }
  955. #endregion
  956. #region Remove Solution Configuration/Platform
  957. /// <summary>
  958. /// Gets the configuration|platform name from a conf item, e.g.
  959. /// "Release|Any CPU" from
  960. /// "{7115F3A9-781C-4A95-90AE-B5AB53C4C588}.Release|Any CPU.Build.0"
  961. /// </summary>
  962. static string GetKeyFromProjectConfItem(string name)
  963. {
  964. int pos = name.IndexOf('.');
  965. if (pos < 0) return null;
  966. name = name.Substring(pos + 1);
  967. if (!ProjectConfigurationPlatformMatching.StripBuild0(ref name)) {
  968. pos = name.LastIndexOf('.');
  969. if (pos < 0) return null;
  970. name = name.Substring(0, pos);
  971. }
  972. return name;
  973. }
  974. public void RemoveSolutionConfiguration(string name)
  975. {
  976. GetSolutionConfigurationsSection().Items.RemoveAll(
  977. delegate(SolutionItem item) {
  978. return AbstractProject.GetConfigurationNameFromKey(item.Name) == name;
  979. });
  980. GetProjectConfigurationsSection().Items.RemoveAll(
  981. delegate(SolutionItem item) {
  982. return AbstractProject.GetConfigurationNameFromKey(GetKeyFromProjectConfItem(item.Name)) == name;
  983. });
  984. }
  985. public void RemoveSolutionPlatform(string name)
  986. {
  987. GetSolutionConfigurationsSection().Items.RemoveAll(
  988. delegate(SolutionItem item) {
  989. return AbstractProject.GetPlatformNameFromKey(item.Name) == name;
  990. });
  991. GetProjectConfigurationsSection().Items.RemoveAll(
  992. delegate(SolutionItem item) {
  993. return AbstractProject.GetPlatformNameFromKey(GetKeyFromProjectConfItem(item.Name)) == name;
  994. });
  995. }
  996. #endregion
  997. #region Remove Project Configuration/Platform
  998. public bool RemoveProjectConfiguration(IProject project, string name)
  999. {
  1000. IProjectAllowChangeConfigurations pacc = project as IProjectAllowChangeConfigurations;
  1001. if (pacc == null)
  1002. return false;
  1003. if (!pacc.RemoveProjectConfiguration(name))
  1004. return false;
  1005. string otherConfigurationName = "other";
  1006. foreach (string configName in project.ConfigurationNames) {
  1007. otherConfigurationName = configName;
  1008. }
  1009. // project configuration was removed successfully, adjust solution:
  1010. foreach (SolutionItem item in GetProjectConfigurationsSection().Items) {
  1011. if (item.Name.ToLowerInvariant().StartsWith(project.IdGuid.ToLowerInvariant())) {
  1012. if (AbstractProject.GetConfigurationNameFromKey(item.Location) == name) {
  1013. // removed configuration was in use here, replace with other configuration
  1014. item.Location = otherConfigurationName + "|" + AbstractProject.GetPlatformNameFromKey(item.Location);
  1015. }
  1016. }
  1017. }
  1018. return true;
  1019. }
  1020. public bool RemoveProjectPlatform(IProject project, string name)
  1021. {
  1022. IProjectAllowChangeConfigurations pacc = project as IProjectAllowChangeConfigurations;
  1023. if (pacc == null)
  1024. return false;
  1025. if (!pacc.RemoveProjectPlatform(name))
  1026. return false;
  1027. string otherPlatformName = "other";
  1028. foreach (string platformName in project.PlatformNames) {
  1029. otherPlatformName = platformName;
  1030. }
  1031. // project configuration was removed successfully, adjust solution:
  1032. foreach (SolutionItem item in GetProjectConfigurationsSection().Items) {
  1033. if (item.Name.ToLowerInvariant().StartsWith(project.IdGuid.ToLowerInvariant())) {
  1034. if (AbstractProject.GetPlatformNameFromKey(item.Location) == name) {
  1035. // removed configuration was in use here, replace with other configuration
  1036. item.Location = AbstractProject.GetConfigurationNameFromKey(item.Location) + "|" + otherPlatformName;
  1037. }
  1038. }
  1039. }
  1040. return true;
  1041. }
  1042. #endregion
  1043. #endregion
  1044. #region Load
  1045. static Solution solutionBeingLoaded;
  1046. public static Solution SolutionBeingLoaded {
  1047. get {
  1048. return solutionBeingLoaded;
  1049. }
  1050. }
  1051. public static Solution Load(string fileName)
  1052. {
  1053. Solution newSolution = new Solution();
  1054. solutionBeingLoaded = newSolution;
  1055. newSolution.Name = Path.GetFileNameWithoutExtension(fileName);
  1056. string extension = Path.GetExtension(fileName).ToUpperInvariant();
  1057. if (extension == ".CMBX") {
  1058. if (!MessageService.AskQuestion("${res:SharpDevelop.Solution.ImportCmbx}")) {
  1059. return null;
  1060. }
  1061. newSolution.fileName = Path.ChangeExtension(fileName, ".sln");
  1062. ICSharpCode.SharpDevelop.Project.Converter.CombineToSolution.ConvertSolution(newSolution, fileName);
  1063. if (newSolution.FixSolutionConfiguration(newSolution.Projects)) {
  1064. newSolution.Save();
  1065. }
  1066. } else if (extension == ".PRJX") {
  1067. if (!MessageService.AskQuestion("${res:SharpDevelop.Solution.ImportPrjx}")) {
  1068. return null;
  1069. }
  1070. newSolution.fileName = Path.ChangeExtension(fileName, ".sln");
  1071. ICSharpCode.SharpDevelop.Project.Converter.CombineToSolution.ConvertProject(newSolution, fileName);
  1072. if (newSolution.FixSolutionConfiguration(newSolution.Projects)) {
  1073. newSolution.Save();
  1074. }
  1075. } else {
  1076. newSolution.fileName = fileName;
  1077. newSolution.isLoading = true;
  1078. try {
  1079. if (!SetupSolution(newSolution)) {
  1080. return null;
  1081. }
  1082. } finally {
  1083. newSolution.isLoading = false;
  1084. }
  1085. }
  1086. solutionBeingLoaded = null;
  1087. return newSolution;
  1088. }
  1089. #endregion
  1090. #region System.IDisposable interface implementation
  1091. public void Dispose()
  1092. {
  1093. foreach (IProject project in Projects) {
  1094. project.Dispose();
  1095. }
  1096. if (buildEngine != null) {
  1097. buildEngine.UnloadAllProjects();
  1098. buildEngine = null;
  1099. }
  1100. }
  1101. #endregion
  1102. #region Building
  1103. ICollection<IBuildable> IBuildable.GetBuildDependencies(ProjectBuildOptions buildOptions)
  1104. {
  1105. List<IBuildable> result = new List<IBuildable>();
  1106. foreach (IProject p in this.Projects)
  1107. result.Add(p);
  1108. return result;
  1109. }
  1110. void IBuildable.StartBuild(ProjectBuildOptions buildOptions, IBuildFeedbackSink feedbackSink)
  1111. {
  1112. // building a solution finishes immediately: we only care for the dependencies
  1113. feedbackSink.Done(true);
  1114. }
  1115. #endregion
  1116. public override string ToString()
  1117. {
  1118. return "[Solution: FileName=" + (this.FileName ?? "<null>") +
  1119. ", HasProjects=" + this.HasProjects.ToString(System.Globalization.CultureInfo.InvariantCulture) +
  1120. ", ReadOnly=" + this.ReadOnly.ToString(System.Globalization.CultureInfo.InvariantCulture) + "]";
  1121. }
  1122. }
  1123. }