@@ -133,6 +133,9 @@ extension PluginTarget {
133
133
134
134
/// Whether at least one error has been reported; this is used to make sure there is at least one error if the plugin fails.
135
135
var hasReportedError = false
136
+
137
+ /// If this is true, we exited early with an error.
138
+ var exitEarly = false
136
139
137
140
init ( invocationDelegate: PluginInvocationDelegate , observabilityScope: ObservabilityScope ) {
138
141
self . invocationDelegate = invocationDelegate
@@ -191,14 +194,19 @@ extension PluginTarget {
191
194
outputFiles: try outputFiles. map { try AbsolutePath ( validating: $0) } )
192
195
193
196
case . definePrebuildCommand( let config, let outputFilesDir) :
194
- self . invocationDelegate. pluginDefinedPrebuildCommand (
197
+ let success = self . invocationDelegate. pluginDefinedPrebuildCommand (
195
198
displayName: config. displayName,
196
199
executable: try AbsolutePath ( validating: config. executable) ,
197
200
arguments: config. arguments,
198
201
environment: config. environment,
199
202
workingDirectory: try config. workingDirectory. map { try AbsolutePath ( validating: $0) } ,
200
203
outputFilesDirectory: try AbsolutePath ( validating: outputFilesDir) )
201
204
205
+ if !success {
206
+ exitEarly = true
207
+ hasReportedError = true
208
+ }
209
+
202
210
case . buildOperationRequest( let subset, let parameters) :
203
211
self . invocationDelegate. pluginRequestedBuildOperation ( subset: . init( subset) , parameters: . init( parameters) ) {
204
212
do {
@@ -264,10 +272,9 @@ extension PluginTarget {
264
272
delegate: runnerDelegate) { result in
265
273
dispatchPrecondition ( condition: . onQueue( callbackQueue) )
266
274
completion ( result. map { exitCode in
267
- // Return a result based on the exit code. If the plugin
268
- // exits with an error but hasn't already emitted an error,
269
- // we do so for it.
270
- let exitedCleanly = ( exitCode == 0 )
275
+ // Return a result based on the exit code or the `exitEarly` parameter. If the plugin
276
+ // exits with an error but hasn't already emitted an error, we do so for it.
277
+ let exitedCleanly = ( exitCode == 0 ) && !runnerDelegate. exitEarly
271
278
if !exitedCleanly && !runnerDelegate. hasReportedError {
272
279
delegate. pluginEmittedDiagnostic (
273
280
. error( " Plugin ended with exit code \( exitCode) " )
@@ -366,6 +373,12 @@ extension PackageGraph {
366
373
dict [ name] = path
367
374
}
368
375
} )
376
+ let builtToolNames = accessibleTools. compactMap { ( accTool) -> String ? in
377
+ if case . builtTool( let name, _) = accTool {
378
+ return name
379
+ }
380
+ return nil
381
+ }
369
382
370
383
// Determine additional input dependencies for any plugin commands, based on any executables the plugin target depends on.
371
384
let toolPaths = toolNamesToPaths. values. sorted ( )
@@ -393,15 +406,17 @@ extension PackageGraph {
393
406
let fileSystem : FileSystem
394
407
let delegateQueue : DispatchQueue
395
408
let toolPaths : [ AbsolutePath ]
409
+ let builtToolNames : [ String ]
396
410
var outputData = Data ( )
397
411
var diagnostics = [ Basics . Diagnostic] ( )
398
412
var buildCommands = [ BuildToolPluginInvocationResult . BuildCommand] ( )
399
413
var prebuildCommands = [ BuildToolPluginInvocationResult . PrebuildCommand] ( )
400
414
401
- init ( fileSystem: FileSystem , delegateQueue: DispatchQueue , toolPaths: [ AbsolutePath ] ) {
415
+ init ( fileSystem: FileSystem , delegateQueue: DispatchQueue , toolPaths: [ AbsolutePath ] , builtToolNames : [ String ] ) {
402
416
self . fileSystem = fileSystem
403
417
self . delegateQueue = delegateQueue
404
418
self . toolPaths = toolPaths
419
+ self . builtToolNames = builtToolNames
405
420
}
406
421
407
422
func pluginCompilationStarted( commandLine: [ String ] , environment: EnvironmentVariables ) {
@@ -436,12 +451,12 @@ extension PackageGraph {
436
451
outputFiles: outputFiles) )
437
452
}
438
453
439
- func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath ) {
454
+ func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath ) -> Bool {
440
455
dispatchPrecondition ( condition: . onQueue( delegateQueue) )
441
456
// executable must exist before running prebuild command
442
- if !fileSystem . exists ( executable) {
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 " ) )
444
- return
457
+ if builtToolNames . contains ( executable. basename ) {
458
+ diagnostics. append ( . error( " a prebuild command cannot use executables built from source, including executable target '\( executable. basename) ' " ) )
459
+ return false
445
460
}
446
461
prebuildCommands. append ( . init(
447
462
configuration: . init(
@@ -451,9 +466,10 @@ extension PackageGraph {
451
466
environment: environment,
452
467
workingDirectory: workingDirectory) ,
453
468
outputFilesDirectory: outputFilesDirectory) )
469
+ return true
454
470
}
455
471
}
456
- let delegate = PluginDelegate ( fileSystem: fileSystem, delegateQueue: delegateQueue, toolPaths: toolPaths)
472
+ let delegate = PluginDelegate ( fileSystem: fileSystem, delegateQueue: delegateQueue, toolPaths: toolPaths, builtToolNames : builtToolNames )
457
473
458
474
// Invoke the build tool plugin with the input parameters and the delegate that will collect outputs.
459
475
let startTime = DispatchTime . now ( )
@@ -655,7 +671,7 @@ public protocol PluginInvocationDelegate {
655
671
func pluginDefinedBuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , inputFiles: [ AbsolutePath ] , outputFiles: [ AbsolutePath ] )
656
672
657
673
/// Called when a plugin defines a prebuild command through the PackagePlugin APIs.
658
- func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath )
674
+ func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath ) -> Bool
659
675
660
676
/// Called when a plugin requests a build operation through the PackagePlugin APIs.
661
677
func pluginRequestedBuildOperation( subset: PluginInvocationBuildSubset , parameters: PluginInvocationBuildParameters , completion: @escaping ( Result < PluginInvocationBuildResult , Error > ) -> Void )
@@ -779,7 +795,8 @@ public struct PluginInvocationTestResult {
779
795
public extension PluginInvocationDelegate {
780
796
func pluginDefinedBuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , inputFiles: [ AbsolutePath ] , outputFiles: [ AbsolutePath ] ) {
781
797
}
782
- func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath ) {
798
+ func pluginDefinedPrebuildCommand( displayName: String ? , executable: AbsolutePath , arguments: [ String ] , environment: [ String : String ] , workingDirectory: AbsolutePath ? , outputFilesDirectory: AbsolutePath ) -> Bool {
799
+ return true
783
800
}
784
801
func pluginRequestedBuildOperation( subset: PluginInvocationBuildSubset , parameters: PluginInvocationBuildParameters , completion: @escaping ( Result < PluginInvocationBuildResult , Error > ) -> Void ) {
785
802
DispatchQueue . sharedConcurrent. async { completion ( Result . failure ( StringError ( " unimplemented " ) ) ) }
0 commit comments