blob: a1d1be19ecb1ae269674864290c477c6d1d8c1ff [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/android/android_workflow.dart' as android_workflow;
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/experimental/templates.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,
);
final templateManager = ExtensionTemplateManager(
extensionManager: manager,
fileSystem: toolDependencies.toolContext.fs,
logger: toolDependencies.toolContext.logger,
featureFlags: featureFlags,
);
return generateCommands(
toolDependencies: toolDependencies,
verboseHelp: verboseHelp,
verbose: verbose,
extensionManager: manager,
extensionTemplateManager: templateManager,
);
},
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 = null;
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,
ExtensionTemplateManager? extensionTemplateManager,
}) => <FlutterCommand>[
AnalyzeCommand(
allProjectValidators: <ProjectValidator>[
GeneralInfoProjectValidator(),
VariableDumpMachineProjectValidator(
logger: toolDependencies.toolContext.logger,
fileSystem: toolDependencies.toolContext.fs,
platform: toolDependencies.toolContext.platform,
git: toolDependencies.toolContext.git,
),
],
suppressAnalytics: !toolDependencies.analytics.okToSend,
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
AssembleCommand(
buildSystem: toolDependencies.buildSystem,
featureFlags: toolDependencies.featureFlags,
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
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(
androidContext: toolDependencies.androidContext,
appleContext: toolDependencies.appleContext,
buildSystem: toolDependencies.buildSystem,
featureFlags: toolDependencies.featureFlags,
templateRenderer: const MustacheTemplateRenderer(),
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
ChannelCommand(verboseHelp: verboseHelp, toolContext: toolDependencies.toolContext),
CleanCommand(
verbose: verbose,
toolContext: toolDependencies.toolContext,
xcode: toolDependencies.appleContext.xcode,
xcodeProjectInterpreter: toolDependencies.appleContext.xcodeProjectInterpreter,
),
ConfigCommand(
verboseHelp: verboseHelp,
androidContext: toolDependencies.androidContext,
toolContext: toolDependencies.toolContext,
featureFlags: featureFlags,
extensionManager: extensionManager,
),
CustomDevicesCommand(featureFlags: featureFlags, toolContext: toolDependencies.toolContext),
CreateCommand(verboseHelp: verboseHelp, extensionTemplateManager: extensionTemplateManager),
DaemonCommand(
androidContext: toolDependencies.androidContext,
androidWorkflow: android_workflow.androidWorkflow,
deviceManager: globals.deviceManager,
hidden: !verboseHelp,
toolContext: toolDependencies.toolContext,
),
DebugAdapterCommand(toolContext: toolDependencies.toolContext, verboseHelp: verboseHelp),
DevicesCommand(
deviceManager: globals.deviceManager!,
doctor: globals.doctor!,
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
DoctorCommand(
verbose: verbose,
toolContext: toolDependencies.toolContext,
// Provide the shared singleton from globals until dependent commands
// (e.g. DevicesCommand, EmulatorsCommand) are migrated to DI.
doctor: globals.doctor,
extensionManager: extensionManager,
),
DowngradeCommand(toolContext: toolDependencies.toolContext, verboseHelp: verboseHelp),
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(
doctor: toolDependencies.doctor,
emulatorManager: toolDependencies.emulatorManager,
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
GenerateCommand(toolContext: toolDependencies.toolContext),
GenerateLocalizationsCommand(toolContext: toolDependencies.toolContext),
InstallCommand(toolContext: toolDependencies.toolContext, verboseHelp: verboseHelp),
LogsCommand(toolContext: toolDependencies.toolContext),
PackagesCommand(
buildSystem: toolDependencies.buildSystem,
toolContext: toolDependencies.toolContext,
verboseHelp: verboseHelp,
),
PrecacheCommand(
verboseHelp: verboseHelp,
cache: toolDependencies.toolContext.cache,
logger: toolDependencies.toolContext.logger,
platform: toolDependencies.toolContext.platform,
featureFlags: featureFlags,
),
RunCommand(verboseHelp: verboseHelp),
ScreenshotCommand(toolContext: toolDependencies.toolContext),
ShellCompletionCommand(toolContext: toolDependencies.toolContext),
TestCommand(
verboseHelp: verboseHelp,
verbose: verbose,
nativeAssetsBuilder: toolDependencies.toolContext.nativeAssetsBuilder,
),
WidgetPreviewCommand(toolContext: toolDependencies.toolContext, verboseHelp: verboseHelp),
UpgradeCommand(toolContext: toolDependencies.toolContext, verboseHelp: verboseHelp),
SymbolizeCommand(toolContext: toolDependencies.toolContext),
// Development-only commands. These are always hidden,
IdeConfigCommand(toolContext: toolDependencies.toolContext),
UpdatePackagesCommand(toolContext: toolDependencies.toolContext, 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 this._terminal,
required this._stdio,
required this._outputPreferences,
this._stopwatchFactory = const StopwatchFactory(),
});
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,
stdio: _stdio,
);
}
if (daemon) {
return NotifyingLogger(verbose: verbose, parent: logger);
}
if (machine) {
return MachineOutputLogger(parent: logger);
}
return logger;
}
}