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

Unified Diff: mojo/public/dart/third_party/args/lib/command_runner.dart

Issue 1346773002: Stop running pub get at gclient sync time and fix build bugs (Closed) Base URL: git@github.com:domokit/mojo.git@master
Patch Set: Created 5 years, 3 months 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 side-by-side diff with in-line comments
Download patch
Index: mojo/public/dart/third_party/args/lib/command_runner.dart
diff --git a/mojo/public/dart/third_party/args/lib/command_runner.dart b/mojo/public/dart/third_party/args/lib/command_runner.dart
new file mode 100644
index 0000000000000000000000000000000000000000..d728cb0269402fd7c160e06c3aa9595f2495e0e1
--- /dev/null
+++ b/mojo/public/dart/third_party/args/lib/command_runner.dart
@@ -0,0 +1,382 @@
+// Copyright (c) 2014, 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.
+
+library args.command_runner;
+
+import 'dart:async';
+import 'dart:collection';
+import 'dart:math' as math;
+
+import 'src/arg_parser.dart';
+import 'src/arg_results.dart';
+import 'src/help_command.dart';
+import 'src/usage_exception.dart';
+import 'src/utils.dart';
+
+export 'src/usage_exception.dart';
+
+/// A class for invoking [Commands] based on raw command-line arguments.
+class CommandRunner {
+ /// The name of the executable being run.
+ ///
+ /// Used for error reporting and [usage].
+ final String executableName;
+
+ /// A short description of this executable.
+ final String description;
+
+ /// A single-line template for how to invoke this executable.
+ ///
+ /// Defaults to "$executableName <command> [arguments]". Subclasses can
+ /// override this for a more specific template.
+ String get invocation => "$executableName <command> [arguments]";
+
+ /// Generates a string displaying usage information for the executable.
+ ///
+ /// This includes usage for the global arguments as well as a list of
+ /// top-level commands.
+ String get usage => "$description\n\n$_usageWithoutDescription";
+
+ /// An optional footer for [usage].
+ ///
+ /// If a subclass overrides this to return a string, it will automatically be
+ /// added to the end of [usage].
+ final String usageFooter = null;
+
+ /// Returns [usage] with [description] removed from the beginning.
+ String get _usageWithoutDescription {
+ var usage = '''
+Usage: $invocation
+
+Global options:
+${argParser.usage}
+
+${_getCommandUsage(_commands)}
+
+Run "$executableName help <command>" for more information about a command.''';
+
+ if (usageFooter != null) usage += "\n$usageFooter";
+ return usage;
+ }
+
+ /// An unmodifiable view of all top-level commands defined for this runner.
+ Map<String, Command> get commands => new UnmodifiableMapView(_commands);
+ final _commands = new Map<String, Command>();
+
+ /// The top-level argument parser.
+ ///
+ /// Global options should be registered with this parser; they'll end up
+ /// available via [Command.globalResults]. Commands should be registered with
+ /// [addCommand] rather than directly on the parser.
+ final argParser = new ArgParser();
+
+ CommandRunner(this.executableName, this.description) {
+ argParser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Print this usage information.');
+ addCommand(new HelpCommand());
+ }
+
+ /// Prints the usage information for this runner.
+ ///
+ /// This is called internally by [run] and can be overridden by subclasses to
+ /// control how output is displayed or integrate with a logging system.
+ void printUsage() => print(usage);
+
+ /// Throws a [UsageException] with [message].
+ void usageException(String message) =>
+ throw new UsageException(message, _usageWithoutDescription);
+
+ /// Adds [Command] as a top-level command to this runner.
+ void addCommand(Command command) {
+ var names = [command.name]..addAll(command.aliases);
+ for (var name in names) {
+ _commands[name] = command;
+ argParser.addCommand(name, command.argParser);
+ }
+ command._runner = this;
+ }
+
+ /// Parses [args] and invokes [Command.run] on the chosen command.
+ ///
+ /// This always returns a [Future] in case the command is asynchronous. The
+ /// [Future] will throw a [UsageError] if [args] was invalid.
+ Future run(Iterable<String> args) =>
+ new Future.sync(() => runCommand(parse(args)));
+
+ /// Parses [args] and returns the result, converting a [FormatException] to a
+ /// [UsageException].
+ ///
+ /// This is notionally a protected method. It may be overridden or called from
+ /// subclasses, but it shouldn't be called externally.
+ ArgResults parse(Iterable<String> args) {
+ try {
+ // TODO(nweiz): if arg parsing fails for a command, print that command's
+ // usage, not the global usage.
+ return argParser.parse(args);
+ } on FormatException catch (error) {
+ usageException(error.message);
+ }
+ }
+
+ /// Runs the command specified by [topLevelResults].
+ ///
+ /// This is notionally a protected method. It may be overridden or called from
+ /// subclasses, but it shouldn't be called externally.
+ ///
+ /// It's useful to override this to handle global flags and/or wrap the entire
+ /// command in a block. For example, you might handle the `--verbose` flag
+ /// here to enable verbose logging before running the command.
+ Future runCommand(ArgResults topLevelResults) {
+ return new Future.sync(() {
+ var argResults = topLevelResults;
+ var commands = _commands;
+ var command;
+ var commandString = executableName;
+
+ while (commands.isNotEmpty) {
+ if (argResults.command == null) {
+ if (argResults.rest.isEmpty) {
+ if (command == null) {
+ // No top-level command was chosen.
+ printUsage();
+ return new Future.value();
+ }
+
+ command.usageException('Missing subcommand for "$commandString".');
+ } else {
+ if (command == null) {
+ usageException(
+ 'Could not find a command named "${argResults.rest[0]}".');
+ }
+
+ command.usageException('Could not find a subcommand named '
+ '"${argResults.rest[0]}" for "$commandString".');
+ }
+ }
+
+ // Step into the command.
+ argResults = argResults.command;
+ command = commands[argResults.name];
+ command._globalResults = topLevelResults;
+ command._argResults = argResults;
+ commands = command._subcommands;
+ commandString += " ${argResults.name}";
+
+ if (argResults['help']) {
+ command.printUsage();
+ return new Future.value();
+ }
+ }
+
+ // Make sure there aren't unexpected arguments.
+ if (!command.takesArguments && argResults.rest.isNotEmpty) {
+ command.usageException(
+ 'Command "${argResults.name}" does not take any arguments.');
+ }
+
+ return command.run();
+ });
+ }
+}
+
+/// A single command.
+///
+/// A command is known as a "leaf command" if it has no subcommands and is meant
+/// to be run. Leaf commands must override [run].
+///
+/// A command with subcommands is known as a "branch command" and cannot be run
+/// itself. It should call [addSubcommand] (often from the constructor) to
+/// register subcommands.
+abstract class Command {
+ /// The name of this command.
+ String get name;
+
+ /// A short description of this command.
+ String get description;
+
+ /// A single-line template for how to invoke this command (e.g. `"pub get
+ /// [package]"`).
+ String get invocation {
+ var parents = [name];
+ for (var command = parent; command != null; command = command.parent) {
+ parents.add(command.name);
+ }
+ parents.add(runner.executableName);
+
+ var invocation = parents.reversed.join(" ");
+ return _subcommands.isNotEmpty
+ ? "$invocation <subcommand> [arguments]"
+ : "$invocation [arguments]";
+ }
+
+ /// The command's parent command, if this is a subcommand.
+ ///
+ /// This will be `null` until [Command.addSubcommmand] has been called with
+ /// this command.
+ Command get parent => _parent;
+ Command _parent;
+
+ /// The command runner for this command.
+ ///
+ /// This will be `null` until [CommandRunner.addCommand] has been called with
+ /// this command or one of its parents.
+ CommandRunner get runner {
+ if (parent == null) return _runner;
+ return parent.runner;
+ }
+ CommandRunner _runner;
+
+ /// The parsed global argument results.
+ ///
+ /// This will be `null` until just before [Command.run] is called.
+ ArgResults get globalResults => _globalResults;
+ ArgResults _globalResults;
+
+ /// The parsed argument results for this command.
+ ///
+ /// This will be `null` until just before [Command.run] is called.
+ ArgResults get argResults => _argResults;
+ ArgResults _argResults;
+
+ /// The argument parser for this command.
+ ///
+ /// Options for this command should be registered with this parser (often in
+ /// the constructor); they'll end up available via [argResults]. Subcommands
+ /// should be registered with [addSubcommand] rather than directly on the
+ /// parser.
+ final argParser = new ArgParser();
+
+ /// Generates a string displaying usage information for this command.
+ ///
+ /// This includes usage for the command's arguments as well as a list of
+ /// subcommands, if there are any.
+ String get usage => "$description\n\n$_usageWithoutDescription";
+
+ /// An optional footer for [usage].
+ ///
+ /// If a subclass overrides this to return a string, it will automatically be
+ /// added to the end of [usage].
+ final String usageFooter = null;
+
+ /// Returns [usage] with [description] removed from the beginning.
+ String get _usageWithoutDescription {
+ var buffer = new StringBuffer()
+ ..writeln('Usage: $invocation')
+ ..writeln(argParser.usage);
+
+ if (_subcommands.isNotEmpty) {
+ buffer.writeln();
+ buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true));
+ }
+
+ buffer.writeln();
+ buffer.write('Run "${runner.executableName} help" to see global options.');
+
+ if (usageFooter != null) {
+ buffer.writeln();
+ buffer.write(usageFooter);
+ }
+
+ return buffer.toString();
+ }
+
+ /// An unmodifiable view of all sublevel commands of this command.
+ Map<String, Command> get subcommands => new UnmodifiableMapView(_subcommands);
+ final _subcommands = new Map<String, Command>();
+
+ /// Whether or not this command should be hidden from help listings.
+ ///
+ /// This is intended to be overridden by commands that want to mark themselves
+ /// hidden.
+ ///
+ /// By default, leaf commands are always visible. Branch commands are visible
+ /// as long as any of their leaf commands are visible.
+ bool get hidden {
+ // Leaf commands are visible by default.
+ if (_subcommands.isEmpty) return false;
+
+ // Otherwise, a command is hidden if all of its subcommands are.
+ return _subcommands.values.every((subcommand) => subcommand.hidden);
+ }
+
+ /// Whether or not this command takes positional arguments in addition to
+ /// options.
+ ///
+ /// If false, [CommandRunner.run] will throw a [UsageException] if arguments
+ /// are provided. Defaults to true.
+ ///
+ /// This is intended to be overridden by commands that don't want to receive
+ /// arguments. It has no effect for branch commands.
+ final takesArguments = true;
+
+ /// Alternate names for this command.
+ ///
+ /// These names won't be used in the documentation, but they will work when
+ /// invoked on the command line.
+ ///
+ /// This is intended to be overridden.
+ final aliases = const <String>[];
+
+ Command() {
+ argParser.addFlag('help',
+ abbr: 'h', negatable: false, help: 'Print this usage information.');
+ }
+
+ /// Runs this command.
+ ///
+ /// If this returns a [Future], [CommandRunner.run] won't complete until the
+ /// returned [Future] does. Otherwise, the return value is ignored.
+ run() {
+ throw new UnimplementedError("Leaf command $this must implement run().");
+ }
+
+ /// Adds [Command] as a subcommand of this.
+ void addSubcommand(Command command) {
+ var names = [command.name]..addAll(command.aliases);
+ for (var name in names) {
+ _subcommands[name] = command;
+ argParser.addCommand(name, command.argParser);
+ }
+ command._parent = this;
+ }
+
+ /// Prints the usage information for this command.
+ ///
+ /// This is called internally by [run] and can be overridden by subclasses to
+ /// control how output is displayed or integrate with a logging system.
+ void printUsage() => print(usage);
+
+ /// Throws a [UsageException] with [message].
+ void usageException(String message) =>
+ throw new UsageException(message, _usageWithoutDescription);
+}
+
+/// Returns a string representation of [commands] fit for use in a usage string.
+///
+/// [isSubcommand] indicates whether the commands should be called "commands" or
+/// "subcommands".
+String _getCommandUsage(Map<String, Command> commands,
+ {bool isSubcommand: false}) {
+ // Don't include aliases.
+ var names =
+ commands.keys.where((name) => !commands[name].aliases.contains(name));
+
+ // Filter out hidden ones, unless they are all hidden.
+ var visible = names.where((name) => !commands[name].hidden);
+ if (visible.isNotEmpty) names = visible;
+
+ // Show the commands alphabetically.
+ names = names.toList()..sort();
+ var length = names.map((name) => name.length).reduce(math.max);
+
+ var buffer =
+ new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:');
+ for (var name in names) {
+ buffer.writeln();
+ buffer.write(' ${padRight(name, length)} '
+ '${commands[name].description.split("\n").first}');
+ }
+
+ return buffer.toString();
+}
« no previous file with comments | « mojo/public/dart/third_party/args/lib/args.dart ('k') | mojo/public/dart/third_party/args/lib/src/arg_parser.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698