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 |