@@ -204,22 +204,65 @@ public final class Process {
204
204
/// Typealias for stdout/stderr output closure.
205
205
public typealias OutputClosure = ( [ UInt8 ] ) -> Void
206
206
207
- /// Global default setting for verbose.
207
+ /// Typealias for logging handling closure
208
+ public typealias LoggingHandler = ( String ) -> Void
209
+
210
+ private static var _loggingHandler : LoggingHandler ?
211
+ private static let loggingHandlerLock = Lock ( )
212
+
213
+ /// Global logging handler. Use with care! preferably use instance level instead of setting one globally.
214
+ public static var loggingHandler : LoggingHandler ? {
215
+ get {
216
+ Self . loggingHandlerLock. withLock {
217
+ self . _loggingHandler
218
+ }
219
+ } set {
220
+ Self . loggingHandlerLock. withLock {
221
+ self . _loggingHandler = newValue
222
+ }
223
+ }
224
+ }
225
+
226
+ // deprecated 2/2022, remove once client migrate to logging handler
208
227
@available ( * , deprecated)
209
228
public static var verbose : Bool {
210
- Self . loggingHandler != nil
229
+ get {
230
+ Self . loggingHandler != nil
231
+ } set {
232
+ Self . loggingHandler = newValue ? Self . logToStdout: . none
233
+ }
211
234
}
212
235
213
- public static var loggingHandler : ( ( String ) -> Void ) ? = nil
236
+ private var _loggingHandler : LoggingHandler ?
237
+
238
+ // the log and setter are only required to backward support verbose setter.
239
+ // remove and make loggingHandler a let property once verbose is deprecated
240
+ private let loggingHandlerLock = Lock ( )
241
+ public private( set) var loggingHandler : LoggingHandler ? {
242
+ get {
243
+ self . loggingHandlerLock. withLock {
244
+ self . _loggingHandler
245
+ }
246
+ }
247
+ set {
248
+ self . loggingHandlerLock. withLock {
249
+ self . _loggingHandler = newValue
250
+ }
251
+ }
252
+ }
214
253
215
- /// If true, prints the subprocess arguments before launching it.
254
+ // deprecated 2/2022, remove once client migrate to logging handler
255
+ // also simplify loggingHandler (see above) once this is removed
216
256
@available ( * , deprecated)
217
257
public var verbose : Bool {
218
- self . loggingHandler != nil
258
+ get {
259
+ self . loggingHandler != nil
260
+ }
261
+ set {
262
+ self . loggingHandler = newValue ? Self . logToStdout : . none
263
+ }
219
264
}
220
265
221
- private let loggingHandler : ( ( String ) -> Void ) ?
222
-
223
266
/// The current environment.
224
267
@available ( * , deprecated, message: " use ProcessEnv.vars instead " )
225
268
static public var env : [ String : String ] {
@@ -248,6 +291,7 @@ public final class Process {
248
291
// process execution mutable state
249
292
private var state : State = . idle
250
293
private let stateLock = Lock ( )
294
+
251
295
private static let sharedCompletionQueue = DispatchQueue ( label: " org.swift.tools-support-core.process-completion " )
252
296
private var completionQueue = Process . sharedCompletionQueue
253
297
@@ -307,14 +351,14 @@ public final class Process {
307
351
workingDirectory: AbsolutePath ,
308
352
outputRedirection: OutputRedirection = . collect,
309
353
startNewProcessGroup: Bool = true ,
310
- loggingHandler: ( ( String ) -> Void ) ? = Process . loggingHandler
354
+ loggingHandler: LoggingHandler ? = . none
311
355
) {
312
356
self . arguments = arguments
313
357
self . environment = environment
314
358
self . workingDirectory = workingDirectory
315
359
self . outputRedirection = outputRedirection
316
360
self . startNewProcessGroup = startNewProcessGroup
317
- self . loggingHandler = loggingHandler
361
+ self . loggingHandler = loggingHandler ?? Process . loggingHandler
318
362
}
319
363
320
364
// deprecated 2/2022
@@ -352,19 +396,20 @@ public final class Process {
352
396
/// - verbose: If true, launch() will print the arguments of the subprocess before launching it.
353
397
/// - startNewProcessGroup: If true, a new progress group is created for the child making it
354
398
/// continue running even if the parent is killed or interrupted. Default value is true.
399
+ /// - loggingHandler: Handler for logging messages
355
400
public init (
356
401
arguments: [ String ] ,
357
402
environment: [ String : String ] = ProcessEnv . vars,
358
403
outputRedirection: OutputRedirection = . collect,
359
404
startNewProcessGroup: Bool = true ,
360
- loggingHandler: ( ( String ) -> Void ) ? = Process . loggingHandler
405
+ loggingHandler: LoggingHandler ? = . none
361
406
) {
362
407
self . arguments = arguments
363
408
self . environment = environment
364
409
self . workingDirectory = nil
365
410
self . outputRedirection = outputRedirection
366
411
self . startNewProcessGroup = startNewProcessGroup
367
- self . loggingHandler = loggingHandler
412
+ self . loggingHandler = loggingHandler ?? Process . loggingHandler
368
413
}
369
414
370
415
@_disfavoredOverload
@@ -381,10 +426,21 @@ public final class Process {
381
426
environment: environment,
382
427
outputRedirection: outputRedirection,
383
428
startNewProcessGroup: startNewProcessGroup,
384
- loggingHandler: verbose ? { message in
385
- stdoutStream <<< message <<< " \n "
386
- stdoutStream. flush ( )
387
- } : nil
429
+ loggingHandler: verbose ? Self . logToStdout : . none
430
+ )
431
+ }
432
+
433
+ public convenience init (
434
+ args: String ... ,
435
+ environment: [ String : String ] = ProcessEnv . vars,
436
+ outputRedirection: OutputRedirection = . collect,
437
+ loggingHandler: LoggingHandler ? = . none
438
+ ) {
439
+ self . init (
440
+ arguments: args,
441
+ environment: environment,
442
+ outputRedirection: outputRedirection,
443
+ loggingHandler: loggingHandler
388
444
)
389
445
}
390
446
@@ -452,8 +508,6 @@ public final class Process {
452
508
// Print the arguments if we are verbose.
453
509
if let loggingHandler = self . loggingHandler {
454
510
loggingHandler ( arguments. map ( { $0. spm_shellEscaped ( ) } ) . joined ( separator: " " ) )
455
- //stdoutStream <<< arguments.map({ $0.spm_shellEscaped() }).joined(separator: " ") <<< "\n"
456
- //stdoutStream.flush()
457
511
}
458
512
459
513
// Look for executable.
@@ -890,11 +944,23 @@ extension Process {
890
944
/// - arguments: The arguments for the subprocess.
891
945
/// - environment: The environment to pass to subprocess. By default the current process environment
892
946
/// will be inherited.
893
- /// - Returns: The process result.
894
- static public func popen( arguments: [ String ] , environment: [ String : String ] = ProcessEnv . vars,
895
- queue: DispatchQueue ? = nil , completion: @escaping ( Result < ProcessResult , Swift . Error > ) -> Void ) {
947
+ /// - loggingHandler: Handler for logging messages
948
+ /// - queue: Queue to use for callbacks
949
+ /// - completion: A completion handler to return the process result
950
+ static public func popen(
951
+ arguments: [ String ] ,
952
+ environment: [ String : String ] = ProcessEnv . vars,
953
+ loggingHandler: LoggingHandler ? = . none,
954
+ queue: DispatchQueue ? = nil ,
955
+ completion: @escaping ( Result < ProcessResult , Swift . Error > ) -> Void
956
+ ) {
896
957
do {
897
- let process = Process ( arguments: arguments, environment: environment, outputRedirection: . collect)
958
+ let process = Process (
959
+ arguments: arguments,
960
+ environment: environment,
961
+ outputRedirection: . collect,
962
+ loggingHandler: loggingHandler
963
+ )
898
964
process. completionQueue = queue ?? Self . sharedCompletionQueue
899
965
try process. launch ( )
900
966
process. waitUntilExit ( completion)
@@ -909,17 +975,39 @@ extension Process {
909
975
/// - arguments: The arguments for the subprocess.
910
976
/// - environment: The environment to pass to subprocess. By default the current process environment
911
977
/// will be inherited.
978
+ /// - loggingHandler: Handler for logging messages
912
979
/// - Returns: The process result.
913
980
@discardableResult
914
- static public func popen( arguments: [ String ] , environment: [ String : String ] = ProcessEnv . vars) throws -> ProcessResult {
915
- let process = Process ( arguments: arguments, environment: environment, outputRedirection: . collect)
981
+ static public func popen(
982
+ arguments: [ String ] ,
983
+ environment: [ String : String ] = ProcessEnv . vars,
984
+ loggingHandler: LoggingHandler ? = . none
985
+ ) throws -> ProcessResult {
986
+ let process = Process (
987
+ arguments: arguments,
988
+ environment: environment,
989
+ outputRedirection: . collect,
990
+ loggingHandler: loggingHandler
991
+ )
916
992
try process. launch ( )
917
993
return try process. waitUntilExit ( )
918
994
}
919
995
996
+ /// Execute a subprocess and block until it finishes execution
997
+ ///
998
+ /// - Parameters:
999
+ /// - args: The arguments for the subprocess.
1000
+ /// - environment: The environment to pass to subprocess. By default the current process environment
1001
+ /// will be inherited.
1002
+ /// - loggingHandler: Handler for logging messages
1003
+ /// - Returns: The process result.
920
1004
@discardableResult
921
- static public func popen( args: String ... , environment: [ String : String ] = ProcessEnv . vars) throws -> ProcessResult {
922
- return try Process . popen ( arguments: args, environment: environment)
1005
+ static public func popen(
1006
+ args: String ... ,
1007
+ environment: [ String : String ] = ProcessEnv . vars,
1008
+ loggingHandler: LoggingHandler ? = . none
1009
+ ) throws -> ProcessResult {
1010
+ return try Process . popen ( arguments: args, environment: environment, loggingHandler: loggingHandler)
923
1011
}
924
1012
925
1013
/// Execute a subprocess and get its (UTF-8) output if it has a non zero exit.
@@ -928,10 +1016,20 @@ extension Process {
928
1016
/// - arguments: The arguments for the subprocess.
929
1017
/// - environment: The environment to pass to subprocess. By default the current process environment
930
1018
/// will be inherited.
1019
+ /// - loggingHandler: Handler for logging messages
931
1020
/// - Returns: The process output (stdout + stderr).
932
1021
@discardableResult
933
- static public func checkNonZeroExit( arguments: [ String ] , environment: [ String : String ] = ProcessEnv . vars) throws -> String {
934
- let process = Process ( arguments: arguments, environment: environment, outputRedirection: . collect)
1022
+ static public func checkNonZeroExit(
1023
+ arguments: [ String ] ,
1024
+ environment: [ String : String ] = ProcessEnv . vars,
1025
+ loggingHandler: LoggingHandler ? = . none
1026
+ ) throws -> String {
1027
+ let process = Process (
1028
+ arguments: arguments,
1029
+ environment: environment,
1030
+ outputRedirection: . collect,
1031
+ loggingHandler: loggingHandler
1032
+ )
935
1033
try process. launch ( )
936
1034
let result = try process. waitUntilExit ( )
937
1035
// Throw if there was a non zero termination.
@@ -941,13 +1039,21 @@ extension Process {
941
1039
return try result. utf8Output ( )
942
1040
}
943
1041
1042
+ /// Execute a subprocess and get its (UTF-8) output if it has a non zero exit.
1043
+ ///
1044
+ /// - Parameters:
1045
+ /// - arguments: The arguments for the subprocess.
1046
+ /// - environment: The environment to pass to subprocess. By default the current process environment
1047
+ /// will be inherited.
1048
+ /// - loggingHandler: Handler for logging messages
1049
+ /// - Returns: The process output (stdout + stderr).
944
1050
@discardableResult
945
- static public func checkNonZeroExit( args : String ... , environment : [ String : String ] = ProcessEnv . vars ) throws -> String {
946
- return try checkNonZeroExit ( arguments : args, environment : environment )
947
- }
948
-
949
- public convenience init ( args : String ... , environment : [ String : String ] = ProcessEnv . vars , outputRedirection : OutputRedirection = . collect ) {
950
- self . init ( arguments: args, environment: environment, outputRedirection : outputRedirection )
1051
+ static public func checkNonZeroExit(
1052
+ args: String ... ,
1053
+ environment : [ String : String ] = ProcessEnv . vars ,
1054
+ loggingHandler : LoggingHandler ? = . none
1055
+ ) throws -> String {
1056
+ return try checkNonZeroExit ( arguments: args, environment: environment, loggingHandler : loggingHandler )
951
1057
}
952
1058
}
953
1059
@@ -1090,3 +1196,12 @@ extension FileHandle: WritableByteStream {
1090
1196
}
1091
1197
}
1092
1198
#endif
1199
+
1200
+
1201
+ extension Process {
1202
+ @available ( * , deprecated)
1203
+ fileprivate static func logToStdout( _ message: String ) {
1204
+ stdoutStream <<< message <<< " \n "
1205
+ stdoutStream. flush ( )
1206
+ }
1207
+ }
0 commit comments