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

Side by Side Diff: utils/pub/io.dart

Issue 11437019: Add logging system to pub and sprinkle some logging in. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Tweak output. Created 8 years 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 | « utils/pub/hosted_source.dart ('k') | utils/pub/log.dart » ('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 /** 5 /**
6 * Helper functionality to make working with IO easier. 6 * Helper functionality to make working with IO easier.
7 */ 7 */
8 library io; 8 library io;
9 9
10 import 'dart:io'; 10 import 'dart:io';
11 import 'dart:isolate'; 11 import 'dart:isolate';
12 import 'dart:uri'; 12 import 'dart:uri';
13 13
14 // TODO(nweiz): Make this import better. 14 // TODO(nweiz): Make this import better.
15 import '../../pkg/http/lib/http.dart' as http; 15 import '../../pkg/http/lib/http.dart' as http;
16 import 'curl_client.dart';
17 import 'log.dart' as log;
16 import 'utils.dart'; 18 import 'utils.dart';
17 import 'curl_client.dart';
18 19
19 bool _isGitInstalledCache; 20 bool _isGitInstalledCache;
20 21
21 /// The cached Git command. 22 /// The cached Git command.
22 String _gitCommandCache; 23 String _gitCommandCache;
23 24
24 /** Gets the current working directory. */ 25 /** Gets the current working directory. */
25 String get currentWorkingDir => new File('.').fullPathSync(); 26 String get currentWorkingDir => new File('.').fullPathSync();
26 27
27 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?"); 28 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
28 29
29 /** 30 /**
30 * Prints the given string to `stderr` on its own line.
31 */
32 void printError(value) {
33 stderr.writeString(value.toString());
34 stderr.writeString('\n');
35 }
36
37
38 /**
39 * Joins a number of path string parts into a single path. Handles 31 * Joins a number of path string parts into a single path. Handles
40 * platform-specific path separators. Parts can be [String], [Directory], or 32 * platform-specific path separators. Parts can be [String], [Directory], or
41 * [File] objects. 33 * [File] objects.
42 */ 34 */
43 String join(part1, [part2, part3, part4]) { 35 String join(part1, [part2, part3, part4]) {
44 final parts = _sanitizePath(part1).split('/'); 36 final parts = _sanitizePath(part1).split('/');
45 37
46 for (final part in [part2, part3, part4]) { 38 for (final part in [part2, part3, part4]) {
47 if (part == null) continue; 39 if (part == null) continue;
48 40
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after
115 return results[0] || results[1]; 107 return results[0] || results[1];
116 }); 108 });
117 } 109 }
118 110
119 /** 111 /**
120 * Asynchronously determines if [file], which can be a [String] file path or a 112 * Asynchronously determines if [file], which can be a [String] file path or a
121 * [File], exists on the file system. Returns a [Future] that completes with 113 * [File], exists on the file system. Returns a [Future] that completes with
122 * the result. 114 * the result.
123 */ 115 */
124 Future<bool> fileExists(file) { 116 Future<bool> fileExists(file) {
125 return new File(_getPath(file)).exists(); 117 var path = _getPath(file);
118 return log.ioAsync("Seeing if file $path exists.",
119 new File(path).exists(),
120 (exists) => "File $path ${exists ? 'exists' : 'does not exist'}.");
126 } 121 }
127 122
128 /** 123 /**
129 * Reads the contents of the text file [file], which can either be a [String] or 124 * Reads the contents of the text file [file], which can either be a [String] or
130 * a [File]. 125 * a [File].
131 */ 126 */
132 Future<String> readTextFile(file) { 127 Future<String> readTextFile(file) {
133 return new File(_getPath(file)).readAsString(Encoding.UTF_8); 128 var path = _getPath(file);
129 return log.ioAsync("Reading text file $path.",
130 new File(path).readAsString(Encoding.UTF_8),
131 (contents) {
132 // Sanity check: don't spew a huge file.
133 if (contents.length < 1024 * 1024) {
134 return "Read $path. Contents:\n$contents";
135 } else {
136 return "Read ${contents.length} characters from $path.";
137 }
138 });
134 } 139 }
135 140
136 /** 141 /**
137 * Creates [file] (which can either be a [String] or a [File]), and writes 142 * Creates [file] (which can either be a [String] or a [File]), and writes
138 * [contents] to it. Completes when the file is written and closed. 143 * [contents] to it. Completes when the file is written and closed.
139 */ 144 */
140 Future<File> writeTextFile(file, String contents) { 145 Future<File> writeTextFile(file, String contents) {
141 file = new File(_getPath(file)); 146 var path = _getPath(file);
147 file = new File(path);
148
149 // Sanity check: don't spew a huge file.
150 log.io("Writing ${contents.length} characters to text file $path.");
151 if (contents.length < 1024 * 1024) {
152 log.fine("Contents:\n$contents");
153 }
154
142 return file.open(FileMode.WRITE).chain((opened) { 155 return file.open(FileMode.WRITE).chain((opened) {
143 return opened.writeString(contents).chain((ignore) { 156 return opened.writeString(contents).chain((ignore) {
144 return opened.close().transform((ignore) => file); 157 return opened.close().transform((_) {
158 log.fine("Wrote text file $path.");
159 return file;
160 });
145 }); 161 });
146 }); 162 });
147 } 163 }
148 164
149 /** 165 /**
150 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a 166 * Asynchronously deletes [file], which can be a [String] or a [File]. Returns a
151 * [Future] that completes when the deletion is done. 167 * [Future] that completes when the deletion is done.
152 */ 168 */
153 Future<File> deleteFile(file) { 169 Future<File> deleteFile(file) {
154 return new File(_getPath(file)).delete(); 170 var path = _getPath(file);
171 return log.ioAsync("delete file $path",
172 new File(path).delete());
155 } 173 }
156 174
157 /// Writes [stream] to a new file at [path], which may be a [String] or a 175 /// Writes [stream] to a new file at [path], which may be a [String] or a
158 /// [File]. Will replace any file already at that path. Completes when the file 176 /// [File]. Will replace any file already at that path. Completes when the file
159 /// is done being written. 177 /// is done being written.
160 Future<File> createFileFromStream(InputStream stream, path) { 178 Future<File> createFileFromStream(InputStream stream, path) {
161 path = _getPath(path); 179 path = _getPath(path);
162 180
181 log.io("Creating $path from stream.");
182
163 var completer = new Completer<File>(); 183 var completer = new Completer<File>();
164 var file = new File(path); 184 var file = new File(path);
165 var outputStream = file.openOutputStream(); 185 var outputStream = file.openOutputStream();
166 stream.pipe(outputStream); 186 stream.pipe(outputStream);
167 187
168 outputStream.onClosed = () { 188 outputStream.onClosed = () {
189 log.fine("Created $path from stream.");
169 completer.complete(file); 190 completer.complete(file);
170 }; 191 };
171 192
172 // TODO(nweiz): remove this when issue 4061 is fixed. 193 // TODO(nweiz): remove this when issue 4061 is fixed.
173 var stackTrace; 194 var stackTrace;
174 try { 195 try {
175 throw ""; 196 throw "";
176 } catch (_, localStackTrace) { 197 } catch (_, localStackTrace) {
177 stackTrace = localStackTrace; 198 stackTrace = localStackTrace;
178 } 199 }
179 200
180 completeError(error) { 201 completeError(error) {
181 if (!completer.isComplete) completer.completeException(error, stackTrace); 202 if (!completer.isComplete) {
203 completer.completeException(error, stackTrace);
204 } else {
205 log.fine("Got error after stream was closed: $error");
206 }
182 } 207 }
183 208
184 stream.onError = completeError; 209 stream.onError = completeError;
185 outputStream.onError = completeError; 210 outputStream.onError = completeError;
186 211
187 return completer.future; 212 return completer.future;
188 } 213 }
189 214
190 /** 215 /**
191 * Creates a directory [dir]. Returns a [Future] that completes when the 216 * Creates a directory [dir]. Returns a [Future] that completes when the
192 * directory is created. 217 * directory is created.
193 */ 218 */
194 Future<Directory> createDir(dir) { 219 Future<Directory> createDir(dir) {
195 dir = _getDirectory(dir); 220 dir = _getDirectory(dir);
196 return dir.create(); 221 return log.ioAsync("create directory ${dir.path}",
222 dir.create());
197 } 223 }
198 224
199 /** 225 /**
200 * Ensures that [path] and all its parent directories exist. If they don't 226 * Ensures that [path] and all its parent directories exist. If they don't
201 * exist, creates them. Returns a [Future] that completes once all the 227 * exist, creates them. Returns a [Future] that completes once all the
202 * directories are created. 228 * directories are created.
203 */ 229 */
204 Future<Directory> ensureDir(path) { 230 Future<Directory> ensureDir(path) {
205 path = _getPath(path); 231 path = _getPath(path);
232 log.fine("Ensuring directory $path exists.");
206 if (path == '.') return new Future.immediate(new Directory('.')); 233 if (path == '.') return new Future.immediate(new Directory('.'));
207 234
208 return dirExists(path).chain((exists) { 235 return dirExists(path).chain((exists) {
209 if (exists) return new Future.immediate(new Directory(path)); 236 if (exists) {
237 log.fine("Directory $path already exists.");
238 return new Future.immediate(new Directory(path));
239 }
240
210 return ensureDir(dirname(path)).chain((_) { 241 return ensureDir(dirname(path)).chain((_) {
211 var completer = new Completer<Directory>(); 242 var completer = new Completer<Directory>();
212 var future = createDir(path); 243 var future = createDir(path);
213 future.handleException((error) { 244 future.handleException((error) {
214 if (error is! DirectoryIOException) return false; 245 if (error is! DirectoryIOException) return false;
215 // Error 17 means the directory already exists (or 183 on Windows). 246 // Error 17 means the directory already exists (or 183 on Windows).
216 if (error.osError.errorCode != 17 && 247 if (error.osError.errorCode != 17 &&
217 error.osError.errorCode != 183) return false; 248 error.osError.errorCode != 183) {
249 log.fine("Got 'already exists' error when creating directory.");
250 return false;
251 }
218 252
219 completer.complete(_getDirectory(path)); 253 completer.complete(_getDirectory(path));
220 return true; 254 return true;
221 }); 255 });
222 future.then(completer.complete); 256 future.then(completer.complete);
223 return completer.future; 257 return completer.future;
224 }); 258 });
225 }); 259 });
226 } 260 }
227 261
228 /** 262 /**
229 * Creates a temp directory whose name will be based on [dir] with a unique 263 * Creates a temp directory whose name will be based on [dir] with a unique
230 * suffix appended to it. If [dir] is not provided, a temp directory will be 264 * suffix appended to it. If [dir] is not provided, a temp directory will be
231 * created in a platform-dependent temporary location. Returns a [Future] that 265 * created in a platform-dependent temporary location. Returns a [Future] that
232 * completes when the directory is created. 266 * completes when the directory is created.
233 */ 267 */
234 Future<Directory> createTempDir([dir = '']) { 268 Future<Directory> createTempDir([dir = '']) {
235 dir = _getDirectory(dir); 269 dir = _getDirectory(dir);
236 return dir.createTemp(); 270 return log.ioAsync("create temp directory ${dir.path}",
271 dir.createTemp());
237 } 272 }
238 273
239 /** 274 /**
240 * Asynchronously recursively deletes [dir], which can be a [String] or a 275 * Asynchronously recursively deletes [dir], which can be a [String] or a
241 * [Directory]. Returns a [Future] that completes when the deletion is done. 276 * [Directory]. Returns a [Future] that completes when the deletion is done.
242 */ 277 */
243 Future<Directory> deleteDir(dir) { 278 Future<Directory> deleteDir(dir) {
244 dir = _getDirectory(dir); 279 dir = _getDirectory(dir);
245 return dir.delete(recursive: true); 280 return log.ioAsync("delete directory ${dir.path}",
281 dir.delete(recursive: true));
246 } 282 }
247 283
248 /** 284 /**
249 * Asynchronously lists the contents of [dir], which can be a [String] directory 285 * Asynchronously lists the contents of [dir], which can be a [String] directory
250 * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents 286 * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents
251 * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files and 287 * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files and
252 * directories beginning with `.` (defaults to `false`). 288 * directories beginning with `.` (defaults to `false`).
253 */ 289 */
254 Future<List<String>> listDir(dir, 290 Future<List<String>> listDir(dir,
255 {bool recursive: false, bool includeHiddenFiles: false}) { 291 {bool recursive: false, bool includeHiddenFiles: false}) {
256 final completer = new Completer<List<String>>(); 292 final completer = new Completer<List<String>>();
257 final contents = <String>[]; 293 final contents = <String>[];
258 294
259 dir = _getDirectory(dir); 295 dir = _getDirectory(dir);
296 log.io("Listing directory ${dir.path}.");
260 var lister = dir.list(recursive: recursive); 297 var lister = dir.list(recursive: recursive);
261 298
262 lister.onDone = (done) { 299 lister.onDone = (done) {
263 // TODO(rnystrom): May need to sort here if it turns out onDir and onFile 300 // TODO(rnystrom): May need to sort here if it turns out onDir and onFile
264 // aren't guaranteed to be called in a certain order. So far, they seem to. 301 // aren't guaranteed to be called in a certain order. So far, they seem to.
265 if (done) completer.complete(contents); 302 if (done) {
303 log.fine("Listed directory ${dir.path}:\n"
304 "${Strings.join(contents, '\n')}");
305 completer.complete(contents);
306 }
266 }; 307 };
267 308
268 // TODO(nweiz): remove this when issue 4061 is fixed. 309 // TODO(nweiz): remove this when issue 4061 is fixed.
269 var stackTrace; 310 var stackTrace;
270 try { 311 try {
271 throw ""; 312 throw "";
272 } catch (_, localStackTrace) { 313 } catch (_, localStackTrace) {
273 stackTrace = localStackTrace; 314 stackTrace = localStackTrace;
274 } 315 }
275 316
(...skipping 10 matching lines...) Expand all
286 return completer.future; 327 return completer.future;
287 } 328 }
288 329
289 /** 330 /**
290 * Asynchronously determines if [dir], which can be a [String] directory path 331 * Asynchronously determines if [dir], which can be a [String] directory path
291 * or a [Directory], exists on the file system. Returns a [Future] that 332 * or a [Directory], exists on the file system. Returns a [Future] that
292 * completes with the result. 333 * completes with the result.
293 */ 334 */
294 Future<bool> dirExists(dir) { 335 Future<bool> dirExists(dir) {
295 dir = _getDirectory(dir); 336 dir = _getDirectory(dir);
296 return dir.exists(); 337 return log.ioAsync("Seeing if directory ${dir.path} exists.",
338 dir.exists(),
339 (exists) => "Directory ${dir.path} "
340 "${exists ? 'exists' : 'does not exist'}.");
297 } 341 }
298 342
299 /** 343 /**
300 * "Cleans" [dir]. If that directory already exists, it will be deleted. Then a 344 * "Cleans" [dir]. If that directory already exists, it will be deleted. Then a
301 * new empty directory will be created. Returns a [Future] that completes when 345 * new empty directory will be created. Returns a [Future] that completes when
302 * the new clean directory is created. 346 * the new clean directory is created.
303 */ 347 */
304 Future<Directory> cleanDir(dir) { 348 Future<Directory> cleanDir(dir) {
305 return dirExists(dir).chain((exists) { 349 return dirExists(dir).chain((exists) {
306 if (exists) { 350 if (exists) {
307 // Delete it first. 351 // Delete it first.
308 return deleteDir(dir).chain((_) => createDir(dir)); 352 return deleteDir(dir).chain((_) => createDir(dir));
309 } else { 353 } else {
310 // Just create it. 354 // Just create it.
311 return createDir(dir); 355 return createDir(dir);
312 } 356 }
313 }); 357 });
314 } 358 }
315 359
316 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with 360 /// Renames (i.e. moves) the directory [from] to [to]. Returns a [Future] with
317 /// the destination directory. 361 /// the destination directory.
318 Future<Directory> renameDir(from, String to) { 362 Future<Directory> renameDir(from, String to) {
319 from = _getDirectory(from); 363 from = _getDirectory(from);
364 log.io("Renaming directory ${from.path} to $to.");
320 365
321 if (Platform.operatingSystem != 'windows') return from.rename(to); 366 if (Platform.operatingSystem != 'windows') {
367 return from.rename(to).transform((dir) {
368 log.fine("Renamed directory ${from.path} to $to.");
369 return dir;
370 });
371 }
322 372
323 // On Windows, we sometimes get failures where the directory is still in use 373 // On Windows, we sometimes get failures where the directory is still in use
324 // when we try to move it. To be a bit more resilient, we wait and retry a 374 // when we try to move it. To be a bit more resilient, we wait and retry a
325 // few times. 375 // few times.
326 var attempts = 0; 376 var attempts = 0;
327 attemptRename(_) { 377 attemptRename(_) {
328 attempts++; 378 attempts++;
329 return from.rename(to).transformException((e) { 379 return from.rename(to).transform((dir) {
380 log.fine("Renamed directory ${from.path} to $to.");
381 return dir;
382 }).transformException((e) {
330 if (attempts >= 10) { 383 if (attempts >= 10) {
331 throw 'Could not move directory "${from.path}" to "$to". Gave up ' 384 throw 'Could not move directory "${from.path}" to "$to". Gave up '
332 'after $attempts attempts.'; 385 'after $attempts attempts.';
333 } 386 }
334 387
335 // Wait a bit and try again. 388 // Wait a bit and try again.
389 log.fine("Rename ${from.path} failed, retrying (attempt $attempts).");
336 return sleep(500).chain(attemptRename); 390 return sleep(500).chain(attemptRename);
337 }); 391 });
338 392
339 return from; 393 return from;
340 } 394 }
341 395
342 return attemptRename(null); 396 return attemptRename(null);
343 } 397 }
344 398
345 /** 399 /**
346 * Creates a new symlink that creates an alias from [from] to [to], both of 400 * Creates a new symlink that creates an alias from [from] to [to], both of
347 * which can be a [String], [File], or [Directory]. Returns a [Future] which 401 * which can be a [String], [File], or [Directory]. Returns a [Future] which
348 * completes to the symlink file (i.e. [to]). 402 * completes to the symlink file (i.e. [to]).
349 */ 403 */
350 Future<File> createSymlink(from, to) { 404 Future<File> createSymlink(from, to) {
351 from = _getPath(from); 405 from = _getPath(from);
352 to = _getPath(to); 406 to = _getPath(to);
353 407
408 log.fine("Create symlink $from -> $to.");
409
354 var command = 'ln'; 410 var command = 'ln';
355 var args = ['-s', from, to]; 411 var args = ['-s', from, to];
356 412
357 if (Platform.operatingSystem == 'windows') { 413 if (Platform.operatingSystem == 'windows') {
358 // Call mklink on Windows to create an NTFS junction point. Only works on 414 // Call mklink on Windows to create an NTFS junction point. Only works on
359 // Vista or later. (Junction points are available earlier, but the "mklink" 415 // Vista or later. (Junction points are available earlier, but the "mklink"
360 // command is not.) I'm using a junction point (/j) here instead of a soft 416 // command is not.) I'm using a junction point (/j) here instead of a soft
361 // link (/d) because the latter requires some privilege shenanigans that 417 // link (/d) because the latter requires some privilege shenanigans that
362 // I'm not sure how to specify from the command line. 418 // I'm not sure how to specify from the command line.
363 command = 'mklink'; 419 command = 'mklink';
(...skipping 11 matching lines...) Expand all
375 * package [from] to [to], both of which can be a [String], [File], or 431 * package [from] to [to], both of which can be a [String], [File], or
376 * [Directory]. Returns a [Future] which completes to the symlink file (i.e. 432 * [Directory]. Returns a [Future] which completes to the symlink file (i.e.
377 * [to]). If [from] does not have a `lib` directory, this shows a warning if 433 * [to]). If [from] does not have a `lib` directory, this shows a warning if
378 * appropriate and then does nothing. 434 * appropriate and then does nothing.
379 */ 435 */
380 Future<File> createPackageSymlink(String name, from, to, 436 Future<File> createPackageSymlink(String name, from, to,
381 {bool isSelfLink: false}) { 437 {bool isSelfLink: false}) {
382 // See if the package has a "lib" directory. 438 // See if the package has a "lib" directory.
383 from = join(from, 'lib'); 439 from = join(from, 'lib');
384 return dirExists(from).chain((exists) { 440 return dirExists(from).chain((exists) {
441 log.fine("Creating ${isSelfLink ? "self" : ""}link for package '$name'.");
385 if (exists) return createSymlink(from, to); 442 if (exists) return createSymlink(from, to);
386 443
387 // It's OK for the self link (i.e. the root package) to not have a lib 444 // It's OK for the self link (i.e. the root package) to not have a lib
388 // directory since it may just be a leaf application that only has 445 // directory since it may just be a leaf application that only has
389 // code in bin or web. 446 // code in bin or web.
390 if (!isSelfLink) { 447 if (!isSelfLink) {
391 printError( 448 log.warning('Warning: Package "$name" does not have a "lib" directory so '
392 'Warning: Package "$name" does not have a "lib" directory so you ' 449 'you will not be able to import any libraries from it.');
393 'will not be able to import any libraries from it.');
394 } 450 }
395 451
396 return new Future.immediate(to); 452 return new Future.immediate(to);
397 }); 453 });
398 } 454 }
399 455
400 /// Given [entry] which may be a [String], [File], or [Directory] relative to 456 /// Given [entry] which may be a [String], [File], or [Directory] relative to
401 /// the current working directory, returns its full canonicalized path. 457 /// the current working directory, returns its full canonicalized path.
402 String getFullPath(entry) { 458 String getFullPath(entry) {
403 var path = _getPath(entry); 459 var path = _getPath(entry);
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
499 555
500 /// An HTTP client that transforms 40* errors and socket exceptions into more 556 /// An HTTP client that transforms 40* errors and socket exceptions into more
501 /// user-friendly error messages. 557 /// user-friendly error messages.
502 class PubHttpClient extends http.BaseClient { 558 class PubHttpClient extends http.BaseClient {
503 final http.Client _inner; 559 final http.Client _inner;
504 560
505 PubHttpClient([http.Client inner]) 561 PubHttpClient([http.Client inner])
506 : _inner = inner == null ? new http.Client() : inner; 562 : _inner = inner == null ? new http.Client() : inner;
507 563
508 Future<http.StreamedResponse> send(http.BaseRequest request) { 564 Future<http.StreamedResponse> send(http.BaseRequest request) {
565 log.io("Sending HTTP request $request.");
566
509 // TODO(nweiz): remove this when issue 4061 is fixed. 567 // TODO(nweiz): remove this when issue 4061 is fixed.
510 var stackTrace; 568 var stackTrace;
511 try { 569 try {
512 throw null; 570 throw null;
513 } catch (_, localStackTrace) { 571 } catch (_, localStackTrace) {
514 stackTrace = localStackTrace; 572 stackTrace = localStackTrace;
515 } 573 }
516 574
517 // TODO(nweiz): Ideally the timeout would extend to reading from the 575 // TODO(nweiz): Ideally the timeout would extend to reading from the
518 // response input stream, but until issue 3657 is fixed that's not feasible. 576 // response input stream, but until issue 3657 is fixed that's not feasible.
519 return timeout(_inner.send(request).chain((streamedResponse) { 577 return timeout(_inner.send(request).chain((streamedResponse) {
578 log.fine("Got response ${streamedResponse.statusCode} "
579 "${streamedResponse.reasonPhrase}.");
580
520 var status = streamedResponse.statusCode; 581 var status = streamedResponse.statusCode;
521 // 401 responses should be handled by the OAuth2 client. It's very 582 // 401 responses should be handled by the OAuth2 client. It's very
522 // unlikely that they'll be returned by non-OAuth2 requests. 583 // unlikely that they'll be returned by non-OAuth2 requests.
523 if (status < 400 || status == 401) { 584 if (status < 400 || status == 401) {
524 return new Future.immediate(streamedResponse); 585 return new Future.immediate(streamedResponse);
525 } 586 }
526 587
527 return http.Response.fromStream(streamedResponse).transform((response) { 588 return http.Response.fromStream(streamedResponse).transform((response) {
528 throw new PubHttpException(response); 589 throw new PubHttpException(response);
529 }); 590 });
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
615 Future<PubProcessResult> runProcess(String executable, List<String> args, 676 Future<PubProcessResult> runProcess(String executable, List<String> args,
616 {workingDir, Map<String, String> environment}) { 677 {workingDir, Map<String, String> environment}) {
617 return _doProcess(Process.run, executable, args, workingDir, environment) 678 return _doProcess(Process.run, executable, args, workingDir, environment)
618 .transform((result) { 679 .transform((result) {
619 // TODO(rnystrom): Remove this and change to returning one string. 680 // TODO(rnystrom): Remove this and change to returning one string.
620 List<String> toLines(String output) { 681 List<String> toLines(String output) {
621 var lines = output.split(NEWLINE_PATTERN); 682 var lines = output.split(NEWLINE_PATTERN);
622 if (!lines.isEmpty && lines.last == "") lines.removeLast(); 683 if (!lines.isEmpty && lines.last == "") lines.removeLast();
623 return lines; 684 return lines;
624 } 685 }
625 return new PubProcessResult(toLines(result.stdout), 686
687 var pubResult = new PubProcessResult(toLines(result.stdout),
626 toLines(result.stderr), 688 toLines(result.stderr),
627 result.exitCode); 689 result.exitCode);
690
691 log.processResult(executable, pubResult);
692 return pubResult;
628 }); 693 });
629 } 694 }
630 695
631 /// Spawns the process located at [executable], passing in [args]. Returns a 696 /// Spawns the process located at [executable], passing in [args]. Returns a
632 /// [Future] that will complete with the [Process] once it's been started. 697 /// [Future] that will complete with the [Process] once it's been started.
633 /// 698 ///
634 /// The spawned process will inherit its parent's environment variables. If 699 /// The spawned process will inherit its parent's environment variables. If
635 /// [environment] is provided, that will be used to augment (not replace) the 700 /// [environment] is provided, that will be used to augment (not replace) the
636 /// the inherited variables. 701 /// the inherited variables.
637 Future<Process> startProcess(String executable, List<String> args, 702 Future<Process> startProcess(String executable, List<String> args,
(...skipping 18 matching lines...) Expand all
656 final options = new ProcessOptions(); 721 final options = new ProcessOptions();
657 if (workingDir != null) { 722 if (workingDir != null) {
658 options.workingDirectory = _getDirectory(workingDir).path; 723 options.workingDirectory = _getDirectory(workingDir).path;
659 } 724 }
660 725
661 if (environment != null) { 726 if (environment != null) {
662 options.environment = new Map.from(Platform.environment); 727 options.environment = new Map.from(Platform.environment);
663 environment.forEach((key, value) => options.environment[key] = value); 728 environment.forEach((key, value) => options.environment[key] = value);
664 } 729 }
665 730
731 log.process(executable, args);
732
666 return fn(executable, args, options); 733 return fn(executable, args, options);
667 } 734 }
668 735
669 /// Closes [response] while ignoring the body of [request]. Returns a Future 736 /// Closes [response] while ignoring the body of [request]. Returns a Future
670 /// that completes once the response is closed. 737 /// that completes once the response is closed.
671 /// 738 ///
672 /// Due to issue 6984, it's necessary to drain the request body before closing 739 /// Due to issue 6984, it's necessary to drain the request body before closing
673 /// the response. 740 /// the response.
674 Future closeHttpResponse(HttpRequest request, HttpResponse response) { 741 Future closeHttpResponse(HttpRequest request, HttpResponse response) {
675 // TODO(nweiz): remove this when issue 4061 is fixed. 742 // TODO(nweiz): remove this when issue 4061 is fixed.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
724 791
725 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] 792 /// Creates a temporary directory and passes its path to [fn]. Once the [Future]
726 /// returned by [fn] completes, the temporary directory and all its contents 793 /// returned by [fn] completes, the temporary directory and all its contents
727 /// will be deleted. 794 /// will be deleted.
728 Future withTempDir(Future fn(String path)) { 795 Future withTempDir(Future fn(String path)) {
729 var tempDir; 796 var tempDir;
730 var future = createTempDir().chain((dir) { 797 var future = createTempDir().chain((dir) {
731 tempDir = dir; 798 tempDir = dir;
732 return fn(tempDir.path); 799 return fn(tempDir.path);
733 }); 800 });
734 future.onComplete((_) => tempDir.delete(recursive: true)); 801 future.onComplete((_) {
802 log.fine('Cleaning up temp directory ${tempDir.path}.');
803 deleteDir(tempDir);
804 });
735 return future; 805 return future;
736 } 806 }
737 807
738 /// Tests whether or not the git command-line app is available for use. 808 /// Tests whether or not the git command-line app is available for use.
739 Future<bool> get isGitInstalled { 809 Future<bool> get isGitInstalled {
740 if (_isGitInstalledCache != null) { 810 if (_isGitInstalledCache != null) {
741 // TODO(rnystrom): The sleep is to pump the message queue. Can use 811 // TODO(rnystrom): The sleep is to pump the message queue. Can use
742 // Future.immediate() when #3356 is fixed. 812 // Future.immediate() when #3356 is fixed.
743 return sleep(0).transform((_) => _isGitInstalledCache); 813 return sleep(0).transform((_) => _isGitInstalledCache);
744 } 814 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
797 return completer.future; 867 return completer.future;
798 } 868 }
799 869
800 /** 870 /**
801 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a 871 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a
802 * directory or a path. Returns whether or not the extraction was successful. 872 * directory or a path. Returns whether or not the extraction was successful.
803 */ 873 */
804 Future<bool> extractTarGz(InputStream stream, destination) { 874 Future<bool> extractTarGz(InputStream stream, destination) {
805 destination = _getPath(destination); 875 destination = _getPath(destination);
806 876
877 log.fine("Extracting .tar.gz stream to $destination.");
878
807 if (Platform.operatingSystem == "windows") { 879 if (Platform.operatingSystem == "windows") {
808 return _extractTarGzWindows(stream, destination); 880 return _extractTarGzWindows(stream, destination);
809 } 881 }
810 882
811 var completer = new Completer<int>(); 883 var completer = new Completer<int>();
812 var processFuture = Process.start("tar", 884 var processFuture = Process.start("tar",
813 ["--extract", "--gunzip", "--directory", destination]); 885 ["--extract", "--gunzip", "--directory", destination]);
814 processFuture.then((process) { 886 processFuture.then((process) {
815 process.onExit = completer.complete; 887 process.onExit = completer.complete;
816 stream.pipe(process.stdin); 888 stream.pipe(process.stdin);
817 process.stdout.pipe(stdout, close: false); 889 process.stdout.pipe(stdout, close: false);
818 process.stderr.pipe(stderr, close: false); 890 process.stderr.pipe(stderr, close: false);
819 }); 891 });
820 processFuture.handleException((error) { 892 processFuture.handleException((error) {
821 completer.completeException(error, processFuture.stackTrace); 893 completer.completeException(error, processFuture.stackTrace);
822 return true; 894 return true;
823 }); 895 });
824 896
825 return completer.future.transform((exitCode) => exitCode == 0); 897 return completer.future.transform((exitCode) {
898 log.fine("Extracted .tar.gz stream to $destination. Exit code $exitCode.");
899 // TODO(rnystrom): Does anything check this result value? If not, it should
900 // throw on a bad exit code.
901 return exitCode == 0;
902 });
826 } 903 }
827 904
828 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { 905 Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
829 // TODO(rnystrom): In the repo's history, there is an older implementation of 906 // TODO(rnystrom): In the repo's history, there is an older implementation of
830 // this that does everything in memory by piping streams directly together 907 // this that does everything in memory by piping streams directly together
831 // instead of writing out temp files. The code is simpler, but unfortunately, 908 // instead of writing out temp files. The code is simpler, but unfortunately,
832 // 7zip seems to periodically fail when we invoke it from Dart and tell it to 909 // 7zip seems to periodically fail when we invoke it from Dart and tell it to
833 // read from stdin instead of a file. Consider resurrecting that version if 910 // read from stdin instead of a file. Consider resurrecting that version if
834 // we can figure out why it fails. 911 // we can figure out why it fails.
835 912
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
872 949
873 // Untar the archive into the destination directory. 950 // Untar the archive into the destination directory.
874 return runProcess(command, ['x', tarFile], workingDir: destination); 951 return runProcess(command, ['x', tarFile], workingDir: destination);
875 }).chain((result) { 952 }).chain((result) {
876 if (result.exitCode != 0) { 953 if (result.exitCode != 0) {
877 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' 954 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n'
878 '${Strings.join(result.stdout, "\n")}\n' 955 '${Strings.join(result.stdout, "\n")}\n'
879 '${Strings.join(result.stderr, "\n")}'; 956 '${Strings.join(result.stderr, "\n")}';
880 } 957 }
881 958
882 // Clean up the temp directory. 959 log.fine('Clean up 7zip temp directory ${tempDir.path}.');
883 // TODO(rnystrom): Should also delete this if anything fails. 960 // TODO(rnystrom): Should also delete this if anything fails.
884 return deleteDir(tempDir); 961 return deleteDir(tempDir);
885 }).transform((_) => true); 962 }).transform((_) => true);
886 } 963 }
887 964
888 /// Create a .tar.gz archive from a list of entries. Each entry can be a 965 /// Create a .tar.gz archive from a list of entries. Each entry can be a
889 /// [String], [Directory], or [File] object. The root of the archive is 966 /// [String], [Directory], or [File] object. The root of the archive is
890 /// considered to be [baseDir], which defaults to the current working directory. 967 /// considered to be [baseDir], which defaults to the current working directory.
891 /// Returns an [InputStream] that will emit the contents of the archive. 968 /// Returns an [InputStream] that will emit the contents of the archive.
892 InputStream createTarGz(List contents, {baseDir}) { 969 InputStream createTarGz(List contents, {baseDir}) {
970 log.fine('Creating .tag.gz stream containing:');
971 contents.forEach(log.fine);
972
893 // TODO(nweiz): Propagate errors to the returned stream (including non-zero 973 // TODO(nweiz): Propagate errors to the returned stream (including non-zero
894 // exit codes). See issue 3657. 974 // exit codes). See issue 3657.
895 var stream = new ListInputStream(); 975 var stream = new ListInputStream();
896 976
897 if (baseDir == null) baseDir = currentWorkingDir; 977 if (baseDir == null) baseDir = currentWorkingDir;
898 baseDir = getFullPath(baseDir); 978 baseDir = getFullPath(baseDir);
899 contents = contents.map((entry) { 979 contents = contents.map((entry) {
900 entry = getFullPath(entry); 980 entry = getFullPath(entry);
901 if (!isBeneath(entry, baseDir)) { 981 if (!isBeneath(entry, baseDir)) {
902 throw 'Entry $entry is not inside $baseDir.'; 982 throw 'Entry $entry is not inside $baseDir.';
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
948 1028
949 /** 1029 /**
950 * Exception thrown when an HTTP operation fails. 1030 * Exception thrown when an HTTP operation fails.
951 */ 1031 */
952 class PubHttpException implements Exception { 1032 class PubHttpException implements Exception {
953 final http.Response response; 1033 final http.Response response;
954 1034
955 const PubHttpException(this.response); 1035 const PubHttpException(this.response);
956 1036
957 String toString() => 'HTTP error ${response.statusCode}: ' 1037 String toString() => 'HTTP error ${response.statusCode}: '
958 '${response.reasonPhrase}'; 1038 '${response.reasonPhrase}';
959 } 1039 }
960 1040
961 /** 1041 /**
962 * Exception thrown when an operation times out. 1042 * Exception thrown when an operation times out.
963 */ 1043 */
964 class TimeoutException implements Exception { 1044 class TimeoutException implements Exception {
965 final String message; 1045 final String message;
966 1046
967 const TimeoutException(this.message); 1047 const TimeoutException(this.message);
968 1048
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1019 return new Directory(entry); 1099 return new Directory(entry);
1020 } 1100 }
1021 1101
1022 /** 1102 /**
1023 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 1103 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
1024 */ 1104 */
1025 Uri _getUri(uri) { 1105 Uri _getUri(uri) {
1026 if (uri is Uri) return uri; 1106 if (uri is Uri) return uri;
1027 return new Uri.fromString(uri); 1107 return new Uri.fromString(uri);
1028 } 1108 }
OLDNEW
« no previous file with comments | « utils/pub/hosted_source.dart ('k') | utils/pub/log.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698