Skip to content

Commit e3ee449

Browse files
committed
[BuildDescription] Add a way to get (recursive) dependencies from a module build description
BuildPlan adjacent things should use this instead of reaching for a module graph and lookup up descriptions.
1 parent 4a260ff commit e3ee449

File tree

6 files changed

+150
-2
lines changed

6 files changed

+150
-2
lines changed

Sources/Build/BuildDescription/ClangModuleBuildDescription.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,3 +526,17 @@ public final class ClangModuleBuildDescription {
526526
)
527527
}
528528
}
529+
530+
extension ClangModuleBuildDescription {
531+
package func dependencies(
532+
using plan: BuildPlan
533+
) -> [ModuleBuildDescription.Dependency] {
534+
ModuleBuildDescription.clang(self).dependencies(using: plan)
535+
}
536+
537+
package func recursiveDependencies(
538+
using plan: BuildPlan
539+
) -> [ModuleBuildDescription.Dependency] {
540+
ModuleBuildDescription.clang(self).recursiveDependencies(using: plan)
541+
}
542+
}

Sources/Build/BuildDescription/ModuleBuildDescription.swift

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basics
1414
import struct PackageGraph.ResolvedModule
1515
import struct PackageGraph.ResolvedPackage
16+
import struct PackageGraph.ResolvedProduct
1617
import struct PackageModel.Resource
1718
import struct PackageModel.ToolsVersion
1819
import struct SPMBuildCore.BuildToolPluginInvocationResult
@@ -158,4 +159,37 @@ extension ModuleBuildDescription: Identifiable {
158159
public var id: ID {
159160
ID(moduleID: self.module.id, destination: self.destination)
160161
}
161-
}
162+
}
163+
164+
extension ModuleBuildDescription {
165+
package enum Dependency {
166+
/// Not all of the modules and products have build descriptions
167+
case product(ResolvedProduct, ProductBuildDescription?)
168+
case module(ResolvedModule, ModuleBuildDescription?)
169+
}
170+
171+
package func dependencies(using plan: BuildPlan) -> [Dependency] {
172+
self.module
173+
.dependencies(satisfying: self.buildParameters.buildEnvironment)
174+
.map {
175+
switch $0 {
176+
case .product(let product, _):
177+
let productDescription = plan.description(for: product, context: self.destination)
178+
return .product(product, productDescription)
179+
case .module(let module, _):
180+
let moduleDescription = plan.description(for: module, context: self.destination)
181+
return .module(module, moduleDescription)
182+
}
183+
}
184+
}
185+
186+
package func recursiveDependencies(using plan: BuildPlan) -> [Dependency] {
187+
var dependencies: [Dependency] = []
188+
plan.traverseDependencies(of: self) { product, _, description in
189+
dependencies.append(.product(product, description))
190+
} onModule: { module, _, description in
191+
dependencies.append(.module(module, description))
192+
}
193+
return dependencies
194+
}
195+
}

Sources/Build/BuildDescription/SwiftModuleBuildDescription.swift

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -988,3 +988,17 @@ public final class SwiftModuleBuildDescription {
988988
return arguments
989989
}
990990
}
991+
992+
extension SwiftModuleBuildDescription {
993+
package func dependencies(
994+
using plan: BuildPlan
995+
) -> [ModuleBuildDescription.Dependency] {
996+
ModuleBuildDescription.swift(self).dependencies(using: plan)
997+
}
998+
999+
package func recursiveDependencies(
1000+
using plan: BuildPlan
1001+
) -> [ModuleBuildDescription.Dependency] {
1002+
ModuleBuildDescription.swift(self).recursiveDependencies(using: plan)
1003+
}
1004+
}

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1137,12 +1137,15 @@ extension BuildPlan {
11371137
onProduct: (ResolvedProduct, BuildParameters.Destination, ProductBuildDescription?) -> Void,
11381138
onModule: (ResolvedModule, BuildParameters.Destination, ModuleBuildDescription?) -> Void
11391139
) {
1140+
var visited = Set<TraversalNode>()
11401141
func successors(
11411142
for product: ResolvedProduct,
11421143
destination: Destination
11431144
) -> [TraversalNode] {
11441145
product.modules.map { module in
11451146
TraversalNode(module: module, context: destination)
1147+
}.filter {
1148+
visited.insert($0).inserted
11461149
}
11471150
}
11481151

@@ -1159,6 +1162,8 @@ extension BuildPlan {
11591162
case .module(let module, _):
11601163
partial.append(.init(module: module, context: destination))
11611164
}
1165+
}.filter {
1166+
visited.insert($0).inserted
11621167
}
11631168
}
11641169

