Skip to content

Commit d561153

Browse files
committed
[PackageGraph] ModuleGraph: Product and target lookups should involve a destination
The products and targets are currently indentified by their package + name + build triple and they should be looked up in the same fashion to avoid situations when results of `product(for:)` and `target(for:)` return target that has an unexpected destination.
1 parent a7d5baf commit d561153

File tree

12 files changed

+91
-97
lines changed

12 files changed

+91
-97
lines changed

Sources/Build/BuildOperation.swift

Lines changed: 49 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -522,19 +522,48 @@ public final class BuildOperation: PackageStructureDelegate, SPMBuildCore.BuildS
522522
return LLBuildManifestBuilder.TargetKind.main.targetName
523523
case .allIncludingTests:
524524
return LLBuildManifestBuilder.TargetKind.test.targetName
525-
case .product(_, let destination), .target(_, let destination):
525+
case .product(let productName, let destination):
526526
// FIXME: This is super unfortunate that we might need to load the package graph.
527527
let graph = try getPackageGraph()
528-
if let result = subset.llbuildTargetName(
529-
for: graph,
530-
using: destination == .host
531-
? self.toolsBuildParameters
532-
: self.productsBuildParameters,
533-
observabilityScope: self.observabilityScope
534-
) {
535-
return result
528+
529+
guard let product = graph.product(
530+
for: productName,
531+
destination: destination == .host ? .tools : .destination
532+
) else {
533+
observabilityScope.emit(error: "no product named '\(productName)'")
534+
throw Diagnostics.fatalError
536535
}
537-
throw Diagnostics.fatalError
536+
// If the product is automatic, we build the main target because automatic products
537+
// do not produce a binary right now.
538+
if product.type == .library(.automatic) {
539+
observabilityScope.emit(
540+
warning:
541+
"'--product' cannot be used with the automatic product '\(productName)'; building the default target instead"
542+
)
543+
return LLBuildManifestBuilder.TargetKind.main.targetName
544+
}
545+
return try product.getLLBuildTargetName(
546+
buildParameters: destination == .host
547+
? self.toolsBuildParameters
548+
: self.productsBuildParameters
549+
)
550+
case .target(let targetName, let destination):
551+
// FIXME: This is super unfortunate that we might need to load the package graph.
552+
let graph = try getPackageGraph()
553+
554+
guard let target = graph.target(
555+
for: targetName,
556+
destination: destination == .host ? .tools : .destination
557+
) else {
558+
observabilityScope.emit(error: "no target named '\(targetName)'")
559+
throw Diagnostics.fatalError
560+
}
561+
562+
return target.getLLBuildTargetName(
563+
buildParameters: destination == .host
564+
? self.toolsBuildParameters
565+
: self.productsBuildParameters
566+
)
538567
}
539568
}
540569

