blob: 6abdb4e0c723733b7b4653db3e8d201291cb6f23 [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 'package:process/process.dart';
import 'runner.dart' as runner;
import 'src/android/gradle.dart';
import 'src/base/context.dart';
import 'src/base/file_system.dart';
import 'src/base/io.dart';
import 'src/base/logger.dart';
import 'src/base/platform.dart';
import 'src/base/process.dart';
import 'src/base/signals.dart';
import 'src/base/terminal.dart';
import 'src/base/time.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/experimental/extension_discovery.dart';
import 'src/experimental/extension_manager.dart';
import 'src/experimental/templates.dart';
import 'src/features.dart';
// 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/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');
const Platform platform = LocalPlatform();
final stdio = Stdio();
final ShutdownHooks shutdownHooks = LocalSignals.instance.shutdownHooks;
final terminal = AnsiTerminal(
stdio: stdio,
platform: platform,
now: const SystemClock().now(),
// So that we don't animate anything before calling applyFeatureFlags, default
// the animations to disabled in real apps.
defaultCliAnimationEnabled: false,
shutdownHooks: shutdownHooks,
);
final outputPreferences = OutputPreferences(
wrapText: stdio.hasTerminal,
showColor: platform.stdoutSupportsAnsi,
stdio: stdio,
);
final Logger logger =
LoggerFactory(
outputPreferences: outputPreferences,
terminal: terminal,
stdio: stdio,
).createLogger(
daemon: daemon,
machine: runMachine,
verbose: verbose && !muteCommandLogging,
prefixedErrors: prefixedErrors,
windows: platform.isWindows,
widgetPreviews: widgetPreviews,
);
await runner.run(
args,
(ToolDependencies toolDependencies) {
final ToolDependencies(
:ExtensionManager? extensionManager,
:FeatureFlags featureFlags,
toolContext: ToolContext(:Cache cache, :FileSystem fs, :Logger logger),
) = toolDependencies;
final ExtensionManager manager = extensionManager!;
final templateManager = ExtensionTemplateManager(
extensionManager: manager,
featureFlags: featureFlags,
fileSystem: fs,
flutterRoot: cache.flutterRoot,
logger: logger,
);
return generateCommands(
toolDependencies: toolDependencies,
verboseHelp: verboseHelp,
verbose: verbose,
extensionTemplateManager: templateManager,
);
},
buildTargets: const BuildTargetsImpl(),
extensionEntryPoints: <ExtensionEntryPoint>[linuxExtensionEntryPoint],
muteCommandLogging: muteCommandLogging,
nativeAssetsBuilder: const TestCompilerNativeAssetsBuilderImpl(),
overrides: <Type, Generator>{
AnsiTerminal: () => terminal,
FlutterHookRunner: () => FlutterHookRunnerNative(),
Logger: () => logger,
OutputPreferences: () => outputPreferences,
Stdio: () => stdio,
// The web runner is not supported in google3 because it depends
// on dwds.
WebRunnerFactory: () => DwdsWebRunnerFactory(),
},
preRunValidatorBuilder: PreRunValidator.new,
shutdownHooks: shutdownHooks,
templateRenderer: const MustacheTemplateRenderer(),
verbose: verbose,
verboseHelp: verboseHelp,
);
}
/// 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 {
@override
final OutputPreferences outputPreferences = OutputPreferences();
@override
Object? noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
}
List<FlutterCommand> generateCommands({
required ToolDependencies toolDependencies,
required bool verbose,
required bool verboseHelp,
ExtensionTemplateManager? extensionTemplateManager,
}) {
final ToolDependencies(:FeatureFlags featureFlags, :ToolContext toolContext) = toolDependencies;
final ToolContext(
:Cache cache,
:Logger logger,
:TestCompilerNativeAssetsBuilder? nativeAssetsBuilder,
:Platform platform,
:ProcessManager processManager,
) = toolContext;
return <FlutterCommand>[
AnalyzeCommand(
allProjectValidators: <ProjectValidator>[
GeneralInfoProjectValidator(
androidContext: toolDependencies.androidContext,
logger: logger,
processManager: processManager,
),
VariableDumpMachineProjectValidator(toolContext: toolContext),
],
androidBuilder: AndroidGradleBuilder.fromContexts(
analytics: toolDependencies.analytics,
androidContext: toolDependencies.androidContext,
toolContext: toolContext,
),
suppressAnalytics: !toolDependencies.analytics.okToSend,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
AssembleCommand(
androidContext: toolDependencies.androidContext,
buildSystem: toolDependencies.buildSystem,
extensionBuildManager: toolDependencies.extensionBuildManager,
featureFlags: featureFlags,
toolContext: toolContext,
verboseHelp: verboseHelp,
xcode: toolDependencies.appleContext.xcode,
),
AttachCommand(
buildSystem: toolDependencies.buildSystem,
buildTargets: toolDependencies.buildTargets,
toolContext: toolContext,
xcode: toolDependencies.appleContext.xcode,
verboseHelp: verboseHelp,
),
BuildCommand(
androidBuilder: AndroidGradleBuilder.fromContexts(
analytics: toolDependencies.analytics,
androidContext: toolDependencies.androidContext,
toolContext: toolContext,
),
androidContext: toolDependencies.androidContext,
appleContext: toolDependencies.appleContext,
buildSystem: toolDependencies.buildSystem,
extensionBuildManager: toolDependencies.extensionBuildManager,
featureFlags: featureFlags,
templateRenderer: const MustacheTemplateRenderer(),
toolContext: toolContext,
verboseHelp: verboseHelp,
),
ChannelCommand(verboseHelp: verboseHelp, toolContext: toolContext),
CleanCommand(
extensionCleanManager: toolDependencies.extensionCleanManager,
verbose: verbose,
toolContext: toolContext,
xcode: toolDependencies.appleContext.xcode,
xcodeProjectInterpreter: toolDependencies.appleContext.xcodeProjectInterpreter,
),
ConfigCommand(
verboseHelp: verboseHelp,
androidContext: toolDependencies.androidContext,
toolContext: toolContext,
featureFlags: featureFlags,
extensionManager: toolDependencies.extensionManager,
),
CustomDevicesCommand(featureFlags: featureFlags, toolContext: toolContext),
CreateCommand(
androidContext: toolDependencies.androidContext,
appleContext: toolDependencies.appleContext,
templateRenderer: const MustacheTemplateRenderer(),
toolContext: toolContext,
extensionTemplateManager: extensionTemplateManager,
verboseHelp: verboseHelp,
),
DaemonCommand(
androidContext: toolDependencies.androidContext,
androidWorkflow: toolDependencies.androidWorkflow,
buildSystem: toolDependencies.buildSystem,
buildTargets: toolDependencies.buildTargets,
deviceManager: toolDependencies.deviceManager,
hidden: !verboseHelp,
toolContext: toolContext,
xcode: toolDependencies.appleContext.xcode,
),
DebugAdapterCommand(toolContext: toolContext, verboseHelp: verboseHelp),
DevicesCommand(
deviceManager: toolDependencies.deviceManager,
doctor: toolDependencies.doctor,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
DoctorCommand(
doctor: toolDependencies.doctor,
extensionManager: toolDependencies.extensionManager,
toolContext: toolContext,
verbose: verbose,
),
DowngradeCommand(toolContext: toolContext, verboseHelp: verboseHelp),
DriveCommand(
buildSystem: toolDependencies.buildSystem,
buildTargets: toolDependencies.buildTargets,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
EmulatorsCommand(
doctor: toolDependencies.doctor,
emulatorManager: toolDependencies.emulatorManager,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
GenerateCommand(toolContext: toolContext),
GenerateLocalizationsCommand(toolContext: toolContext),
InstallCommand(toolContext: toolContext, verboseHelp: verboseHelp),
LogsCommand(toolContext: toolContext),
PackagesCommand(
buildSystem: toolDependencies.buildSystem,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
PrecacheCommand(
verboseHelp: verboseHelp,
cache: cache,
logger: logger,
platform: platform,
featureFlags: featureFlags,
toolContext: toolContext,
),
RunCommand(
appleContext: toolDependencies.appleContext,
buildSystem: toolDependencies.buildSystem,
buildTargets: toolDependencies.buildTargets,
toolContext: toolContext,
androidContext: toolDependencies.androidContext,
androidWorkflow: toolDependencies.androidWorkflow,
deviceManager: toolDependencies.deviceManager,
verboseHelp: verboseHelp,
),
ScreenshotCommand(toolContext: toolContext),
ShellCompletionCommand(toolContext: toolContext),
TestCommand(
templateRenderer: const MustacheTemplateRenderer(),
toolContext: toolContext,
verboseHelp: verboseHelp,
verbose: verbose,
nativeAssetsBuilder: nativeAssetsBuilder,
),
WidgetPreviewCommand(
buildSystem: toolDependencies.buildSystem,
buildTargets: toolDependencies.buildTargets,
toolContext: toolContext,
verboseHelp: verboseHelp,
),
UpgradeCommand(toolContext: toolContext, verboseHelp: verboseHelp),
SymbolizeCommand(toolContext: toolContext),
// Development-only commands. These are always hidden,
IdeConfigCommand(toolContext: toolContext),
UpdatePackagesCommand(toolContext: 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;
}
}