Skip to content

Commit 0a8e7d9

Browse files
authored
Pass supported triples info to invoke/run plugin for better diagnostics (#5843)
* Pass supported triples info to invoke/run plugin for better diagnostics Resolves rdar://91000836
1 parent c29adb3 commit 0a8e7d9

File tree

10 files changed

+199
-18
lines changed

10 files changed

+199
-18
lines changed

Sources/Build/BuildPlan.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2442,7 +2442,8 @@ public class BuildPlan: SPMBuildCore.BuildPlan {
24422442
/// Extracts the artifacts from an artifactsArchive
24432443
private func parseArtifactsArchive(for target: BinaryTarget) throws -> [ExecutableInfo] {
24442444
try self.externalExecutablesCache.memoize(key: target) {
2445-
return try target.parseArtifactArchives(for: self.buildParameters.triple, fileSystem: self.fileSystem)
2445+
let execInfos = try target.parseArtifactArchives(for: self.buildParameters.triple, fileSystem: self.fileSystem)
2446+
return execInfos.filter{!$0.supportedTriples.isEmpty}
24462447
}
24472448
}
24482449
}

Sources/Commands/PackageTools/PluginCommand.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ struct PluginCommand: SwiftCommand {
161161

162162
// Build or bring up-to-date any executable host-side tools on which this plugin depends. Add them and any binary dependencies to the tool-names-to-path map.
163163
var toolNamesToPaths: [String: AbsolutePath] = [:]
164+
// Add supported triples info per tool so they can be looked up when running the tool
165+
var toolNamesToTriples: [String: [String]] = [:]
164166
for dep in try plugin.accessibleTools(packageGraph: packageGraph, fileSystem: swiftTool.fileSystem, environment: try swiftTool.buildParameters().buildEnvironment, for: try pluginScriptRunner.hostTriple) {
165167
let buildSystem = try swiftTool.createBuildSystem(explicitBuildSystem: .native, cacheBuildManifest: false)
166168
switch dep {
@@ -170,8 +172,10 @@ struct PluginCommand: SwiftCommand {
170172
if let builtTool = try buildSystem.buildPlan.buildProducts.first(where: { $0.product.name == name}) {
171173
toolNamesToPaths[name] = builtTool.binaryPath
172174
}
173-
case .vendedTool(let name, let path):
175+
case .vendedTool(let name, let path, let triples):
174176
toolNamesToPaths[name] = path
177+
// Need triples info for .vendedTool
178+
toolNamesToTriples[name] = triples
175179
}
176180
}
177181

@@ -189,6 +193,7 @@ struct PluginCommand: SwiftCommand {
189193
outputDirectory: outputDir,
190194
toolSearchDirectories: toolSearchDirs,
191195
toolNamesToPaths: toolNamesToPaths,
196+
toolNamesToTriples: toolNamesToTriples,
192197
writableDirectories: writableDirectories,
193198
readOnlyDirectories: readOnlyDirectories,
194199
fileSystem: swiftTool.fileSystem,

Sources/PackagePlugin/Context.swift

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,14 @@ public struct PluginContext {
3939
/// an error if the tool cannot be found. The lookup is case sensitive.
4040
public func tool(named name: String) throws -> Tool {
4141
if let path = self.toolNamesToPaths[name] {
42+
// For PluginAccessibleTool.builtTool, the triples value is not saved, thus
43+
// the value is always nil; this is intentional since if we are able to
44+
// build the tool, it is by definition supporting the target platform.
45+
// For PluginAccessibleTool.vendedTool, only supported triples are saved,
46+
// so empty triples means the tool is not supported on the target platform.
47+
if let triples = toolNamesToTriples[name], triples.isEmpty {
48+
throw PluginContextError.toolNotSupportedOnTargetPlatform(name: name)
49+
}
4250
return Tool(name: name, path: path)
4351
} else {
4452
for dir in toolSearchDirectories {
@@ -59,7 +67,10 @@ public struct PluginContext {
5967
/// A mapping from tool names to their definitions. Not directly available
6068
/// to the plugin, but used by the `tool(named:)` API.
6169
let toolNamesToPaths: [String: Path]
62-
70+
71+
/// Supported triples per tool name; looked up in `tool(named:)`
72+
let toolNamesToTriples: [String: [String]]
73+
6374
/// The paths of directories of in which to search for tools that aren't in
6475
/// the `toolNamesToPaths` map.
6576
let toolSearchDirectories: [Path]

Sources/PackagePlugin/Errors.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ public enum PluginContextError: Error {
1515
/// it doesn't exist, or because the plugin doesn't have a dependency on it.
1616
case toolNotFound(name: String)
1717

18+
/// Tool is not supported on the target platform
19+
case toolNotSupportedOnTargetPlatform(name: String)
20+
1821
/// Could not find a target with the given name.
1922
case targetNotFound(name: String, package: Package)
2023

@@ -27,6 +30,8 @@ extension PluginContextError: CustomStringConvertible {
2730
switch self {
2831
case .toolNotFound(let name):
2932
return "Plugin does not have access to a tool named ‘\(name)"
33+
case .toolNotSupportedOnTargetPlatform(let name):
34+
return "Tool ‘\(name)’ is not supported on the target platform"
3035
case .targetNotFound(let name, let package):
3136
return "Package ‘\(package.displayName)’ has no target named ‘\(name)"
3237
case .productNotFound(let name, let package):

Sources/PackagePlugin/Plugin.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,13 @@ extension Plugin {
133133
let toolNamesToPaths = try wireInput.toolNamesToPathIds.mapValues {
134134
try deserializer.path(for: $0)
135135
}
136+
let toolNamesToTriples = wireInput.toolNamesToTriples
137+
136138
context = PluginContext(
137139
package: package,
138140
pluginWorkDirectory: pluginWorkDirectory,
139141
toolNamesToPaths: toolNamesToPaths,
142+
toolNamesToTriples: toolNamesToTriples,
140143
toolSearchDirectories: toolSearchDirectories)
141144
target = try deserializer.target(for: targetId)
142145
}
@@ -211,10 +214,13 @@ extension Plugin {
211214
let toolNamesToPaths = try wireInput.toolNamesToPathIds.mapValues {
212215
try deserializer.path(for: $0)
213216
}
217+
218+
let toolNamesToTriples = wireInput.toolNamesToTriples
214219
context = PluginContext(
215220
package: package,
216221
pluginWorkDirectory: pluginWorkDirectory,
217222
toolNamesToPaths: toolNamesToPaths,
223+
toolNamesToTriples: toolNamesToTriples,
218224
toolSearchDirectories: toolSearchDirectories)
219225
}
220226
catch {

Sources/PackagePlugin/PluginMessages.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ enum HostToPluginMessage: Codable {
2727
let pluginWorkDirId: Path.Id
2828
let toolSearchDirIds: [Path.Id]
2929
let toolNamesToPathIds: [String: Path.Id]
30+
let toolNamesToTriples: [String: [String]]
3031

3132
/// A single absolute path in the wire structure, represented as a tuple
3233
/// consisting of the ID of the base path and subpath off of that path.

Sources/SPMBuildCore/BinaryTarget+Extensions.swift

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ public struct ExecutableInfo: Equatable {
3535

3636
/// The path to the executable.
3737
public let executablePath: AbsolutePath
38+
39+
/// Supported triples, e.g. `x86_64-apple-macosx`
40+
public let supportedTriples: [Triple]
3841
}
3942

4043

@@ -68,11 +71,10 @@ extension BinaryTarget {
6871
let executables = metadata.artifacts.filter { $0.value.type == .executable }
6972
// Construct an ExecutableInfo for each matching variant.
7073
return try executables.flatMap { entry in
71-
// FIXME: this filter needs to become more sophisticated
72-
try entry.value.variants.filter {
73-
return $0.supportedTriples.contains(versionLessTriple)
74-
}.map{
75-
ExecutableInfo(name: entry.key, executablePath: try AbsolutePath(validating: $0.path, relativeTo: self.artifactPath))
74+
// Filter supported triples with versionLessTriple and pass into
75+
// ExecutableInfo; empty if non matching triples found.
76+
try entry.value.variants.map{
77+
ExecutableInfo(name: entry.key, executablePath: try AbsolutePath(validating: $0.path, relativeTo: self.artifactPath), supportedTriples: $0.supportedTriples.filter({$0 == versionLessTriple}))
7678
}
7779
}
7880
}

Sources/SPMBuildCore/PluginInvocation.swift

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ extension PluginTarget {
5555
outputDirectory: AbsolutePath,
5656
toolSearchDirectories: [AbsolutePath],
5757
toolNamesToPaths: [String: AbsolutePath],
58+
toolNamesToTriples: [String: [String]],
5859
writableDirectories: [AbsolutePath],
5960
readOnlyDirectories: [AbsolutePath],
6061
fileSystem: FileSystem,
@@ -78,6 +79,7 @@ extension PluginTarget {
7879
let pluginWorkDirId = try serializer.serialize(path: outputDirectory)
7980
let toolSearchDirIds = try toolSearchDirectories.map{ try serializer.serialize(path: $0) }
8081
let toolNamesToPathIds = try toolNamesToPaths.mapValues{ try serializer.serialize(path: $0) }
82+
let toolNamesToTriplesDict = toolNamesToTriples
8183
let actionMessage: HostToPluginMessage
8284
switch action {
8385

@@ -93,12 +95,12 @@ extension PluginTarget {
9395
packages: serializer.packages,
9496
pluginWorkDirId: pluginWorkDirId,
9597
toolSearchDirIds: toolSearchDirIds,
96-
toolNamesToPathIds: toolNamesToPathIds)
98+
toolNamesToPathIds: toolNamesToPathIds,
99+
toolNamesToTriples: toolNamesToTriplesDict)
97100
actionMessage = .createBuildToolCommands(
98101
context: wireInput,
99102
rootPackageId: rootPackageId,
100103
targetId: targetId)
101-
102104
case .performCommand(let package, let arguments):
103105
let rootPackageId = try serializer.serialize(package: package)
104106
let wireInput = WireInput(
@@ -108,7 +110,8 @@ extension PluginTarget {
108110
packages: serializer.packages,
109111
pluginWorkDirId: pluginWorkDirId,
110112
toolSearchDirIds: toolSearchDirIds,
111-
toolNamesToPathIds: toolNamesToPathIds)
113+
toolNamesToPathIds: toolNamesToPathIds,
114+
toolNamesToTriples: toolNamesToTriples)
112115
actionMessage = .performCommand(
113116
context: wireInput,
114117
rootPackageId: rootPackageId,
@@ -320,7 +323,6 @@ extension PackageGraph {
320323
fileSystem: FileSystem
321324
) throws -> [ResolvedTarget: [BuildToolPluginInvocationResult]] {
322325
var pluginResultsByTarget: [ResolvedTarget: [BuildToolPluginInvocationResult]] = [:]
323-
324326
for target in self.reachableTargets.sorted(by: { $0.name < $1.name }) {
325327
// Infer plugins from the declared dependencies, and collect them as well as any regular dependencies. Although usage of build tool plugins is declared separately from dependencies in the manifest, in the internal model we currently consider both to be dependencies.
326328
var pluginTargets: [PluginTarget] = []
@@ -360,14 +362,22 @@ extension PackageGraph {
360362
switch tool {
361363
case .builtTool(let name, let path):
362364
dict[name] = builtToolsDir.appending(path)
363-
case .vendedTool(let name, let path):
365+
case .vendedTool(let name, let path, _):
364366
dict[name] = path
365367
}
366368
})
367369

368370
// Determine additional input dependencies for any plugin commands, based on any executables the plugin target depends on.
369371
let toolPaths = toolNamesToPaths.values.sorted()
370372

373+
let toolNamesToTriples = accessibleTools.reduce(into: [String: [String]](), { dict, tool in
374+
switch tool {
375+
case .vendedTool(let name, _, let triple):
376+
dict[name] = triple
377+
default: break
378+
}
379+
})
380+
371381
// Assign a plugin working directory based on the package, target, and plugin.
372382
let pluginOutputDir = outputDir.appending(components: package.identity.description, target.name, pluginTarget.name)
373383

@@ -430,7 +440,7 @@ extension PackageGraph {
430440
dispatchPrecondition(condition: .onQueue(delegateQueue))
431441
// executable must exist before running prebuild command
432442
if !fileSystem.exists(executable) {
433-
diagnostics.append(.error("exectuable target '\(executable.basename)' is not pre-built; a plugin running a prebuild command should only rely on an existing binary; as a workaround, build '\(executable.basename)' first and then run the plugin "))
443+
diagnostics.append(.error("executable target '\(executable.basename)' is not pre-built; a plugin running a prebuild command should only rely on an existing binary; as a workaround, build '\(executable.basename)' first and then run the plugin "))
434444
return
435445
}
436446
prebuildCommands.append(.init(
@@ -455,6 +465,7 @@ extension PackageGraph {
455465
outputDirectory: pluginOutputDir,
456466
toolSearchDirectories: toolSearchDirectories,
457467
toolNamesToPaths: toolNamesToPaths,
468+
toolNamesToTriples: toolNamesToTriples,
458469
writableDirectories: writableDirectories,
459470
readOnlyDirectories: readOnlyDirectories,
460471
fileSystem: fileSystem,
@@ -492,7 +503,7 @@ public enum PluginAccessibleTool: Hashable {
492503
case builtTool(name: String, path: RelativePath)
493504

494505
/// A tool that is vended by a BinaryTarget (the path is absolute and refers to an unpackaged binary target).
495-
case vendedTool(name: String, path: AbsolutePath)
506+
case vendedTool(name: String, path: AbsolutePath, supportedTriples: [String])
496507
}
497508

498509
public extension PluginTarget {
@@ -522,7 +533,7 @@ public extension PluginTarget {
522533
if let target = executableOrBinaryTarget as? BinaryTarget {
523534
// TODO: Memoize this result for the host triple
524535
let execInfos = try target.parseArtifactArchives(for: hostTriple, fileSystem: fileSystem)
525-
return execInfos.map{ .vendedTool(name: $0.name, path: $0.executablePath) }
536+
return execInfos.map{ .vendedTool(name: $0.name, path: $0.executablePath, supportedTriples: $0.supportedTriples.map{$0.tripleString}) }
526537
}
527538
// For an executable target we create a `builtTool`.
528539
else if executableOrBinaryTarget.type == .executable {

Sources/Workspace/DefaultPluginScriptRunner.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,6 @@ public struct DefaultPluginScriptRunner: PluginScriptRunner, Cancellable {
211211
if (verboseOutput) {
212212
commandLine.append("-v")
213213
}
214-
215214
// Pass through the compilation environment.
216215
let environment = toolchain.swiftCompilerEnvironment
217216

0 commit comments

Comments
 (0)