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

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

Issue 11470031: Finish integrating logging. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Respond to review. 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/entrypoint.dart ('k') | utils/pub/oauth2.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 // TODO(rnystrom): Log request body when it's available and plaintext.
567
509 // TODO(nweiz): remove this when issue 4061 is fixed. 568 // TODO(nweiz): remove this when issue 4061 is fixed.
510 var stackTrace; 569 var stackTrace;
511 try { 570 try {
512 throw null; 571 throw null;
513 } catch (_, localStackTrace) { 572 } catch (_, localStackTrace) {
514 stackTrace = localStackTrace; 573 stackTrace = localStackTrace;
515 } 574 }
516 575
517 // TODO(nweiz): Ideally the timeout would extend to reading from the 576 // 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. 577 // response input stream, but until issue 3657 is fixed that's not feasible.
519 return timeout(_inner.send(request).chain((streamedResponse) { 578 return timeout(_inner.send(request).chain((streamedResponse) {
579 log.fine("Got response ${streamedResponse.statusCode} "
580 "${streamedResponse.reasonPhrase}.");
581
520 var status = streamedResponse.statusCode; 582 var status = streamedResponse.statusCode;
521 // 401 responses should be handled by the OAuth2 client. It's very 583 // 401 responses should be handled by the OAuth2 client. It's very
522 // unlikely that they'll be returned by non-OAuth2 requests. 584 // unlikely that they'll be returned by non-OAuth2 requests.
523 if (status < 400 || status == 401) { 585 if (status < 400 || status == 401) {
524 return new Future.immediate(streamedResponse); 586 return new Future.immediate(streamedResponse);
525 } 587 }
526 588
527 return http.Response.fromStream(streamedResponse).transform((response) { 589 return http.Response.fromStream(streamedResponse).transform((response) {
528 throw new PubHttpException(response); 590 throw new PubHttpException(response);
529 }); 591 });
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after
625 Future<PubProcessResult> runProcess(String executable, List<String> args, 687 Future<PubProcessResult> runProcess(String executable, List<String> args,
626 {workingDir, Map<String, String> environment}) { 688 {workingDir, Map<String, String> environment}) {
627 return _doProcess(Process.run, executable, args, workingDir, environment) 689 return _doProcess(Process.run, executable, args, workingDir, environment)
628 .transform((result) { 690 .transform((result) {
629 // TODO(rnystrom): Remove this and change to returning one string. 691 // TODO(rnystrom): Remove this and change to returning one string.
630 List<String> toLines(String output) { 692 List<String> toLines(String output) {
631 var lines = output.split(NEWLINE_PATTERN); 693 var lines = output.split(NEWLINE_PATTERN);
632 if (!lines.isEmpty && lines.last == "") lines.removeLast(); 694 if (!lines.isEmpty && lines.last == "") lines.removeLast();
633 return lines; 695 return lines;
634 } 696 }
635 return new PubProcessResult(toLines(result.stdout), 697
698 var pubResult = new PubProcessResult(toLines(result.stdout),
636 toLines(result.stderr), 699 toLines(result.stderr),
637 result.exitCode); 700 result.exitCode);
701
702 log.processResult(executable, pubResult);
703 return pubResult;
638 }); 704 });
639 } 705 }
640 706
641 /// Spawns the process located at [executable], passing in [args]. Returns a 707 /// Spawns the process located at [executable], passing in [args]. Returns a
642 /// [Future] that will complete with the [Process] once it's been started. 708 /// [Future] that will complete with the [Process] once it's been started.
643 /// 709 ///
644 /// The spawned process will inherit its parent's environment variables. If 710 /// The spawned process will inherit its parent's environment variables. If
645 /// [environment] is provided, that will be used to augment (not replace) the 711 /// [environment] is provided, that will be used to augment (not replace) the
646 /// the inherited variables. 712 /// the inherited variables.
647 Future<Process> startProcess(String executable, List<String> args, 713 Future<Process> startProcess(String executable, List<String> args,
(...skipping 18 matching lines...) Expand all
666 final options = new ProcessOptions(); 732 final options = new ProcessOptions();
667 if (workingDir != null) { 733 if (workingDir != null) {
668 options.workingDirectory = _getDirectory(workingDir).path; 734 options.workingDirectory = _getDirectory(workingDir).path;
669 } 735 }
670 736
671 if (environment != null) { 737 if (environment != null) {
672 options.environment = new Map.from(Platform.environment); 738 options.environment = new Map.from(Platform.environment);
673 environment.forEach((key, value) => options.environment[key] = value); 739 environment.forEach((key, value) => options.environment[key] = value);
674 } 740 }
675 741
742 log.process(executable, args);
743
676 return fn(executable, args, options); 744 return fn(executable, args, options);
677 } 745 }
678 746
679 /// Closes [response] while ignoring the body of [request]. Returns a Future 747 /// Closes [response] while ignoring the body of [request]. Returns a Future
680 /// that completes once the response is closed. 748 /// that completes once the response is closed.
681 /// 749 ///
682 /// Due to issue 6984, it's necessary to drain the request body before closing 750 /// Due to issue 6984, it's necessary to drain the request body before closing
683 /// the response. 751 /// the response.
684 Future closeHttpResponse(HttpRequest request, HttpResponse response) { 752 Future closeHttpResponse(HttpRequest request, HttpResponse response) {
685 // TODO(nweiz): remove this when issue 4061 is fixed. 753 // TODO(nweiz): remove this when issue 4061 is fixed.
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
734 802
735 /// Creates a temporary directory and passes its path to [fn]. Once the [Future] 803 /// Creates a temporary directory and passes its path to [fn]. Once the [Future]
736 /// returned by [fn] completes, the temporary directory and all its contents 804 /// returned by [fn] completes, the temporary directory and all its contents
737 /// will be deleted. 805 /// will be deleted.
738 Future withTempDir(Future fn(String path)) { 806 Future withTempDir(Future fn(String path)) {
739 var tempDir; 807 var tempDir;
740 var future = createTempDir().chain((dir) { 808 var future = createTempDir().chain((dir) {
741 tempDir = dir; 809 tempDir = dir;
742 return fn(tempDir.path); 810 return fn(tempDir.path);
743 }); 811 });
744 future.onComplete((_) => tempDir.delete(recursive: true)); 812 future.onComplete((_) {
813 log.fine('Cleaning up temp directory ${tempDir.path}.');
814 deleteDir(tempDir);
815 });
745 return future; 816 return future;
746 } 817 }
747 818
748 /// Tests whether or not the git command-line app is available for use. 819 /// Tests whether or not the git command-line app is available for use.
749 Future<bool> get isGitInstalled { 820 Future<bool> get isGitInstalled {
750 if (_isGitInstalledCache != null) { 821 if (_isGitInstalledCache != null) {
751 // TODO(rnystrom): The sleep is to pump the message queue. Can use 822 // TODO(rnystrom): The sleep is to pump the message queue. Can use
752 // Future.immediate() when #3356 is fixed. 823 // Future.immediate() when #3356 is fixed.
753 return sleep(0).transform((_) => _isGitInstalledCache); 824 return sleep(0).transform((_) => _isGitInstalledCache);
754 } 825 }
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
807 return completer.future; 878 return completer.future;
808 } 879 }
809 880
810 /** 881 /**
811 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a 882 * Extracts a `.tar.gz` file from [stream] to [destination], which can be a
812 * directory or a path. Returns whether or not the extraction was successful. 883 * directory or a path. Returns whether or not the extraction was successful.
813 */ 884 */
814 Future<bool> extractTarGz(InputStream stream, destination) { 885 Future<bool> extractTarGz(InputStream stream, destination) {
815 destination = _getPath(destination); 886 destination = _getPath(destination);
816 887
888 log.fine("Extracting .tar.gz stream to $destination.");
889
817 if (Platform.operatingSystem == "windows") { 890 if (Platform.operatingSystem == "windows") {
818 return _extractTarGzWindows(stream, destination); 891 return _extractTarGzWindows(stream, destination);
819 } 892 }
820 893
821 var completer = new Completer<int>(); 894 var completer = new Completer<int>();
822 var processFuture = Process.start("tar", 895 var processFuture = Process.start("tar",
823 ["--extract", "--gunzip", "--directory", destination]); 896 ["--extract", "--gunzip", "--directory", destination]);
824 processFuture.then((process) { 897 processFuture.then((process) {
825 process.onExit = completer.complete; 898 process.onExit = completer.complete;
826 stream.pipe(process.stdin); 899 stream.pipe(process.stdin);
827 process.stdout.pipe(stdout, close: false); 900 process.stdout.pipe(stdout, close: false);
828 process.stderr.pipe(stderr, close: false); 901 process.stderr.pipe(stderr, close: false);
829 }); 902 });
830 processFuture.handleException((error) { 903 processFuture.handleException((error) {
831 completer.completeException(error, processFuture.stackTrace); 904 completer.completeException(error, processFuture.stackTrace);
832 return true; 905 return true;
833 }); 906 });
834 907
835 return completer.future.transform((exitCode) => exitCode == 0); 908 return completer.future.transform((exitCode) {
909 log.fine("Extracted .tar.gz stream to $destination. Exit code $exitCode.");
910 // TODO(rnystrom): Does anything check this result value? If not, it should
911 // throw on a bad exit code.
912 return exitCode == 0;
913 });
836 } 914 }
837 915
838 Future<bool> _extractTarGzWindows(InputStream stream, String destination) { 916 Future<bool> _extractTarGzWindows(InputStream stream, String destination) {
839 // TODO(rnystrom): In the repo's history, there is an older implementation of 917 // TODO(rnystrom): In the repo's history, there is an older implementation of
840 // this that does everything in memory by piping streams directly together 918 // this that does everything in memory by piping streams directly together
841 // instead of writing out temp files. The code is simpler, but unfortunately, 919 // instead of writing out temp files. The code is simpler, but unfortunately,
842 // 7zip seems to periodically fail when we invoke it from Dart and tell it to 920 // 7zip seems to periodically fail when we invoke it from Dart and tell it to
843 // read from stdin instead of a file. Consider resurrecting that version if 921 // read from stdin instead of a file. Consider resurrecting that version if
844 // we can figure out why it fails. 922 // we can figure out why it fails.
845 923
(...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after
882 960
883 // Untar the archive into the destination directory. 961 // Untar the archive into the destination directory.
884 return runProcess(command, ['x', tarFile], workingDir: destination); 962 return runProcess(command, ['x', tarFile], workingDir: destination);
885 }).chain((result) { 963 }).chain((result) {
886 if (result.exitCode != 0) { 964 if (result.exitCode != 0) {
887 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n' 965 throw 'Could not un-tar (exit code ${result.exitCode}). Error:\n'
888 '${Strings.join(result.stdout, "\n")}\n' 966 '${Strings.join(result.stdout, "\n")}\n'
889 '${Strings.join(result.stderr, "\n")}'; 967 '${Strings.join(result.stderr, "\n")}';
890 } 968 }
891 969
892 // Clean up the temp directory. 970 log.fine('Clean up 7zip temp directory ${tempDir.path}.');
893 // TODO(rnystrom): Should also delete this if anything fails. 971 // TODO(rnystrom): Should also delete this if anything fails.
894 return deleteDir(tempDir); 972 return deleteDir(tempDir);
895 }).transform((_) => true); 973 }).transform((_) => true);
896 } 974 }
897 975
898 /// Create a .tar.gz archive from a list of entries. Each entry can be a 976 /// Create a .tar.gz archive from a list of entries. Each entry can be a
899 /// [String], [Directory], or [File] object. The root of the archive is 977 /// [String], [Directory], or [File] object. The root of the archive is
900 /// considered to be [baseDir], which defaults to the current working directory. 978 /// considered to be [baseDir], which defaults to the current working directory.
901 /// Returns an [InputStream] that will emit the contents of the archive. 979 /// Returns an [InputStream] that will emit the contents of the archive.
902 InputStream createTarGz(List contents, {baseDir}) { 980 InputStream createTarGz(List contents, {baseDir}) {
981 var buffer = new StringBuffer();
982 buffer.add('Creating .tag.gz stream containing:\n');
983 contents.forEach(buffer.add);
984 log.fine(buffer.toString());
985
903 // TODO(nweiz): Propagate errors to the returned stream (including non-zero 986 // TODO(nweiz): Propagate errors to the returned stream (including non-zero
904 // exit codes). See issue 3657. 987 // exit codes). See issue 3657.
905 var stream = new ListInputStream(); 988 var stream = new ListInputStream();
906 989
907 if (baseDir == null) baseDir = currentWorkingDir; 990 if (baseDir == null) baseDir = currentWorkingDir;
908 baseDir = getFullPath(baseDir); 991 baseDir = getFullPath(baseDir);
909 contents = contents.map((entry) { 992 contents = contents.map((entry) {
910 entry = getFullPath(entry); 993 entry = getFullPath(entry);
911 if (!isBeneath(entry, baseDir)) { 994 if (!isBeneath(entry, baseDir)) {
912 throw 'Entry $entry is not inside $baseDir.'; 995 throw 'Entry $entry is not inside $baseDir.';
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
958 1041
959 /** 1042 /**
960 * Exception thrown when an HTTP operation fails. 1043 * Exception thrown when an HTTP operation fails.
961 */ 1044 */
962 class PubHttpException implements Exception { 1045 class PubHttpException implements Exception {
963 final http.Response response; 1046 final http.Response response;
964 1047
965 const PubHttpException(this.response); 1048 const PubHttpException(this.response);
966 1049
967 String toString() => 'HTTP error ${response.statusCode}: ' 1050 String toString() => 'HTTP error ${response.statusCode}: '
968 '${response.reasonPhrase}'; 1051 '${response.reasonPhrase}';
969 } 1052 }
970 1053
971 /** 1054 /**
972 * Exception thrown when an operation times out. 1055 * Exception thrown when an operation times out.
973 */ 1056 */
974 class TimeoutException implements Exception { 1057 class TimeoutException implements Exception {
975 final String message; 1058 final String message;
976 1059
977 const TimeoutException(this.message); 1060 const TimeoutException(this.message);
978 1061
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
1029 return new Directory(entry); 1112 return new Directory(entry);
1030 } 1113 }
1031 1114
1032 /** 1115 /**
1033 * Gets a [Uri] for [uri], which can either already be one, or be a [String]. 1116 * Gets a [Uri] for [uri], which can either already be one, or be a [String].
1034 */ 1117 */
1035 Uri _getUri(uri) { 1118 Uri _getUri(uri) {
1036 if (uri is Uri) return uri; 1119 if (uri is Uri) return uri;
1037 return new Uri.fromString(uri); 1120 return new Uri.fromString(uri);
1038 } 1121 }
OLDNEW
« no previous file with comments | « utils/pub/entrypoint.dart ('k') | utils/pub/oauth2.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698