Skip to content
This repository was archived by the owner on Feb 26, 2026. It is now read-only.

Commit c011c75

Browse files
authored
fix: fix project paths respecting nested projects (#145)
* fix: fix project paths respecting nested projects * feat: Add unit tests for group based projects
1 parent 40a1d9a commit c011c75

2 files changed

Lines changed: 79 additions & 1 deletion

File tree

Sources/XcodeGraphMapper/Mappers/Graph/XcodeGraphMapper.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,10 @@ public struct XcodeGraphMapper: XcodeGraphMapping {
320320
paths.append(refPath)
321321
}
322322
case let .group(group):
323-
let nestedPaths = try await extractProjectPaths(from: group.children, srcPath: srcPath)
323+
// Set a new src root to account for projects in nested directories
324+
let recursiveRoot = srcPath.appending(component: group.location.path)
325+
326+
let nestedPaths = try await extractProjectPaths(from: group.children, srcPath: recursiveRoot)
324327
paths.append(contentsOf: nestedPaths)
325328
}
326329
}

Tests/XcodeGraphMapperTests/MapperTests/Graph/XcodeGraphMapperTests.swift

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,81 @@ struct XcodeGraphMapperTests {
253253
#expect(graph.dependencyConditions.isEmpty == true)
254254
}
255255

256+
@Test("Maps a workspace with multiple projects in different directories into a single graph")
257+
func testWorkspaceGraphMultipleProjectsInDifferentDirectories() async throws {
258+
// Given
259+
//
260+
// A project structure like this:
261+
// .
262+
// ├── Workspace.xcworkspace
263+
// ├── App
264+
// │ └── ProjectA.xcodeproj
265+
// └── Modules
266+
// └── ProjectB.xcodeproj
267+
//
268+
269+
let pbxProjA = PBXProj()
270+
let pbxProjB = PBXProj()
271+
272+
let debug: XCBuildConfiguration = .testDebug().add(to: pbxProjA).add(to: pbxProjB)
273+
let releaseConfig: XCBuildConfiguration = .testRelease().add(to: pbxProjA).add(to: pbxProjB)
274+
let configurationList: XCConfigurationList = .test(
275+
buildConfigurations: [debug, releaseConfig]
276+
)
277+
.add(to: pbxProjA)
278+
.add(to: pbxProjB)
279+
280+
let projectA = try await XcodeProj.test(
281+
projectName: "ProjectA",
282+
configurationList: configurationList,
283+
pbxProj: pbxProjA,
284+
path: "/tmp/App/ProjectA.xcodeproj"
285+
)
286+
287+
let projectB = try await XcodeProj.test(
288+
projectName: "ProjectB",
289+
configurationList: configurationList,
290+
pbxProj: pbxProjB,
291+
path: "/tmp/Modules/ProjectB/ProjectB.xcodeproj"
292+
)
293+
294+
let projectAPath = try #require(projectA.path?.string)
295+
let projectBPath = try #require(projectB.path?.string)
296+
297+
let xcworkspace = XCWorkspace(
298+
data: XCWorkspaceData(
299+
children: [
300+
.group(.init(location: .group("App"), name: "App", children: [
301+
.file(.init(location: .absolute(projectAPath))),
302+
])),
303+
.group(.init(location: .group("Modules"), name: "Modules", children: [
304+
.file(.init(location: .absolute(projectBPath))),
305+
])),
306+
]
307+
),
308+
path: .init("/tmp/Workspace.xcworkspace")
309+
)
310+
311+
try projectA.write(path: projectA.path!)
312+
try projectB.write(path: projectB.path!)
313+
let mapper = XcodeGraphMapper()
314+
315+
// When
316+
let graph = try await mapper.buildGraph(from: .workspace(xcworkspace))
317+
318+
// Then
319+
320+
// General workspace checks
321+
#expect(graph.workspace.name == "Workspace")
322+
#expect(graph.workspace.projects.contains(projectA.projectPath) == true)
323+
#expect(graph.workspace.projects.contains(projectB.projectPath) == true)
324+
#expect(graph.projects.count == 2)
325+
326+
// Nested paths are correct
327+
#expect(graph.projects["/tmp/App"] != nil)
328+
#expect(graph.projects["/tmp/Modules/ProjectB"] != nil)
329+
}
330+
256331
@Test("Maps a project graph with dependencies between targets")
257332
func testGraphWithDependencies() async throws {
258333
// Given

0 commit comments

Comments
 (0)