Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(52)

Side by Side Diff: runtime/bin/process_patch.dart

Issue 12316036: Merge IO v2 branch to bleeding edge (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Rebased to r18818 Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « runtime/bin/process.cc ('k') | runtime/bin/secure_socket.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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(String executable,
19 List<String> arguments, 19 List<String> arguments,
20 [ProcessOptions options]) { 20 [ProcessOptions options]) {
21 _ProcessImpl process = new _ProcessImpl(executable, arguments, options); 21 _ProcessImpl process = new _ProcessImpl(executable, arguments, options);
22 return process._start(); 22 return process._start();
23 } 23 }
24 24
25 /* patch */ static Future<ProcessResult> run(String executable, 25 /* patch */ static Future<ProcessResult> run(String executable,
26 List<String> arguments, 26 List<String> arguments,
27 [ProcessOptions options]) { 27 [ProcessOptions options]) {
28 return new _NonInteractiveProcess(executable, arguments, options)._result; 28 return _runNonInteractiveProcess(executable, arguments, options);
29 } 29 }
30 } 30 }
31 31
32 32
33 patch class _ProcessUtils { 33 patch class _ProcessUtils {
34 /* patch */ static _exit(int status) native "Process_Exit"; 34 /* patch */ static _exit(int status) native "Process_Exit";
35 /* patch */ static _setExitCode(int status) native "Process_SetExitCode"; 35 /* patch */ static _setExitCode(int status) native "Process_SetExitCode";
36 } 36 }
37 37
38 38
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
81 _environment = []; 81 _environment = [];
82 env.forEach((key, value) { 82 env.forEach((key, value) {
83 if (key is !String || value is !String) { 83 if (key is !String || value is !String) {
84 throw new ArgumentError( 84 throw new ArgumentError(
85 "Environment key or value is not a string: ($key, $value)"); 85 "Environment key or value is not a string: ($key, $value)");
86 } 86 }
87 _environment.add('$key=$value'); 87 _environment.add('$key=$value');
88 }); 88 });
89 } 89 }
90 90
91 _in = new _Socket._internalReadOnly(); // stdout coming from process. 91 // stdin going to process.
92 _out = new _Socket._internalWriteOnly(); // stdin going to process. 92 _stdin = new _Socket._writePipe();
93 _err = new _Socket._internalReadOnly(); // stderr coming from process. 93 // stdout coming from process.
94 _exitHandler = new _Socket._internalReadOnly(); 94 _stdout = new _Socket._readPipe();
95 // stderr coming from process.
96 _stderr = new _Socket._readPipe();
97 _exitHandler = new _Socket._readPipe();
95 _ended = false; 98 _ended = false;
96 _started = false; 99 _started = false;
97 _onExit = null;
98 } 100 }
99 101
100 String _windowsArgumentEscape(String argument) { 102 String _windowsArgumentEscape(String argument) {
101 var result = argument; 103 var result = argument;
102 if (argument.contains('\t') || argument.contains(' ')) { 104 if (argument.contains('\t') || argument.contains(' ')) {
103 // Produce something that the C runtime on Windows will parse 105 // Produce something that the C runtime on Windows will parse
104 // back as this string. 106 // back as this string.
105 107
106 // Replace any number of '\' followed by '"' with 108 // Replace any number of '\' followed by '"' with
107 // twice as many '\' followed by '\"'. 109 // twice as many '\' followed by '\"'.
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
153 Future<Process> _start() { 155 Future<Process> _start() {
154 var completer = new Completer(); 156 var completer = new Completer();
155 // TODO(ager): Make the actual process starting really async instead of 157 // TODO(ager): Make the actual process starting really async instead of
156 // simulating it with a timer. 158 // simulating it with a timer.
157 new Timer(0, (_) { 159 new Timer(0, (_) {
158 var status = new _ProcessStartStatus(); 160 var status = new _ProcessStartStatus();
159 bool success = _startNative(_path, 161 bool success = _startNative(_path,
160 _arguments, 162 _arguments,
161 _workingDirectory, 163 _workingDirectory,
162 _environment, 164 _environment,
163 _in, 165 _stdin._nativeSocket,
164 _out, 166 _stdout._nativeSocket,
165 _err, 167 _stderr._nativeSocket,
166 _exitHandler, 168 _exitHandler._nativeSocket,
167 status); 169 status);
168 if (!success) { 170 if (!success) {
169 _in.close();
170 _out.close();
171 _err.close();
172 _exitHandler.close();
173 completer.completeError( 171 completer.completeError(
174 new ProcessException(_path, 172 new ProcessException(_path,
175 _arguments, 173 _arguments,
176 status._errorMessage, 174 status._errorMessage,
177 status._errorCode)); 175 status._errorCode));
178 return; 176 return;
179 } 177 }
180 _started = true; 178 _started = true;
181 179
182 _in._closed = false;
183 _out._closed = false;
184 _err._closed = false;
185 _exitHandler._closed = false;
186
187 // Make sure to activate socket handlers now that the file
188 // descriptors have been set.
189 _in._activateHandlers();
190 _out._activateHandlers();
191 _err._activateHandlers();
192
193 // Setup an exit handler to handle internal cleanup and possible 180 // Setup an exit handler to handle internal cleanup and possible
194 // callback when a process terminates. 181 // callback when a process terminates.
195 int exitDataRead = 0; 182 int exitDataRead = 0;
196 final int EXIT_DATA_SIZE = 8; 183 final int EXIT_DATA_SIZE = 8;
197 List<int> exitDataBuffer = new List<int>.fixedLength(EXIT_DATA_SIZE); 184 List<int> exitDataBuffer = new List<int>.fixedLength(EXIT_DATA_SIZE);
198 _exitHandler.inputStream.onData = () { 185 _exitHandler.listen((data) {
199 186
200 int exitCode(List<int> ints) { 187 int exitCode(List<int> ints) {
201 var code = _intFromBytes(ints, 0); 188 var code = _intFromBytes(ints, 0);
202 var negative = _intFromBytes(ints, 4); 189 var negative = _intFromBytes(ints, 4);
203 assert(negative == 0 || negative == 1); 190 assert(negative == 0 || negative == 1);
204 return (negative == 0) ? code : -code; 191 return (negative == 0) ? code : -code;
205 } 192 }
206 193
207 void handleExit() { 194 void handleExit() {
208 _ended = true; 195 _ended = true;
209 _exitCode = exitCode(exitDataBuffer); 196 _exitCode.complete(exitCode(exitDataBuffer));
210 if (_onExit != null) _onExit(_exitCode); 197 // Kill stdin, helping hand if the user forgot to do it.
211 _out.close(); 198 _stdin.destroy();
212 } 199 }
213 200
214 exitDataRead += _exitHandler.inputStream.readInto( 201 exitDataBuffer.setRange(exitDataRead, data.length, data);
215 exitDataBuffer, exitDataRead, EXIT_DATA_SIZE - exitDataRead); 202 exitDataRead += data.length;
216 if (exitDataRead == EXIT_DATA_SIZE) { 203 if (exitDataRead == EXIT_DATA_SIZE) {
217 _exitHandler.close();
218 handleExit(); 204 handleExit();
219 } 205 }
220 }; 206 });
221 207
222 completer.complete(this); 208 completer.complete(this);
223 }); 209 });
224 return completer.future; 210 return completer.future;
225 } 211 }
226 212
227 bool _startNative(String path, 213 bool _startNative(String path,
228 List<String> arguments, 214 List<String> arguments,
229 String workingDirectory, 215 String workingDirectory,
230 List<String> environment, 216 List<String> environment,
231 Socket input, 217 _NativeSocket stdin,
232 Socket output, 218 _NativeSocket stdout,
233 Socket error, 219 _NativeSocket stderr,
234 Socket exitHandler, 220 _NativeSocket exitHandler,
235 _ProcessStartStatus status) native "Process_Start"; 221 _ProcessStartStatus status) native "Process_Start";
236 222
237 InputStream get stdout { 223 Stream<List<int>> get stdout {
238 return _in.inputStream; 224 // TODO(ajohnsen): Get stream object only.
225 return _stdout;
239 } 226 }
240 227
241 InputStream get stderr { 228 Stream<List<int>> get stderr {
242 return _err.inputStream; 229 // TODO(ajohnsen): Get stream object only.
230 return _stderr;
243 } 231 }
244 232
245 OutputStream get stdin { 233 IOSink get stdin {
246 return _out.outputStream; 234 // TODO(ajohnsen): Get consumer object only.
235 return _stdin;
247 } 236 }
248 237
238 Future<int> get exitCode => _exitCode.future;
239
249 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) { 240 bool kill([ProcessSignal signal = ProcessSignal.SIGTERM]) {
250 if (signal is! ProcessSignal) { 241 if (signal is! ProcessSignal) {
251 throw new ArgumentError( 242 throw new ArgumentError(
252 "Argument 'signal' must be a ProcessSignal"); 243 "Argument 'signal' must be a ProcessSignal");
253 } 244 }
254 assert(_started); 245 assert(_started);
255 if (_ended) return false; 246 if (_ended) return false;
256 return _kill(this, signal._signalNumber); 247 return _kill(this, signal._signalNumber);
257 } 248 }
258 249
259 bool _kill(Process p, int signal) native "Process_Kill"; 250 bool _kill(Process p, int signal) native "Process_Kill";
260 251
261 void set onExit(void callback(int exitCode)) {
262 if (_ended) callback(_exitCode);
263 _onExit = callback;
264 }
265
266 String _path; 252 String _path;
267 List<String> _arguments; 253 List<String> _arguments;
268 String _workingDirectory; 254 String _workingDirectory;
269 List<String> _environment; 255 List<String> _environment;
270 // Private methods of _Socket are used by _in, _out, and _err. 256 // Private methods of Socket are used by _in, _out, and _err.
271 _Socket _in; 257 Socket _stdin;
272 _Socket _out; 258 Socket _stdout;
273 _Socket _err; 259 Socket _stderr;
274 Socket _exitHandler; 260 Socket _exitHandler;
275 int _exitCode;
276 bool _ended; 261 bool _ended;
277 bool _started; 262 bool _started;
278 Function _onExit; 263 final Completer<int> _exitCode = new Completer<int>();
279 } 264 }
280 265
281 266
282 // _NonInteractiveProcess is a wrapper around an interactive process 267 // _NonInteractiveProcess is a wrapper around an interactive process
283 // that buffers output so it can be delivered when the process exits. 268 // that buffers output so it can be delivered when the process exits.
284 // _NonInteractiveProcess is used to implement the Process.run 269 // _NonInteractiveProcess is used to implement the Process.run
285 // method. 270 // method.
286 class _NonInteractiveProcess { 271 Future<ProcessResult> _runNonInteractiveProcess(String path,
287 _NonInteractiveProcess(String path, 272 List<String> arguments,
288 List<String> arguments, 273 ProcessOptions options) {
289 ProcessOptions options) { 274 // Extract output encoding options and verify arguments.
290 _completer = new Completer<ProcessResult>(); 275 var stdoutEncoding = Encoding.SYSTEM;
291 // Extract output encoding options and verify arguments. 276 var stderrEncoding = Encoding.SYSTEM;
292 var stdoutEncoding = Encoding.SYSTEM; 277 if (options != null) {
293 var stderrEncoding = Encoding.SYSTEM; 278 if (options.stdoutEncoding != null) {
294 if (options != null) { 279 stdoutEncoding = options.stdoutEncoding;
295 if (options.stdoutEncoding != null) { 280 if (stdoutEncoding is !Encoding) {
296 stdoutEncoding = options.stdoutEncoding; 281 throw new ArgumentError(
297 if (stdoutEncoding is !Encoding) { 282 'stdoutEncoding option is not an encoding: $stdoutEncoding');
298 throw new ArgumentError(
299 'stdoutEncoding option is not an encoding: $stdoutEncoding');
300 }
301 }
302 if (options.stderrEncoding != null) {
303 stderrEncoding = options.stderrEncoding;
304 if (stderrEncoding is !Encoding) {
305 throw new ArgumentError(
306 'stderrEncoding option is not an encoding: $stderrEncoding');
307 }
308 } 283 }
309 } 284 }
310 285 if (options.stderrEncoding != null) {
311 // Start the underlying process. 286 stderrEncoding = options.stderrEncoding;
312 var processFuture = new _ProcessImpl(path, arguments, options)._start(); 287 if (stderrEncoding is !Encoding) {
313 288 throw new ArgumentError(
314 processFuture.then((Process p) { 289 'stderrEncoding option is not an encoding: $stderrEncoding');
315 // Make sure the process stdin is closed. 290 }
316 p.stdin.close();
317
318 // Setup process exit handling.
319 p.onExit = (exitCode) {
320 _exitCode = exitCode;
321 _checkDone();
322 };
323
324 // Setup stdout handling.
325 _stdoutBuffer = new StringBuffer();
326 var stdoutStream = new StringInputStream(p.stdout, stdoutEncoding);
327 stdoutStream.onData = () {
328 var data = stdoutStream.read();
329 if (data != null) _stdoutBuffer.add(data);
330 };
331 stdoutStream.onClosed = () {
332 _stdoutClosed = true;
333 _checkDone();
334 };
335
336 // Setup stderr handling.
337 _stderrBuffer = new StringBuffer();
338 var stderrStream = new StringInputStream(p.stderr, stderrEncoding);
339 stderrStream.onData = () {
340 var data = stderrStream.read();
341 if (data != null) _stderrBuffer.add(data);
342 };
343 stderrStream.onClosed = () {
344 _stderrClosed = true;
345 _checkDone();
346 };
347 }).catchError((error) {
348 _completer.completeError(error.error);
349 });
350 }
351
352 void _checkDone() {
353 if (_exitCode != null && _stderrClosed && _stdoutClosed) {
354 _completer.complete(new _ProcessResult(_exitCode,
355 _stdoutBuffer.toString(),
356 _stderrBuffer.toString()));
357 } 291 }
358 } 292 }
359 293
360 Future<ProcessResult> get _result => _completer.future; 294 // Start the underlying process.
295 return Process.start(path, arguments, options).then((Process p) {
296 // Make sure the process stdin is closed.
297 p.stdin.close();
361 298
362 Completer<ProcessResult> _completer; 299 // Setup stdout handling.
363 StringBuffer _stdoutBuffer; 300 Future<StringBuffer> stdout = p.stdout
364 StringBuffer _stderrBuffer; 301 .transform(new StringDecoder(stdoutEncoding))
365 int _exitCode; 302 .reduce(
366 bool _stdoutClosed = false; 303 new StringBuffer(),
367 bool _stderrClosed = false; 304 (buf, data) {
305 buf.add(data);
306 return buf;
307 });
308
309 Future<StringBuffer> stderr = p.stderr
310 .transform(new StringDecoder(stderrEncoding))
311 .reduce(
312 new StringBuffer(),
313 (buf, data) {
314 buf.add(data);
315 return buf;
316 });
317
318 return Future.wait([p.exitCode, stdout, stderr]).then((result) {
319 return new _ProcessResult(result[0],
320 result[1].toString(),
321 result[2].toString());
322 });
323 });
368 } 324 }
369 325
370 326
371 class _ProcessResult implements ProcessResult { 327 class _ProcessResult implements ProcessResult {
372 const _ProcessResult(int this.exitCode, 328 const _ProcessResult(int this.exitCode,
373 String this.stdout, 329 String this.stdout,
374 String this.stderr); 330 String this.stderr);
375 331
376 final int exitCode; 332 final int exitCode;
377 final String stdout; 333 final String stdout;
378 final String stderr; 334 final String stderr;
379 } 335 }
OLDNEW
« no previous file with comments | « runtime/bin/process.cc ('k') | runtime/bin/secure_socket.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698