Chromium Code Reviews| Index: utils/pub/io.dart |
| diff --git a/utils/pub/io.dart b/utils/pub/io.dart |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..57788941f869367b875edde519cc355b4307de81 |
| --- /dev/null |
| +++ b/utils/pub/io.dart |
| @@ -0,0 +1,253 @@ |
| +// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +/** |
| + * Helper functionality to make working with IO easier. |
| + */ |
| +#library('pub_io'); |
| + |
| +#import('dart:io'); |
| + |
| +#import('../lib/file_system.dart', prefix: 'fs'); |
| + |
| +/** |
| + * Joins a number of path string parts into a single path. Handles |
| + * platform-specific path separators. Parts can be [String], [Directory], or |
| + * [File] objects. |
| + */ |
| +String join(part1, [part2, part3, part4]) { |
| + final parts = _getPath(part1).split('/'); |
| + |
| + for (final part in [part2, part3, part4]) { |
| + if (part == null) continue; |
| + |
| + for (final piece in _getPath(part).split('/')) { |
| + if (piece == '..' && parts.length > 0 && |
|
nweiz
2012/04/17 20:08:46
Unless you have a good reason to try and clean up
Bob Nystrom
2012/04/18 18:09:18
This is mostly copied from file_system and I have
|
| + parts.last() != '.' && parts.last() != '..') { |
| + parts.removeLast(); |
| + } else if (piece != '') { |
| + if (parts.length > 0 && parts.last() == '.') { |
| + parts.removeLast(); |
| + } |
| + parts.add(piece); |
| + } |
| + } |
| + } |
| + |
| + return Strings.join(parts, new Platform().pathSeparator()); |
| +} |
| + |
| +/** |
| + * Gets the basename, the file name without any leading directory path, for |
| + * [file], which can either be a [String], [File], or [Directory]. |
| + */ |
| +String basename(file) { |
| + return fs.basename(_getPath(file)); |
| +} |
| + |
| +/** |
| + * Reads the contents of the text file [file], which can either be a [String] or |
| + * a [File]. |
| + */ |
| +Future<String> readTextFile(file) { |
| + file = new File(_getPath(file)); |
| + final completer = new Completer<String>(); |
| + file.onError = (error) => completer.completeException(error); |
| + file.readAsText(Encoding.UTF_8, (text) { |
| + completer.complete(text); |
|
nweiz
2012/04/17 20:08:46
I don't understand your heuristic for when you use
Bob Nystrom
2012/04/18 18:09:18
It was apparently based on astrology. Made less cr
|
| + }); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Creates [file] (which can either be a [String] or a [File]), and writes |
| + * [contents] to it. Completes when the file is written and closed. |
| + */ |
| +Future<File> writeTextFile(file, String contents) { |
| + file = new File(_getPath(file)); |
| + final completer = new Completer<File>(); |
| + file.onError = (error) => completer.completeException(error); |
| + file.open(FileMode.WRITE, (opened) { |
| + opened.onError = (error) => completer.completeException(error); |
| + opened.onNoPendingWrites = () { |
| + opened.close(() { |
| + completer.complete(file); |
| + }); |
| + }; |
| + opened.writeString(contents); |
| + }); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Creates a directory [dir]. Returns a [Future] that completes when the |
| + * directory is created. |
| + */ |
| +Future<Directory> createDir(dir) { |
| + final completer = new Completer<Directory>(); |
| + dir = _getDirectory(dir); |
| + dir.onError = (error) => completer.completeException(error); |
| + dir.create(() { |
| + completer.complete(dir); |
| + }); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Creates a temp directory whose name will be based on [dir] with a unique |
| + * suffix appended to it. Returns a [Future] that completes when the directory |
| + * is created. |
| + */ |
| +Future<Directory> createTempDir(dir) { |
| + final completer = new Completer<Directory>(); |
| + dir = _getDirectory(dir); |
| + dir.onError = (error) => completer.completeException(error); |
| + dir.createTemp(() { |
| + completer.complete(dir); |
| + }); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Asynchronously recursively deletes [dir], which can be a [String] or a |
| + * [Directory]. Returns a [Future] that completes when the deletion is done. |
| + */ |
| +Future<Directory> deleteDir(dir) { |
| + final completer = new Completer<Directory>(); |
| + dir = _getDirectory(dir); |
| + dir.onError = (error) => completer.completeException(error); |
| + dir.deleteRecursively(() { |
| + completer.complete(dir); |
| + }); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Asynchronously lists the contents of [dir], which can be a [String] directory |
| + * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents |
| + * (defaults to `false`). If [includeSpecialFiles] is `true`, includes "hidden" |
| + * files like `.DS_Store` (defaults to `false`). |
| + */ |
| +Future<List<String>> listDir(dir, |
| + [bool recursive = false, bool includeSpecialFiles = false]) { |
| + final completer = new Completer<List<String>>(); |
| + final contents = <String>[]; |
| + |
| + dir = _getDirectory(dir); |
| + |
| + dir.onDone = (done) { |
|
nweiz
2012/04/17 20:08:46
nit: extra space after "Done".
What are the seman
Bob Nystrom
2012/04/18 18:09:18
Done.
|
| + if (done) completer.complete(contents); |
| + }; |
| + |
| + dir.onError = (error) { |
| + completer.completeException(error); |
| + }; |
| + |
| + dir.onDir = (file) { |
| + contents.add(file); |
| + }; |
| + |
| + dir.onFile = (file) { |
| + if (!includeSpecialFiles) { |
| + if (basename(file) == '.DS_Store') return; |
|
nweiz
2012/04/17 20:08:46
The documentation implies that this is going to fi
Bob Nystrom
2012/04/18 18:09:18
Clarified docs.
|
| + } |
| + |
| + contents.add(file); |
| + }; |
| + |
| + dir.list(recursive: recursive); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Spawns and runs the process located at [executable], passing in [args]. |
| + * Returns a [Future] that will complete the results of the process after it |
| + * has ended. |
| + */ |
| +Future<ProcessResult> runProcess(String executable, List<String> args) { |
| + var exitCode; |
|
nweiz
2012/04/17 20:08:46
Explicitly type this because it's not being initia
Bob Nystrom
2012/04/18 18:09:18
Done.
|
| + var error; |
|
nweiz
2012/04/17 20:08:46
Unused variable
Bob Nystrom
2012/04/18 18:09:18
Done.
|
| + |
| + final process = new Process.start(executable, args); |
| + |
| + final outStream = new StringInputStream(process.stdout); |
| + final processStdout = <String>[]; |
| + |
| + final errStream = new StringInputStream(process.stderr); |
| + final processStderr = <String>[]; |
| + bool processDone = false; |
|
nweiz
2012/04/17 20:08:46
var or final
Bob Nystrom
2012/04/18 18:09:18
Unused.
|
| + |
| + final completer = new Completer<ProcessResult>(); |
| + |
| + checkComplete() { |
| + // Wait until the process is done and its output streams are closed. |
| + if (!outStream.closed) return; |
| + if (!errStream.closed) return; |
| + if (exitCode == null) return; |
| + |
| + completer.complete(new ProcessResult( |
| + processStdout, processStderr, exitCode)); |
| + } |
| + |
| + outStream.onLine = () { |
| + processStdout.add(outStream.readLine()); |
| + }; |
| + |
| + outStream.onClosed = checkComplete; |
| + outStream.onError = (error) => completer.completeException(error); |
| + |
| + errStream.onLine = () { |
| + processStderr.add(errStream.readLine()); |
| + }; |
| + |
| + errStream.onClosed = checkComplete; |
| + errStream.onError = (error) => completer.completeException(error); |
| + |
| + process.onExit = (actualExitCode) { |
| + exitCode = actualExitCode; |
| + checkComplete(); |
| + }; |
| + |
| + process.onError = (error) => completer.completeException(error); |
| + |
| + return completer.future; |
| +} |
| + |
| +/** |
| + * Contains the results of invoking a [Process] and waiting for it to complete. |
| + */ |
| +class ProcessResult { |
| + final List<String> stdout; |
| + final List<String> stderr; |
| + final int exitCode; |
| + |
| + const ProcessResult(this.stdout, this.stderr, this.exitCode); |
| +} |
| + |
| +/** |
| + * Gets the path string for [entry], which can either already be a path string, |
| + * or be a [File] or [Directory]. Allows working generically with "file-like" |
| + * objects. |
| + */ |
| +String _getPath(entry) { |
| + if (entry is String) return entry; |
| + if (entry is File) return entry.name; |
| + if (entry is Directory) return entry.path; |
| + throw 'Entry $entry is not a supported type.'; |
| +} |
| + |
| +/** |
| + * Gets a [Directory] for [entry], which can either already be one, or be a |
| + * [String]. |
| + */ |
| +Directory _getDirectory(entry) { |
| + if (entry is Directory) return entry; |
| + return new Directory(entry); |
| +} |