From 2c560528520e03989a7cfd009060711b77752c54 Mon Sep 17 00:00:00 2001 From: ybai08 <271969568+ybai08@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:20:30 -0500 Subject: [PATCH 1/2] Fix hang when createIntermediateGroups reaches the file system root A source whose only common ancestor with the project is "/" (e.g. an absolute path like /usr/bin/xcrun) made createIntermediaGroups add the "/" group as a child of itself, since "/".parent() is "/". Generation then looped forever in makePathRelative. Stop creating intermediate groups once the root of the file system is reached. Resolves #1539 --- Sources/XcodeGenKit/SourceGenerator.swift | 4 ++-- .../SourceGeneratorTests.swift | 20 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/Sources/XcodeGenKit/SourceGenerator.swift b/Sources/XcodeGenKit/SourceGenerator.swift index d07737ae..008b6636 100644 --- a/Sources/XcodeGenKit/SourceGenerator.swift +++ b/Sources/XcodeGenKit/SourceGenerator.swift @@ -856,8 +856,8 @@ class SourceGenerator { private func createIntermediaGroups(for fileElement: PBXFileElement, at path: Path) { let parentPath = path.parent() - guard parentPath != project.basePath else { - // we've reached the top + guard parentPath != project.basePath, parentPath != path else { + // we've reached the top, or the root of the file system return } diff --git a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift index f24ad71e..14ec65f1 100644 --- a/Tests/XcodeGenKitTests/SourceGeneratorTests.swift +++ b/Tests/XcodeGenKitTests/SourceGeneratorTests.swift @@ -961,6 +961,26 @@ class SourceGeneratorTests: XCTestCase { try pbxProj.expectFile(paths: ["Sources/B", "b.swift"], names: ["B", "b.swift"], buildPhase: .sources) } + $0.it("generates intermediate groups up to the file system root") { + try createDirectories(""" + Sources: + - a.swift + """) + + let target = Target(name: "Test", type: .application, platform: .iOS, sources: [ + "Sources", + "/usr/bin/env", + ]) + let options = SpecOptions(createIntermediateGroups: true) + let basePath = directoryPath.absolute() + let project = Project(basePath: basePath, name: "Test", targets: [target], options: options) + + let pbxProj = try project.generatePbxProj() + let rootPath = try Path("/").relativePath(from: basePath).string + try pbxProj.expectFile(paths: ["Sources", "a.swift"], buildPhase: .sources) + try pbxProj.expectFile(paths: [rootPath, "usr", "bin", "env"], names: ["/", "usr", "bin", "env"]) + } + $0.it("generates intermediate groups with different projectDirectory") { let directories = """ From 1d026c978e097b8481c7bb3b19634d78a2848762 Mon Sep 17 00:00:00 2001 From: ybai08 <271969568+ybai08@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:26:59 -0500 Subject: [PATCH 2/2] Add changelog entry --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7aa70da5..a9a54906 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Fix nested target attributes (e.g. `attributes.SystemCapabilities`) being serialized as a stringified Swift `Dictionary` description instead of a proper nested plist dictionary, which also caused non-deterministic key ordering in generated `project.pbxproj` files across runs #1639 @imadaan @sergeyospanov - Fix `.DS_Store` and `.orig` files causing projects to be regenerated when using `--use-cache` #1622 @kimjune01 - Fix generated "Copy Bundle Resources" phases missing `dstPath`, which caused Xcode 27.2 project conversion to fail #1654 @Ckitakishi +- Fix `xcodegen generate` hanging when `createIntermediateGroups` is enabled and a source's only common ancestor with the project is the file system root, such as an absolute path like `/usr/bin/xcrun` #1659 @ybai08 ### Internal - Use a dedicated local package in the SPM fixture so generated fixtures don't depend on the checkout directory name, such as when running tests from a git worktree @yonaskolb