Migrate flutter_command to null safety (#92871)

This commit is contained in:
Jenn Magder 2021-11-02 14:31:35 -07:00 committed by GitHub
parent 1980492997
commit a4e7b5994c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 153 additions and 161 deletions

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:file/file.dart'; import 'package:file/file.dart';
@ -70,7 +68,7 @@ class FlutterCommandResult {
/// Optional data that can be appended to the timing event. /// Optional data that can be appended to the timing event.
/// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#timingLabel /// https://developers.google.com/analytics/devguides/collection/analyticsjs/field-reference#timingLabel
/// Do not add PII. /// Do not add PII.
final List<String> timingLabelParts; final List<String>? timingLabelParts;
/// Optional epoch time when the command's non-interactive wait time is /// Optional epoch time when the command's non-interactive wait time is
/// complete during the command's execution. Use to measure user perceivable /// complete during the command's execution. Use to measure user perceivable
@ -78,7 +76,7 @@ class FlutterCommandResult {
/// ///
/// [FlutterCommand] will automatically measure and report the command's /// [FlutterCommand] will automatically measure and report the command's
/// complete time if not overridden. /// complete time if not overridden.
final DateTime endTimeOverride; final DateTime? endTimeOverride;
@override @override
String toString() { String toString() {
@ -92,7 +90,6 @@ class FlutterCommandResult {
case ExitStatus.killed: case ExitStatus.killed:
return 'killed'; return 'killed';
} }
return null; // dead code, remove with null safety migration
} }
} }
@ -131,7 +128,7 @@ abstract class FlutterCommand extends Command<void> {
/// The currently executing command (or sub-command). /// The currently executing command (or sub-command).
/// ///
/// Will be `null` until the top-most command has begun execution. /// Will be `null` until the top-most command has begun execution.
static FlutterCommand get current => context.get<FlutterCommand>(); static FlutterCommand? get current => context.get<FlutterCommand>();
/// The option name for a custom observatory port. /// The option name for a custom observatory port.
static const String observatoryPortOption = 'observatory-port'; static const String observatoryPortOption = 'observatory-port';
@ -168,7 +165,7 @@ abstract class FlutterCommand extends Command<void> {
); );
@override @override
FlutterCommandRunner get runner => super.runner as FlutterCommandRunner; FlutterCommandRunner? get runner => super.runner as FlutterCommandRunner?;
bool _requiresPubspecYaml = false; bool _requiresPubspecYaml = false;
@ -197,7 +194,7 @@ abstract class FlutterCommand extends Command<void> {
_requiresPubspecYaml = true; _requiresPubspecYaml = true;
} }
void usesWebOptions({ @required bool verboseHelp }) { void usesWebOptions({ required bool verboseHelp }) {
argParser.addOption('web-hostname', argParser.addOption('web-hostname',
defaultsTo: 'localhost', defaultsTo: 'localhost',
help: help:
@ -208,7 +205,6 @@ abstract class FlutterCommand extends Command<void> {
hide: !verboseHelp, hide: !verboseHelp,
); );
argParser.addOption('web-port', argParser.addOption('web-port',
defaultsTo: null,
help: 'The host port to serve the web application from. If not provided, the tool ' help: 'The host port to serve the web application from. If not provided, the tool '
'will select a random open port on the host.', 'will select a random open port on the host.',
hide: !verboseHelp, hide: !verboseHelp,
@ -241,13 +237,11 @@ abstract class FlutterCommand extends Command<void> {
hide: !verboseHelp, hide: !verboseHelp,
); );
argParser.addFlag('web-allow-expose-url', argParser.addFlag('web-allow-expose-url',
defaultsTo: false,
help: 'Enables daemon-to-editor requests (app.exposeUrl) for exposing URLs ' help: 'Enables daemon-to-editor requests (app.exposeUrl) for exposing URLs '
'when running on remote machines.', 'when running on remote machines.',
hide: !verboseHelp, hide: !verboseHelp,
); );
argParser.addFlag('web-run-headless', argParser.addFlag('web-run-headless',
defaultsTo: false,
help: 'Launches the browser in headless mode. Currently only Chrome ' help: 'Launches the browser in headless mode. Currently only Chrome '
'supports this option.', 'supports this option.',
hide: !verboseHelp, hide: !verboseHelp,
@ -278,11 +272,12 @@ abstract class FlutterCommand extends Command<void> {
} }
String get targetFile { String get targetFile {
if (argResults.wasParsed('target')) { if (argResults?.wasParsed('target') == true) {
return stringArg('target'); return stringArg('target')!;
} }
if (argResults.rest.isNotEmpty) { final List<String>? rest = argResults?.rest;
return argResults.rest.first; if (rest != null && rest.isNotEmpty) {
return rest.first;
} }
return bundle.defaultMainPath; return bundle.defaultMainPath;
} }
@ -290,12 +285,12 @@ abstract class FlutterCommand extends Command<void> {
/// Path to the Dart's package config file. /// Path to the Dart's package config file.
/// ///
/// This can be overridden by some of its subclasses. /// This can be overridden by some of its subclasses.
String get packagesPath => globalResults['packages'] as String; String? get packagesPath => globalResults?['packages'] as String?;
/// The value of the `--filesystem-scheme` argument. /// The value of the `--filesystem-scheme` argument.
/// ///
/// This can be overridden by some of its subclasses. /// This can be overridden by some of its subclasses.
String get fileSystemScheme => String? get fileSystemScheme =>
argParser.options.containsKey(FlutterOptions.kFileSystemScheme) argParser.options.containsKey(FlutterOptions.kFileSystemScheme)
? stringArg(FlutterOptions.kFileSystemScheme) ? stringArg(FlutterOptions.kFileSystemScheme)
: null; : null;
@ -303,7 +298,7 @@ abstract class FlutterCommand extends Command<void> {
/// The values of the `--filesystem-root` argument. /// The values of the `--filesystem-root` argument.
/// ///
/// This can be overridden by some of its subclasses. /// This can be overridden by some of its subclasses.
List<String> get fileSystemRoots => List<String>? get fileSystemRoots =>
argParser.options.containsKey(FlutterOptions.kFileSystemRoot) argParser.options.containsKey(FlutterOptions.kFileSystemRoot)
? stringsArg(FlutterOptions.kFileSystemRoot) ? stringsArg(FlutterOptions.kFileSystemRoot)
: null; : null;
@ -320,7 +315,7 @@ abstract class FlutterCommand extends Command<void> {
/// ///
/// The `hide` argument indicates whether or not to hide these options when /// The `hide` argument indicates whether or not to hide these options when
/// the user asks for help. /// the user asks for help.
void usesFilesystemOptions({ @required bool hide }) { void usesFilesystemOptions({ required bool hide }) {
argParser argParser
..addOption('output-dill', ..addOption('output-dill',
hide: hide, hide: hide,
@ -342,7 +337,7 @@ abstract class FlutterCommand extends Command<void> {
} }
/// Adds options for connecting to the Dart VM observatory port. /// Adds options for connecting to the Dart VM observatory port.
void usesPortOptions({ @required bool verboseHelp }) { void usesPortOptions({ required bool verboseHelp }) {
argParser.addOption(observatoryPortOption, argParser.addOption(observatoryPortOption,
help: '(deprecated; use host-vmservice-port instead) ' help: '(deprecated; use host-vmservice-port instead) '
'Listen to the given port for an observatory debugger connection.\n' 'Listen to the given port for an observatory debugger connection.\n'
@ -364,7 +359,7 @@ abstract class FlutterCommand extends Command<void> {
_usesPortOption = true; _usesPortOption = true;
} }
void addDevToolsOptions({@required bool verboseHelp}) { void addDevToolsOptions({required bool verboseHelp}) {
argParser.addFlag( argParser.addFlag(
kEnableDevTools, kEnableDevTools,
hide: !verboseHelp, hide: !verboseHelp,
@ -382,7 +377,7 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void addDdsOptions({@required bool verboseHelp}) { void addDdsOptions({required bool verboseHelp}) {
argParser.addOption('dds-port', argParser.addOption('dds-port',
help: 'When this value is provided, the Dart Development Service (DDS) will be ' help: 'When this value is provided, the Dart Development Service (DDS) will be '
'bound to the provided port.\n' 'bound to the provided port.\n'
@ -407,55 +402,61 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
bool _ddsEnabled; late final bool enableDds = () {
bool get enableDds { bool ddsEnabled = false;
if (_ddsEnabled == null) { if (argResults?.wasParsed('disable-dds') == true) {
if (argResults.wasParsed('disable-dds')) { if (argResults?.wasParsed('dds') == true) {
if (argResults.wasParsed('dds')) { throwToolExit(
throwToolExit('The "--[no-]dds" and "--[no-]disable-dds" arguments are mutually exclusive. Only specify "--[no-]dds".'); 'The "--[no-]dds" and "--[no-]disable-dds" arguments are mutually exclusive. Only specify "--[no-]dds".');
}
_ddsEnabled = !boolArg('disable-dds');
// TODO(ianh): enable the following code once google3 is migrated away from --disable-dds (and add test to flutter_command_test.dart)
if (false) { // ignore: dead_code
if (_ddsEnabled) {
globals.printError('${globals.logger.terminal.warningMark} The "--no-disable-dds" argument is deprecated and redundant, and should be omitted.');
} else {
globals.printError('${globals.logger.terminal.warningMark} The "--disable-dds" argument is deprecated. Use "--no-dds" instead.');
}
}
} else {
_ddsEnabled = boolArg('dds');
} }
ddsEnabled = !boolArg('disable-dds');
// TODO(ianh): enable the following code once google3 is migrated away from --disable-dds (and add test to flutter_command_test.dart)
if (false) { // ignore: dead_code
if (ddsEnabled) {
globals.printError('${globals.logger.terminal
.warningMark} The "--no-disable-dds" argument is deprecated and redundant, and should be omitted.');
} else {
globals.printError('${globals.logger.terminal
.warningMark} The "--disable-dds" argument is deprecated. Use "--no-dds" instead.');
}
}
} else {
ddsEnabled = boolArg('dds');
} }
return _ddsEnabled; return ddsEnabled;
} }();
bool get _hostVmServicePortProvided => argResults.wasParsed('observatory-port') || bool get _hostVmServicePortProvided => argResults?.wasParsed('observatory-port') == true ||
argResults.wasParsed('host-vmservice-port'); argResults?.wasParsed('host-vmservice-port') == true;
int _tryParseHostVmservicePort() { int _tryParseHostVmservicePort() {
final String? observatoryPort = stringArg('observatory-port');
final String? hostPort = stringArg('host-vmservice-port');
if (observatoryPort == null && hostPort == null) {
throwToolExit('Invalid port for `--observatory-port/--host-vmservice-port`');
}
try { try {
return int.parse(stringArg('observatory-port') ?? stringArg('host-vmservice-port')); return int.parse((observatoryPort ?? hostPort)!);
} on FormatException catch (error) { } on FormatException catch (error) {
throwToolExit('Invalid port for `--observatory-port/--host-vmservice-port`: $error'); throwToolExit('Invalid port for `--observatory-port/--host-vmservice-port`: $error');
} }
} }
int get ddsPort { int get ddsPort {
if (!argResults.wasParsed('dds-port') && _hostVmServicePortProvided) { if (argResults?.wasParsed('dds-port') != true && _hostVmServicePortProvided) {
// If an explicit DDS port is _not_ provided, use the host-vmservice-port for DDS. // If an explicit DDS port is _not_ provided, use the host-vmservice-port for DDS.
return _tryParseHostVmservicePort(); return _tryParseHostVmservicePort();
} else if (argResults.wasParsed('dds-port')) { } else if (argResults?.wasParsed('dds-port') == true) {
// If an explicit DDS port is provided, use dds-port for DDS. // If an explicit DDS port is provided, use dds-port for DDS.
return int.tryParse(stringArg('dds-port')) ?? 0; return int.tryParse(stringArg('dds-port')!) ?? 0;
} }
// Otherwise, DDS can bind to a random port. // Otherwise, DDS can bind to a random port.
return 0; return 0;
} }
Uri get devToolsServerAddress { Uri? get devToolsServerAddress {
if (argResults.wasParsed(kDevToolsServerAddress)) { if (argResults?.wasParsed(kDevToolsServerAddress) == true) {
final Uri uri = Uri.tryParse(stringArg(kDevToolsServerAddress)); final Uri? uri = Uri.tryParse(stringArg(kDevToolsServerAddress)!);
if (uri != null && uri.host.isNotEmpty && uri.port != 0) { if (uri != null && uri.host.isNotEmpty && uri.port != 0) {
return uri; return uri;
} }
@ -470,19 +471,19 @@ abstract class FlutterCommand extends Command<void> {
/// specified. /// specified.
/// ///
/// If no port is set, returns null. /// If no port is set, returns null.
int get hostVmservicePort { int? get hostVmservicePort {
if (!_usesPortOption || !_hostVmServicePortProvided) { if (!_usesPortOption || !_hostVmServicePortProvided) {
return null; return null;
} }
if (argResults.wasParsed('observatory-port') && if (argResults?.wasParsed('observatory-port') == true &&
argResults.wasParsed('host-vmservice-port')) { argResults?.wasParsed('host-vmservice-port') == true) {
throwToolExit('Only one of "--observatory-port" and ' throwToolExit('Only one of "--observatory-port" and '
'"--host-vmservice-port" may be specified.'); '"--host-vmservice-port" may be specified.');
} }
// If DDS is enabled and no explicit DDS port is provided, use the // If DDS is enabled and no explicit DDS port is provided, use the
// host-vmservice-port for DDS instead and bind the VM service to a random // host-vmservice-port for DDS instead and bind the VM service to a random
// port. // port.
if (enableDds && !argResults.wasParsed('dds-port')) { if (enableDds && argResults?.wasParsed('dds-port') != true) {
return null; return null;
} }
return _tryParseHostVmservicePort(); return _tryParseHostVmservicePort();
@ -491,12 +492,13 @@ abstract class FlutterCommand extends Command<void> {
/// Gets the vmservice port provided to in the 'device-vmservice-port' option. /// Gets the vmservice port provided to in the 'device-vmservice-port' option.
/// ///
/// If no port is set, returns null. /// If no port is set, returns null.
int get deviceVmservicePort { int? get deviceVmservicePort {
if (!_usesPortOption || argResults['device-vmservice-port'] == null) { final String? devicePort = stringArg('device-vmservice-port');
if (!_usesPortOption || devicePort == null) {
return null; return null;
} }
try { try {
return int.parse(stringArg('device-vmservice-port')); return int.parse(devicePort);
} on FormatException catch (error) { } on FormatException catch (error) {
throwToolExit('Invalid port for `--device-vmservice-port`: $error'); throwToolExit('Invalid port for `--device-vmservice-port`: $error');
} }
@ -504,7 +506,6 @@ abstract class FlutterCommand extends Command<void> {
void addPublishPort({ bool enabledByDefault = true, bool verboseHelp = false }) { void addPublishPort({ bool enabledByDefault = true, bool verboseHelp = false }) {
argParser.addFlag('publish-port', argParser.addFlag('publish-port',
negatable: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Publish the VM service port over mDNS. Disable to prevent the ' help: 'Publish the VM service port over mDNS. Disable to prevent the '
'local network permission app dialog in debug and profile build modes (iOS devices only).', 'local network permission app dialog in debug and profile build modes (iOS devices only).',
@ -514,7 +515,7 @@ abstract class FlutterCommand extends Command<void> {
bool get disablePortPublication => !boolArg('publish-port'); bool get disablePortPublication => !boolArg('publish-port');
void usesIpv6Flag({@required bool verboseHelp}) { void usesIpv6Flag({required bool verboseHelp}) {
argParser.addFlag(ipv6Flag, argParser.addFlag(ipv6Flag,
negatable: false, negatable: false,
help: 'Binds to IPv6 localhost instead of IPv4 when the flutter tool ' help: 'Binds to IPv6 localhost instead of IPv4 when the flutter tool '
@ -525,7 +526,7 @@ abstract class FlutterCommand extends Command<void> {
_usesIpv6Flag = true; _usesIpv6Flag = true;
} }
bool get ipv6 => _usesIpv6Flag ? boolArg('ipv6') : null; bool? get ipv6 => _usesIpv6Flag ? boolArg('ipv6') : null;
void usesBuildNumberOption() { void usesBuildNumberOption() {
argParser.addOption('build-number', argParser.addOption('build-number',
@ -592,22 +593,20 @@ abstract class FlutterCommand extends Command<void> {
/// Whether this command should report null safety analytics. /// Whether this command should report null safety analytics.
bool get reportNullSafety => false; bool get reportNullSafety => false;
Duration get deviceDiscoveryTimeout { late final Duration? deviceDiscoveryTimeout = () {
if (_deviceDiscoveryTimeout == null if (argResults?.options.contains(FlutterOptions.kDeviceTimeout) == true
&& argResults.options.contains(FlutterOptions.kDeviceTimeout) && argResults?.wasParsed(FlutterOptions.kDeviceTimeout) == true) {
&& argResults.wasParsed(FlutterOptions.kDeviceTimeout)) { final int? timeoutSeconds = int.tryParse(stringArg(FlutterOptions.kDeviceTimeout)!);
final int timeoutSeconds = int.tryParse(stringArg(FlutterOptions.kDeviceTimeout));
if (timeoutSeconds == null) { if (timeoutSeconds == null) {
throwToolExit( 'Could not parse "--${FlutterOptions.kDeviceTimeout}" argument. It must be an integer.'); throwToolExit( 'Could not parse "--${FlutterOptions.kDeviceTimeout}" argument. It must be an integer.');
} }
_deviceDiscoveryTimeout = Duration(seconds: timeoutSeconds); return Duration(seconds: timeoutSeconds);
} }
return _deviceDiscoveryTimeout; return null;
} }();
Duration _deviceDiscoveryTimeout;
void addBuildModeFlags({ void addBuildModeFlags({
@required bool verboseHelp, required bool verboseHelp,
bool defaultToRelease = true, bool defaultToRelease = true,
bool excludeDebug = false, bool excludeDebug = false,
bool excludeRelease = false, bool excludeRelease = false,
@ -669,7 +668,7 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void addBundleSkSLPathOption({ @required bool hide }) { void addBundleSkSLPathOption({ required bool hide }) {
argParser.addOption(FlutterOptions.kBundleSkSLPathOption, argParser.addOption(FlutterOptions.kBundleSkSLPathOption,
help: 'A path to a file containing precompiled SkSL shaders generated ' help: 'A path to a file containing precompiled SkSL shaders generated '
'during "flutter run". These can be included in an application to ' 'during "flutter run". These can be included in an application to '
@ -680,26 +679,24 @@ abstract class FlutterCommand extends Command<void> {
} }
void addTreeShakeIconsFlag({ void addTreeShakeIconsFlag({
bool enabledByDefault bool? enabledByDefault
}) { }) {
argParser.addFlag('tree-shake-icons', argParser.addFlag('tree-shake-icons',
negatable: true,
defaultsTo: enabledByDefault defaultsTo: enabledByDefault
?? kIconTreeShakerEnabledDefault, ?? kIconTreeShakerEnabledDefault,
help: 'Tree shake icon fonts so that only glyphs used by the application remain.', help: 'Tree shake icon fonts so that only glyphs used by the application remain.',
); );
} }
void addShrinkingFlag({ @required bool verboseHelp }) { void addShrinkingFlag({ required bool verboseHelp }) {
argParser.addFlag('shrink', argParser.addFlag('shrink',
negatable: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'This flag has no effect. Code shrinking is always enabled in release builds. ' help: 'This flag has no effect. Code shrinking is always enabled in release builds. '
'To learn more, see: https://developer.android.com/studio/build/shrink-code' 'To learn more, see: https://developer.android.com/studio/build/shrink-code'
); );
} }
void addNullSafetyModeOptions({ @required bool hide }) { void addNullSafetyModeOptions({ required bool hide }) {
argParser.addFlag(FlutterOptions.kNullSafety, argParser.addFlag(FlutterOptions.kNullSafety,
help: help:
'Whether to override the inferred null safety mode. This allows null-safe ' 'Whether to override the inferred null safety mode. This allows null-safe '
@ -720,13 +717,12 @@ abstract class FlutterCommand extends Command<void> {
/// Enables support for the hidden options --extra-front-end-options and /// Enables support for the hidden options --extra-front-end-options and
/// --extra-gen-snapshot-options. /// --extra-gen-snapshot-options.
void usesExtraDartFlagOptions({ @required bool verboseHelp }) { void usesExtraDartFlagOptions({ required bool verboseHelp }) {
argParser.addMultiOption(FlutterOptions.kExtraFrontEndOptions, argParser.addMultiOption(FlutterOptions.kExtraFrontEndOptions,
aliases: <String>[ kExtraFrontEndOptions ], // supported for historical reasons aliases: <String>[ kExtraFrontEndOptions ], // supported for historical reasons
help: 'A comma-separated list of additional command line arguments that will be passed directly to the Dart front end. ' help: 'A comma-separated list of additional command line arguments that will be passed directly to the Dart front end. '
'For example, "--${FlutterOptions.kExtraFrontEndOptions}=--enable-experiment=nonfunction-type-aliases".', 'For example, "--${FlutterOptions.kExtraFrontEndOptions}=--enable-experiment=nonfunction-type-aliases".',
valueHelp: '--foo,--bar', valueHelp: '--foo,--bar',
splitCommas: true,
hide: !verboseHelp, hide: !verboseHelp,
); );
argParser.addMultiOption(FlutterOptions.kExtraGenSnapshotOptions, argParser.addMultiOption(FlutterOptions.kExtraGenSnapshotOptions,
@ -735,7 +731,6 @@ abstract class FlutterCommand extends Command<void> {
'(Only used in "--profile" or "--release" builds.) ' '(Only used in "--profile" or "--release" builds.) '
'For example, "--${FlutterOptions.kExtraGenSnapshotOptions}=--no-strip".', 'For example, "--${FlutterOptions.kExtraGenSnapshotOptions}=--no-strip".',
valueHelp: '--foo,--bar', valueHelp: '--foo,--bar',
splitCommas: true,
hide: !verboseHelp, hide: !verboseHelp,
); );
} }
@ -757,7 +752,7 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void addEnableExperimentation({ @required bool hide }) { void addEnableExperimentation({ required bool hide }) {
argParser.addMultiOption( argParser.addMultiOption(
FlutterOptions.kEnableExperiment, FlutterOptions.kEnableExperiment,
help: help:
@ -810,7 +805,7 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void usesInitializeFromDillOption({ @required bool hide }) { void usesInitializeFromDillOption({ required bool hide }) {
argParser.addOption(FlutterOptions.kInitializeFromDill, argParser.addOption(FlutterOptions.kInitializeFromDill,
help: 'Initializes the resident compiler with a specific kernel file instead of ' help: 'Initializes the resident compiler with a specific kernel file instead of '
'the default cached location.', 'the default cached location.',
@ -820,7 +815,6 @@ abstract class FlutterCommand extends Command<void> {
void addMultidexOption({ bool hide = false }) { void addMultidexOption({ bool hide = false }) {
argParser.addFlag('multidex', argParser.addFlag('multidex',
negatable: true,
defaultsTo: true, defaultsTo: true,
help: 'When enabled, indicates that the app should be built with multidex support. This ' help: 'When enabled, indicates that the app should be built with multidex support. This '
'flag adds the dependencies for multidex when the minimum android sdk is 20 or ' 'flag adds the dependencies for multidex when the minimum android sdk is 20 or '
@ -829,7 +823,7 @@ abstract class FlutterCommand extends Command<void> {
} }
/// Adds build options common to all of the desktop build commands. /// Adds build options common to all of the desktop build commands.
void addCommonDesktopBuildOptions({ @required bool verboseHelp }) { void addCommonDesktopBuildOptions({ required bool verboseHelp }) {
addBuildModeFlags(verboseHelp: verboseHelp); addBuildModeFlags(verboseHelp: verboseHelp);
addBuildPerformanceFile(hide: !verboseHelp); addBuildPerformanceFile(hide: !verboseHelp);
addBundleSkSLPathOption(hide: !verboseHelp); addBundleSkSLPathOption(hide: !verboseHelp);
@ -850,7 +844,7 @@ abstract class FlutterCommand extends Command<void> {
/// explicitly specified. /// explicitly specified.
/// ///
/// Use [getBuildMode] to obtain the actual effective build mode. /// Use [getBuildMode] to obtain the actual effective build mode.
BuildMode defaultBuildMode; BuildMode defaultBuildMode = BuildMode.debug;
BuildMode getBuildMode() { BuildMode getBuildMode() {
// No debug when _excludeDebug is true. // No debug when _excludeDebug is true.
@ -866,7 +860,7 @@ abstract class FlutterCommand extends Command<void> {
]; ];
if (modeFlags.where((bool flag) => flag).length > 1) { if (modeFlags.where((bool flag) => flag).length > 1) {
throw UsageException('Only one of "--debug", "--profile", "--jit-release", ' throw UsageException('Only one of "--debug", "--profile", "--jit-release", '
'or "--release" can be specified.', null); 'or "--release" can be specified.', '');
} }
if (debugResult) { if (debugResult) {
return BuildMode.debug; return BuildMode.debug;
@ -892,7 +886,7 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
void usesTrackWidgetCreation({ bool hasEffect = true, @required bool verboseHelp }) { void usesTrackWidgetCreation({ bool hasEffect = true, required bool verboseHelp }) {
argParser.addFlag( argParser.addFlag(
'track-widget-creation', 'track-widget-creation',
hide: !hasEffect && !verboseHelp, hide: !hasEffect && !verboseHelp,
@ -905,7 +899,6 @@ abstract class FlutterCommand extends Command<void> {
void usesAnalyzeSizeFlag() { void usesAnalyzeSizeFlag() {
argParser.addFlag( argParser.addFlag(
FlutterOptions.kAnalyzeSize, FlutterOptions.kAnalyzeSize,
defaultsTo: false,
help: 'Whether to produce additional profile information for artifact output size. ' help: 'Whether to produce additional profile information for artifact output size. '
'This flag is only supported on "--release" builds. When building for Android, a single ' 'This flag is only supported on "--release" builds. When building for Android, a single '
'ABI must be specified at a time with the "--target-platform" flag. When building for iOS, ' 'ABI must be specified at a time with the "--target-platform" flag. When building for iOS, '
@ -928,11 +921,11 @@ abstract class FlutterCommand extends Command<void> {
/// ///
/// Throws a [ToolExit] if the current set of options is not compatible with /// Throws a [ToolExit] if the current set of options is not compatible with
/// each other. /// each other.
Future<BuildInfo> getBuildInfo({ BuildMode forcedBuildMode, File forcedTargetFile }) async { Future<BuildInfo> getBuildInfo({ BuildMode? forcedBuildMode, File? forcedTargetFile }) async {
final bool trackWidgetCreation = argParser.options.containsKey('track-widget-creation') && final bool trackWidgetCreation = argParser.options.containsKey('track-widget-creation') &&
boolArg('track-widget-creation'); boolArg('track-widget-creation');
final String buildNumber = argParser.options.containsKey('build-number') final String? buildNumber = argParser.options.containsKey('build-number')
? stringArg('build-number') ? stringArg('build-number')
: null; : null;
@ -962,7 +955,7 @@ abstract class FlutterCommand extends Command<void> {
} }
} }
String codeSizeDirectory; String? codeSizeDirectory;
if (argParser.options.containsKey(FlutterOptions.kAnalyzeSize) && boolArg(FlutterOptions.kAnalyzeSize)) { if (argParser.options.containsKey(FlutterOptions.kAnalyzeSize) && boolArg(FlutterOptions.kAnalyzeSize)) {
Directory directory = globals.fsUtils.getUniqueDirectory( Directory directory = globals.fsUtils.getUniqueDirectory(
globals.fs.directory(getBuildDirectory()), globals.fs.directory(getBuildDirectory()),
@ -980,13 +973,13 @@ abstract class FlutterCommand extends Command<void> {
// Explicitly check for `true` and `false` so that `null` results in not // Explicitly check for `true` and `false` so that `null` results in not
// passing a flag. Examine the entrypoint file to determine if it // passing a flag. Examine the entrypoint file to determine if it
// is opted in or out. // is opted in or out.
final bool wasNullSafetyFlagParsed = argResults.wasParsed(FlutterOptions.kNullSafety); final bool wasNullSafetyFlagParsed = argResults?.wasParsed(FlutterOptions.kNullSafety) == true;
if (!wasNullSafetyFlagParsed && (argParser.options.containsKey('target') || forcedTargetFile != null)) { if (!wasNullSafetyFlagParsed && (argParser.options.containsKey('target') || forcedTargetFile != null)) {
final File entrypointFile = forcedTargetFile ?? globals.fs.file(targetFile); final File entrypointFile = forcedTargetFile ?? globals.fs.file(targetFile);
final LanguageVersion languageVersion = determineLanguageVersion( final LanguageVersion languageVersion = determineLanguageVersion(
entrypointFile, entrypointFile,
packageConfig.packageOf(entrypointFile.absolute.uri), packageConfig.packageOf(entrypointFile.absolute.uri),
Cache.flutterRoot, Cache.flutterRoot!,
); );
// Extra frontend options are only provided if explicitly // Extra frontend options are only provided if explicitly
// requested. // requested.
@ -1011,7 +1004,7 @@ abstract class FlutterCommand extends Command<void> {
final bool dartObfuscation = argParser.options.containsKey(FlutterOptions.kDartObfuscationOption) final bool dartObfuscation = argParser.options.containsKey(FlutterOptions.kDartObfuscationOption)
&& boolArg(FlutterOptions.kDartObfuscationOption); && boolArg(FlutterOptions.kDartObfuscationOption);
final String splitDebugInfoPath = argParser.options.containsKey(FlutterOptions.kSplitDebugInfoOption) final String? splitDebugInfoPath = argParser.options.containsKey(FlutterOptions.kSplitDebugInfoOption)
? stringArg(FlutterOptions.kSplitDebugInfoOption) ? stringArg(FlutterOptions.kSplitDebugInfoOption)
: null; : null;
@ -1037,10 +1030,10 @@ abstract class FlutterCommand extends Command<void> {
} }
final bool treeShakeIcons = argParser.options.containsKey('tree-shake-icons') final bool treeShakeIcons = argParser.options.containsKey('tree-shake-icons')
&& buildMode.isPrecompiled && buildMode.isPrecompiled == true
&& boolArg('tree-shake-icons'); && boolArg('tree-shake-icons');
final String bundleSkSLPath = argParser.options.containsKey(FlutterOptions.kBundleSkSLPathOption) final String? bundleSkSLPath = argParser.options.containsKey(FlutterOptions.kBundleSkSLPathOption)
? stringArg(FlutterOptions.kBundleSkSLPathOption) ? stringArg(FlutterOptions.kBundleSkSLPathOption)
: null; : null;
@ -1048,7 +1041,7 @@ abstract class FlutterCommand extends Command<void> {
throwToolExit('No SkSL shader bundle found at $bundleSkSLPath.'); throwToolExit('No SkSL shader bundle found at $bundleSkSLPath.');
} }
final String performanceMeasurementFile = argParser.options.containsKey(FlutterOptions.kPerformanceMeasurementFile) final String? performanceMeasurementFile = argParser.options.containsKey(FlutterOptions.kPerformanceMeasurementFile)
? stringArg(FlutterOptions.kPerformanceMeasurementFile) ? stringArg(FlutterOptions.kPerformanceMeasurementFile)
: null; : null;
@ -1057,7 +1050,7 @@ abstract class FlutterCommand extends Command<void> {
: <String>[]; : <String>[];
if (argParser.options.containsKey('web-renderer')) { if (argParser.options.containsKey('web-renderer')) {
dartDefines = updateDartDefines(dartDefines, stringArg('web-renderer')); dartDefines = updateDartDefines(dartDefines, stringArg('web-renderer')!);
} }
return BuildInfo(buildMode, return BuildInfo(buildMode,
@ -1065,10 +1058,10 @@ abstract class FlutterCommand extends Command<void> {
? stringArg('flavor') ? stringArg('flavor')
: null, : null,
trackWidgetCreation: trackWidgetCreation, trackWidgetCreation: trackWidgetCreation,
extraFrontEndOptions: extraFrontEndOptions?.isNotEmpty ?? false extraFrontEndOptions: extraFrontEndOptions.isNotEmpty
? extraFrontEndOptions ? extraFrontEndOptions
: null, : null,
extraGenSnapshotOptions: extraGenSnapshotOptions?.isNotEmpty ?? false extraGenSnapshotOptions: extraGenSnapshotOptions.isNotEmpty
? extraGenSnapshotOptions ? extraGenSnapshotOptions
: null, : null,
fileSystemRoots: fileSystemRoots, fileSystemRoots: fileSystemRoots,
@ -1102,10 +1095,10 @@ abstract class FlutterCommand extends Command<void> {
/// The path to send to Google Analytics. Return null here to disable /// The path to send to Google Analytics. Return null here to disable
/// tracking of the command. /// tracking of the command.
Future<String> get usagePath async { Future<String?> get usagePath async {
if (parent is FlutterCommand) { if (parent is FlutterCommand) {
final FlutterCommand commandParent = parent as FlutterCommand; final FlutterCommand? commandParent = parent as FlutterCommand?;
final String path = await commandParent.usagePath; final String? path = await commandParent?.usagePath;
// Don't report for parents that return null for usagePath. // Don't report for parents that return null for usagePath.
return path == null ? null : '$path/$name'; return path == null ? null : '$path/$name';
} else { } else {
@ -1133,15 +1126,19 @@ abstract class FlutterCommand extends Command<void> {
// Prints the welcome message if needed. // Prints the welcome message if needed.
globals.flutterUsage.printWelcome(); globals.flutterUsage.printWelcome();
_printDeprecationWarning(); _printDeprecationWarning();
final String commandPath = await usagePath; final String? commandPath = await usagePath;
_registerSignalHandlers(commandPath, startTime); if (commandPath != null) {
_registerSignalHandlers(commandPath, startTime);
}
FlutterCommandResult commandResult = FlutterCommandResult.fail(); FlutterCommandResult commandResult = FlutterCommandResult.fail();
try { try {
commandResult = await verifyThenRunCommand(commandPath); commandResult = await verifyThenRunCommand(commandPath);
} finally { } finally {
final DateTime endTime = globals.systemClock.now(); final DateTime endTime = globals.systemClock.now();
globals.printTrace(userMessages.flutterElapsedTime(name, getElapsedAsMilliseconds(endTime.difference(startTime)))); globals.printTrace(userMessages.flutterElapsedTime(name, getElapsedAsMilliseconds(endTime.difference(startTime))));
_sendPostUsage(commandPath, commandResult, startTime, endTime); if (commandPath != null) {
_sendPostUsage(commandPath, commandResult, startTime, endTime);
}
} }
}, },
); );
@ -1167,7 +1164,10 @@ abstract class FlutterCommand extends Command<void> {
&& dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='))) { && dartDefines.any((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='))) {
dartDefinesSet.removeWhere((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA=')); dartDefinesSet.removeWhere((String d) => d.startsWith('FLUTTER_WEB_USE_SKIA='));
} }
dartDefinesSet.addAll(_webRendererDartDefines[webRenderer]); final Iterable<String>? webRendererDefine = _webRendererDartDefines[webRenderer];
if (webRendererDefine != null) {
dartDefinesSet.addAll(webRendererDefine);
}
return dartDefinesSet.toList(); return dartDefinesSet.toList();
} }
@ -1207,7 +1207,7 @@ abstract class FlutterCommand extends Command<void> {
if (commandResult.exitStatus != null) if (commandResult.exitStatus != null)
getEnumName(commandResult.exitStatus), getEnumName(commandResult.exitStatus),
if (commandResult.timingLabelParts?.isNotEmpty ?? false) if (commandResult.timingLabelParts?.isNotEmpty ?? false)
...commandResult.timingLabelParts, ...?commandResult.timingLabelParts,
]; ];
final String label = labels final String label = labels
@ -1233,7 +1233,7 @@ abstract class FlutterCommand extends Command<void> {
/// then call this method to execute the command /// then call this method to execute the command
/// rather than calling [runCommand] directly. /// rather than calling [runCommand] directly.
@mustCallSuper @mustCallSuper
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async { Future<FlutterCommandResult> verifyThenRunCommand(String? commandPath) async {
// Populate the cache. We call this before pub get below so that the // Populate the cache. We call this before pub get below so that the
// sky_engine package is available in the flutter cache for pub to find. // sky_engine package is available in the flutter cache for pub to find.
if (shouldUpdateCache) { if (shouldUpdateCache) {
@ -1249,7 +1249,7 @@ abstract class FlutterCommand extends Command<void> {
if (shouldRunPub) { if (shouldRunPub) {
final FlutterProject project = FlutterProject.current(); final FlutterProject project = FlutterProject.current();
final Environment environment = Environment( final Environment environment = Environment(
artifacts: globals.artifacts, artifacts: globals.artifacts!,
logger: globals.logger, logger: globals.logger,
cacheDir: globals.cache.getRoot(), cacheDir: globals.cache.getRoot(),
engineVersion: globals.flutterVersion.engineRevision, engineVersion: globals.flutterVersion.engineRevision,
@ -1264,7 +1264,7 @@ abstract class FlutterCommand extends Command<void> {
await generateLocalizationsSyntheticPackage( await generateLocalizationsSyntheticPackage(
environment: environment, environment: environment,
buildSystem: globals.buildSystem, buildSystem: globals.buildSystem!,
); );
await pub.get( await pub.get(
@ -1314,21 +1314,21 @@ abstract class FlutterCommand extends Command<void> {
/// devices and criteria entered by the user on the command line. /// devices and criteria entered by the user on the command line.
/// If no device can be found that meets specified criteria, /// If no device can be found that meets specified criteria,
/// then print an error message and return null. /// then print an error message and return null.
Future<List<Device>> findAllTargetDevices({ Future<List<Device>?> findAllTargetDevices({
bool includeUnsupportedDevices = false, bool includeUnsupportedDevices = false,
}) async { }) async {
if (!globals.doctor.canLaunchAnything) { if (!globals.doctor!.canLaunchAnything) {
globals.printError(userMessages.flutterNoDevelopmentDevice); globals.printError(userMessages.flutterNoDevelopmentDevice);
return null; return null;
} }
final DeviceManager deviceManager = globals.deviceManager; final DeviceManager deviceManager = globals.deviceManager!;
List<Device> devices = await deviceManager.findTargetDevices( List<Device> devices = await deviceManager.findTargetDevices(
includeUnsupportedDevices ? null : FlutterProject.current(), includeUnsupportedDevices ? null : FlutterProject.current(),
timeout: deviceDiscoveryTimeout, timeout: deviceDiscoveryTimeout,
); );
if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) { if (devices.isEmpty && deviceManager.hasSpecifiedDeviceId) {
globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId)); globals.printStatus(userMessages.flutterNoMatchingDevice(deviceManager.specifiedDeviceId!));
return null; return null;
} else if (devices.isEmpty) { } else if (devices.isEmpty) {
if (deviceManager.hasSpecifiedAllDevices) { if (deviceManager.hasSpecifiedAllDevices) {
@ -1346,7 +1346,7 @@ abstract class FlutterCommand extends Command<void> {
.toList(), .toList(),
'\n', '\n',
); );
result.writeln(''); result.writeln();
result.writeln(userMessages.flutterMissPlatformProjects( result.writeln(userMessages.flutterMissPlatformProjects(
Device.devicesPlatformTypes(unsupportedDevices), Device.devicesPlatformTypes(unsupportedDevices),
)); ));
@ -1355,7 +1355,7 @@ abstract class FlutterCommand extends Command<void> {
return null; return null;
} else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) { } else if (devices.length > 1 && !deviceManager.hasSpecifiedAllDevices) {
if (deviceManager.hasSpecifiedDeviceId) { if (deviceManager.hasSpecifiedDeviceId) {
globals.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId)); globals.printStatus(userMessages.flutterFoundSpecifiedDevices(devices.length, deviceManager.specifiedDeviceId!));
} else { } else {
globals.printStatus(userMessages.flutterSpecifyDeviceWithAllOption); globals.printStatus(userMessages.flutterSpecifyDeviceWithAllOption);
devices = await deviceManager.getAllConnectedDevices(); devices = await deviceManager.getAllConnectedDevices();
@ -1374,16 +1374,16 @@ abstract class FlutterCommand extends Command<void> {
/// ///
/// If [includeUnsupportedDevices] is true, the tool does not filter /// If [includeUnsupportedDevices] is true, the tool does not filter
/// the list by the current project support list. /// the list by the current project support list.
Future<Device> findTargetDevice({ Future<Device?> findTargetDevice({
bool includeUnsupportedDevices = false, bool includeUnsupportedDevices = false,
}) async { }) async {
List<Device> deviceList = await findAllTargetDevices(includeUnsupportedDevices: includeUnsupportedDevices); List<Device>? deviceList = await findAllTargetDevices(includeUnsupportedDevices: includeUnsupportedDevices);
if (deviceList == null) { if (deviceList == null) {
return null; return null;
} }
if (deviceList.length > 1) { if (deviceList.length > 1) {
globals.printStatus(userMessages.flutterSpecifyDevice); globals.printStatus(userMessages.flutterSpecifyDevice);
deviceList = await globals.deviceManager.getAllConnectedDevices(); deviceList = await globals.deviceManager!.getAllConnectedDevices();
globals.printStatus(''); globals.printStatus('');
await Device.printDevices(deviceList, globals.logger); await Device.printDevices(deviceList, globals.logger);
return null; return null;
@ -1394,7 +1394,7 @@ abstract class FlutterCommand extends Command<void> {
@protected @protected
@mustCallSuper @mustCallSuper
Future<void> validateCommand() async { Future<void> validateCommand() async {
if (_requiresPubspecYaml && !globalResults.wasParsed('packages')) { if (_requiresPubspecYaml && globalResults?.wasParsed('packages') != true) {
// Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path. // Don't expect a pubspec.yaml file if the user passed in an explicit .packages file path.
// If there is no pubspec in the current directory, look in the parent // If there is no pubspec in the current directory, look in the parent
@ -1433,23 +1433,23 @@ abstract class FlutterCommand extends Command<void> {
description, description,
'', '',
'Global options:', 'Global options:',
runner.argParser.usage, '${runner?.argParser.usage}',
'', '',
usageWithoutDescription, usageWithoutDescription,
].join('\n'); ].join('\n');
return help; return help;
} }
ApplicationPackageFactory applicationPackages; ApplicationPackageFactory? applicationPackages;
/// Gets the parsed command-line option named [name] as `bool`. /// Gets the parsed command-line option named [name] as `bool`.
bool boolArg(String name) => argResults[name] as bool; bool boolArg(String name) => argResults?[name] as bool? ?? false;
/// Gets the parsed command-line option named [name] as `String`. /// Gets the parsed command-line option named [name] as `String`.
String stringArg(String name) => argResults[name] as String; String? stringArg(String name) => argResults?[name] as String?;
/// Gets the parsed command-line option named [name] as `List<String>`. /// Gets the parsed command-line option named [name] as `List<String>`.
List<String> stringsArg(String name) => argResults[name] as List<String>; List<String> stringsArg(String name) => argResults?[name] as List<String>? ?? <String>[];
} }
/// A mixin which applies an implementation of [requiredArtifacts] that only /// A mixin which applies an implementation of [requiredArtifacts] that only
@ -1460,7 +1460,7 @@ mixin DeviceBasedDevelopmentArtifacts on FlutterCommand {
// If there are no attached devices, use the default configuration. // If there are no attached devices, use the default configuration.
// Otherwise, only add development artifacts which correspond to a // Otherwise, only add development artifacts which correspond to a
// connected device. // connected device.
final List<Device> devices = await globals.deviceManager.getDevices(); final List<Device> devices = await globals.deviceManager!.getDevices();
if (devices.isEmpty) { if (devices.isEmpty) {
return super.requiredArtifacts; return super.requiredArtifacts;
} }
@ -1469,7 +1469,7 @@ mixin DeviceBasedDevelopmentArtifacts on FlutterCommand {
}; };
for (final Device device in devices) { for (final Device device in devices) {
final TargetPlatform targetPlatform = await device.targetPlatform; final TargetPlatform targetPlatform = await device.targetPlatform;
final DevelopmentArtifact developmentArtifact = artifactFromTargetPlatform(targetPlatform); final DevelopmentArtifact? developmentArtifact = artifactFromTargetPlatform(targetPlatform);
if (developmentArtifact != null) { if (developmentArtifact != null) {
artifacts.add(developmentArtifact); artifacts.add(developmentArtifact);
} }
@ -1481,7 +1481,7 @@ mixin DeviceBasedDevelopmentArtifacts on FlutterCommand {
// Returns the development artifact for the target platform, or null // Returns the development artifact for the target platform, or null
// if none is supported // if none is supported
@protected @protected
DevelopmentArtifact artifactFromTargetPlatform(TargetPlatform targetPlatform) { DevelopmentArtifact? artifactFromTargetPlatform(TargetPlatform targetPlatform) {
switch (targetPlatform) { switch (targetPlatform) {
case TargetPlatform.android: case TargetPlatform.android:
case TargetPlatform.android_arm: case TargetPlatform.android_arm:
@ -1518,7 +1518,6 @@ DevelopmentArtifact artifactFromTargetPlatform(TargetPlatform targetPlatform) {
} }
return null; return null;
} }
return null;
} }
/// Returns true if s is either null, empty or is solely made of whitespace characters (as defined by String.trim). /// Returns true if s is either null, empty or is solely made of whitespace characters (as defined by String.trim).

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// @dart = 2.8
import 'package:args/args.dart'; import 'package:args/args.dart';
import 'package:args/command_runner.dart'; import 'package:args/command_runner.dart';
import 'package:completion/completion.dart'; import 'package:completion/completion.dart';
@ -45,14 +43,12 @@ class FlutterCommandRunner extends CommandRunner<void> {
argParser.addFlag('prefixed-errors', argParser.addFlag('prefixed-errors',
negatable: false, negatable: false,
help: 'Causes lines sent to stderr to be prefixed with "ERROR:".', help: 'Causes lines sent to stderr to be prefixed with "ERROR:".',
hide: !verboseHelp, hide: !verboseHelp);
defaultsTo: false);
argParser.addFlag('quiet', argParser.addFlag('quiet',
negatable: false, negatable: false,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Reduce the amount of output from some commands.'); help: 'Reduce the amount of output from some commands.');
argParser.addFlag('wrap', argParser.addFlag('wrap',
negatable: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Toggles output word wrapping, regardless of whether or not the output is a terminal.', help: 'Toggles output word wrapping, regardless of whether or not the output is a terminal.',
defaultsTo: true); defaultsTo: true);
@ -60,8 +56,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
hide: !verboseHelp, hide: !verboseHelp,
help: 'Sets the output wrap column. If not set, uses the width of the terminal. No ' help: 'Sets the output wrap column. If not set, uses the width of the terminal. No '
'wrapping occurs if not writing to a terminal. Use "--no-wrap" to turn off wrapping ' 'wrapping occurs if not writing to a terminal. Use "--no-wrap" to turn off wrapping '
'when connected to a terminal.', 'when connected to a terminal.');
defaultsTo: null);
argParser.addOption('device-id', argParser.addOption('device-id',
abbr: 'd', abbr: 'd',
help: 'Target device id or name (prefixes allowed).'); help: 'Target device id or name (prefixes allowed).');
@ -73,12 +68,10 @@ class FlutterCommandRunner extends CommandRunner<void> {
hide: !verboseHelp, hide: !verboseHelp,
help: 'When used with the "--version" flag, outputs the information using JSON.'); help: 'When used with the "--version" flag, outputs the information using JSON.');
argParser.addFlag('color', argParser.addFlag('color',
negatable: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Whether to use terminal colors (requires support for ANSI escape sequences).', help: 'Whether to use terminal colors (requires support for ANSI escape sequences).',
defaultsTo: true); defaultsTo: true);
argParser.addFlag('version-check', argParser.addFlag('version-check',
negatable: true,
defaultsTo: true, defaultsTo: true,
hide: !verboseHelp, hide: !verboseHelp,
help: 'Allow Flutter to check for updates when this command runs.'); help: 'Allow Flutter to check for updates when this command runs.');
@ -158,12 +151,12 @@ class FlutterCommandRunner extends CommandRunner<void> {
usageException(error.message); usageException(error.message);
} }
Command<void> command = commands[error.commands.first]; Command<void>? command = commands[error.commands.first];
for (final String commandName in error.commands.skip(1)) { for (final String commandName in error.commands.skip(1)) {
command = command.subcommands[commandName]; command = command?.subcommands[commandName];
} }
command.usageException(error.message); command!.usageException(error.message);
} }
} }
@ -184,12 +177,12 @@ class FlutterCommandRunner extends CommandRunner<void> {
@override @override
Future<void> runCommand(ArgResults topLevelResults) async { Future<void> runCommand(ArgResults topLevelResults) async {
final Map<Type, dynamic> contextOverrides = <Type, dynamic>{}; final Map<Type, Object?> contextOverrides = <Type, Object?>{};
// Don't set wrapColumns unless the user said to: if it's set, then all // Don't set wrapColumns unless the user said to: if it's set, then all
// wrapping will occur at this width explicitly, and won't adapt if the // wrapping will occur at this width explicitly, and won't adapt if the
// terminal size changes during a run. // terminal size changes during a run.
int wrapColumn; int? wrapColumn;
if (topLevelResults.wasParsed('wrap-column')) { if (topLevelResults.wasParsed('wrap-column')) {
try { try {
wrapColumn = int.parse(topLevelResults['wrap-column'] as String); wrapColumn = int.parse(topLevelResults['wrap-column'] as String);
@ -208,53 +201,53 @@ class FlutterCommandRunner extends CommandRunner<void> {
: globals.stdio.terminalColumns != null && topLevelResults['wrap'] as bool; : globals.stdio.terminalColumns != null && topLevelResults['wrap'] as bool;
contextOverrides[OutputPreferences] = OutputPreferences( contextOverrides[OutputPreferences] = OutputPreferences(
wrapText: useWrapping, wrapText: useWrapping,
showColor: topLevelResults['color'] as bool, showColor: topLevelResults['color'] as bool?,
wrapColumn: wrapColumn, wrapColumn: wrapColumn,
); );
if (topLevelResults['show-test-device'] as bool || if ((topLevelResults['show-test-device'] as bool?) == true ||
topLevelResults['device-id'] == FlutterTesterDevices.kTesterDeviceId) { topLevelResults['device-id'] == FlutterTesterDevices.kTesterDeviceId) {
FlutterTesterDevices.showFlutterTesterDevice = true; FlutterTesterDevices.showFlutterTesterDevice = true;
} }
if (topLevelResults['show-web-server-device'] as bool || if ((topLevelResults['show-web-server-device'] as bool?) == true ||
topLevelResults['device-id'] == WebServerDevice.kWebServerDeviceId) { topLevelResults['device-id'] == WebServerDevice.kWebServerDeviceId) {
WebServerDevice.showWebServerDevice = true; WebServerDevice.showWebServerDevice = true;
} }
// Set up the tooling configuration. // Set up the tooling configuration.
final EngineBuildPaths engineBuildPaths = await globals.localEngineLocator.findEnginePath( final EngineBuildPaths? engineBuildPaths = await globals.localEngineLocator?.findEnginePath(
topLevelResults['local-engine-src-path'] as String, topLevelResults['local-engine-src-path'] as String?,
topLevelResults['local-engine'] as String, topLevelResults['local-engine'] as String?,
topLevelResults['packages'] as String, topLevelResults['packages'] as String?,
); );
if (engineBuildPaths != null) { if (engineBuildPaths != null) {
contextOverrides.addAll(<Type, dynamic>{ contextOverrides.addAll(<Type, Object?>{
Artifacts: Artifacts.getLocalEngine(engineBuildPaths), Artifacts: Artifacts.getLocalEngine(engineBuildPaths),
}); });
} }
await context.run<void>( await context.run<void>(
overrides: contextOverrides.map<Type, Generator>((Type type, dynamic value) { overrides: contextOverrides.map<Type, Generator>((Type type, Object? value) {
return MapEntry<Type, Generator>(type, () => value); return MapEntry<Type, Generator>(type, () => value);
}), }),
body: () async { body: () async {
globals.logger.quiet = topLevelResults['quiet'] as bool; globals.logger.quiet = (topLevelResults['quiet'] as bool?) == true;
if (globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { if (globals.platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') {
await globals.cache.lock(); await globals.cache.lock();
} }
if (topLevelResults['suppress-analytics'] as bool) { if ((topLevelResults['suppress-analytics'] as bool?) == true) {
globals.flutterUsage.suppressAnalytics = true; globals.flutterUsage.suppressAnalytics = true;
} }
globals.flutterVersion.ensureVersionFile(); globals.flutterVersion.ensureVersionFile();
final bool machineFlag = topLevelResults['machine'] as bool; final bool machineFlag = topLevelResults['machine'] as bool? ?? false;
final bool ci = await globals.botDetector.isRunningOnBot; final bool ci = await globals.botDetector.isRunningOnBot;
final bool redirectedCompletion = !globals.stdio.hasTerminal && final bool redirectedCompletion = !globals.stdio.hasTerminal &&
(topLevelResults.command?.name ?? '').endsWith('-completion'); (topLevelResults.command?.name ?? '').endsWith('-completion');
final bool isMachine = machineFlag || ci || redirectedCompletion; final bool isMachine = machineFlag || ci || redirectedCompletion;
final bool versionCheckFlag = topLevelResults['version-check'] as bool; final bool versionCheckFlag = topLevelResults['version-check'] as bool? ?? false;
final bool explicitVersionCheckPassed = topLevelResults.wasParsed('version-check') && versionCheckFlag; final bool explicitVersionCheckPassed = topLevelResults.wasParsed('version-check') && versionCheckFlag;
if (topLevelResults.command?.name != 'upgrade' && if (topLevelResults.command?.name != 'upgrade' &&
@ -263,16 +256,16 @@ class FlutterCommandRunner extends CommandRunner<void> {
} }
// See if the user specified a specific device. // See if the user specified a specific device.
globals.deviceManager.specifiedDeviceId = topLevelResults['device-id'] as String; globals.deviceManager?.specifiedDeviceId = topLevelResults['device-id'] as String?;
if (topLevelResults['version'] as bool) { if ((topLevelResults['version'] as bool?) == true) {
globals.flutterUsage.sendCommand('version'); globals.flutterUsage.sendCommand('version');
globals.flutterVersion.fetchTagsAndUpdate(); globals.flutterVersion.fetchTagsAndUpdate();
String status; String status;
if (machineFlag) { if (machineFlag) {
final Map<String, Object> jsonOut = globals.flutterVersion.toJson(); final Map<String, Object> jsonOut = globals.flutterVersion.toJson();
if (jsonOut != null) { if (jsonOut != null) {
jsonOut['flutterRoot'] = Cache.flutterRoot; jsonOut['flutterRoot'] = Cache.flutterRoot!;
} }
status = const JsonEncoder.withIndent(' ').convert(jsonOut); status = const JsonEncoder.withIndent(' ').convert(jsonOut);
} else { } else {
@ -292,7 +285,7 @@ class FlutterCommandRunner extends CommandRunner<void> {
/// Get the root directories of the repo - the directories containing Dart packages. /// Get the root directories of the repo - the directories containing Dart packages.
List<String> getRepoRoots() { List<String> getRepoRoots() {
final String root = globals.fs.path.absolute(Cache.flutterRoot); final String root = globals.fs.path.absolute(Cache.flutterRoot!);
// not bin, and not the root // not bin, and not the root
return <String>['dev', 'examples', 'packages'].map<String>((String item) { return <String>['dev', 'examples', 'packages'].map<String>((String item) {
return globals.fs.path.join(root, item); return globals.fs.path.join(root, item);