private static void MergeProjects(SlnMergeMergeContext ctx) { foreach (var project in ctx.OverlaySolutionFile.Projects) { if (!ctx.MergedSolutionFile.Projects.ContainsKey(project.Key)) { // If a solution contains a project that has same name, resolve conflict by resolution strategy. if (ctx.MergedSolutionFile.Projects.Any(x => x.Value.Name == project.Value.Name)) { if (ctx.Settings.ProjectConflictResolution == ProjectConflictResolution.PreserveUnity) { // Ignore Overlay's project continue; } else if (ctx.Settings.ProjectConflictResolution == ProjectConflictResolution.PreserveOverlay) { // Remove Unity generated project var duplicatedProject = ctx.MergedSolutionFile.Projects.First(x => x.Value.Name == project.Value.Name); ctx.MergedSolutionFile.Projects.Remove(duplicatedProject.Key); } else { // Preserve all projects } } if (project.Value.IsFolder) { // If the project is a folder and it contains solution items, we adjust the path of solution items. if (project.Value.Sections.TryGetValue(("SolutionItems", "preProject"), out var solutionItems)) { foreach (var solutionItem in solutionItems.Values.ToArray()) { solutionItems.Values.Remove(solutionItem.Key); var solutionItemPathAbsolute = NormalizePath(Path.Combine(Path.GetDirectoryName(ctx.OverlaySolutionFile.Path), solutionItem.Key)); var solutionItemPathRelative = MakeRelative(ctx.MergedSolutionFile.Path, solutionItemPathAbsolute); solutionItems.Values[solutionItemPathRelative] = solutionItemPathRelative; } } } else { var overlayProjectPathAbsolute = NormalizePath(Path.Combine(Path.GetDirectoryName(ctx.OverlaySolutionFile.Path), project.Value.Path)); project.Value.Path = MakeRelative(ctx.MergedSolutionFile.Path, overlayProjectPathAbsolute); } ctx.MergedSolutionFile.Projects.Add(project.Key, project.Value); } else { // A project already exists. } } }
public static SolutionFile Merge(SolutionFile solutionFile, SolutionFile overlaySolutionFile, SlnMergeSettings settings, ISlnMergeLogger logger) { logger.Debug($"Merge solution: Base={solutionFile.Path}; Overlay={overlaySolutionFile.Path}"); var ctx = new SlnMergeMergeContext(solutionFile, overlaySolutionFile, solutionFile.Clone(), settings, logger); MergeProjects(ctx); MergeGlobalSections(ctx); ModifySolutionFolders(ctx); return(ctx.MergedSolutionFile); }
private static void MergeGlobalSections(SlnMergeMergeContext ctx) { foreach (var sectionKeyValue in ctx.OverlaySolutionFile.Global.Sections) { if (ctx.MergedSolutionFile.Global.Sections.TryGetValue(sectionKeyValue.Key, out var targetSection)) { foreach (var keyValue in sectionKeyValue.Value.Values) { targetSection.Values[keyValue.Key] = keyValue.Value; } targetSection.Children.AddRange(sectionKeyValue.Value.Children); } else { ctx.MergedSolutionFile.Global.Sections.Add(sectionKeyValue.Key, sectionKeyValue.Value); } } }
private static void MergeProjects(SlnMergeMergeContext ctx) { foreach (var project in ctx.OverlaySolutionFile.Projects) { if (!ctx.MergedSolutionFile.Projects.ContainsKey(project.Key)) { // If a solution contains a project that has same name, resolve conflict by resolution strategy. if (ctx.MergedSolutionFile.Projects.Any(x => x.Value.Name == project.Value.Name)) { if (ctx.Settings.ProjectConflictResolution == ProjectConflictResolution.PreserveUnity) { // Ignore Overlay's project continue; } else if (ctx.Settings.ProjectConflictResolution == ProjectConflictResolution.PreserveOverlay) { // Remove Unity generated project var duplicatedProject = ctx.MergedSolutionFile.Projects.First(x => x.Value.Name == project.Value.Name); ctx.MergedSolutionFile.Projects.Remove(duplicatedProject.Key); } else { // Preserve all projects } } if (!project.Value.IsFolder) { var overlayProjectPathAbsolute = NormalizePath(Path.Combine(Path.GetDirectoryName(ctx.OverlaySolutionFile.Path), project.Value.Path)); project.Value.Path = MakeRelative(ctx.MergedSolutionFile.Path, overlayProjectPathAbsolute); } ctx.MergedSolutionFile.Projects.Add(project.Key, project.Value); } else { // A project already exists. } } }
private static void ModifySolutionFolders(SlnMergeMergeContext ctx) { if (ctx.Settings.NestedProjects == null || ctx.Settings.NestedProjects.Length == 0) { return; } // Build a solution folder tree. var solutionTree = BuildSolutionFlatTree(ctx.MergedSolutionFile); // Create a NestedProject section in the solution if it does not exist. if (!ctx.MergedSolutionFile.Global.Sections.TryGetValue(("NestedProjects", "preSolution"), out var section)) { section = new SolutionGlobalSection(ctx.MergedSolutionFile.Global, "NestedProjects", "preSolution"); ctx.MergedSolutionFile.Global.Sections.Add((section.Category, section.Value), section); } // Prepare to add nested projects. var definedSolutionFolders = (ctx.Settings.SolutionFolders ?? Array.Empty <SlnMergeSettings.SolutionFolder>()) .ToDictionary(k => k.FolderPath.Replace('\\', '/'), v => v.Guid); var nestedProjects = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase); foreach (var nestedProject in ctx.Settings.NestedProjects) { IEnumerable <string> nestedProjectGuids; var nestedProjectFolderGuid = default(string); // Find a target project if (string.IsNullOrEmpty(nestedProject.ProjectName)) { // by GUID nestedProjectGuids = new[] { nestedProject.ProjectGuid }; } else { // by Name var nestedProjectNamePattern = new Regex( "^" + Regex.Escape(nestedProject.ProjectName) .Replace(@"\*", ".*").Replace(@"\?", ".") + "$"); nestedProjectGuids = ctx.MergedSolutionFile.Projects.Values .Where(x => nestedProjectNamePattern.IsMatch(x.Name)) .Select(x => x.Guid); } // Find a solution folder if (string.IsNullOrEmpty(nestedProject.FolderPath)) { // by GUID nestedProjectFolderGuid = nestedProject.FolderGuid; } else { // by Path if (solutionTree.TryGetValue(nestedProject.FolderPath, out var folderNode)) { if (!folderNode.IsFolder) { throw new Exception($"Path '{nestedProject.FolderPath}' is not a Solution Folder."); } nestedProjectFolderGuid = folderNode.Project.Guid; } else { // The target Solution Folder does not exist. make the Solution Folders. var pathParts = nestedProject.FolderPath.Split('/', '\\'); for (var i = 0; i < pathParts.Length; i++) { var path = string.Join("/", pathParts.Take(i + 1)); var parentPath = string.Join("/", pathParts.Take(i)); if (solutionTree.TryGetValue(path, out var folderNode2)) { // A solution tree node already exists. if (!folderNode2.IsFolder) { throw new Exception($"Path '{path}' is not a Solution Folder."); } } else { // Create a new solution folder. if (!definedSolutionFolders.TryGetValue(path, out var guid)) { throw new Exception($"Path '{path}' doesn't exist in the Solution or Solution Folder definitions. To create a new folder, you need to define SolutionFolder in .mergesettings."); } var newFolder = new SolutionProject(ctx.MergedSolutionFile, typeGuid: GuidProjectTypeFolder, guid: guid.ToUpper(), name: pathParts[i], path: pathParts[i] ); ctx.MergedSolutionFile.Projects.Add(newFolder.Guid, newFolder); // If the solution folder has a parent folder, add the created folder as a child immediately. if (!string.IsNullOrEmpty(parentPath)) { section.Values[newFolder.Guid] = solutionTree[parentPath].Project.Guid; } // Rebuild the solution tree. solutionTree = BuildSolutionFlatTree(ctx.MergedSolutionFile); nestedProjectFolderGuid = newFolder.Guid; } } } } // Verify GUIDs / Paths if (!nestedProjectGuids.Any()) { throw new Exception($"Project '{nestedProject.ProjectName}' does not exists in the solution."); } if (nestedProjectFolderGuid == null) { throw new Exception($"Solution Folder '{nestedProject.FolderGuid}' (GUID) does not exists in the solution."); } if (!ctx.MergedSolutionFile.Projects.ContainsKey(nestedProjectFolderGuid)) { throw new Exception($"Solution Folder '{nestedProject.FolderGuid}' (GUID) does not exists in the solution."); } foreach (var nestedProjectGuid in nestedProjectGuids) { if (!ctx.MergedSolutionFile.Projects.ContainsKey(nestedProjectGuid)) { throw new Exception($"Project '{nestedProject.FolderGuid}' (GUID) does not exists in the solution."); } nestedProjects.Add(nestedProjectGuid, nestedProjectFolderGuid); } } // Add nested projects. foreach (var keyValue in nestedProjects) { section.Values[keyValue.Key] = keyValue.Value; } }