blob: 64587199bb495e5c4dfdced9708f3c780e6ddca1 [file]
// Copyright 2014 The Flutter 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 'package:flutter_tools_extension_linux_prototype/flutter_tools_extension_linux_prototype.dart';
import 'package:meta/meta.dart';
import 'runner.dart' as runner;
import 'src/base/context.dart';
import 'src/base/io.dart';
import 'src/base/logger.dart';
import 'src/base/platform.dart';
import 'src/base/template.dart';
import 'src/base/terminal.dart';
import 'src/base/user_messages.dart';
import 'src/build_system/build_targets.dart';
import 'src/build_system/targets/hook_runner_native.dart' show FlutterHookRunnerNative;
import 'src/cache.dart';
import 'src/commands/analyze.dart';
import 'src/commands/assemble.dart';
import 'src/commands/attach.dart';
import 'src/commands/build.dart';
import 'src/commands/channel.dart';
import 'src/commands/clean.dart';
import 'src/commands/config.dart';
import 'src/commands/create.dart';
import 'src/commands/custom_devices.dart';
import 'src/commands/daemon.dart';
import 'src/commands/debug_adapter.dart';
import 'src/commands/devices.dart';
import 'src/commands/doctor.dart';
import 'src/commands/downgrade.dart';
import 'src/commands/drive.dart';
import 'src/commands/emulators.dart';
import 'src/commands/generate.dart';
import 'src/commands/generate_localizations.dart';
import 'src/commands/ide_config.dart';
import 'src/commands/install.dart';
import 'src/commands/logs.dart';
import 'src/commands/packages.dart';
import 'src/commands/precache.dart';
import 'src/commands/run.dart';
import 'src/commands/screenshot.dart';
import 'src/commands/shell_completion.dart';
import 'src/commands/symbolize.dart';
import 'src/commands/test.dart';
import 'src/commands/update_packages.dart';
import 'src/commands/upgrade.dart';
import 'src/commands/widget_preview.dart';
import 'src/context/tool_context.dart';
import 'src/context/tool_dependencies.dart';
import 'src/devtools_launcher.dart';
import 'src/experimental/extension_discovery.dart';
import 'src/experimental/extension_manager.dart';
import 'src/features.dart';
import 'src/globals.dart' as globals;
// Files in `isolated` are intentionally excluded from google3 tooling.
import 'src/hook_runner.dart' show FlutterHookRunner;
import 'src/isolated/build_targets.dart';
import 'src/isolated/mustache_template.dart';
import 'src/isolated/native_assets/test/native_assets.dart';
import 'src/isolated/resident_web_runner.dart';
import 'src/native_assets.dart';
import 'src/pre_run_validator.dart';
import 'src/project_validator.dart';
import 'src/resident_runner.dart';
import 'src/runner/flutter_command.dart';
import 'src/runner/flutter_command_runner.dart';
import 'src/web/web_runner.dart';
/// Main entry point for commands.
///
/// This function is intended to be used from the `flutter` command line tool.
Future<void> main(List<String> args) async {
final bool veryVerbose = args.contains('-vv');
final bool verbose = args.contains('-v') || args.contains('--verbose') || veryVerbose;
final bool prefixedErrors = args.contains('--prefixed-errors');
// Support universal help idioms.
final int powershellHelpIndex = args.indexOf('-?');
if (powershellHelpIndex != -1) {
args[powershellHelpIndex] = '-h';
}
final int slashQuestionHelpIndex = args.indexOf('/?');
if (slashQuestionHelpIndex != -1) {
args[slashQuestionHelpIndex] = '-h';
}
final String? commandName = findCommandName(args);
final doctor = commandName == 'doctor';
final bool help =
args.contains('-h') ||
args.contains('--help') ||
commandName == 'help' ||
(args.length == 1 && verbose);
final bool muteCommandLogging = (help || doctor) && !veryVerbose;
final bool verboseHelp = help && verbose;
final daemon = commandName == 'daemon';
final widgetPreviews = commandName == WidgetPreviewCommand.kWidgetPreview;
final bool runMachine = args.contains('--machine');
// Cache.flutterRoot must be set early because other features use it (e.g.
// enginePath's initializer uses it). This can only work with the real
// instances of the platform or filesystem, so just use those.
Cache.flutterRoot = Cache.defaultFlutterRoot(
platform: const LocalPlatform(),
fileSystem: globals.localFileSystem,
userMessages: UserMessages(),
);
await runner.run(
args,
(ToolDependencies toolDependencies) {
final manager = ExtensionManager(
hostPlatform: globals.os.hostPlatform,
logger: globals.logger,
entryPoints: <ExtensionEntryPoint>[linuxExtensionEntryPoint],
featureFlags: featureFlags,
);
return generateCommands(
toolDependencies: toolDependencies,
verboseHelp: verboseHelp,
verbose: verbose,
extensionManager: manager,
);
},
verbose: verbose,
muteCommandLogging: muteCommandLogging,
verboseHelp: verboseHelp,
overrides: <Type, Generator>{
FlutterHookRunner: () => FlutterHookRunnerNative(),
// The web runner is not supported in google3 because it depends
// on dwds.
WebRunnerFactory: () => DwdsWebRunnerFactory(),
// The mustache dependency is different in google3
TemplateRenderer: () => const MustacheTemplateRenderer(),
// The devtools launcher is not supported in google3 because it depends on
// devtools source code.
DevtoolsLauncher: () => DevtoolsServerLauncher(
processManager: globals.processManager,
artifacts: globals.artifacts!,
logger: globals.logger,
botDetector: globals.botDetector,
),
BuildTargets: () => const BuildTargetsImpl(),
Logger: () {
final loggerFactory = LoggerFactory(
outputPreferences: globals.outputPreferences,
terminal: globals.terminal,
stdio: globals.stdio,
);
return loggerFactory.createLogger(
daemon: daemon,
machine: runMachine,
verbose: verbose && !muteCommandLogging,
prefixedErrors: prefixedErrors,
windows: globals.platform.isWindows,
widgetPreviews: widgetPreviews,
);
},
AnsiTerminal: () {
return AnsiTerminal(
stdio: globals.stdio,
platform: globals.platform,
now: DateTime.now(),
// So that we don't animate anything before calling applyFeatureFlags, default
// the animations to disabled in real apps.
defaultCliAnimationEnabled: false,
shutdownHooks: globals.shutdownHooks,
);
// runner.run calls "terminal.applyFeatureFlags()"
},
PreRunValidator: () => PreRunValidator(fileSystem: globals.fs),
TestCompilerNativeAssetsBuilder: () => const TestCompilerNativeAssetsBuilderImpl(),
},
shutdownHooks: globals.shutdownHooks,
);
}
/// The name of the command in [args], or null if there isn't one.
///
/// Global options can come before the command, so it can't be found by
/// position. A throwaway parser walks past them instead: trailing options are
/// disabled, so parsing stops at the command and leaves it at the head of
/// [ArgResults.rest]. `help` is the exception, since the command runner
/// registers it on the parser itself and so reports it as a parsed command.
@visibleForTesting
String? findCommandName(List<String> args, {ToolContext? toolContext}) {
final ArgResults results;
try {
results = FlutterCommandRunner(
toolContext: toolContext ?? _FallbackToolContext(),
).argParser.parse(args);
} on ArgParserException {
// The real parser will complain about these later.
return null;
}
return results.command?.name ?? results.rest.firstOrNull;
}
class _FallbackToolContext implements ToolContext {
_FallbackToolContext({OutputPreferences? outputPreferences})
: _outputPreferences = outputPreferences;
final OutputPreferences? _outputPreferences;
@override
OutputPreferences get outputPreferences {
if (_outputPreferences != null) {
return _outputPreferences;
}
try {
return globals.outputPreferences;
} on Object catch (_) {
return OutputPreferences.test();
}
}
@override
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
List<FlutterCommand> generateCommands({
required ToolDependencies toolDependencies,
required bool verbose,
required bool verboseHelp,
ExtensionManager? extensionManager,
}) => <FlutterCommand>[
AnalyzeCommand(
verboseHelp: verboseHelp,
fileSystem: toolDependencies.toolContext.fs,
platform: toolDependencies.toolContext.platform,
processManager: toolDependencies.toolContext.processManager,
logger: toolDependencies.toolContext.logger,
terminal: toolDependencies.toolContext.terminal,
artifacts: toolDependencies.toolContext.artifacts,
// new ProjectValidators should be added here for the --suggestions to run
allProjectValidators: <ProjectValidator>[
GeneralInfoProjectValidator(),
VariableDumpMachineProjectValidator(
logger: toolDependencies.toolContext.logger,
fileSystem: toolDependencies.toolContext.fs,
platform: toolDependencies.toolContext.platform,
git: toolDependencies.toolContext.git,
),
],
suppressAnalytics: !toolDependencies.analytics.okToSend,
),
AssembleCommand(verboseHelp: verboseHelp, buildSystem: toolDependencies.buildSystem),
AttachCommand(
verboseHelp: verboseHelp,
stdio: toolDependencies.toolContext.stdio,
logger: toolDependencies.toolContext.logger,
terminal: toolDependencies.toolContext.terminal,
signals: toolDependencies.toolContext.signals,
platform: toolDependencies.toolContext.platform,
processInfo: ProcessInfo(toolDependencies.toolContext.fs),
fileSystem: toolDependencies.toolContext.fs,
),
BuildCommand(
fileSystem: toolDependencies.toolContext.fs,
buildSystem: toolDependencies.buildSystem,
osUtils: toolDependencies.toolContext.os,
verboseHelp: verboseHelp,
androidSdk: toolDependencies.androidContext.androidSdk,
logger: toolDependencies.toolContext.logger,
config: toolDependencies.toolContext.config,
platform: toolDependencies.toolContext.platform,
fileSystemUtils: toolDependencies.toolContext.fileSystemUtils,
terminal: toolDependencies.toolContext.terminal,
plistParser: toolDependencies.appleContext.plistParser,
processUtils: toolDependencies.toolContext.processUtils,
processManager: toolDependencies.toolContext.processManager,
templateRenderer: const MustacheTemplateRenderer(),
xcode: toolDependencies.appleContext.xcode,
artifacts: toolDependencies.toolContext.artifacts,
cache: toolDependencies.toolContext.cache,
flutterVersion: toolDependencies.toolContext.flutterVersion,
),
ChannelCommand(verboseHelp: verboseHelp),
CleanCommand(
verbose: verbose,
toolContext: toolDependencies.toolContext,
xcode: toolDependencies.appleContext.xcode,
xcodeProjectInterpreter: toolDependencies.appleContext.xcodeProjectInterpreter,
),
ConfigCommand(verboseHelp: verboseHelp, extensionManager: extensionManager),
CustomDevicesCommand(
customDevicesConfig: toolDependencies.toolContext.customDevicesConfig,
operatingSystemUtils: toolDependencies.toolContext.os,
terminal: toolDependencies.toolContext.terminal,
platform: toolDependencies.toolContext.platform,
featureFlags: featureFlags,
processManager: toolDependencies.toolContext.processManager,
fileSystem: toolDependencies.toolContext.fs,
logger: toolDependencies.toolContext.logger,
),
CreateCommand(verboseHelp: verboseHelp),
DaemonCommand(hidden: !verboseHelp),
DebugAdapterCommand(verboseHelp: verboseHelp),
DevicesCommand(verboseHelp: verboseHelp),
DoctorCommand(verbose: verbose, extensionManager: extensionManager),
DowngradeCommand(verboseHelp: verboseHelp, logger: toolDependencies.toolContext.logger),
DriveCommand(
verboseHelp: verboseHelp,
fileSystem: toolDependencies.toolContext.fs,
logger: toolDependencies.toolContext.logger,
platform: toolDependencies.toolContext.platform,
terminal: toolDependencies.toolContext.terminal,
outputPreferences: toolDependencies.toolContext.outputPreferences,
signals: toolDependencies.toolContext.signals,
),
EmulatorsCommand(),
GenerateCommand(),
GenerateLocalizationsCommand(
fileSystem: toolDependencies.toolContext.fs,
logger: toolDependencies.toolContext.logger,
artifacts: toolDependencies.toolContext.artifacts,
processManager: toolDependencies.toolContext.processManager,
),
InstallCommand(verboseHelp: verboseHelp),
LogsCommand(sigint: ProcessSignal.sigint, sigterm: ProcessSignal.sigterm),
PackagesCommand(),
PrecacheCommand(
verboseHelp: verboseHelp,
cache: toolDependencies.toolContext.cache,
logger: toolDependencies.toolContext.logger,
platform: toolDependencies.toolContext.platform,
featureFlags: featureFlags,
),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(fs: toolDependencies.toolContext.fs),
ShellCompletionCommand(),
TestCommand(
verboseHelp: verboseHelp,
verbose: verbose,
nativeAssetsBuilder: toolDependencies.toolContext.nativeAssetsBuilder,
),
WidgetPreviewCommand(
verboseHelp: verboseHelp,
logger: toolDependencies.toolContext.logger,
fs: toolDependencies.toolContext.fs,
projectFactory: toolDependencies.toolContext.projectFactory,
cache: toolDependencies.toolContext.cache,
platform: toolDependencies.toolContext.platform,
shutdownHooks: toolDependencies.toolContext.shutdownHooks,
os: toolDependencies.toolContext.os,
processManager: toolDependencies.toolContext.processManager,
artifacts: toolDependencies.toolContext.artifacts,
terminal: toolDependencies.toolContext.terminal,
),
UpgradeCommand(verboseHelp: verboseHelp),
SymbolizeCommand(
stdio: toolDependencies.toolContext.stdio,
fileSystem: toolDependencies.toolContext.fs,
),
// Development-only commands. These are always hidden,
IdeConfigCommand(),
UpdatePackagesCommand(verboseHelp: verboseHelp),
];
/// An abstraction for instantiation of the correct logger type.
///
/// Our logger class hierarchy and runtime requirements are overly complicated.
class LoggerFactory {
LoggerFactory({
required Terminal terminal,
required Stdio stdio,
required OutputPreferences outputPreferences,
StopwatchFactory stopwatchFactory = const StopwatchFactory(),
}) : _terminal = terminal,
_stdio = stdio,
_stopwatchFactory = stopwatchFactory,
_outputPreferences = outputPreferences;
final Terminal _terminal;
final Stdio _stdio;
final StopwatchFactory _stopwatchFactory;
final OutputPreferences _outputPreferences;
/// Create the appropriate logger for the current platform and configuration.
Logger createLogger({
required bool verbose,
required bool prefixedErrors,
required bool machine,
required bool daemon,
required bool windows,
required bool widgetPreviews,
}) {
Logger logger;
if (windows) {
logger = WindowsStdoutLogger(
terminal: _terminal,
stdio: _stdio,
outputPreferences: _outputPreferences,
stopwatchFactory: _stopwatchFactory,
);
} else {
logger = StdoutLogger(
terminal: _terminal,
stdio: _stdio,
outputPreferences: _outputPreferences,
stopwatchFactory: _stopwatchFactory,
);
}
if (verbose) {
logger = VerboseLogger(logger, stopwatchFactory: _stopwatchFactory);
}
if (prefixedErrors) {
logger = PrefixedErrorLogger(logger);
}
if (widgetPreviews) {
return WidgetPreviewMachineAwareLogger(logger, machine: machine, verbose: verbose);
}
if (daemon) {
return NotifyingLogger(verbose: verbose, parent: logger);
}
if (machine) {
return MachineOutputLogger(parent: logger);
}
return logger;
}
}