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

Commit a29a743

Browse files
fortmarekclaude
andcommitted
feat: add foreignBuild case to TargetDependency
Add support for foreign build system dependencies (KMP, Rust, CMake, etc.) by introducing a new `foreignBuild` case on `TargetDependency` and a `ForeignBuildCacheInput` model for content hashing. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ea2c6d3 commit a29a743

5 files changed

Lines changed: 65 additions & 0 deletions

File tree

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import Path
2+
3+
public enum ForeignBuildCacheInput: Equatable, Hashable, Codable, Sendable {
4+
case file(AbsolutePath)
5+
case folder(AbsolutePath)
6+
case glob(String)
7+
case script(String)
8+
}

Sources/XcodeGraph/Models/TargetDependency.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,13 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
5050
case package(product: String, type: PackageType, condition: PlatformCondition? = nil)
5151
case sdk(name: String, status: LinkingStatus, condition: PlatformCondition? = nil)
5252
case xctest
53+
indirect case foreignBuild(
54+
name: String,
55+
script: String,
56+
output: TargetDependency,
57+
cacheInputs: [ForeignBuildCacheInput],
58+
condition: PlatformCondition? = nil
59+
)
5360

5461
public var condition: PlatformCondition? {
5562
switch self {
@@ -68,6 +75,8 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
6875
case .sdk(name: _, status: _, condition: let condition):
6976
condition
7077
case .xctest: nil
78+
case .foreignBuild(name: _, script: _, output: _, cacheInputs: _, condition: let condition):
79+
condition
7180
}
7281
}
7382

@@ -88,6 +97,8 @@ public enum TargetDependency: Equatable, Hashable, Codable, Sendable {
8897
case .sdk(name: let name, status: let status, condition: _):
8998
return .sdk(name: name, status: status, condition: condition)
9099
case .xctest: return .xctest
100+
case .foreignBuild(name: let name, script: let script, output: let output, cacheInputs: let cacheInputs, condition: _):
101+
return .foreignBuild(name: name, script: script, output: output, cacheInputs: cacheInputs, condition: condition)
91102
}
92103
}
93104
}

Sources/XcodeGraphMapper/Extensions/TargetDependency+Extensions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ extension TargetDependency {
2020
return path.basenameWithoutExt
2121
case .xctest:
2222
return "xctest"
23+
case let .foreignBuild(name, _, _, _, _):
24+
return name
2325
}
2426
}
2527
}

Sources/XcodeGraphMapper/Mappers/Targets/TargetDependency+GraphMapping.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ extension TargetDependency {
101101
source: .developer
102102
)
103103

104+
case let .foreignBuild(_, _, output, _, _):
105+
return try await output.graphDependency(
106+
sourceDirectory: sourceDirectory,
107+
target: target,
108+
xcframeworkMetadataProvider: xcframeworkMetadataProvider,
109+
libraryMetadataProvider: libraryMetadataProvider,
110+
frameworkMetadataProvider: frameworkMetadataProvider,
111+
systemFrameworkMetadataProvider: systemFrameworkMetadataProvider,
112+
developerDirectoryProvider: developerDirectoryProvider
113+
)
114+
104115
// MARK: - XCTest (System Provided)
105116

106117
case .xctest:

Tests/XcodeGraphTests/Models/TargetDependencyTests.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,28 @@ final class TargetDependencyTests: XCTestCase {
3636
XCTAssertCodable(subject)
3737
}
3838

39+
func test_codable_foreignBuild() {
40+
// Given
41+
let subject = TargetDependency.foreignBuild(
42+
name: "SharedKMP",
43+
script: "./gradlew build",
44+
output: .xcframework(
45+
path: try! AbsolutePath(validating: "/path/to/output.xcframework"),
46+
expectedSignature: nil,
47+
status: .required
48+
),
49+
cacheInputs: [
50+
.file(try! AbsolutePath(validating: "/path/to/input.kt")),
51+
.folder(try! AbsolutePath(validating: "/path/to/src")),
52+
.glob("**/*.kt"),
53+
.script("git rev-parse HEAD"),
54+
]
55+
)
56+
57+
// Then
58+
XCTAssertCodable(subject)
59+
}
60+
3961
func test_filtering() {
4062
let expected: PlatformCondition? = .when([.macos])
4163

@@ -57,6 +79,17 @@ final class TargetDependencyTests: XCTestCase {
5779
condition: expected
5880
),
5981
.project(target: "", path: try! AbsolutePath(validating: "/"), condition: expected),
82+
.foreignBuild(
83+
name: "KMP",
84+
script: "./build.sh",
85+
output: .xcframework(
86+
path: try! AbsolutePath(validating: "/output.xcframework"),
87+
expectedSignature: nil,
88+
status: .required
89+
),
90+
cacheInputs: [.file(try! AbsolutePath(validating: "/input.kt"))],
91+
condition: expected
92+
),
6093
]
6194

6295
for subject in subjects {

0 commit comments

Comments
 (0)