OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of pipeline; | |
6 | |
7 List log; | |
8 var replyPort; | |
9 int _procId = 1; | |
10 Map _procs = {}; | |
11 | |
12 /** | |
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 | |
15 * stripped and replaced by [suffix]. | |
16 */ | |
17 String createTempName(String tmpDir, String basis, [String suffix='']) { | |
18 var p = new Path(basis); | |
19 return '$tmpDir${Platform.pathSeparator}' | |
20 '${p.filenameWithoutExtension}${suffix}'; | |
21 } | |
22 | |
23 /** | |
24 * Given a file path [file], make it absolute if it is relative, | |
25 * and return the result as a [Path]. | |
26 */ | |
27 Path getAbsolutePath(String file) { | |
28 var p = new Path(file).canonicalize(); | |
29 if (p.isAbsolute) { | |
30 return p; | |
31 } else { | |
32 var cwd = new Path(Directory.current.path); | |
33 return cwd.join(p); | |
34 } | |
35 } | |
36 | |
37 /** Get the directory that contains a [file]. */ | |
38 String getDirectory(String file) => | |
39 getAbsolutePath(file).directoryPath.toString(); | |
40 | |
41 /** Create a file [fileName] and populate it with [contents]. */ | |
42 void writeFile(String fileName, String contents) { | |
43 var file = new File(fileName); | |
44 file.writeAsStringSync(contents); | |
45 } | |
46 | |
47 /* | |
48 * Run an external process [cmd] with command line arguments [args]. | |
49 * [timeout] can be used to forcefully terminate the process after | |
50 * some number of seconds. This is used by runCommand and startProcess. | |
51 * If [procId] is non-zero (i.e. called from startProcess) then a reference | |
52 * to the [Process] will be put in a map with key [procId]; in this case | |
53 * the process can be terminated later by calling [stopProcess] and | |
54 * passing in the [procId]. | |
55 * [outputMonitor] is an optional function that will be called back with each | |
56 * line of output from the process. | |
57 * Returns a [Future] for when the process terminates. | |
58 */ | |
59 Future _processHelper(String command, List<String> args, | |
60 List stdout, List stderr, | |
61 [int timeout = 30, int procId = 0, Function outputMonitor]) { | |
62 var timer = null; | |
63 if (Platform.operatingSystem == 'windows' && command.endsWith('.bat')) { | |
64 var oldArgs = args; | |
65 args = new List(); | |
66 args.add('/c'); | |
67 // TODO(gram): We may need some escaping here if any of the | |
68 // components contain spaces. | |
69 args.add("$command ${oldArgs.join(' ')}"); | |
70 command='cmd.exe'; | |
71 } | |
72 log.add('Running $command ${args.join(" ")}'); | |
73 | |
74 return Process.start(command, args) | |
75 .then((process) { | |
76 _procs[procId.toString()] = process; | |
77 | |
78 var stdoutFuture = _pipeStream(process.stdout, stdout, outputMonitor); | |
79 var stderrFuture = _pipeStream(process.stderr, stderr, outputMonitor); | |
80 | |
81 timer = new Timer(new Duration(seconds: timeout), () { | |
82 timer = null; | |
83 process.kill(); | |
84 }); | |
85 return Future.wait([process.exitCode, stdoutFuture, stderrFuture]) | |
86 .then((values) { | |
87 if (timer != null) { | |
88 timer.cancel(); | |
89 } | |
90 return values[0]; | |
91 }); | |
92 }) | |
93 .catchError((e) { | |
94 stderr.add("Error starting process:"); | |
95 stderr.add(" Command: $command"); | |
96 stderr.add(" Error: ${e.toString()}"); | |
97 return new Future.value(-1); | |
98 }); | |
99 } | |
100 | |
101 Future _pipeStream(Stream stream, List<String> destination, | |
102 Function outputMonitor) { | |
103 return stream | |
104 .transform(UTF8.decoder) | |
105 .transform(new LineTransformer()) | |
106 .listen((String line) { | |
107 if (config["immediate"] && line.startsWith('###')) { | |
108 print(line.substring(3)); | |
109 } | |
110 if (outputMonitor != null) { | |
111 outputMonitor(line); | |
112 } | |
113 destination.add(line); | |
114 }) | |
115 .asFuture(); | |
116 } | |
117 | |
118 /** | |
119 * Run an external process [cmd] with command line arguments [args]. | |
120 * [timeout] can be used to forcefully terminate the process after | |
121 * some number of seconds. | |
122 * Returns a [Future] for when the process terminates. | |
123 */ | |
124 Future runCommand(String command, List<String> args, | |
125 List stdout, List stderr, | |
126 [int timeout = 30, Function outputMonitor]) { | |
127 return _processHelper(command, args, stdout, stderr, | |
128 timeout, 0, outputMonitor); | |
129 } | |
130 | |
131 /** | |
132 * Start an external process [cmd] with command line arguments [args]. | |
133 * Returns an ID by which it can later be stopped. | |
134 */ | |
135 int startProcess(String command, List<String> args, List stdout, List stderr, | |
136 [Function outputMonitor]) { | |
137 int id = _procId++; | |
138 var f = _processHelper(command, args, stdout, stderr, 3000, id, | |
139 outputMonitor); | |
140 if (f != null) { | |
141 f.then((e) { | |
142 _procs.remove(id.toString()); | |
143 }); | |
144 } | |
145 return id; | |
146 } | |
147 | |
148 /** Checks if a process is still running. */ | |
149 bool isProcessRunning(int id) { | |
150 return _procs.containsKey(id.toString()); | |
151 } | |
152 | |
153 /** | |
154 * Stop a process previously started with [startProcess] or [runCommand], | |
155 * given the id string. | |
156 */ | |
157 void stopProcess(int id) { | |
158 var sid = id.toString(); | |
159 if (_procs.containsKey(sid)) { | |
160 Process p = _procs.remove(sid); | |
161 p.kill(); | |
162 } | |
163 } | |
164 | |
165 /** Delete a file named [fname] if it exists. */ | |
166 bool cleanup(String fname) { | |
167 if (fname != null && config['clean-files']) { | |
168 var f = new File(fname); | |
169 try { | |
170 if (f.existsSync()) { | |
171 logMessage('Removing $fname'); | |
172 f.deleteSync(); | |
173 } | |
174 } catch (e) { | |
175 return false; | |
176 } | |
177 } | |
178 return true; | |
179 } | |
180 | |
181 /** Delete a directory named [dname] if it exists. */ | |
182 bool cleanupDir(String dname) { | |
183 if (dname != null && config['clean-files']) { | |
184 var d = new Directory(dname); | |
185 try { | |
186 if (d.existsSync()) { | |
187 logMessage('Removing $dname'); | |
188 d.deleteSync(recursive: true); | |
189 } | |
190 } catch (e) { | |
191 return false; | |
192 } | |
193 } | |
194 return true; | |
195 } | |
196 | |
197 initPipeline(port) { | |
198 replyPort = port; | |
199 stdout = new List(); | |
200 stderr = new List(); | |
201 log = new List(); | |
202 } | |
203 | |
204 void completePipeline(List stdout, List stderr, [exitCode = 0]) { | |
205 replyPort.send([stdout, stderr, log, exitCode]); | |
206 } | |
207 | |
208 /** Utility function to log diagnostic messages. */ | |
209 void logMessage(msg) => log.add(msg); | |
210 | |
211 /** Turn file paths into standard form with forward slashes. */ | |
212 String normalizePath(String p) => (new Path(p)).toString(); | |
OLD | NEW |