| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 patch class _WindowsCodePageDecoder { | 5 patch class _WindowsCodePageDecoder { |
| 6 /* patch */ static String _decodeBytes(List<int> bytes) | 6 /* patch */ static String _decodeBytes(List<int> bytes) |
| 7 native "SystemEncodingToString"; | 7 native "SystemEncodingToString"; |
| 8 } | 8 } |
| 9 | 9 |
| 10 | 10 |
| 11 patch class _WindowsCodePageEncoder { | 11 patch class _WindowsCodePageEncoder { |
| 12 /* patch */ static List<int> _encodeString(String string) | 12 /* patch */ static List<int> _encodeString(String string) |
| 13 native "StringToSystemEncoding"; | 13 native "StringToSystemEncoding"; |
| 14 } | 14 } |
| 15 | 15 |
| 16 | 16 |
| 17 patch class Process { | 17 patch class Process { |
| 18 /* patch */ static Future<Process> start(String executable, | 18 /* patch */ static Future<Process> start( |
| 19 List<String> arguments, | 19 String executable, |
| 20 [ProcessOptions options]) { | 20 List<String> arguments, |
| 21 _ProcessImpl process = new _ProcessImpl(executable, arguments, options); | 21 {String workingDirectory, |
| 22 Map<String, String> environment, |
| 23 bool runInShell}) { |
| 24 _ProcessImpl process = new _ProcessImpl(executable, |
| 25 arguments, |
| 26 workingDirectory, |
| 27 environment, |
| 28 runInShell); |
| 22 return process._start(); | 29 return process._start(); |
| 23 } | 30 } |
| 24 | 31 |
| 25 /* patch */ static Future<ProcessResult> run(String executable, | 32 /* patch */ static Future<ProcessResult> run( |
| 26 List<String> arguments, | 33 String executable, |
| 27 [ProcessOptions options]) { | 34 List<String> arguments, |
| 28 return _runNonInteractiveProcess(executable, arguments, options); | 35 {String workingDirectory, |
| 36 Map<String, String> environment, |
| 37 bool runInShell, |
| 38 Encoding stdoutEncoding: Encoding.SYSTEM, |
| 39 Encoding stderrEncoding: Encoding.SYSTEM}) { |
| 40 return _runNonInteractiveProcess(executable, |
| 41 arguments, |
| 42 workingDirectory, |
| 43 environment, |
| 44 runInShell, |
| 45 stdoutEncoding, |
| 46 stderrEncoding); |
| 29 } | 47 } |
| 30 } | 48 } |
| 31 | 49 |
| 32 | 50 |
| 33 patch class _ProcessUtils { | 51 patch class _ProcessUtils { |
| 34 /* patch */ static void _exit(int status) native "Process_Exit"; | 52 /* patch */ static void _exit(int status) native "Process_Exit"; |
| 35 /* patch */ static void _setExitCode(int status) | 53 /* patch */ static void _setExitCode(int status) |
| 36 native "Process_SetExitCode"; | 54 native "Process_SetExitCode"; |
| 37 /* patch */ static void _sleep(int millis) native "Process_Sleep"; | 55 /* patch */ static void _sleep(int millis) native "Process_Sleep"; |
| 38 /* patch */ static int _pid(Process process) native "Process_Pid"; | 56 /* patch */ static int _pid(Process process) native "Process_Pid"; |
| 39 } | 57 } |
| 40 | 58 |
| 41 | 59 |
| 42 class _ProcessStartStatus { | 60 class _ProcessStartStatus { |
| 43 int _errorCode; // Set to OS error code if process start failed. | 61 int _errorCode; // Set to OS error code if process start failed. |
| 44 String _errorMessage; // Set to OS error message if process start failed. | 62 String _errorMessage; // Set to OS error message if process start failed. |
| 45 } | 63 } |
| 46 | 64 |
| 47 | 65 |
| 48 class _ProcessImpl extends NativeFieldWrapperClass1 implements Process { | 66 class _ProcessImpl extends NativeFieldWrapperClass1 implements Process { |
| 49 _ProcessImpl(String path, List<String> arguments, ProcessOptions options) { | 67 _ProcessImpl(String path, |
| 68 List<String> arguments, |
| 69 String this._workingDirectory, |
| 70 Map<String, String> environment, |
| 71 bool runInShell) { |
| 72 if (identical(runInShell, true)) { |
| 73 arguments = _getShellArguments(path, arguments); |
| 74 path = _getShellCommand(); |
| 75 } |
| 76 |
| 50 if (path is !String) { | 77 if (path is !String) { |
| 51 throw new ArgumentError("Path is not a String: $path"); | 78 throw new ArgumentError("Path is not a String: $path"); |
| 52 } | 79 } |
| 53 _path = path; | 80 _path = path; |
| 54 | 81 |
| 55 if (arguments is !List) { | 82 if (arguments is !List) { |
| 56 throw new ArgumentError("Arguments is not a List: $arguments"); | 83 throw new ArgumentError("Arguments is not a List: $arguments"); |
| 57 } | 84 } |
| 58 int len = arguments.length; | 85 int len = arguments.length; |
| 59 _arguments = new List<String>(len); | 86 _arguments = new List<String>(len); |
| 60 for (int i = 0; i < len; i++) { | 87 for (int i = 0; i < len; i++) { |
| 61 var arg = arguments[i]; | 88 var arg = arguments[i]; |
| 62 if (arg is !String) { | 89 if (arg is !String) { |
| 63 throw new ArgumentError("Non-string argument: $arg"); | 90 throw new ArgumentError("Non-string argument: $arg"); |
| 64 } | 91 } |
| 65 _arguments[i] = arguments[i]; | 92 _arguments[i] = arguments[i]; |
| 66 if (Platform.operatingSystem == 'windows') { | 93 if (Platform.operatingSystem == 'windows') { |
| 67 _arguments[i] = _windowsArgumentEscape(_arguments[i]); | 94 _arguments[i] = _windowsArgumentEscape(_arguments[i]); |
| 68 } | 95 } |
| 69 } | 96 } |
| 70 | 97 |
| 71 if (options != null && options.workingDirectory != null) { | 98 if (_workingDirectory != null && _workingDirectory is !String) { |
| 72 _workingDirectory = options.workingDirectory; | 99 throw new ArgumentError( |
| 73 if (_workingDirectory is !String) { | 100 "WorkingDirectory is not a String: $_workingDirectory"); |
| 74 throw new ArgumentError( | |
| 75 "WorkingDirectory is not a String: $_workingDirectory"); | |
| 76 } | |
| 77 } | 101 } |
| 78 | 102 |
| 79 if (options != null && options.environment != null) { | 103 if (environment != null) { |
| 80 var env = options.environment; | 104 var env = environment; |
| 81 if (env is !Map) { | 105 if (env is !Map) { |
| 82 throw new ArgumentError("Environment is not a map: $env"); | 106 throw new ArgumentError("Environment is not a map: $env"); |
| 83 } | 107 } |
| 84 _environment = []; | 108 _environment = []; |
| 85 env.forEach((key, value) { | 109 env.forEach((key, value) { |
| 86 if (key is !String || value is !String) { | 110 if (key is !String || value is !String) { |
| 87 throw new ArgumentError( | 111 throw new ArgumentError( |
| 88 "Environment key or value is not a string: ($key, $value)"); | 112 "Environment key or value is not a string: ($key, $value)"); |
| 89 } | 113 } |
| 90 _environment.add('$key=$value'); | 114 _environment.add('$key=$value'); |
| 91 }); | 115 }); |
| 92 } | 116 } |
| 93 | 117 |
| 94 // stdin going to process. | 118 // stdin going to process. |
| 95 _stdin = new _StdSink(new _Socket._writePipe()); | 119 _stdin = new _StdSink(new _Socket._writePipe()); |
| 96 // stdout coming from process. | 120 // stdout coming from process. |
| 97 _stdout = new _StdStream(new _Socket._readPipe()); | 121 _stdout = new _StdStream(new _Socket._readPipe()); |
| 98 // stderr coming from process. | 122 // stderr coming from process. |
| 99 _stderr = new _StdStream(new _Socket._readPipe()); | 123 _stderr = new _StdStream(new _Socket._readPipe()); |
| 100 _exitHandler = new _Socket._readPipe(); | 124 _exitHandler = new _Socket._readPipe(); |
| 101 _ended = false; | 125 _ended = false; |
| 102 _started = false; | 126 _started = false; |
| 103 } | 127 } |
| 104 | 128 |
| 129 static String _getShellCommand() { |
| 130 if (Platform.operatingSystem == 'windows') { |
| 131 return 'cmd.exe'; |
| 132 } |
| 133 return '/bin/sh'; |
| 134 } |
| 135 |
| 136 static List<String> _getShellArguments(String executable, |
| 137 List<String> arguments) { |
| 138 List<String> shellArguments = []; |
| 139 if (Platform.operatingSystem == 'windows') { |
| 140 shellArguments.add('/c'); |
| 141 shellArguments.add(executable); |
| 142 for (var arg in arguments) { |
| 143 arg = arg.replaceAll('"', r'\"'); |
| 144 shellArguments.add(arg); |
| 145 } |
| 146 } else { |
| 147 var commandLine = new StringBuffer(); |
| 148 executable = executable.replaceAll("'", "'\"'\"'"); |
| 149 commandLine.write("'$executable'"); |
| 150 shellArguments.add("-c"); |
| 151 for (var arg in arguments) { |
| 152 arg = arg.replaceAll("'", "'\"'\"'"); |
| 153 commandLine.write(" '$arg'"); |
| 154 } |
| 155 shellArguments.add(commandLine.toString()); |
| 156 } |
| 157 return shellArguments; |
| 158 } |
| 159 |
| 105 String _windowsArgumentEscape(String argument) { | 160 String _windowsArgumentEscape(String argument) { |
| 106 var result = argument; | 161 var result = argument; |
| 107 if (argument.contains('\t') || argument.contains(' ')) { | 162 if (argument.contains('\t') || argument.contains(' ')) { |
| 108 // Produce something that the C runtime on Windows will parse | 163 // Produce something that the C runtime on Windows will parse |
| 109 // back as this string. | 164 // back as this string. |
| 110 | 165 |
| 111 // Replace any number of '\' followed by '"' with | 166 // Replace any number of '\' followed by '"' with |
| 112 // twice as many '\' followed by '\"'. | 167 // twice as many '\' followed by '\"'. |
| 113 var backslash = '\\'.codeUnitAt(0); | 168 var backslash = '\\'.codeUnitAt(0); |
| 114 var sb = new StringBuffer(); | 169 var sb = new StringBuffer(); |
| (...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 265 final Completer<int> _exitCode = new Completer<int>(); | 320 final Completer<int> _exitCode = new Completer<int>(); |
| 266 } | 321 } |
| 267 | 322 |
| 268 | 323 |
| 269 // _NonInteractiveProcess is a wrapper around an interactive process | 324 // _NonInteractiveProcess is a wrapper around an interactive process |
| 270 // that buffers output so it can be delivered when the process exits. | 325 // that buffers output so it can be delivered when the process exits. |
| 271 // _NonInteractiveProcess is used to implement the Process.run | 326 // _NonInteractiveProcess is used to implement the Process.run |
| 272 // method. | 327 // method. |
| 273 Future<ProcessResult> _runNonInteractiveProcess(String path, | 328 Future<ProcessResult> _runNonInteractiveProcess(String path, |
| 274 List<String> arguments, | 329 List<String> arguments, |
| 275 ProcessOptions options) { | 330 String workingDirectory, |
| 331 Map<String, String> environment, |
| 332 bool runInShell, |
| 333 Encoding stdoutEncoding, |
| 334 Encoding stderrEncoding) { |
| 276 // Extract output encoding options and verify arguments. | 335 // Extract output encoding options and verify arguments. |
| 277 var stdoutEncoding = Encoding.SYSTEM; | 336 if (stdoutEncoding == null) stdoutEncoding = Encoding.SYSTEM; |
| 278 var stderrEncoding = Encoding.SYSTEM; | 337 if (stderrEncoding == null) stderrEncoding = Encoding.SYSTEM; |
| 279 if (options != null) { | |
| 280 if (options.stdoutEncoding != null) { | |
| 281 stdoutEncoding = options.stdoutEncoding; | |
| 282 if (stdoutEncoding is !Encoding) { | |
| 283 throw new ArgumentError( | |
| 284 'stdoutEncoding option is not an encoding: $stdoutEncoding'); | |
| 285 } | |
| 286 } | |
| 287 if (options.stderrEncoding != null) { | |
| 288 stderrEncoding = options.stderrEncoding; | |
| 289 if (stderrEncoding is !Encoding) { | |
| 290 throw new ArgumentError( | |
| 291 'stderrEncoding option is not an encoding: $stderrEncoding'); | |
| 292 } | |
| 293 } | |
| 294 } | |
| 295 | 338 |
| 296 // Start the underlying process. | 339 // Start the underlying process. |
| 297 return Process.start(path, arguments, options).then((Process p) { | 340 return Process.start(path, |
| 341 arguments, |
| 342 workingDirectory: workingDirectory, |
| 343 environment: environment, |
| 344 runInShell: runInShell).then((Process p) { |
| 298 int pid = p.pid; | 345 int pid = p.pid; |
| 299 | 346 |
| 300 // Make sure the process stdin is closed. | 347 // Make sure the process stdin is closed. |
| 301 p.stdin.close(); | 348 p.stdin.close(); |
| 302 | 349 |
| 303 // Setup stdout handling. | 350 // Setup stdout handling. |
| 304 Future<StringBuffer> stdout = p.stdout | 351 Future<StringBuffer> stdout = p.stdout |
| 305 .transform(new StringDecoder(stdoutEncoding)) | 352 .transform(new StringDecoder(stdoutEncoding)) |
| 306 .fold( | 353 .fold( |
| 307 new StringBuffer(), | 354 new StringBuffer(), |
| (...skipping 25 matching lines...) Expand all Loading... |
| 333 const _ProcessResult(int this.pid, | 380 const _ProcessResult(int this.pid, |
| 334 int this.exitCode, | 381 int this.exitCode, |
| 335 String this.stdout, | 382 String this.stdout, |
| 336 String this.stderr); | 383 String this.stderr); |
| 337 | 384 |
| 338 final int pid; | 385 final int pid; |
| 339 final int exitCode; | 386 final int exitCode; |
| 340 final String stdout; | 387 final String stdout; |
| 341 final String stderr; | 388 final String stderr; |
| 342 } | 389 } |
| OLD | NEW |