Skip to content

Commit c2a3c4f

Browse files
authored
deprecate global and process verbosity (#290)
motivation: fine grained control over verbosity setting and handling of logging changes: * deprecate global verbosity flag * deprecate Process verbosity attribute in favor of an optional logging handler (closure)
1 parent e9309c9 commit c2a3c4f

File tree

2 files changed

+207
-30
lines changed

2 files changed

+207
-30
lines changed

Sources/TSCBasic/Process.swift

Lines changed: 203 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -204,11 +204,64 @@ public final class Process {
204204
/// Typealias for stdout/stderr output closure.
205205
public typealias OutputClosure = ([UInt8]) -> Void
206206

207-
/// Global default setting for verbose.
208-
public static var verbose = false
207+
/// Typealias for logging handling closure
208+
public typealias LoggingHandler = (String) -> Void
209209

210-
/// If true, prints the subprocess arguments before launching it.
211-
public let verbose: Bool
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
227+
@available(*, deprecated)
228+
public static var verbose: Bool {
229+
get {
230+
Self.loggingHandler != nil
231+
} set {
232+
Self.loggingHandler = newValue ? Self.logToStdout: .none
233+
}
234+
}
235+
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+
}
253+
254+
// deprecated 2/2022, remove once client migrate to logging handler
255+
// also simplify loggingHandler (see above) once this is removed
256+
@available(*, deprecated)
257+
public var verbose: Bool {
258+
get {
259+
self.loggingHandler != nil
260+
}
261+
set {
262+
self.loggingHandler = newValue ? Self.logToStdout : .none
263+
}
264+
}
212265

213266
/// The current environment.
214267
@available(*, deprecated, message: "use ProcessEnv.vars instead")
@@ -238,6 +291,7 @@ public final class Process {
238291
// process execution mutable state
239292
private var state: State = .idle
240293
private let stateLock = Lock()
294+
241295
private static let sharedCompletionQueue = DispatchQueue(label: "org.swift.tools-support-core.process-completion")
242296
private var completionQueue = Process.sharedCompletionQueue
243297

@@ -286,24 +340,50 @@ public final class Process {
286340
/// will be inherited.
287341
/// - workingDirectory: The path to the directory under which to run the process.
288342
/// - outputRedirection: How process redirects its output. Default value is .collect.
289-
/// - verbose: If true, launch() will print the arguments of the subprocess before launching it.
290343
/// - startNewProcessGroup: If true, a new progress group is created for the child making it
291344
/// continue running even if the parent is killed or interrupted. Default value is true.
345+
/// - loggingHandler: Handler for logging messages
346+
///
292347
@available(macOS 10.15, *)
293348
public init(
294349
arguments: [String],
295350
environment: [String: String] = ProcessEnv.vars,
296351
workingDirectory: AbsolutePath,
297352
outputRedirection: OutputRedirection = .collect,
298-
verbose: Bool = Process.verbose,
299-
startNewProcessGroup: Bool = true
353+
startNewProcessGroup: Bool = true,
354+
loggingHandler: LoggingHandler? = .none
300355
) {
301356
self.arguments = arguments
302357
self.environment = environment
303358
self.workingDirectory = workingDirectory
304359
self.outputRedirection = outputRedirection
305-
self.verbose = verbose
306360
self.startNewProcessGroup = startNewProcessGroup
361+
self.loggingHandler = loggingHandler ?? Process.loggingHandler
362+
}
363+
364+
// deprecated 2/2022
365+
@_disfavoredOverload
366+
@available(*, deprecated, message: "use version without verbosity flag")
367+
@available(macOS 10.15, *)
368+
public convenience init(
369+
arguments: [String],
370+
environment: [String: String] = ProcessEnv.vars,
371+
workingDirectory: AbsolutePath,
372+
outputRedirection: OutputRedirection = .collect,
373+
verbose: Bool,
374+
startNewProcessGroup: Bool = true
375+
) {
376+
self.init(
377+
arguments: arguments,
378+
environment: environment,
379+
workingDirectory: workingDirectory,
380+
outputRedirection: outputRedirection,
381+
startNewProcessGroup: startNewProcessGroup,
382+
loggingHandler: verbose ? { message in
383+
stdoutStream <<< message <<< "\n"
384+
stdoutStream.flush()
385+
} : nil
386+
)
307387
}
308388

309389
/// Create a new process instance.
@@ -316,19 +396,52 @@ public final class Process {
316396
/// - verbose: If true, launch() will print the arguments of the subprocess before launching it.
317397
/// - startNewProcessGroup: If true, a new progress group is created for the child making it
318398
/// continue running even if the parent is killed or interrupted. Default value is true.
399+
/// - loggingHandler: Handler for logging messages
319400
public init(
320401
arguments: [String],
321402
environment: [String: String] = ProcessEnv.vars,
322403
outputRedirection: OutputRedirection = .collect,
323-
verbose: Bool = Process.verbose,
324-
startNewProcessGroup: Bool = true
404+
startNewProcessGroup: Bool = true,
405+
loggingHandler: LoggingHandler? = .none
325406
) {
326407
self.arguments = arguments
327408
self.environment = environment
328409
self.workingDirectory = nil
329410
self.outputRedirection = outputRedirection
330-
self.verbose = verbose
331411
self.startNewProcessGroup = startNewProcessGroup
412+
self.loggingHandler = loggingHandler ?? Process.loggingHandler
413+
}
414+
415+
@_disfavoredOverload
416+
@available(*, deprecated, message: "user version without verbosity flag")
417+
public convenience init(
418+
arguments: [String],
419+
environment: [String: String] = ProcessEnv.vars,
420+
outputRedirection: OutputRedirection = .collect,
421+
verbose: Bool = Process.verbose,
422+
startNewProcessGroup: Bool = true
423+
) {
424+
self.init(
425+
arguments: arguments,
426+
environment: environment,
427+
outputRedirection: outputRedirection,
428+
startNewProcessGroup: startNewProcessGroup,
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
444+
)
332445
}
333446

334447
/// Returns the path of the the given program if found in the search paths.
@@ -393,9 +506,8 @@ public final class Process {
393506
}
394507

395508
// Print the arguments if we are verbose.
396-
if self.verbose {
397-
stdoutStream <<< arguments.map({ $0.spm_shellEscaped() }).joined(separator: " ") <<< "\n"
398-
stdoutStream.flush()
509+
if let loggingHandler = self.loggingHandler {
510+
loggingHandler(arguments.map({ $0.spm_shellEscaped() }).joined(separator: " "))
399511
}
400512

401513
// Look for executable.
@@ -832,11 +944,23 @@ extension Process {
832944
/// - arguments: The arguments for the subprocess.
833945
/// - environment: The environment to pass to subprocess. By default the current process environment
834946
/// will be inherited.
835-
/// - Returns: The process result.
836-
static public func popen(arguments: [String], environment: [String: String] = ProcessEnv.vars,
837-
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+
) {
838957
do {
839-
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+
)
840964
process.completionQueue = queue ?? Self.sharedCompletionQueue
841965
try process.launch()
842966
process.waitUntilExit(completion)
@@ -851,17 +975,39 @@ extension Process {
851975
/// - arguments: The arguments for the subprocess.
852976
/// - environment: The environment to pass to subprocess. By default the current process environment
853977
/// will be inherited.
978+
/// - loggingHandler: Handler for logging messages
854979
/// - Returns: The process result.
855980
@discardableResult
856-
static public func popen(arguments: [String], environment: [String: String] = ProcessEnv.vars) throws -> ProcessResult {
857-
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+
)
858992
try process.launch()
859993
return try process.waitUntilExit()
860994
}
861995

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.
8621004
@discardableResult
863-
static public func popen(args: String..., environment: [String: String] = ProcessEnv.vars) throws -> ProcessResult {
864-
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)
8651011
}
8661012

8671013
/// Execute a subprocess and get its (UTF-8) output if it has a non zero exit.
@@ -870,10 +1016,20 @@ extension Process {
8701016
/// - arguments: The arguments for the subprocess.
8711017
/// - environment: The environment to pass to subprocess. By default the current process environment
8721018
/// will be inherited.
1019+
/// - loggingHandler: Handler for logging messages
8731020
/// - Returns: The process output (stdout + stderr).
8741021
@discardableResult
875-
static public func checkNonZeroExit(arguments: [String], environment: [String: String] = ProcessEnv.vars) throws -> String {
876-
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+
)
8771033
try process.launch()
8781034
let result = try process.waitUntilExit()
8791035
// Throw if there was a non zero termination.
@@ -883,13 +1039,21 @@ extension Process {
8831039
return try result.utf8Output()
8841040
}
8851041

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).
8861050
@discardableResult
887-
static public func checkNonZeroExit(args: String..., environment: [String: String] = ProcessEnv.vars) throws -> String {
888-
return try checkNonZeroExit(arguments: args, environment: environment)
889-
}
890-
891-
public convenience init(args: String..., environment: [String: String] = ProcessEnv.vars, outputRedirection: OutputRedirection = .collect) {
892-
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)
8931057
}
8941058
}
8951059

@@ -1032,3 +1196,12 @@ extension FileHandle: WritableByteStream {
10321196
}
10331197
}
10341198
#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+
}

Sources/TSCUtility/Verbosity.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
See http://swift.org/CONTRIBUTORS.txt for Swift project authors
99
*/
1010

11+
// verbose 2/2022
12+
@available(*, deprecated)
1113
public enum Verbosity: Int {
1214
case concise
1315
case verbose
@@ -37,4 +39,6 @@ public enum Verbosity: Int {
3739
}
3840
}
3941

42+
// verbose 2/2022
43+
@available(*, deprecated)
4044
public var verbosity = Verbosity.concise

0 commit comments

Comments
 (0)