Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 part of pipeline; | 5 part of pipeline; |
| 6 | 6 |
| 7 List stdout, stderr, log; | 7 List log; |
| 8 var replyPort; | 8 var replyPort; |
| 9 int _procId = 1; | 9 int _procId = 1; |
| 10 Map _procs = {}; | 10 Map _procs = {}; |
| 11 | 11 |
| 12 /** | 12 /** |
| 13 * Create a file path for a temporary file. The file will be in the | 13 * Create a file path for a temporary file. The file will be in the |
| 14 * [tmpDir] directory, with name [basis], but with any extension | 14 * [tmpDir] directory, with name [basis], but with any extension |
| 15 * stripped and replaced by [suffix]. | 15 * stripped and replaced by [suffix]. |
| 16 */ | 16 */ |
| 17 String createTempName(String tmpDir, String basis, String suffix) { | 17 String createTempName(String tmpDir, String basis, [String suffix='']) { |
| 18 var p = new Path(basis); | 18 var p = new Path(basis); |
| 19 return '$tmpDir${Platform.pathSeparator}' | 19 return '$tmpDir${Platform.pathSeparator}' |
| 20 '${p.filenameWithoutExtension}${suffix}'; | 20 '${p.filenameWithoutExtension}${suffix}'; |
| 21 } | 21 } |
| 22 | 22 |
| 23 /** | 23 /** |
| 24 * Given a file path [file], make it absolute if it is relative, | 24 * Given a file path [file], make it absolute if it is relative, |
| 25 * and return the result as a [Path]. | 25 * and return the result as a [Path]. |
| 26 */ | 26 */ |
| 27 Path getAbsolutePath(String file) { | 27 Path getAbsolutePath(String file) { |
| 28 var p = new Path(file).canonicalize(); | 28 var p = new Path(file).canonicalize(); |
| 29 if (p.isAbsolute) { | 29 if (p.isAbsolute) { |
| 30 return p; | 30 return p; |
| 31 } else { | 31 } else { |
| 32 var cwd = new Path((new Directory.current()).path); | 32 var cwd = new Path((new Directory.current()).path); |
| 33 return cwd.join(p); | 33 return cwd.join(p); |
| 34 } | 34 } |
| 35 } | 35 } |
| 36 | 36 |
| 37 /** Get the directory that contains a [file]. */ | 37 /** Get the directory that contains a [file]. */ |
| 38 String getDirectory(String file) => | 38 String getDirectory(String file) => |
| 39 getAbsolutePath(file).directoryPath.toString(); | 39 getAbsolutePath(file).directoryPath.toString(); |
| 40 | 40 |
| 41 /** Create a file [fileName] and populate it with [contents]. */ | 41 /** Create a file [fileName] and populate it with [contents]. */ |
| 42 void writeFile(String fileName, String contents) { | 42 void writeFile(String fileName, String contents) { |
| 43 var file = new File(fileName); | 43 var file = new File(fileName); |
| 44 var ostream = file.openOutputStream(FileMode.WRITE); | 44 file.writeAsStringSync(contents); |
| 45 ostream.writeString(contents); | |
| 46 ostream.close(); | |
| 47 } | 45 } |
| 48 | 46 |
| 49 /* | 47 /* |
| 50 * Run an external process [cmd] with command line arguments [args]. | 48 * Run an external process [cmd] with command line arguments [args]. |
| 51 * [timeout] can be used to forcefully terminate the process after | 49 * [timeout] can be used to forcefully terminate the process after |
| 52 * some number of seconds. This is used by runCommand and startProcess. | 50 * some number of seconds. This is used by runCommand and startProcess. |
| 53 * If [procId] is non-zero (i.e. called from startProcess) then a reference | 51 * If [procId] is non-zero (i.e. called from startProcess) then a reference |
| 54 * to the [Process] will be put in a map with key [procId]; in this case | 52 * to the [Process] will be put in a map with key [procId]; in this case |
| 55 * the process can be terminated later by calling [stopProcess] and | 53 * the process can be terminated later by calling [stopProcess] and |
| 56 * passing in the [procId]. | 54 * passing in the [procId]. |
| 57 * [outputMonitor] is an optional function that will be called back with each | 55 * [outputMonitor] is an optional function that will be called back with each |
| 58 * line of output from the process. | 56 * line of output from the process. |
| 59 * Returns a [Future] for when the process terminates. | 57 * Returns a [Future] for when the process terminates. |
| 60 */ | 58 */ |
| 61 Future _processHelper(String command, List<String> args, | 59 Future _processHelper(String command, List<String> args, |
|
Siggi Cherem (dart-lang)
2013/04/19 21:39:39
is this duplicate code with the other _processHelp
gram
2013/04/22 23:54:27
No, it is not a dup; it has some subtle difference
Siggi Cherem (dart-lang)
2013/04/23 01:10:04
I think you can use Future.wait instead, using str
gram
2013/04/23 22:53:40
Done.
| |
| 62 [int timeout = 300, int procId = 0, Function outputMonitor]) { | 60 List stdout, List stderr, |
| 63 var completer = procId == 0 ? new Completer() : null; | 61 [int timeout = 30, int procId = 0, Function outputMonitor]) { |
| 62 var completer = procId == 0 ? (new Completer()) : null; | |
| 63 var timer = null; | |
| 64 if (Platform.operatingSystem == 'windows' && command.endsWith('.bat')) { | |
| 65 var oldArgs = args; | |
| 66 args = new List(); | |
| 67 args.add('/k'); | |
| 68 // TODO(gram): We may need some escaping here if any of the | |
| 69 // components contain spaces. | |
| 70 args.add("$command ${oldArgs.join(' ')}"); | |
| 71 command='cmd.exe'; | |
| 72 } | |
| 64 log.add('Running $command ${args.join(" ")}'); | 73 log.add('Running $command ${args.join(" ")}'); |
| 65 var timer = null; | 74 // We are not done until the process has exited and both the stdout |
| 66 var stdoutHandler, stderrHandler; | 75 // and stderr streams have drained; at that point we want to return |
| 76 // the process exit code. We manage this with local state consisting | |
| 77 // of three flags and an int in a List. | |
| 78 // There is probably a better way to do this. | |
| 79 var doneState = [ false, false, false, 0 ]; | |
| 67 var processFuture = Process.start(command, args); | 80 var processFuture = Process.start(command, args); |
| 68 processFuture.then((process) { | 81 processFuture.then((process) { |
| 69 _procs[procId] = process; | 82 _procs[procId.toString()] = process; |
| 70 | 83 |
| 71 timer = new Timer(new Duration(seconds: timeout), () { | 84 timer = new Timer(new Duration(seconds: timeout), () { |
| 72 timer = null; | 85 timer = null; |
| 73 process.kill(); | 86 process.kill(); |
| 74 }); | 87 }); |
| 75 | 88 |
| 76 process.onExit = (exitCode) { | 89 process.exitCode.then((exitCode) { |
| 77 if (timer != null) { | 90 if (timer != null) { |
| 78 timer.cancel(); | 91 timer.cancel(); |
| 79 } | 92 } |
| 80 process.close(); | |
| 81 if (completer != null) { | 93 if (completer != null) { |
| 82 completer.complete(exitCode); | 94 doneState[3] = exitCode; |
| 95 _checkComplete(doneState, 2, completer); | |
| 83 } | 96 } |
| 84 }; | 97 }); |
| 85 | 98 _pipeStream(process.stdout, stdout, outputMonitor, |
| 86 _pipeStream(process.stdout, stdout, outputMonitor); | 99 doneState, 0, completer); |
| 87 _pipeStream(process.stderr, stderr, outputMonitor); | 100 _pipeStream(process.stderr, stderr, outputMonitor, |
| 88 }); | 101 doneState, 1, completer); |
| 89 processFuture.handleException((e) { | 102 }) |
| 103 .catchError((e) { | |
| 90 stderr.add("Error starting process:"); | 104 stderr.add("Error starting process:"); |
| 91 stderr.add(" Command: $command"); | 105 stderr.add(" Command: $command"); |
| 92 stderr.add(" Error: $e"); | 106 stderr.add(" Error: ${e.error}"); |
| 93 completePipeline(-1); | 107 completePipeline(stdout, stderr, -1); |
| 94 return true; | |
| 95 }); | 108 }); |
| 96 | 109 |
| 97 return completer.future; | 110 return completer == null ? null : completer.future; |
| 98 } | 111 } |
| 99 | 112 |
| 100 void _pipeStream(InputStream stream, List<String> destination, | 113 void _checkComplete(List doneState, int index, Completer completer) { |
| 101 Function outputMonitor) { | 114 doneState[index] = true; |
| 102 var source = new StringInputStream(stream); | 115 if (completer != null && doneState[0] && doneState[1] && doneState[2]) { |
| 103 source.onLine = () { | 116 completer.complete(doneState[3]); |
| 104 if (source.available() == 0) return; | 117 } |
| 105 var line = source.readLine(); | 118 } |
| 106 while (null != line) { | 119 |
| 120 void _pipeStream(Stream stream, List<String> destination, | |
| 121 Function outputMonitor, | |
| 122 List doneState, int stateIndex, Completer completer) { | |
| 123 stream | |
| 124 .transform(new StringDecoder()) | |
| 125 .transform(new LineTransformer()) | |
| 126 .listen((String line) { | |
| 107 if (config["immediate"] && line.startsWith('###')) { | 127 if (config["immediate"] && line.startsWith('###')) { |
| 108 // TODO - when we dump the list later skip '###' messages if immediate. | |
| 109 print(line.substring(3)); | 128 print(line.substring(3)); |
| 110 } | 129 } |
| 111 if (outputMonitor != null) { | 130 if (outputMonitor != null) { |
| 112 outputMonitor(line); | 131 outputMonitor(line); |
| 113 } | 132 } |
| 114 destination.add(line); | 133 destination.add(line); |
| 115 line = source.readLine(); | 134 }, |
| 116 } | 135 onDone: () { |
| 117 }; | 136 _checkComplete(doneState, stateIndex, completer); |
| 137 }); | |
| 118 } | 138 } |
| 119 | 139 |
| 120 /** | 140 /** |
| 121 * Run an external process [cmd] with command line arguments [args]. | 141 * Run an external process [cmd] with command line arguments [args]. |
| 122 * [timeout] can be used to forcefully terminate the process after | 142 * [timeout] can be used to forcefully terminate the process after |
| 123 * some number of seconds. | 143 * some number of seconds. |
| 124 * Returns a [Future] for when the process terminates. | 144 * Returns a [Future] for when the process terminates. |
| 125 */ | 145 */ |
| 126 Future runCommand(String command, List<String> args, | 146 Future runCommand(String command, List<String> args, |
| 127 [int timeout = 300, Function outputMonitor]) { | 147 List stdout, List stderr, |
| 128 return _processHelper(command, args, timeout, outputMonitor:outputMonitor); | 148 [int timeout = 30, Function outputMonitor]) { |
| 149 return _processHelper(command, args, stdout, stderr, | |
| 150 timeout, 0, outputMonitor); | |
| 129 } | 151 } |
| 130 | 152 |
| 131 /** | 153 /** |
| 132 * Start an external process [cmd] with command line arguments [args]. | 154 * Start an external process [cmd] with command line arguments [args]. |
| 133 * Returns an ID by which it can later be stopped. | 155 * Returns an ID by which it can later be stopped. |
| 134 */ | 156 */ |
| 135 int startProcess(String command, List<String> args, [Function outputMonitor]) { | 157 int startProcess(String command, List<String> args, List stdout, List stderr, |
| 158 [Function outputMonitor]) { | |
| 136 int id = _procId++; | 159 int id = _procId++; |
| 137 _processHelper(command, args, 3000, id, | 160 var f = _processHelper(command, args, stdout, stderr, 3000, id, |
| 138 outputMonitor:outputMonitor).then((e) { | 161 outputMonitor); |
| 139 _procs.remove(id); | 162 if (f != null) { |
| 140 }); | 163 f.then((e) { |
| 164 _procs.remove(id.toString()); | |
| 165 }); | |
| 166 } | |
| 141 return id; | 167 return id; |
| 142 } | 168 } |
| 143 | 169 |
| 144 /** Checks if a process is still running. */ | 170 /** Checks if a process is still running. */ |
| 145 bool isProcessRunning(int id) { | 171 bool isProcessRunning(int id) { |
| 146 return _procs.containsKey(id); | 172 return _procs.containsKey(id.toString()); |
| 147 } | 173 } |
| 148 | 174 |
| 149 /** | 175 /** |
| 150 * Stop a process previously started with [startProcess] or [runCommand], | 176 * Stop a process previously started with [startProcess] or [runCommand], |
| 151 * given the id string. | 177 * given the id string. |
| 152 */ | 178 */ |
| 153 void stopProcess(int id) { | 179 void stopProcess(int id) { |
| 154 if (_procs.containsKey(id)) { | 180 var sid = id.toString(); |
| 155 Process p = _procs.remove(id); | 181 if (_procs.containsKey(sid)) { |
| 182 Process p = _procs.remove(sid); | |
| 156 p.kill(); | 183 p.kill(); |
| 157 } | 184 } |
| 158 } | 185 } |
| 159 | 186 |
| 160 /** Delete a file named [fname] if it exists. */ | 187 /** Delete a file named [fname] if it exists. */ |
| 161 bool cleanup(String fname) { | 188 bool cleanup(String fname) { |
| 162 if (fname != null && !config['keep-files']) { | 189 if (fname != null && config['clean-files']) { |
| 163 var f = new File(fname); | 190 var f = new File(fname); |
| 164 try { | 191 try { |
| 165 if (f.existsSync()) { | 192 if (f.existsSync()) { |
| 166 logMessage('Removing $fname'); | 193 logMessage('Removing $fname'); |
| 167 f.deleteSync(); | 194 f.deleteSync(); |
| 168 } | 195 } |
| 169 } catch (e) { | 196 } catch (e) { |
| 170 return false; | 197 return false; |
| 171 } | 198 } |
| 172 } | 199 } |
| 173 return true; | 200 return true; |
| 174 } | 201 } |
| 175 | 202 |
| 203 /** Delete a directory named [dname] if it exists. */ | |
| 204 bool cleanupDir(String dname) { | |
| 205 if (dname != null && config['clean-files']) { | |
| 206 var d = new Directory(dname); | |
| 207 try { | |
| 208 if (d.existsSync()) { | |
| 209 logMessage('Removing $dname'); | |
| 210 d.deleteSync(recursive: true); | |
| 211 } | |
| 212 } catch (e) { | |
| 213 return false; | |
| 214 } | |
| 215 } | |
| 216 return true; | |
| 217 } | |
| 218 | |
| 176 initPipeline(port) { | 219 initPipeline(port) { |
| 177 replyPort = port; | 220 replyPort = port; |
| 178 stdout = new List(); | 221 stdout = new List(); |
| 179 stderr = new List(); | 222 stderr = new List(); |
| 180 log = new List(); | 223 log = new List(); |
| 181 } | 224 } |
| 182 | 225 |
| 183 void completePipeline([exitCode = 0]) { | 226 void completePipeline(List stdout, List stderr, [exitCode = 0]) { |
| 184 replyPort.send([stdout, stderr, log, exitCode]); | 227 replyPort.send([stdout, stderr, log, exitCode]); |
| 185 } | 228 } |
| 186 | 229 |
| 187 /** Utility function to log diagnostic messages. */ | 230 /** Utility function to log diagnostic messages. */ |
| 188 void logMessage(msg) => log.add(msg); | 231 void logMessage(msg) => log.add(msg); |
| 232 | |
| 233 /** Turn file paths into standard form with forward slashes. */ | |
| 234 String normalizePath(String p) => (new Path(p)).toString(); | |
| OLD | NEW |