From 0df37029cce2f14264e5852a7055c5dead855ce4 Mon Sep 17 00:00:00 2001 From: Romuald CARI Date: Wed, 7 Aug 2019 12:03:44 -0400 Subject: [PATCH] Fix TargetSource group behavior When targeting a file, a folder should not be created for its directory, only the file should appear in the specified custom group. Moreover, if the custom groups contains actual filesystem folders, they should map to these. --- Sources/XcodeGenKit/SourceGenerator.swift | 49 +++++++++++++++---- .../SourceGeneratorTests.swift | 30 +++++++----- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Sources/XcodeGenKit/SourceGenerator.swift b/Sources/XcodeGenKit/SourceGenerator.swift index 932a0d4c..3a409b53 100644 --- a/Sources/XcodeGenKit/SourceGenerator.swift +++ b/Sources/XcodeGenKit/SourceGenerator.swift @@ -281,6 +281,7 @@ class SourceGenerator { // Check equality by path and sourceTree because XcodeProj.PBXObject.== is very slow. if !cachedGroupChildren.contains(where: { $0.name == child.name && $0.path == child.path && $0.sourceTree == child.sourceTree }) { cachedGroupChildren.append(child) + child.parent = cachedGroup } } cachedGroup.children = cachedGroupChildren @@ -394,7 +395,6 @@ class SourceGenerator { targetSource: TargetSource, path: Path, isBaseGroup: Bool, - createIntermediateGroups: Bool, hasCustomParent: Bool, excludePaths: Set, includePaths: Set @@ -426,7 +426,6 @@ class SourceGenerator { targetSource: targetSource, path: path, isBaseGroup: false, - createIntermediateGroups: createIntermediateGroups, hasCustomParent: false, excludePaths: excludePaths, includePaths: includePaths @@ -581,7 +580,10 @@ class SourceGenerator { let sourceFile = generateSourceFile(targetType: targetType, targetSource: targetSource, path: path) - if parentPath == project.basePath { + if hasCustomParent { + sourcePath = path + sourceReference = fileReference + } else if parentPath == project.basePath { sourcePath = path sourceReference = fileReference rootGroups.insert(fileReference) @@ -609,7 +611,6 @@ class SourceGenerator { targetSource: targetSource, path: path, isBaseGroup: true, - createIntermediateGroups: createIntermediateGroups, hasCustomParent: hasCustomParent, excludePaths: excludePaths, includePaths: includePaths @@ -626,6 +627,7 @@ class SourceGenerator { if hasCustomParent { createParentGroups(customParentGroups, for: sourceReference) + try makePathRelative(for: sourceReference, at: path) } else if createIntermediateGroups { createIntermediaGroups(for: sourceReference, at: sourcePath) } @@ -639,21 +641,24 @@ class SourceGenerator { } let parentPath = project.basePath + Path(parentGroups.joined(separator: "/")) + let parentPathExists = parentPath.exists + let parentGroupAlreadyExists = groupsByPath[parentPath] != nil - let hasParentGroup = groupsByPath[parentPath] != nil let parentGroup = getGroup( path: parentPath, mergingChildren: [fileElement], createIntermediateGroups: false, hasCustomParent: false, - isBaseGroup: false + isBaseGroup: parentGroups.count == 1 ) // As this path is a custom group, remove the path reference - parentGroup.name = String(parentName) - parentGroup.path = nil + if !parentPathExists { + parentGroup.name = String(parentName) + parentGroup.path = nil + } - if !hasParentGroup { + if !parentGroupAlreadyExists { createParentGroups(parentGroups.dropLast(), for: parentGroup) } } @@ -681,6 +686,32 @@ class SourceGenerator { } } + // Make the fileElement path and name relative to its parents aggregated paths + private func makePathRelative(for fileElement: PBXFileElement, at path: Path) throws { + // This makes the fileElement path relative to its parent and not to the project. Xcode then rebuilds the actual + // path for the file based on the hierarchy this fileElement lives in. + var paths: [String] = [] + var element: PBXFileElement = fileElement + while true { + guard let parent = element.parent else { break } + + if let path = parent.path { + paths.insert(path, at: 0) + } + + element = parent + } + + let completePath = project.basePath + Path(paths.joined(separator: "/")) + let relativePath = try path.relativePath(from: completePath) + let relativePathString = relativePath.string + + if relativePathString != fileElement.path { + fileElement.path = relativePathString + fileElement.name = relativePath.lastComponent + } + } + private func findCurrentCoreDataModelVersionPath(using versionedModels: [Path]) -> Path? { // Find and parse the current version model stored in the .xccurrentversion file guard diff --git a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift index 76af3b62..49458675 100644 --- a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift @@ -469,35 +469,41 @@ class SourceGeneratorTests: XCTestCase { try pbxProj.expectFile(paths: ["../OtherDirectory/C/D", "e.swift"], names: ["D", "e.swift"], buildPhase: .sources) try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources) } - + $0.it("generates custom groups") { - + let directories = """ - Sources: - A: + - Sources: + - a.swift + - A: - b.swift - F: + - F: - G: - h.swift - B: + - i.swift + - B: - b.swift - C: - c.swift """ try createDirectories(directories) - + let target = Target(name: "Test", type: .application, platform: .iOS, sources: [ + TargetSource(path: "Sources/a.swift", group: "CustomGroup1"), TargetSource(path: "Sources/A/b.swift", group: "CustomGroup1"), TargetSource(path: "Sources/F/G/h.swift", group: "CustomGroup1"), TargetSource(path: "Sources/B", group: "CustomGroup2", createIntermediateGroups: false), + TargetSource(path: "Sources/F/G/i.swift", group: "Sources/F/G/CustomGroup3"), ]) - + let options = SpecOptions(createIntermediateGroups: true) let project = Project(basePath: directoryPath, name: "Test", targets: [target], options: options) - + let pbxProj = try project.generatePbxProj() - try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/A", "b.swift"], names: ["CustomGroup1", "A", "b.swift"], buildPhase: .sources) - try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/F/G", "h.swift"], names: ["CustomGroup1", "G", "h.swift"], buildPhase: .sources) + try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/a.swift"], names: ["CustomGroup1", "a.swift"], buildPhase: .sources) + try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/A/b.swift"], names: ["CustomGroup1", "b.swift"], buildPhase: .sources) + try pbxProj.expectFile(paths: ["CustomGroup1", "Sources/F/G/h.swift"], names: ["CustomGroup1", "h.swift"], buildPhase: .sources) + try pbxProj.expectFile(paths: ["Sources", "F", "G", "CustomGroup3", "i.swift"], names: ["Sources", "F", "G", "CustomGroup3", "i.swift"], buildPhase: .sources) try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "b.swift"], names: ["CustomGroup2", "B", "b.swift"], buildPhase: .sources) try pbxProj.expectFile(paths: ["CustomGroup2", "Sources/B", "C", "c.swift"], names: ["CustomGroup2", "B", "C", "c.swift"], buildPhase: .sources) } @@ -988,7 +994,7 @@ extension PBXProj { guard !paths.isEmpty else { return nil } - + let path = paths.first! let name = names.first! let restOfPath = Array(paths.dropFirst())