/// <summary> /// Saves solution into .sln file. Where to save is defined by path. /// </summary> /// <param name="_path">path to .sln, null if use from 'path' variable.</param> /// <param name="uinfo">Update information</param> public void SaveSolution(UpdateInfo uinfo, String _path = null) { String slnPath = _path; if (_path == null) { slnPath = path; } // // For all projects which does not have uuid, we generated uuid based on project name. // SolutionProjectBuilder.externalproject(null); // Release any active project if we have one. if (fileFormatVersion >= 2017 && String.IsNullOrEmpty(SolutionGuid)) { SolutionProjectBuilder.uuid(name + "_solution" /* Add some uniqueness - just not to collise with project guids. */); } foreach (Project p in projects) { if (String.IsNullOrEmpty(p.ProjectGuid)) { SolutionProjectBuilder.m_project = p; SolutionProjectBuilder.uuid(p.ProjectName); SolutionProjectBuilder.m_project = null; } } //foreach StringBuilder o = new StringBuilder(); o.AppendLine(); int verTag = fileFormatVersion; if (verTag == 0) { verTag = 2015; } String formatVersion = "12.00"; if (verTag <= 2010) { formatVersion = "11.00"; } o.AppendLine("Microsoft Visual Studio Solution File, Format Version " + formatVersion); int verTag2; switch (verTag) { case 2015: verTag2 = 14; break; case 2017: verTag2 = 15; break; case 2019: verTag2 = 16; break; default: // Try to predict the future here... verTag2 = 14 + (verTag - 2015) / 2; break; } if (verTag2 >= 16) { o.AppendLine("# Visual Studio Version " + verTag2.ToString()); } else { o.AppendLine("# Visual Studio " + verTag2.ToString()); } // Must be specified, otherwise Visual studio will try to save project after load. if (fileFormatVersion >= 2017) { // Those numbers are pretty ugly to hardcode, but no other choice at the moment. String ver = VisualStudioVersion; if (ver == null) { switch (fileFormatVersion) { case 2017: ver = "15.0.28307.136"; break; default: case 2019: ver = "16.0.28315.86"; break; } } o.AppendLine("VisualStudioVersion = " + ver); } // Must be specified, otherwise Visual studio will try to save project after load. if (fileFormatVersion >= 2015) { String ver = MinimumVisualStudioVersion; if (ver == null) { ver = "10.0.40219.1"; } o.AppendLine("MinimumVisualStudioVersion = " + ver); } // Visual studio 2015 itself dumps also VisualStudioVersion & MinimumVisualStudioVersion - but we cannot support it, as it's targetted per visual studio toolset version. // // Dump projects. // foreach (Project p in projects) { o.AppendLine("Project(\"" + p.ProjectHostGuid + "\") = \"" + p.ProjectName + "\", \"" + p.getRelativePath() + "\", \"" + p.ProjectGuid.ToUpper() + "\""); // // Dump project dependencies. // if (p.ProjectDependencies != null) { o.AppendLine(" ProjectSection(ProjectDependencies) = postProject"); foreach (String depProjName in p.ProjectDependencies) { String guid = null; // Dependency specified by {guid} if (SolutionProjectBuilder.guidMatcher.Match(depProjName).Success) { guid = depProjName; } else { // Dependency specified by project name Project dproj = projects.Where(x => x.ProjectName == depProjName).FirstOrDefault(); if (dproj != null) { guid = dproj.ProjectGuid.ToUpper(); } } if (guid != null) { o.AppendLine(" "+ guid + " = " + guid); } } o.AppendLine(" EndProjectSection"); } //if o.AppendLine("EndProject"); } List <String> sortedConfs = Project.getSortedConfigurations(configurations, false, null, true); // // Dump configurations. // o.AppendLine("Global"); o.AppendLine(" GlobalSection(SolutionConfigurationPlatforms) = preSolution"); foreach (String cfg in sortedConfs) { o.AppendLine(" "+ cfg + " = " + cfg); } o.AppendLine(" EndGlobalSection"); // // Dump solution to project configuration mapping and whether or not to build specific project. // o.AppendLine(" GlobalSection(ProjectConfigurationPlatforms) = postSolution"); foreach (Project p in projects) { if (p.IsSubFolder()) // If sub-folder no need to list it here. { continue; } List <String> projConfs = p.getConfigurationNames(); List <String> projPlatforms = p.getPlatforms(); foreach (String conf in sortedConfs) { int iConf = configurations.IndexOf(conf); String mappedConf = conf; bool bPeformBuild = true; bool?bPerformDeploy = null; if (p.bIsPackagingProject) { bPerformDeploy = true; } if (p.slnConfigurations != null && iConf < p.slnConfigurations.Count) { // Mapped configuration item is specified. mappedConf = p.slnConfigurations[iConf]; } else { if (p.bDefinedAsExternal) { // Hack - assume one to one mapping for timebeing. mappedConf = conf; } else { // // Try to map configuration by ourselfs. Map x86 to Win32 automatically. // if (!p.configurations.Contains(conf)) { String[] confPlat = conf.Split('|'); if (projConfs.Contains(confPlat[0]) && confPlat[1] == "x86" && projPlatforms.Contains("Win32")) { mappedConf = confPlat[0] + '|' + "Win32"; } else { // Configuration cannot be mapped (E.g. Solution has "Debug|Arm", project supports only "Debug|Win32". // We disable project build, but try to map configuration anyway - otherwise Visual Studio will // try to save solution by itself. bPeformBuild = false; bPerformDeploy = null; mappedConf = p.configurations.Where(x => x.StartsWith(confPlat[0])).FirstOrDefault(); if (mappedConf == null) { mappedConf = p.configurations[0]; } } //if-else } //if } } if (p.slnBuildProject != null && iConf < p.slnBuildProject.Count) { bPeformBuild = p.slnBuildProject[iConf]; } if (p.slnDeployProject != null && iConf < p.slnConfigurations.Count) { bPerformDeploy = p.slnDeployProject[iConf]; } o.AppendLine(" "+ p.ProjectGuid.ToUpper() + "." + conf + ".ActiveCfg = " + mappedConf); if (bPeformBuild) { o.AppendLine(" "+ p.ProjectGuid.ToUpper() + "." + conf + ".Build.0 = " + mappedConf); } if (bPerformDeploy.HasValue && bPerformDeploy.Value) { o.AppendLine(" "+ p.ProjectGuid.ToUpper() + "." + conf + ".Deploy.0 = " + mappedConf); } } //for } //foreach o.AppendLine(" EndGlobalSection"); o.AppendLine(" GlobalSection(SolutionProperties) = preSolution"); o.AppendLine(" HideSolutionNode = FALSE"); o.AppendLine(" EndGlobalSection"); // // Dump project dependency hierarchy. // Project root = projects.FirstOrDefault(); if (root != null) { while (root.parent != null) { root = root.parent; } // // Flatten tree without recursion. // int treeIndex = 0; List <Project> projects2 = new List <Project>(); projects2.AddRange(root.nodes); for (; treeIndex < projects2.Count; treeIndex++) { if (projects2[treeIndex].nodes.Count == 0) { continue; } projects2.AddRange(projects2[treeIndex].nodes); } bool bDump = projects2.Count(x => x.parent.parent != null) != 0; if (bDump) { o.AppendLine(" GlobalSection(NestedProjects) = preSolution"); } foreach (Project p in projects2) { if (p.parent.parent == null) { continue; } o.AppendLine(" "+ p.ProjectGuid.ToUpper() + " = " + p.parent.ProjectGuid.ToUpper()); } if (bDump) { o.AppendLine(" EndGlobalSection"); } } //if if (SolutionGuid != null) { o.AppendLine(" GlobalSection(ExtensibilityGlobals) = postSolution"); o.AppendLine(" SolutionGuid = "+ SolutionGuid); o.AppendLine(" EndGlobalSection"); } o.AppendLine("EndGlobal"); String currentSln = ""; if (File.Exists(slnPath)) { currentSln = File.ReadAllText(slnPath); } String newSln = o.ToString(); // // Save only if needed. // if (currentSln == newSln) { uinfo.MarkFileUpdated(slnPath, false); } else { if (SolutionProjectBuilder.isDeveloper() && File.Exists(slnPath)) { File.Copy(slnPath, slnPath + ".bkp", true); } File.WriteAllText(slnPath, newSln, Encoding.UTF8); uinfo.MarkFileUpdated(slnPath, true); } //if-else } //SaveSolution