1
1
// This source file is part of the Swift.org open source project
2
2
//
3
- // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
3
+ // Copyright (c) 2014 - 2016, 2018 Apple Inc. and the Swift project authors
4
4
// Licensed under Apache License v2.0 with Runtime Library Exception
5
5
//
6
6
// See http://swift.org/LICENSE.txt for license information
@@ -153,7 +153,7 @@ open class Process: NSObject {
153
153
}
154
154
}
155
155
}
156
-
156
+
157
157
// Create an Process which can be run at a later time
158
158
// An Process can only be run once. Subsequent attempts to
159
159
// run an Process will raise.
@@ -162,48 +162,36 @@ open class Process: NSObject {
162
162
//
163
163
164
164
public override init ( ) {
165
-
165
+
166
166
}
167
-
168
- // These methods can only be set before a launch.
169
-
170
- open var launchPath : String ?
167
+
168
+ // These properties can only be set before a launch.
169
+ open var executableURL : URL ?
170
+ open var currentDirectoryURL = URL ( fileURLWithPath : FileManager . default . currentDirectoryPath , isDirectory : true )
171
171
open var arguments : [ String ] ?
172
172
open var environment : [ String : String ] ? // if not set, use current
173
-
174
- open var currentDirectoryPath : String = FileManager . default. currentDirectoryPath
175
-
176
- open var executableURL : URL ? {
177
- get {
178
- guard let launchPath = self . launchPath else {
179
- return nil
180
- }
181
-
182
- return URL ( fileURLWithPath: launchPath)
183
- }
184
- set {
185
- self . launchPath = newValue? . path
186
- }
173
+
174
+ @available ( * , deprecated: 4 , renamed: " executableURL " )
175
+ open var launchPath : String ? {
176
+ get { return executableURL? . path }
177
+ set { executableURL = ( newValue != nil ) ? URL ( fileURLWithPath: newValue!) : nil }
187
178
}
188
-
189
- open var currentDirectoryURL : URL {
190
- get {
191
- return URL ( fileURLWithPath: self . currentDirectoryPath)
192
- }
193
- set {
194
- self . currentDirectoryPath = newValue. path
195
- }
179
+
180
+ @available ( * , deprecated: 4 , renamed: " currentDirectoryURL " )
181
+ open var currentDirectoryPath : String {
182
+ get { return currentDirectoryURL. path }
183
+ set { currentDirectoryURL = URL ( fileURLWithPath: newValue) }
196
184
}
197
-
185
+
198
186
// Standard I/O channels; could be either a FileHandle or a Pipe
199
-
187
+
200
188
open var standardInput : Any ? {
201
189
willSet {
202
190
precondition ( newValue is Pipe || newValue is FileHandle ,
203
191
" standardInput must be either Pipe or FileHandle " )
204
192
}
205
193
}
206
-
194
+
207
195
open var standardOutput : Any ? {
208
196
willSet {
209
197
precondition ( newValue is Pipe || newValue is FileHandle ,
@@ -227,7 +215,30 @@ open class Process: NSObject {
227
215
228
216
// Actions
229
217
218
+ @available ( * , deprecated: 4 , renamed: " run " )
230
219
open func launch( ) {
220
+ do {
221
+ try run ( )
222
+ } catch let nserror as NSError {
223
+ if let path = nserror. userInfo [ NSFilePathErrorKey] as? String , path == currentDirectoryPath {
224
+ // Foundation throws an NSException when changing the working directory fails,
225
+ // and unfortunately launch() is not marked `throws`, so we get away with a
226
+ // fatalError.
227
+ switch CocoaError . Code ( rawValue: nserror. code) {
228
+ case . fileReadNoSuchFile:
229
+ fatalError ( " Process: The specified working directory does not exist. " )
230
+ case . fileReadNoPermission:
231
+ fatalError ( " Process: The specified working directory cannot be accessed. " )
232
+ default :
233
+ fatalError ( " Process: The specified working directory cannot be set. " )
234
+ }
235
+ }
236
+ } catch {
237
+ fatalError ( String ( describing: error) )
238
+ }
239
+ }
240
+
241
+ open func run( ) throws {
231
242
232
243
self . processLaunchedCondition. lock ( )
233
244
@@ -236,9 +247,8 @@ open class Process: NSObject {
236
247
Process . setup ( )
237
248
238
249
// Ensure that the launch path is set
239
-
240
- guard let launchPath = self . launchPath else {
241
- fatalError ( )
250
+ guard let launchPath = self . executableURL? . path else {
251
+ throw NSError ( domain: NSCocoaErrorDomain, code: NSFileNoSuchFileError)
242
252
}
243
253
244
254
// Convert the arguments array into a posix_spawn-friendly format
@@ -260,7 +270,6 @@ open class Process: NSObject {
260
270
for arg in argv ..< argv + args. count {
261
271
free ( UnsafeMutableRawPointer ( arg. pointee) )
262
272
}
263
-
264
273
argv. deallocate ( )
265
274
}
266
275
@@ -294,7 +303,7 @@ open class Process: NSObject {
294
303
context. version = 0
295
304
context. retain = runLoopSourceRetain
296
305
context. release = runLoopSourceRelease
297
- context. info = Unmanaged . passUnretained ( self ) . toOpaque ( )
306
+ context. info = Unmanaged . passUnretained ( self ) . toOpaque ( )
298
307
299
308
let socket = CFSocketCreateWithNative ( nil , taskSocketPair [ 0 ] , CFOptionFlags ( kCFSocketDataCallBack) , {
300
309
( socket, type, address, data, info ) in
@@ -316,7 +325,7 @@ open class Process: NSObject {
316
325
}
317
326
#endif
318
327
var waitResult : Int32 = 0
319
-
328
+
320
329
repeat {
321
330
#if CYGWIN
322
331
waitResult = waitpid ( process. processIdentifier, exitCodePtrWrapper, 0 )
@@ -344,9 +353,9 @@ open class Process: NSObject {
344
353
}
345
354
346
355
// Set the running flag to false
347
-
348
356
process. isRunning = false
349
-
357
+ process. processIdentifier = - 1
358
+
350
359
// Invalidate the source and wake up the run loop, if it's available
351
360
352
361
CFRunLoopSourceInvalidate ( process. runLoopSource)
@@ -417,24 +426,16 @@ open class Process: NSObject {
417
426
418
427
let fileManager = FileManager ( )
419
428
let previousDirectoryPath = fileManager. currentDirectoryPath
420
- if !fileManager. changeCurrentDirectoryPath ( currentDirectoryPath) {
421
- // Foundation throws an NSException when changing the working directory fails,
422
- // and unfortunately launch() is not marked `throws`, so we get away with a
423
- // fatalError.
424
- switch errno {
425
- case ENOENT:
426
- fatalError ( " Process: The specified working directory does not exist. " )
427
- case EACCES:
428
- fatalError ( " Process: The specified working directory cannot be accessed. " )
429
- default :
430
- fatalError ( " Process: The specified working directory cannot be set. " )
431
- }
429
+ if !fileManager. changeCurrentDirectoryPath ( currentDirectoryURL. path) {
430
+ throw _NSErrorWithErrno ( errno, reading: true , url: currentDirectoryURL)
432
431
}
433
432
434
433
// Launch
435
434
436
435
var pid = pid_t ( )
437
- posix ( posix_spawn ( & pid, launchPath, & fileActions, nil , argv, envp) )
436
+ guard posix_spawn ( & pid, launchPath, & fileActions, nil , argv, envp) == 0 else {
437
+ throw _NSErrorWithErrno ( errno, reading: true , path: launchPath)
438
+ }
438
439
439
440
// Reset the previous working directory path.
440
441
fileManager. changeCurrentDirectoryPath ( previousDirectoryPath)
@@ -506,10 +507,18 @@ open class Process: NSObject {
506
507
*/
507
508
open var terminationHandler : ( ( Process ) -> Void ) ?
508
509
open var qualityOfService : QualityOfService = . default // read-only after the process is launched
509
- }
510
510
511
- extension Process {
512
-
511
+
512
+ open class func run( _ url: URL , arguments: [ String ] , terminationHandler: ( ( Process ) -> Void ) ? = nil ) throws -> Process {
513
+ let process = Process ( )
514
+ process. executableURL = url
515
+ process. arguments = arguments
516
+ process. terminationHandler = terminationHandler
517
+ try process. run ( )
518
+ return process
519
+ }
520
+
521
+ @available ( * , deprecated: 4 , renamed: " run(_:arguments:terminationHandler:) " )
513
522
// convenience; create and launch
514
523
open class func launchedProcess( launchPath path: String , arguments: [ String ] ) -> Process {
515
524
let process = Process ( )
@@ -519,7 +528,7 @@ extension Process {
519
528
520
529
return process
521
530
}
522
-
531
+
523
532
// poll the runLoop in defaultMode until process completes
524
533
open func waitUntilExit( ) {
525
534
0 commit comments