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())