Skip to content

Commit 3e6e5e3

Browse files
yonaskolbclaude
andauthored
Fix makePathRelative using wrong base path when projectDirectory differs (#1608)
* Fix makePathRelative using wrong base path when projectDirectory differs (#1606) `makePathRelative` used `project.basePath` to compute parent paths, while `resolveGroupPath` used `projectDirectory ?? project.basePath`. When these differ (e.g. via --project flag), intermediate group paths become inconsistent, causing Xcode to resolve files to the wrong location. Regression from #1596 which added the `makePathRelative` call in the `createIntermediateGroups` code path. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Extract basePath computed property in SourceGenerator Consolidates `projectDirectory ?? project.basePath` into a single `basePath` property to prevent future mismatches between path resolution call sites. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 90dfa9d commit 3e6e5e3

2 files changed

Lines changed: 43 additions & 3 deletions

File tree

Sources/XcodeGenKit/SourceGenerator.swift

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,20 @@ class SourceGenerator {
3232

3333
private(set) var knownRegions: Set<String> = []
3434

35+
/// The effective base path for resolving group and file paths in the generated project.
36+
/// Uses `projectDirectory` when the xcodeproj is generated in a different location than the spec.
37+
private var basePath: Path {
38+
projectDirectory ?? project.basePath
39+
}
40+
3541
init(project: Project, pbxProj: PBXProj, projectDirectory: Path?) {
3642
self.project = project
3743
self.pbxProj = pbxProj
3844
self.projectDirectory = projectDirectory
3945
}
4046

4147
private func resolveGroupPath(_ path: Path, isTopLevelGroup: Bool) -> String {
42-
if isTopLevelGroup, let relativePath = try? path.relativePath(from: projectDirectory ?? project.basePath).string {
48+
if isTopLevelGroup, let relativePath = try? path.relativePath(from: basePath).string {
4349
return relativePath
4450
} else {
4551
return path.lastComponent
@@ -62,7 +68,7 @@ class SourceGenerator {
6268
let absolutePath = project.basePath + path.normalize()
6369

6470
// Get the local package's relative path from the project root
65-
let fileReferencePath = try? absolutePath.relativePath(from: projectDirectory ?? project.basePath).string
71+
let fileReferencePath = try? absolutePath.relativePath(from: basePath).string
6672

6773
let fileReference = addObject(
6874
PBXFileReference(
@@ -886,7 +892,7 @@ class SourceGenerator {
886892
element = parent
887893
}
888894

889-
let completePath = project.basePath + Path(paths.joined(separator: "/"))
895+
let completePath = (basePath) + Path(paths.joined(separator: "/"))
890896
let relativePath = try path.relativePath(from: completePath)
891897
let relativePathString = relativePath.string
892898

Tests/XcodeGenKitTests/SourceGeneratorTests.swift

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,40 @@ class SourceGeneratorTests: XCTestCase {
911911
try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources)
912912
}
913913

914+
$0.it("generates intermediate groups with different projectDirectory") {
915+
916+
let directories = """
917+
Sources:
918+
- a.swift
919+
- b.swift
920+
Modules:
921+
- m.swift
922+
"""
923+
try createDirectories(directories)
924+
925+
let target = Target(name: "Test", type: .application, platform: .iOS, sources: [
926+
"../Sources",
927+
"../Modules",
928+
])
929+
let options = SpecOptions(createIntermediateGroups: true)
930+
// basePath is a subdirectory (simulating spec in a subdir like ProjectRoot/XcodeGen/)
931+
// projectDirectory is the parent (simulating --project pointing to ProjectRoot/)
932+
let subdir = directoryPath + "SubDir"
933+
try subdir.mkpath()
934+
let project = Project(basePath: subdir, name: "Test", targets: [target], options: options)
935+
936+
let generator = PBXProjGenerator(project: project, projectDirectory: directoryPath)
937+
let pbxProj = try generator.generate()
938+
939+
// Sources and Modules should have path = "Sources"/"Modules" (not "../Sources")
940+
// The intermediate group for TestDirectory should have path = "."
941+
// So Xcode resolves: projectDir/./Sources = correct
942+
// Before fix: path was "../Sources", resolving to projectDir/./../Sources = wrong
943+
try pbxProj.expectFile(paths: [".", "Sources", "a.swift"], names: ["TestDirectory", "Sources", "a.swift"], buildPhase: .sources)
944+
try pbxProj.expectFile(paths: [".", "Sources", "b.swift"], names: ["TestDirectory", "Sources", "b.swift"], buildPhase: .sources)
945+
try pbxProj.expectFile(paths: [".", "Modules", "m.swift"], names: ["TestDirectory", "Modules", "m.swift"], buildPhase: .sources)
946+
}
947+
914948
$0.it("generates custom groups") {
915949

916950
let directories = """

0 commit comments

Comments
 (0)