@@ -898,59 +927,24 @@ extension BuildSubset {
898927
return Array(graph.reachableTargets)
899928
case .allExcludingTests:
900929
return graph.reachableTargets.filter { $0.type != .test }
901-
case .product(let productName, _):
902-
guard let product = graph.product(for: productName) else {
903-
observabilityScope.emit(error: "no product named '\(productName)'")
904-
return nil
905-
}
906-
return try product.recursiveTargetDependencies()
907-
case .target(let targetName, _):
908-
guard let target = graph.target(for: targetName) else {
909-
observabilityScope.emit(error: "no target named '\(targetName)'")
910-
return nil
911-
}
912-
return try target.recursiveTargetDependencies()
913-
}
914-
}
915-
916-
/// Returns the name of the llbuild target that corresponds to the build subset.
917-
func llbuildTargetName(
918-
for graph: ModulesGraph,
919-
using buildParameters: BuildParameters,
920-
observabilityScope: ObservabilityScope
921-
) -> String? {
922-
switch self {
923-
case .allExcludingTests:
924-
return LLBuildManifestBuilder.TargetKind.main.targetName
925-
case .allIncludingTests:
926-
return LLBuildManifestBuilder.TargetKind.test.targetName
927930
case .product(let productName, let destination):
928-
precondition(buildParameters.destination == destination)
929-
930-
guard let product = graph.product(for: productName) else {
931+
guard let product = graph.product(
932+
for: productName,
933+
destination: destination == .host ? .tools : .destination
934+
) else {
931935
observabilityScope.emit(error: "no product named '\(productName)'")
932936
return nil
933937
}
934-
// If the product is automatic, we build the main target because automatic products
935-
// do not produce a binary right now.
936-
if product.type == .library(.automatic) {
937-
observabilityScope.emit(
938-
warning:
939-
"'--product' cannot be used with the automatic product '\(productName)'; building the default target instead"
940-
)
941-
return LLBuildManifestBuilder.TargetKind.main.targetName
942-
}
943-
return observabilityScope.trap {
944-
try product.getLLBuildTargetName(buildParameters: buildParameters)
945-
}
938+
return try product.recursiveTargetDependencies()
946939
case .target(let targetName, let destination):
947-
precondition(buildParameters.destination == destination)
948-
949-
guard let target = graph.target(for: targetName) else {
940+
guard let target = graph.target(
941+
for: targetName,
942+
destination: destination == .host ? .tools : .destination
943+
) else {
950944
observabilityScope.emit(error: "no target named '\(targetName)'")
951945
return nil
952946
}
953-
return target.getLLBuildTargetName(buildParameters: buildParameters)
947+
return try target.recursiveTargetDependencies()
954948
}
955949
}
956950
}

Sources/Build/BuildPlan/BuildPlan.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
580580
arguments.append("-l" + replProductName)
581581

582582
// The graph should have the REPL product.
583-
assert(self.graph.product(for: replProductName) != nil)
583+
assert(self.graph.product(for: replProductName, destination: .destination) != nil)
584584

585585
// Add the search path to the directory containing the modulemap file.
586586
for target in self.targets {

Sources/Commands/Snippets/Cards/SnippetCard.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ struct SnippetCard: Card {
9696
let buildSystem = try swiftCommandState.createBuildSystem(explicitProduct: snippet.name)
9797
try buildSystem.build(subset: .product(snippet.name))
9898
let executablePath = try swiftCommandState.productsBuildParameters.buildPath.appending(component: snippet.name)
99-
if let exampleTarget = try buildSystem.getPackageGraph().target(for: snippet.name) {
99+
if let exampleTarget = try buildSystem.getPackageGraph().target(for: snippet.name, destination: .destination) {
100100
try ProcessEnv.chdir(exampleTarget.sources.paths[0].parentDirectory)
101101
}
102102
try exec(path: executablePath.pathString, args: [])

Sources/Commands/SwiftRunCommand.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,7 @@ public struct SwiftRunCommand: AsyncSwiftCommand {
220220
if let executable = options.executable {
221221
// There should be only one product with the given name in the graph
222222
// and it should be executable or snippet.
223-
guard let product = graph.product(for: executable),
223+
guard let product = graph.product(for: executable, destination: .destination),
224224
product.type == .executable || product.type == .snippet
225225
else {
226226
throw RunError.executableNotFound(executable)

Sources/Commands/Utilities/PluginDelegate.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -385,7 +385,7 @@ final class PluginDelegate: PluginInvocationDelegate {
385385

386386
// Find the target in the build operation's package graph; it's an error if we don't find it.
387387
let packageGraph = try buildSystem.getPackageGraph()
388-
guard let target = packageGraph.target(for: targetName) else {
388+
guard let target = packageGraph.target(for: targetName, destination: .destination) else {
389389
throw StringError("could not find a target named “\(targetName)")
390390
}
391391

@@ -430,6 +430,7 @@ final class PluginDelegate: PluginInvocationDelegate {
430430
let result = try symbolGraphExtractor.extractSymbolGraph(
431431
module: target,
432432
buildPlan: try buildSystem.buildPlan,
433+
buildParameters: buildSystem.buildPlan.destinationBuildParameters,
433434
outputRedirection: .collect,
434435
outputDirectory: outputDir,
435436
verboseOutput: self.swiftCommandState.logLevel <= .info

Sources/PackageGraph/ModulesGraph.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -166,14 +166,12 @@ public struct ModulesGraph {
166166
package.dependencies.compactMap { self.package(for: $0) }
167167
}
168168

169-
public func product(for name: String) -> ResolvedProduct? {
170-
// FIXME: This is wrong since graph can contain products with the same name but different triples.
171-
self.allProducts.first { $0.name == name }
169+
public func product(for name: String, destination: BuildTriple) -> ResolvedProduct? {
170+
self.allProducts.first { $0.name == name && $0.buildTriple == destination }
172171
}
173172

174-
public func target(for name: String) -> ResolvedModule? {
175-
// FIXME: This is wrong since graph can contain products with the same name but different triples.
176-
self.allTargets.first { $0.name == name }
173+
public func target(for name: String, destination: BuildTriple) -> ResolvedModule? {
174+
self.allTargets.first { $0.name == name && $0.buildTriple == destination }
177175
}
178176

179177
/// All root and root dependency packages provided as input to the graph.

Sources/SPMBuildCore/Plugins/PluginInvocation.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -675,7 +675,7 @@ public extension PluginTarget {
675675
executableOrBinaryTarget = target
676676
case .product(let productRef, _):
677677
guard
678-
let product = packageGraph.product(for: productRef.name),
678+
let product = packageGraph.product(for: productRef.name, destination: .tools),
679679
let executableTarget = product.targets.map({ $0.underlying }).executables.spm_only
680680
else {
681681
throw StringError("no product named \(productRef.name)")

Sources/SPMTestSupport/PackageGraphTester.swift

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -88,19 +88,18 @@ public final class PackageGraphResult {
8888

8989
public func checkTarget(
9090
_ name: String,
91+
destination: BuildTriple = .destination,
9192
file: StaticString = #file,
9293
line: UInt = #line,
9394
body: (ResolvedTargetResult) -> Void
9495
) {
95-
let targets = find(target: name)
96+
let target = graph.target(for: name, destination: destination)
9697

97-
guard targets.count > 0 else {
98+
guard let target else {
9899
return XCTFail("Target \(name) not found", file: file, line: line)
99100
}
100-
guard targets.count == 1 else {
101-
return XCTFail("More than a single target with name \(name) found", file: file, line: line)
102-
}
103-
body(ResolvedTargetResult(targets[0]))
101+
102+
body(ResolvedTargetResult(target))
104103
}
105104

106105
package func checkTargets(
@@ -114,20 +113,18 @@ public final class PackageGraphResult {
114113

115114
public func checkProduct(
116115
_ name: String,
116+
destination: BuildTriple = .destination,
117117
file: StaticString = #file,
118118
line: UInt = #line,
119119
body: (ResolvedProductResult) -> Void
120120
) {
121-
let products = find(product: name)
121+
let product = graph.product(for: name, destination: destination)
122122

123-
guard products.count > 0 else {
123+
guard let product else {
124124
return XCTFail("Product \(name) not found", file: file, line: line)
125125
}
126126

127-
guard products.count == 1 else {
128-
return XCTFail("More than a single product with name \(name) found", file: file, line: line)
129-
}
130-
body(ResolvedProductResult(products[0]))
127+
body(ResolvedProductResult(product))
131128
}
132129

133130
public func check(testModules: String..., file: StaticString = #file, line: UInt = #line) {
@@ -138,14 +135,6 @@ public final class PackageGraphResult {
138135
.sorted(), testModules.sorted(), file: file, line: line)
139136
}
140137

141-
public func find(target: String) -> [ResolvedModule] {
142-
return graph.allTargets.filter { $0.name == target }
143-
}
144-
145-
public func find(product: String) -> [ResolvedProduct] {
146-
return graph.allProducts.filter { $0.name == product }
147-
}
148-
149138
public func find(package: PackageIdentity) -> ResolvedPackage? {
150139
return graph.package(for: package)
151140
}

Tests/PackageGraphTests/CrossCompilationPackageGraphTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ final class CrossCompilationPackageGraphTests: XCTestCase {
119119
result.checkTargets("MMIOMacros") { results in
120120
XCTAssertEqual(results.count, 1)
121121
}
122-
result.checkTarget("MMIOMacrosTests") { result in
122+
result.checkTarget("MMIOMacrosTests", destination: .tools) { result in
123123
result.check(buildTriple: .tools)
124124
result.checkDependency("MMIOMacros") { result in
125125
result.checkTarget { result in

Tests/PackageGraphTests/ModulesGraphTests.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,12 @@ final class ModulesGraphTests: XCTestCase {
8484
}
8585

8686
let fooPackage = try XCTUnwrap(g.package(for: .plain("Foo")))
87-
let fooTarget = try XCTUnwrap(g.target(for: "Foo"))
88-
let fooDepTarget = try XCTUnwrap(g.target(for: "FooDep"))
87+
let fooTarget = try XCTUnwrap(g.target(for: "Foo", destination: .destination))
88+
let fooDepTarget = try XCTUnwrap(g.target(for: "FooDep", destination: .destination))
8989
XCTAssertEqual(g.package(for: fooTarget)?.id, fooPackage.id)
9090
XCTAssertEqual(g.package(for: fooDepTarget)?.id, fooPackage.id)
9191
let barPackage = try XCTUnwrap(g.package(for: .plain("Bar")))
92-
let barTarget = try XCTUnwrap(g.target(for: "Bar"))
92+
let barTarget = try XCTUnwrap(g.target(for: "Bar", destination: .destination))
9393
XCTAssertEqual(g.package(for: barTarget)?.id, barPackage.id)
9494
}
9595

Tests/SPMBuildCoreTests/PluginInvocationTests.swift

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ final class PluginInvocationTests: XCTestCase {
3636
let fileSystem = InMemoryFileSystem(emptyFiles:
3737
"/Foo/Plugins/FooPlugin/source.swift",
3838
"/Foo/Sources/FooTool/source.swift",
39+
"/Foo/Sources/FooToolLib/source.swift",
3940
"/Foo/Sources/Foo/source.swift",
4041
"/Foo/Sources/Foo/SomeFile.abc"
4142
)
@@ -67,9 +68,14 @@ final class PluginInvocationTests: XCTestCase {
6768
),
6869
TargetDescription(
6970
name: "FooTool",
70-
dependencies: [],
71+
dependencies: ["FooToolLib"],
7172
type: .executable
7273
),
74+
TargetDescription(
75+
name: "FooToolLib",
76+
dependencies: [],
77+
type: .regular
78+
),
7379
]
7480
)
7581
],
@@ -80,18 +86,24 @@ final class PluginInvocationTests: XCTestCase {
8086
XCTAssertNoDiagnostics(observability.diagnostics)
8187
PackageGraphTester(graph) { graph in
8288
graph.check(packages: "Foo")
83-
// "FooTool" duplicated as it's present for both build tools and end products triples.
84-
graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool")
89+
// "FooTool{Lib}" duplicated as it's present for both build tools and end products triples.
90+
graph.check(targets: "Foo", "FooPlugin", "FooTool", "FooTool", "FooToolLib", "FooToolLib")
8591
graph.checkTarget("Foo") { target in
8692
target.check(dependencies: "FooPlugin")
8793
}
88-
graph.checkTarget("FooPlugin") { target in
94+
graph.checkTarget("FooPlugin", destination: .tools) { target in
8995
target.check(type: .plugin)
9096
target.check(dependencies: "FooTool")
9197
}
92-
graph.checkTargets("FooTool") { targets in
93-
for target in targets {
98+
for destination: BuildTriple in [.tools, .destination] {
99+
graph.checkTarget("FooTool", destination: destination) { target in
94100
target.check(type: .executable)
101+
target.check(buildTriple: destination)
102+
target.checkDependency("FooToolLib") { dependency in
103+
dependency.checkTarget {
104+
$0.check(buildTriple: destination)
105+
}
106+
}
95107
}
96108
}
97109
}

Tests/SourceKitLSPAPITests/SourceKitLSPAPITests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ extension SourceKitLSPAPI.BuildDescription {
9292
partialArguments: [String],
9393
isPartOfRootPackage: Bool
9494
) throws -> Bool {
95-
let target = try XCTUnwrap(graph.target(for: targetName))
95+
let target = try XCTUnwrap(graph.target(for: targetName, destination: .destination))
9696
let buildTarget = try XCTUnwrap(self.getBuildTarget(for: target, in: graph))
9797

9898
guard let file = buildTarget.sources.first else {

0 commit comments

Comments
 (0)