Sources/SPMBuildCore/BuildParameters/BuildParameters.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public struct BuildParameters: Encodable {
3838
}
3939

4040
/// The destination for which code should be compiled for.
41-
public enum Destination: Encodable {
41+
public enum Destination: Hashable, Encodable {
4242
/// The destination for which build tools are compiled.
4343
case host
4444

Tests/BuildTests/BuildPlanTraversalTests.swift

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,5 +185,86 @@ final class BuildPlanTraversalTests: XCTestCase {
185185
XCTAssertEqual(moduleDependencies[index].1, .host)
186186
XCTAssertNotNil(moduleDependencies[index].2)
187187
}
188+
189+
let directDependencies = mmioModule.dependencies(using: plan)
190+
191+
XCTAssertEqual(directDependencies.count, 1)
192+
193+
let dependency = try XCTUnwrap(directDependencies.first)
194+
if case .module(let module, let description) = dependency {
195+
XCTAssertEqual(module.name, "MMIOMacros")
196+
try XCTAssertEqual(XCTUnwrap(description).destination, .host)
197+
} else {
198+
XCTFail("Expected MMIOMacros module")
199+
}
200+
201+
let dependencies = mmioModule.recursiveDependencies(using: plan)
202+
203+
XCTAssertEqual(dependencies.count, 3)
204+
205+
// MMIOMacros (module) -> SwiftSyntax (product) -> SwiftSyntax (module)
206+
207+
if case .module(let module, let description) = dependencies[0] {
208+
XCTAssertEqual(module.name, "MMIOMacros")
209+
try XCTAssertEqual(XCTUnwrap(description).destination, .host)
210+
} else {
211+
XCTFail("Expected MMIOMacros module")
212+
}
213+
214+
if case .product(let product, let description) = dependencies[1] {
215+
XCTAssertEqual(product.name, "SwiftSyntax")
216+
XCTAssertNil(description)
217+
} else {
218+
XCTFail("Expected SwiftSyntax product")
219+
}
220+
221+
if case .module(let module, let description) = dependencies[2] {
222+
XCTAssertEqual(module.name, "SwiftSyntax")
223+
try XCTAssertEqual(XCTUnwrap(description).destination, .host)
224+
} else {
225+
XCTFail("Expected SwiftSyntax module")
226+
}
227+
}
228+
229+
func testRecursiveDependencyTraversalWithDuplicates() async throws {
230+
let destinationTriple = Triple.arm64Linux
231+
let toolsTriple = Triple.x86_64MacOS
232+
233+
let (graph, fs, scope) = try macrosTestsPackageGraph()
234+
let plan = try await BuildPlan(
235+
destinationBuildParameters: mockBuildParameters(
236+
destination: .target,
237+
triple: destinationTriple
238+
),
239+
toolsBuildParameters: mockBuildParameters(
240+
destination: .host,
241+
triple: toolsTriple
242+
),
243+
graph: graph,
244+
fileSystem: fs,
245+
observabilityScope: scope
246+
)
247+
248+
let testModule = try XCTUnwrap(plan.description(for: graph.module(for: "MMIOMacrosTests")!, context: .host))
249+
250+
let dependencies = testModule.recursiveDependencies(using: plan)
251+
XCTAssertEqual(dependencies.count, 9)
252+
253+
struct ModuleResult: Hashable {
254+
let module: ResolvedModule
255+
let destination: Dest
256+
}
257+
258+
var uniqueModules = Set<ModuleResult>()
259+
for dependency in dependencies {
260+
if case .module(let module, let description) = dependency {
261+
XCTAssertNotNil(description)
262+
XCTAssertEqual(description!.destination, .host)
263+
XCTAssertTrue(
264+
uniqueModules.insert(.init(module: module, destination: description!.destination))
265+
.inserted
266+
)
267+
}
268+
}
188269
}
189270
}

0 commit comments

Comments
 (0)