diff --git a/packages/flutter_tools/lib/src/base/flags.dart b/packages/flutter_tools/lib/src/base/flags.dart new file mode 100644 index 0000000000..8007266893 --- /dev/null +++ b/packages/flutter_tools/lib/src/base/flags.dart @@ -0,0 +1,67 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:args/args.dart'; + +import 'context.dart'; + +/// command-line flags and options that were specified during the invocation of +/// the Flutter tool. +Flags get flags => context?.getVariable(Flags) ?? const _EmptyFlags(); + +/// Encapsulation of the command-line flags and options that were specified +/// during the invocation of the Flutter tool. +/// +/// An instance of this class is set into the [AppContext] upon invocation of +/// the Flutter tool (immediately after the arguments have been parsed in +/// [FlutterCommandRunner]) and is available via the [flags] global property. +class Flags { + Flags(this._globalResults) { + assert(_globalResults != null); + } + + final ArgResults _globalResults; + + /// Gets the value of the specified command-line flag/option that was set + /// during the invocation of the Flutter tool. + /// + /// This will first search for flags that are specific to the command and will + /// fall back to global flags. + /// + /// If a flag has a default value and the user did not explicitly specify a + /// value on the command-line, this will return the default value. + /// + /// If the specified flag is not defined or was not specified and had no + /// default, then this will return `null`. + dynamic operator [](String key) { + final ArgResults commandResults = _globalResults.command; + final Iterable options = commandResults?.options; + if (options != null && options.contains(key)) + return commandResults[key]; + else if (_globalResults.options.contains(key)) + return _globalResults[key]; + return null; + } + + /// `true` iff the given flag/option was either explicitly specified by the + /// user at the command-line or it was defined to have a default value. + bool contains(String key) { + final ArgResults commandResults = _globalResults.command; + final Iterable options = commandResults?.options; + return (options != null && options.contains(key)) || _globalResults.options.contains(key); + } +} + +class _EmptyFlags implements Flags { + const _EmptyFlags(); + + @override + ArgResults get _globalResults => null; + + @override + String operator [](String key) => null; + + @override + bool contains(String key) => false; +} diff --git a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart index 7f6f98c0fd..9b3b49e29a 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command_runner.dart @@ -13,6 +13,7 @@ import '../artifacts.dart'; import '../base/common.dart'; import '../base/context.dart'; import '../base/file_system.dart'; +import '../base/flags.dart'; import '../base/logger.dart'; import '../base/os.dart'; import '../base/platform.dart'; @@ -163,6 +164,8 @@ class FlutterCommandRunner extends CommandRunner { @override Future runCommand(ArgResults globalResults) async { + context.setVariable(Flags, new Flags(globalResults)); + // Check for verbose. if (globalResults['verbose']) { // Override the logger. diff --git a/packages/flutter_tools/test/base/flags_test.dart b/packages/flutter_tools/test/base/flags_test.dart new file mode 100644 index 0000000000..9779d3f165 --- /dev/null +++ b/packages/flutter_tools/test/base/flags_test.dart @@ -0,0 +1,99 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:async'; + +import 'package:flutter_tools/src/base/flags.dart'; +import 'package:flutter_tools/src/cache.dart'; +import 'package:flutter_tools/src/runner/flutter_command.dart'; +import 'package:test/test.dart'; + +import '../src/common.dart'; +import '../src/context.dart'; + +typedef FutureOr _TestMethod(); + +void main() { + Cache.disableLocking(); + + Future runCommand(Iterable flags, _TestMethod testMethod) async { + final List args = ['test']..addAll(flags); + final _TestCommand command = new _TestCommand(testMethod); + await createTestCommandRunner(command).run(args); + } + + testUsingContext('runCommand works as expected', () async { + bool testRan = false; + await runCommand([], () { + testRan = true; + }); + expect(testRan, isTrue); + }); + + group('flags', () { + test('returns no-op flags when not inside Flutter runner', () { + expect(flags, isNotNull); + expect(flags['foo'], isNull); + }); + + testUsingContext('returns null for undefined flags', () async { + await runCommand([], () { + expect(flags['undefined-flag'], isNull); + }); + }); + + testUsingContext('picks up default values', () async { + await runCommand([], () { + expect(flags['verbose'], isFalse); + expect(flags['flag-defaults-to-false'], isFalse); + expect(flags['flag-defaults-to-true'], isTrue); + expect(flags['option-defaults-to-foo'], 'foo'); + }); + }); + + testUsingContext('returns null for flags with no default values', () async { + await runCommand([], () { + expect(flags['device-id'], isNull); + expect(flags['option-no-default'], isNull); + }); + }); + + testUsingContext('picks up explicit values', () async { + await runCommand([ + '--verbose', + '--flag-defaults-to-false', + '--option-no-default=explicit', + '--option-defaults-to-foo=qux', + ], () { + expect(flags['verbose'], isTrue); + expect(flags['flag-defaults-to-false'], isTrue); + expect(flags['option-no-default'], 'explicit'); + expect(flags['option-defaults-to-foo'], 'qux'); + }); + }); + }); +} + +class _TestCommand extends FlutterCommand { + _TestCommand(this.testMethod) { + argParser.addFlag('flag-defaults-to-false', defaultsTo: false); + argParser.addFlag('flag-defaults-to-true', defaultsTo: true); + argParser.addOption('option-no-default'); + argParser.addOption('option-defaults-to-foo', defaultsTo: 'foo'); + } + + final _TestMethod testMethod; + + @override + String get name => 'test'; + + @override + String get description => 'runs a test method'; + + @override + Future runCommand() async { + await testMethod(); + return null; + } +}