flutter-tool: enum cleanup (#124760)

flutter-tool: enum cleanup
This commit is contained in:
Kevin Moore 2023-04-13 18:55:05 -07:00 committed by GitHub
parent dca8d7c286
commit 76e587bf78
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 335 additions and 381 deletions

View File

@ -109,9 +109,9 @@ class AndroidApk extends ApplicationPackage implements PrebuiltApplicationPackag
if (buildInfo == null) {
filename = 'app.apk';
} else if (buildInfo.flavor == null) {
filename = 'app-${buildInfo.mode.name}.apk';
filename = 'app-${buildInfo.mode.cliName}.apk';
} else {
filename = 'app-${buildInfo.lowerCasedFlavor}-${buildInfo.mode.name}.apk';
filename = 'app-${buildInfo.lowerCasedFlavor}-${buildInfo.mode.cliName}.apk';
}
if (androidProject.isUsingGradle && androidProject.isSupportedVersion) {
@ -119,7 +119,7 @@ class AndroidApk extends ApplicationPackage implements PrebuiltApplicationPackag
if (androidProject.parent.isModule) {
// Module builds output the apk in a subdirectory that corresponds
// to the buildmode of the apk.
apkDirectory = apkDirectory.childDirectory(buildInfo!.mode.name);
apkDirectory = apkDirectory.childDirectory(buildInfo!.mode.cliName);
}
apkFile = apkDirectory.childFile(filename);
if (apkFile.existsSync()) {

View File

@ -20,7 +20,7 @@ void validateBuild(AndroidBuildInfo androidBuildInfo) {
}
if (buildInfo.mode.isPrecompiled && androidBuildInfo.targetArchs.contains(AndroidArch.x86)) {
throwToolExit(
'Cannot build ${androidBuildInfo.buildInfo.mode.name} mode for x86 ABI.\n'
'Cannot build ${androidBuildInfo.buildInfo.mode.cliName} mode for x86 ABI.\n'
'For more information see $kSupportedAbis .'
);
}

View File

@ -673,7 +673,7 @@ class CachedArtifacts implements Artifacts {
// https://github.com/flutter/flutter/issues/38935
String platformDirName = _enginePlatformDirectoryName(platform);
if (mode == BuildMode.profile || mode == BuildMode.release) {
platformDirName = '$platformDirName-${getNameForBuildMode(mode!)}';
platformDirName = '$platformDirName-${mode!.cliName}';
}
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, _platform, mode));
@ -713,7 +713,7 @@ class CachedArtifacts implements Artifacts {
if (mode == BuildMode.debug || mode == null) {
return _fileSystem.path.join(engineDir, platformName);
}
final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode), '-')}' : '';
final String suffix = mode != BuildMode.debug ? '-${snakeCase(mode.cliName, '-')}' : '';
return _fileSystem.path.join(engineDir, platformName + suffix);
case TargetPlatform.fuchsia_arm64:
case TargetPlatform.fuchsia_x64:
@ -727,7 +727,7 @@ class CachedArtifacts implements Artifacts {
case TargetPlatform.android_x64:
case TargetPlatform.android_x86:
assert(mode != null, 'Need to specify a build mode for platform $platform.');
final String suffix = mode != BuildMode.debug ? '-${snakeCase(getModeName(mode!), '-')}' : '';
final String suffix = mode != BuildMode.debug ? '-${snakeCase(mode!.cliName, '-')}' : '';
return _fileSystem.path.join(engineDir, platformName + suffix);
case TargetPlatform.android:
assert(false, 'cannot use TargetPlatform.android to look up artifacts');

View File

@ -34,6 +34,18 @@ String snakeCase(String str, [ String sep = '_' ]) {
(Match m) => '${m.start == 0 ? '' : sep}${m[0]!.toLowerCase()}');
}
abstract interface class CliEnum implements Enum {
String get cliName;
String get helpText;
static Map<String, String> allowedHelp<T extends CliEnum>(List<T> values) =>
Map<String, String>.fromEntries(
values.map(
(T e) => MapEntry<String, String>(e.cliName, e.helpText),
),
);
}
/// Converts `fooBar` to `FooBar`.
///
/// This uses [toBeginningOfSentenceCase](https://pub.dev/documentation/intl/latest/intl/toBeginningOfSentenceCase.html),
@ -53,13 +65,6 @@ String snakeCaseToTitleCase(String snakeCaseString) {
/// Return the plural of the given word (`cat(s)`).
String pluralize(String word, int count) => count == 1 ? word : '${word}s';
/// Return the name of an enum item.
String getEnumName(dynamic enumItem) {
final String name = '$enumItem';
final int index = name.indexOf('.');
return index == -1 ? name : name.substring(index + 1);
}
String toPrettyJson(Object jsonable) {
final String value = const JsonEncoder.withIndent(' ').convert(jsonable);
return '$value\n';

View File

@ -214,7 +214,7 @@ class BuildInfo {
bool get usesAot => isAotBuildMode(mode);
bool get supportsEmulator => isEmulatorBuildMode(mode);
bool get supportsSimulator => isEmulatorBuildMode(mode);
String get modeName => getModeName(mode);
String get modeName => mode.cliName;
String get friendlyModeName => getFriendlyModeName(mode);
/// the flavor name in the output apk files is lower-cased (see flutter.gradle),
@ -233,7 +233,7 @@ class BuildInfo {
// packagesPath and performanceMeasurementFile are not passed into
// the Environment map.
return <String, String>{
kBuildMode: getNameForBuildMode(mode),
kBuildMode: mode.cliName,
if (dartDefines.isNotEmpty)
kDartDefines: encodeDartDefines(dartDefines),
kDartObfuscation: dartObfuscation.toString(),
@ -377,41 +377,25 @@ class AndroidBuildInfo {
}
/// A summary of the compilation strategy used for Dart.
class BuildMode {
const BuildMode._(this.name);
factory BuildMode.fromName(String value) {
switch (value) {
case 'debug':
return BuildMode.debug;
case 'profile':
return BuildMode.profile;
case 'release':
return BuildMode.release;
case 'jit_release':
return BuildMode.jitRelease;
}
throw ArgumentError('$value is not a supported build mode');
}
enum BuildMode {
/// Built in JIT mode with no optimizations, enabled asserts, and a VM service.
static const BuildMode debug = BuildMode._('debug');
debug,
/// Built in AOT mode with some optimizations and a VM service.
static const BuildMode profile = BuildMode._('profile');
profile,
/// Built in AOT mode with all optimizations and no VM service.
static const BuildMode release = BuildMode._('release');
release,
/// Built in JIT mode with all optimizations and no VM service.
static const BuildMode jitRelease = BuildMode._('jit_release');
jitRelease;
factory BuildMode.fromCliName(String value) => values.singleWhere(
(BuildMode element) => element.cliName == value,
orElse: () =>
throw ArgumentError('$value is not a supported build mode'),
);
static const List<BuildMode> values = <BuildMode>[
debug,
profile,
release,
jitRelease,
];
static const Set<BuildMode> releaseModes = <BuildMode>{
release,
jitRelease,
@ -433,21 +417,10 @@ class BuildMode {
/// Whether this mode is using the precompiled runtime.
bool get isPrecompiled => !isJit;
/// The name for this build mode.
final String name;
String get cliName => snakeCase(name);
@override
String toString() => name;
}
/// Return the name for the build mode, or "any" if null.
String getNameForBuildMode(BuildMode buildMode) {
return buildMode.name;
}
/// Returns the [BuildMode] for a particular `name`.
BuildMode getBuildModeForName(String name) {
return BuildMode.fromName(name);
String toString() => cliName;
}
/// Environment type of the target device.
@ -540,10 +513,8 @@ String? validatedBuildNameForPlatform(TargetPlatform targetPlatform, String? bui
return buildName;
}
String getModeName(BuildMode mode) => getEnumName(mode);
String getFriendlyModeName(BuildMode mode) {
return snakeCase(getModeName(mode)).replaceAll('_', ' ');
return snakeCase(mode.cliName).replaceAll('_', ' ');
}
// Returns true if the selected build mode uses ahead-of-time compilation.

View File

@ -46,7 +46,7 @@ abstract class AndroidAssetBundle extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Directory outputDirectory = environment.outputDir
.childDirectory('flutter_assets')
..createSync(recursive: true);
@ -167,7 +167,7 @@ class AndroidAot extends AotElfBase {
}
@override
String get name => 'android_aot_${getNameForBuildMode(buildMode)}_'
String get name => 'android_aot_${buildMode.cliName}_'
'${getNameForTargetPlatform(targetPlatform)}';
/// The specific Android ABI we are building for.
@ -229,7 +229,7 @@ class AndroidAot extends AotElfBase {
extraGenSnapshotOptions.add('--loading_unit_manifest=$manifestPath');
outputs.add(environment.fileSystem.file(manifestPath));
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
final String? codeSizeDirectory = environment.defines[kCodeSizeDirectory];
@ -299,7 +299,7 @@ class AndroidAotBundle extends Target {
}
@override
String get name => 'android_aot_bundle_${getNameForBuildMode(dependency.buildMode)}_'
String get name => 'android_aot_bundle_${dependency.buildMode.cliName}_'
'${getNameForTargetPlatform(dependency.targetPlatform)}';
TargetPlatform get targetPlatform => dependency.targetPlatform;
@ -390,7 +390,7 @@ class AndroidAotDeferredComponentsBundle extends Target {
}
@override
String get name => 'android_aot_deferred_components_bundle_${getNameForBuildMode(dependency.buildMode)}_'
String get name => 'android_aot_deferred_components_bundle_${dependency.buildMode.cliName}_'
'${getNameForTargetPlatform(dependency.targetPlatform)}';
TargetPlatform get targetPlatform => dependency.targetPlatform;
@ -499,7 +499,7 @@ Depfile copyDeferredComponentSoFiles(
.childDirectory(component.name)
.childDirectory('intermediates')
.childDirectory('flutter')
.childDirectory(buildMode.name)
.childDirectory(buildMode.cliName)
.childDirectory('deferred_libs')
.childDirectory(abi)
.childFile('libapp.so-${unit.id}.part.so');

View File

@ -178,7 +178,7 @@ Future<Depfile> copyAssets(
// If deferred components are disabled, then copy assets to regular location.
final File file = environment.defines[kDeferredComponents] == 'true'
? environment.fileSystem.file(
environment.fileSystem.path.join(componentOutputDir.path, buildMode.name, 'deferred_assets', 'flutter_assets', entry.key))
environment.fileSystem.path.join(componentOutputDir.path, buildMode.cliName, 'deferred_assets', 'flutter_assets', entry.key))
: environment.fileSystem.file(
environment.fileSystem.path.join(outputDirectory.path, entry.key));
outputs.add(file);

View File

@ -56,7 +56,7 @@ class CopyFlutterBundle extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'copy_flutter_bundle');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
environment.outputDir.createSync(recursive: true);
// Only copy the prebuilt runtimes and kernel blob in debug mode.
@ -167,7 +167,7 @@ class KernelSnapshot extends Target {
if (targetPlatformEnvironment == null) {
throw MissingDefineException(kTargetPlatform, 'kernel_snapshot');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final String targetFile = environment.defines[kTargetFile] ?? environment.fileSystem.path.join('lib', 'main.dart');
final File packagesFile = environment.projectDir
.childDirectory('.dart_tool')
@ -272,7 +272,7 @@ abstract class AotElfBase extends Target {
throw MissingDefineException(kTargetPlatform, 'aot_elf');
}
final List<String> extraGenSnapshotOptions = decodeCommaSeparated(environment.defines, kExtraGenSnapshotOptions);
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final TargetPlatform targetPlatform = getTargetPlatformForName(targetPlatformEnvironment);
final String? splitDebugInfo = environment.defines[kSplitDebugInfo];
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';

View File

@ -56,7 +56,7 @@ abstract class AotAssemblyBase extends Target {
}
final List<String> extraGenSnapshotOptions = decodeCommaSeparated(environment.defines, kExtraGenSnapshotOptions);
final BuildMode buildMode = getBuildModeForName(environmentBuildMode);
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
final TargetPlatform targetPlatform = getTargetPlatformForName(environmentTargetPlatform);
final String? splitDebugInfo = environment.defines[kSplitDebugInfo];
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
@ -461,7 +461,7 @@ abstract class IosAssetBundle extends Target {
if (environmentBuildMode == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(environmentBuildMode);
final BuildMode buildMode = BuildMode.fromCliName(environmentBuildMode);
final Directory frameworkDirectory = environment.outputDir.childDirectory('App.framework');
final String frameworkBinaryPath = frameworkDirectory.childFile('App').path;
final Directory assetDirectory = frameworkDirectory.childDirectory('flutter_assets');

View File

@ -53,7 +53,7 @@ class UnpackLinux extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final String engineSourcePath = environment.artifacts
.getArtifactPath(
Artifact.linuxDesktopPath,
@ -125,7 +125,7 @@ abstract class BundleLinuxAssets extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'bundle_linux_assets');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Directory outputDirectory = environment.outputDir
.childDirectory('flutter_assets');
if (!outputDirectory.existsSync()) {

View File

@ -50,7 +50,7 @@ abstract class UnpackMacOS extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'unpack_macos');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final String basePath = environment.artifacts.getArtifactPath(Artifact.flutterMacOSFramework, mode: buildMode);
final ProcessResult result = environment.processManager.runSync(<String>[
@ -245,7 +245,7 @@ class CompileMacOSFramework extends Target {
if (targetPlatformEnvironment == null) {
throw MissingDefineException(kTargetPlatform, 'kernel_snapshot');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
if (buildMode == BuildMode.debug) {
throw Exception('precompiled macOS framework only supported in release/profile builds.');
}
@ -372,7 +372,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'compile_macos_framework');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Directory frameworkRootDirectory = environment
.outputDir
.childDirectory('App.framework');

View File

@ -40,20 +40,6 @@ const String kBaseHref = 'baseHref';
/// The caching strategy to use for service worker generation.
const String kServiceWorkerStrategy = 'ServiceWorkerStrategy';
const String kOfflineFirst = 'offline-first';
const String kNoneWorker = 'none';
/// Convert a [value] into a [ServiceWorkerStrategy].
ServiceWorkerStrategy _serviceWorkerStrategyFromString(String? value) {
switch (value) {
case kNoneWorker:
return ServiceWorkerStrategy.none;
// offline-first is the default value for any invalid requests.
default:
return ServiceWorkerStrategy.offlineFirst;
}
}
/// Generates an entry point for a web target.
// Keep this in sync with build_runner/resident_web_runner.dart
class WebEntrypointTarget extends Target {
@ -175,7 +161,7 @@ class Dart2JSTarget extends Dart2WebTarget {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final JsCompilerConfig compilerConfig = JsCompilerConfig.fromBuildSystemEnvironment(environment.defines);
final Artifacts artifacts = globals.artifacts!;
final String platformBinariesPath = getWebPlatformBinariesDirectory(artifacts, webRenderer).path;
@ -256,7 +242,7 @@ class Dart2WasmTarget extends Dart2WebTarget {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Artifacts artifacts = globals.artifacts!;
final File outputWasmFile = environment.buildDir.childFile('main.dart.wasm');
final File depFile = environment.buildDir.childFile('dart2wasm.d');
@ -590,9 +576,8 @@ class WebServiceWorker extends Target {
final File serviceWorkerFile = environment.outputDir
.childFile('flutter_service_worker.js');
final Depfile depfile = Depfile(contents, <File>[serviceWorkerFile]);
final ServiceWorkerStrategy serviceWorkerStrategy = _serviceWorkerStrategyFromString(
environment.defines[kServiceWorkerStrategy],
);
final ServiceWorkerStrategy serviceWorkerStrategy =
ServiceWorkerStrategy.fromCliName(environment.defines[kServiceWorkerStrategy]);
final String fileGeneratorsPath =
globals.artifacts!.getArtifactPath(Artifact.flutterToolsFileGenerators);
final String serviceWorker = generateServiceWorker(

View File

@ -56,7 +56,7 @@ class UnpackWindows extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, name);
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final String engineSourcePath = environment.artifacts
.getArtifactPath(
Artifact.windowsDesktopPath,
@ -127,7 +127,7 @@ abstract class BundleWindowsAssets extends Target {
if (buildModeEnvironment == null) {
throw MissingDefineException(kBuildMode, 'bundle_windows_assets');
}
final BuildMode buildMode = getBuildModeForName(buildModeEnvironment);
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
final Directory outputDirectory = environment.outputDir
.childDirectory('flutter_assets');
if (!outputDirectory.existsSync()) {

View File

@ -125,7 +125,7 @@ class BuildAarCommand extends BuildSubCommand {
androidBuildInfo.add(
AndroidBuildInfo(
await getBuildInfo(
forcedBuildMode: BuildMode.fromName(buildMode),
forcedBuildMode: BuildMode.fromCliName(buildMode),
forcedTargetFile: targetFile,
),
targetArchs: targetArchitectures,

View File

@ -139,7 +139,7 @@ class BuildAppBundleCommand extends BuildSubCommand {
.childDirectory(component.name)
.childDirectory('intermediates')
.childDirectory('flutter')
.childDirectory(androidBuildInfo.buildInfo.mode.name)
.childDirectory(androidBuildInfo.buildInfo.mode.cliName)
.childDirectory('deferred_libs');
if (deferredLibsIntermediate.existsSync()) {
deferredLibsIntermediate.deleteSync(recursive: true);

View File

@ -239,8 +239,8 @@ class BuildIOSFrameworkCommand extends BuildFrameworkCommand {
displayNullSafetyMode(buildInfos.first);
for (final BuildInfo buildInfo in buildInfos) {
final String? productBundleIdentifier = await project.ios.productBundleIdentifier(buildInfo);
globals.printStatus('Building frameworks for $productBundleIdentifier in ${getNameForBuildMode(buildInfo.mode)} mode...');
final String xcodeBuildConfiguration = sentenceCase(getNameForBuildMode(buildInfo.mode));
globals.printStatus('Building frameworks for $productBundleIdentifier in ${buildInfo.mode.cliName} mode...');
final String xcodeBuildConfiguration = sentenceCase(buildInfo.mode.cliName);
final Directory modeDirectory = outputDirectory.childDirectory(xcodeBuildConfiguration);
if (modeDirectory.existsSync()) {
@ -332,7 +332,7 @@ class BuildIOSFrameworkCommand extends BuildFrameworkCommand {
throwToolExit('Could not find license at ${license.path}');
}
final String licenseSource = license.readAsStringSync();
final String artifactsMode = mode == BuildMode.debug ? 'ios' : 'ios-${mode.name}';
final String artifactsMode = mode == BuildMode.debug ? 'ios' : 'ios-${mode.cliName}';
final String podspecContents = '''
Pod::Spec.new do |s|
@ -510,7 +510,7 @@ end
}
// Always build debug for simulator.
final String simulatorConfiguration = sentenceCase(getNameForBuildMode(BuildMode.debug));
final String simulatorConfiguration = sentenceCase(BuildMode.debug.cliName);
pluginsBuildCommand = <String>[
...globals.xcode!.xcrunCommand(),
'xcodebuild',

View File

@ -72,8 +72,8 @@ class BuildMacOSFrameworkCommand extends BuildFrameworkCommand {
displayNullSafetyMode(buildInfos.first);
for (final BuildInfo buildInfo in buildInfos) {
globals.printStatus('Building macOS frameworks in ${getNameForBuildMode(buildInfo.mode)} mode...');
final String xcodeBuildConfiguration = sentenceCase(getNameForBuildMode(buildInfo.mode));
globals.printStatus('Building macOS frameworks in ${buildInfo.mode.cliName} mode...');
final String xcodeBuildConfiguration = sentenceCase(buildInfo.mode.cliName);
final Directory modeDirectory = outputDirectory.childDirectory(xcodeBuildConfiguration);
if (modeDirectory.existsSync()) {
@ -143,7 +143,7 @@ class BuildMacOSFrameworkCommand extends BuildFrameworkCommand {
throwToolExit('Could not find license at ${license.path}');
}
final String licenseSource = license.readAsStringSync();
final String artifactsMode = mode == BuildMode.debug ? 'darwin-x64' : 'darwin-x64-${mode.name}';
final String artifactsMode = mode == BuildMode.debug ? 'darwin-x64' : 'darwin-x64-${mode.cliName}';
final String podspecContents = '''
Pod::Spec.new do |s|

View File

@ -4,8 +4,8 @@
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../build_system/targets/web.dart';
import '../features.dart';
import '../globals.dart' as globals;
import '../html_utils.dart';
@ -13,6 +13,7 @@ import '../project.dart';
import '../runner/flutter_command.dart'
show DevelopmentArtifact, FlutterCommandResult, FlutterOptions;
import '../web/compile.dart';
import '../web/file_generators/flutter_service_worker_js.dart';
import 'build.dart';
class BuildWebCommand extends BuildSubCommand {
@ -43,22 +44,12 @@ class BuildWebCommand extends BuildSubCommand {
'The value has to start and end with a slash "/". '
'For more information: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base'
);
argParser.addOption('pwa-strategy',
defaultsTo: kOfflineFirst,
argParser.addOption(
'pwa-strategy',
defaultsTo: ServiceWorkerStrategy.offlineFirst.cliName,
help: 'The caching strategy to be used by the PWA service worker.',
allowed: <String>[
kOfflineFirst,
kNoneWorker,
],
allowedHelp: <String, String>{
kOfflineFirst: 'Attempt to cache the application shell eagerly and '
'then lazily cache all subsequent assets as they are loaded. When '
'making a network request for an asset, the offline cache will be '
'preferred.',
kNoneWorker: 'Generate a service worker with no body. This is useful for '
'local testing or in cases where the service worker caching functionality '
'is not desirable',
},
allowed: ServiceWorkerStrategy.values.map((ServiceWorkerStrategy e) => e.cliName),
allowedHelp: CliEnum.allowedHelp(ServiceWorkerStrategy.values),
);
usesWebRendererOption();
usesWebResourcesCdnFlag();
@ -196,7 +187,7 @@ class BuildWebCommand extends BuildSubCommand {
flutterProject,
target,
buildInfo,
stringArg('pwa-strategy')!,
ServiceWorkerStrategy.fromCliName(stringArg('pwa-strategy')),
compilerConfig: compilerConfig,
baseHref: baseHref,
outputDirectoryPath: outputDirectoryPath,

View File

@ -36,25 +36,10 @@ class CreateCommand extends CreateBase {
argParser.addOption(
'template',
abbr: 't',
allowed: FlutterProjectType.values.map<String>(flutterProjectTypeToString),
allowed: FlutterProjectType.values.map<String>((FlutterProjectType e) => e.cliName),
help: 'Specify the type of project to create.',
valueHelp: 'type',
allowedHelp: <String, String>{
flutterProjectTypeToString(FlutterProjectType.app): '(default) Generate a Flutter application.',
flutterProjectTypeToString(FlutterProjectType.skeleton): 'Generate a List View / Detail View Flutter '
'application that follows community best practices.',
flutterProjectTypeToString(FlutterProjectType.package): 'Generate a shareable Flutter project containing modular '
'Dart code.',
flutterProjectTypeToString(FlutterProjectType.plugin): 'Generate a shareable Flutter project containing an API '
'in Dart code with a platform-specific implementation through method channels for Android, iOS, '
'Linux, macOS, Windows, web, or any combination of these.',
flutterProjectTypeToString(FlutterProjectType.ffiPlugin):
'Generate a shareable Flutter project containing an API '
'in Dart code with a platform-specific implementation through dart:ffi for Android, iOS, '
'Linux, macOS, Windows, or any combination of these.',
flutterProjectTypeToString(FlutterProjectType.module): 'Generate a project to add a Flutter module to an '
'existing Android or iOS application.',
},
allowedHelp: CliEnum.allowedHelp(FlutterProjectType.values),
);
argParser.addOption(
'sample',
@ -164,7 +149,7 @@ class CreateCommand extends CreateBase {
final bool metadataExists = projectDir.absolute.childFile('.metadata').existsSync();
final String? templateArgument = stringArg('template');
if (templateArgument != null) {
template = stringToProjectType(templateArgument);
template = FlutterProjectType.fromCliName(templateArgument);
}
// If the project directory exists and isn't empty, then try to determine the template
// type from the project directory.
@ -182,8 +167,8 @@ class CreateCommand extends CreateBase {
if (detectedProjectType != null && template != detectedProjectType && metadataExists) {
// We can only be definitive that this is the wrong type if the .metadata file
// exists and contains a type that doesn't match.
throwToolExit("The requested template type '${flutterProjectTypeToString(template)}' doesn't match the "
"existing template type of '${flutterProjectTypeToString(detectedProjectType)}'.");
throwToolExit("The requested template type '${template.cliName}' doesn't match the "
"existing template type of '${detectedProjectType.cliName}'.");
}
return template;
}
@ -210,9 +195,9 @@ class CreateCommand extends CreateBase {
final bool emptyArgument = boolArg('empty');
if (sampleArgument != null) {
final String? templateArgument = stringArg('template');
if (templateArgument != null && stringToProjectType(templateArgument) != FlutterProjectType.app) {
if (templateArgument != null && FlutterProjectType.fromCliName(templateArgument) != FlutterProjectType.app) {
throwToolExit('Cannot specify --sample with a project type other than '
'"${flutterProjectTypeToString(FlutterProjectType.app)}"');
'"${FlutterProjectType.app.cliName}"');
}
// Fetch the sample from the server.
sampleCode = await _fetchSampleFromServer(sampleArgument);
@ -221,7 +206,7 @@ class CreateCommand extends CreateBase {
final FlutterProjectType template = _getProjectType(projectDir);
final bool generateModule = template == FlutterProjectType.module;
final bool generateMethodChannelsPlugin = template == FlutterProjectType.plugin;
final bool generateFfiPlugin = template == FlutterProjectType.ffiPlugin;
final bool generateFfiPlugin = template == FlutterProjectType.pluginFfi;
final bool generatePackage = template == FlutterProjectType.package;
final List<String> platforms = stringsArg('platforms');
@ -391,7 +376,7 @@ class CreateCommand extends CreateBase {
projectType: template,
);
pubContext = PubContext.createPlugin;
case FlutterProjectType.ffiPlugin:
case FlutterProjectType.pluginFfi:
generatedFileCount += await _generateFfiPlugin(
relativeDir,
templateContext,

View File

@ -954,7 +954,7 @@ class DeviceDomain extends Domain {
throw DaemonException("device '$deviceId' not found");
}
final String buildMode = _getStringArg(args, 'buildMode', required: true)!;
return await device.supportsRuntimeMode(getBuildModeForName(buildMode));
return await device.supportsRuntimeMode(BuildMode.fromCliName(buildMode));
}
/// Creates an application package from a file in the temp directory.

View File

@ -17,10 +17,12 @@ import '../base/io.dart';
import '../base/logger.dart';
import '../base/platform.dart';
import '../base/signals.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../dart/package_map.dart';
import '../device.dart';
import '../drive/drive_service.dart';
import '../drive/web_driver_service.dart' show Browser;
import '../globals.dart' as globals;
import '../ios/devices.dart';
import '../resident_runner.dart';
@ -111,25 +113,12 @@ class DriveCommand extends RunCommandBase {
defaultsTo: true,
help: 'Whether the driver browser is going to be launched in headless mode.',
)
..addOption('browser-name',
defaultsTo: 'chrome',
..addOption(
'browser-name',
defaultsTo: Browser.chrome.cliName,
help: 'Name of the browser where tests will be executed.',
allowed: <String>[
'android-chrome',
'chrome',
'edge',
'firefox',
'ios-safari',
'safari',
],
allowedHelp: <String, String>{
'android-chrome': 'Chrome on Android (see also "--android-emulator").',
'chrome': 'Google Chrome on this computer (see also "--chrome-binary").',
'edge': 'Microsoft Edge on this computer (Windows only).',
'firefox': 'Mozilla Firefox on this computer.',
'ios-safari': 'Apple Safari on an iOS device.',
'safari': 'Apple Safari on this computer (macOS only).',
},
allowed: Browser.values.map((Browser e) => e.cliName),
allowedHelp: CliEnum.allowedHelp(Browser.values),
)
..addOption('browser-dimension',
defaultsTo: '1600,1024',

View File

@ -774,7 +774,7 @@ class RunCommand extends RunCommandBase {
ExitStatus.success,
timingLabelParts: <String?>[
if (hotMode) 'hot' else 'cold',
getModeName(getBuildMode()),
getBuildMode().cliName,
if (devices!.length == 1)
getNameForTargetPlatform(await devices![0].targetPlatform)
else

View File

@ -165,37 +165,33 @@ class StdoutHandler {
}
/// List the preconfigured build options for a given build mode.
List<String> buildModeOptions(BuildMode mode, List<String> dartDefines) {
switch (mode) {
case BuildMode.debug:
return <String>[
// These checks allow the CLI to override the value of this define for unit
// testing the framework.
if (!dartDefines.any((String define) => define.startsWith('dart.vm.profile')))
List<String> buildModeOptions(BuildMode mode, List<String> dartDefines) =>
switch (mode) {
BuildMode.debug => <String>[
// These checks allow the CLI to override the value of this define for unit
// testing the framework.
if (!dartDefines.any((String define) => define.startsWith('dart.vm.profile')))
'-Ddart.vm.profile=false',
if (!dartDefines.any((String define) => define.startsWith('dart.vm.product')))
'-Ddart.vm.product=false',
'--enable-asserts',
],
BuildMode.profile => <String>[
// These checks allow the CLI to override the value of this define for
// benchmarks with most timeline traces disabled.
if (!dartDefines.any((String define) => define.startsWith('dart.vm.profile')))
'-Ddart.vm.profile=true',
if (!dartDefines.any((String define) => define.startsWith('dart.vm.product')))
'-Ddart.vm.product=false',
...kDartCompilerExperiments,
],
BuildMode.release => <String>[
'-Ddart.vm.profile=false',
if (!dartDefines.any((String define) => define.startsWith('dart.vm.product')))
'-Ddart.vm.product=false',
'--enable-asserts',
];
case BuildMode.profile:
return <String>[
// These checks allow the CLI to override the value of this define for
// benchmarks with most timeline traces disabled.
if (!dartDefines.any((String define) => define.startsWith('dart.vm.profile')))
'-Ddart.vm.profile=true',
if (!dartDefines.any((String define) => define.startsWith('dart.vm.product')))
'-Ddart.vm.product=false',
...kDartCompilerExperiments,
];
case BuildMode.release:
return <String>[
'-Ddart.vm.profile=false',
'-Ddart.vm.product=true',
...kDartCompilerExperiments,
];
}
throw Exception('Unknown BuildMode: $mode');
}
'-Ddart.vm.product=true',
...kDartCompilerExperiments,
],
_ => throw Exception('Unknown BuildMode: $mode')
};
/// A compiler interface for producing single (non-incremental) kernel files.
class KernelCompiler {

View File

@ -2,8 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:math' as math;
@ -16,6 +14,7 @@ import '../base/common.dart';
import '../base/io.dart';
import '../base/logger.dart';
import '../base/process.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../convert.dart';
import '../device.dart';
@ -140,7 +139,7 @@ class WebDriverService extends DriverService {
String? profileMemory,
}) async {
late async_io.WebDriver webDriver;
final Browser browser = _browserNameToEnum(browserName);
final Browser browser = Browser.fromCliName(browserName);
try {
webDriver = await async_io.createDriver(
uri: Uri.parse('http://localhost:$driverPort/'),
@ -211,9 +210,9 @@ class WebDriverService extends DriverService {
'DRIVER_SESSION_URI': webDriver.uri.toString(),
'DRIVER_SESSION_SPEC': webDriver.spec.toString(),
'DRIVER_SESSION_CAPABILITIES': json.encode(webDriver.capabilities),
'SUPPORT_TIMELINE_ACTION': (_browserNameToEnum(browserName) == Browser.chrome).toString(),
'SUPPORT_TIMELINE_ACTION': (Browser.fromCliName(browserName) == Browser.chrome).toString(),
'FLUTTER_WEB_TEST': 'true',
'ANDROID_CHROME_ON_EMULATOR': (_browserNameToEnum(browserName) == Browser.androidChrome && androidEmulator!).toString(),
'ANDROID_CHROME_ON_EMULATOR': (Browser.fromCliName(browserName) == Browser.androidChrome && androidEmulator!).toString(),
};
}
@ -224,19 +223,42 @@ class WebDriverService extends DriverService {
}
/// A list of supported browsers.
enum Browser {
/// Chrome on Android: https://developer.chrome.com/multidevice/android/overview
enum Browser implements CliEnum {
/// Chrome on Android: https://developer.chrome.com/docs/multidevice/android/
androidChrome,
/// Chrome: https://www.google.com/chrome/
chrome,
/// Edge: https://www.microsoft.com/en-us/windows/microsoft-edge
/// Edge: https://www.microsoft.com/edge
edge,
/// Firefox: https://www.mozilla.org/en-US/firefox/
firefox,
/// Safari in iOS: https://www.apple.com/safari/
iosSafari,
/// Safari in macOS: https://www.apple.com/safari/
safari,
safari;
@override
String get helpText => switch (this) {
Browser.androidChrome => 'Chrome on Android (see also "--android-emulator").',
Browser.chrome => 'Google Chrome on this computer (see also "--chrome-binary").',
Browser.edge => 'Microsoft Edge on this computer (Windows only).',
Browser.firefox => 'Mozilla Firefox on this computer.',
Browser.iosSafari => 'Apple Safari on an iOS device.',
Browser.safari => 'Apple Safari on this computer (macOS only).',
};
@override
String get cliName => snakeCase(name, '-');
static Browser fromCliName(String? value) => Browser.values.singleWhere(
(Browser element) => element.cliName == value,
orElse: () => throw UnsupportedError('Browser $value not supported'),
);
}
/// Returns desired capabilities for given [browser], [headless], [chromeBinary]
@ -247,102 +269,80 @@ Map<String, dynamic> getDesiredCapabilities(
bool? headless, {
List<String> webBrowserFlags = const <String>[],
String? chromeBinary,
}) {
switch (browser) {
case Browser.chrome:
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
async_io.LogType.browser: 'INFO',
async_io.LogType.performance: 'ALL',
},
'goog:chromeOptions': <String, dynamic>{
if (chromeBinary != null)
'binary': chromeBinary,
'w3c': true,
'args': <String>[
'--bwsi',
'--disable-background-timer-throttling',
'--disable-default-apps',
'--disable-extensions',
'--disable-popup-blocking',
'--disable-translate',
'--no-default-browser-check',
'--no-sandbox',
'--no-first-run',
if (headless!) '--headless',
...webBrowserFlags,
],
'perfLoggingPrefs': <String, String>{
'traceCategories':
'devtools.timeline,'
'v8,blink.console,benchmark,blink,'
'blink.user_timing',
}) =>
switch (browser) {
Browser.chrome => <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'chrome',
'goog:loggingPrefs': <String, String>{
async_io.LogType.browser: 'INFO',
async_io.LogType.performance: 'ALL',
},
'goog:chromeOptions': <String, dynamic>{
if (chromeBinary != null) 'binary': chromeBinary,
'w3c': true,
'args': <String>[
'--bwsi',
'--disable-background-timer-throttling',
'--disable-default-apps',
'--disable-extensions',
'--disable-popup-blocking',
'--disable-translate',
'--no-default-browser-check',
'--no-sandbox',
'--no-first-run',
if (headless!) '--headless',
...webBrowserFlags,
],
'perfLoggingPrefs': <String, String>{
'traceCategories': 'devtools.timeline,'
'v8,blink.console,benchmark,blink,'
'blink.user_timing',
},
},
},
};
case Browser.firefox:
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions' : <String, dynamic>{
'args': <String>[
if (headless!) '-headless',
...webBrowserFlags,
],
'prefs': <String, dynamic>{
'dom.file.createInChild': true,
'dom.timeout.background_throttling_max_budget': -1,
'media.autoplay.default': 0,
'media.gmp-manager.url': '',
'media.gmp-provider.enabled': false,
'network.captive-portal-service.enabled': false,
'security.insecure_field_warning.contextual.enabled': false,
'test.currentTimeOffsetSeconds': 11491200,
Browser.firefox => <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'firefox',
'moz:firefoxOptions': <String, dynamic>{
'args': <String>[
if (headless!) '-headless',
...webBrowserFlags,
],
'prefs': <String, dynamic>{
'dom.file.createInChild': true,
'dom.timeout.background_throttling_max_budget': -1,
'media.autoplay.default': 0,
'media.gmp-manager.url': '',
'media.gmp-provider.enabled': false,
'network.captive-portal-service.enabled': false,
'security.insecure_field_warning.contextual.enabled': false,
'test.currentTimeOffsetSeconds': 11491200,
},
'log': <String, String>{'level': 'trace'},
},
'log': <String, String>{'level': 'trace'},
},
};
case Browser.edge:
return <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'edge',
};
case Browser.safari:
return <String, dynamic>{
'browserName': 'safari',
};
case Browser.iosSafari:
return <String, dynamic>{
'platformName': 'ios',
'browserName': 'safari',
'safari:useSimulator': true,
};
case Browser.androidChrome:
return <String, dynamic>{
'browserName': 'chrome',
'platformName': 'android',
'goog:chromeOptions': <String, dynamic>{
'androidPackage': 'com.android.chrome',
'args': <String>[
'--disable-fullscreen',
...webBrowserFlags,
],
Browser.edge => <String, dynamic>{
'acceptInsecureCerts': true,
'browserName': 'edge',
},
};
}
}
/// Converts [browserName] string to [Browser]
Browser _browserNameToEnum(String? browserName) {
switch (browserName) {
case 'android-chrome': return Browser.androidChrome;
case 'chrome': return Browser.chrome;
case 'edge': return Browser.edge;
case 'firefox': return Browser.firefox;
case 'ios-safari': return Browser.iosSafari;
case 'safari': return Browser.safari;
}
throw UnsupportedError('Browser $browserName not supported');
}
Browser.safari => <String, dynamic>{
'browserName': 'safari',
},
Browser.iosSafari => <String, dynamic>{
'platformName': 'ios',
'browserName': 'safari',
'safari:useSimulator': true,
},
Browser.androidChrome => <String, dynamic>{
'browserName': 'chrome',
'platformName': 'android',
'goog:chromeOptions': <String, dynamic>{
'androidPackage': 'com.android.chrome',
'args': <String>[
'--disable-fullscreen',
...webBrowserFlags,
],
},
},
};

View File

@ -10,44 +10,59 @@ import 'base/utils.dart';
import 'project.dart';
import 'version.dart';
enum FlutterProjectType {
enum FlutterProjectType implements CliEnum {
/// This is the default project with the user-managed host code.
/// It is different than the "module" template in that it exposes and doesn't
/// manage the platform code.
app,
/// A List/Detail app template that follows community best practices.
skeleton,
/// The is a project that has managed platform host code. It is an application with
/// ephemeral .ios and .android directories that can be updated automatically.
module,
/// This is a Flutter Dart package project. It doesn't have any native
/// components, only Dart.
package,
/// This is a native plugin project.
plugin,
/// This is an FFI native plugin project.
ffiPlugin,
}
pluginFfi;
String flutterProjectTypeToString(FlutterProjectType? type) {
if (type == null) {
return '';
}
if (type == FlutterProjectType.ffiPlugin) {
return 'plugin_ffi';
}
return getEnumName(type);
}
@override
String get cliName => snakeCase(name);
FlutterProjectType? stringToProjectType(String value) {
FlutterProjectType? result;
for (final FlutterProjectType type in FlutterProjectType.values) {
if (value == flutterProjectTypeToString(type)) {
result = type;
break;
@override
String get helpText => switch (this) {
FlutterProjectType.app => '(default) Generate a Flutter application.',
FlutterProjectType.skeleton =>
'Generate a List View / Detail View Flutter application that follows community best practices.',
FlutterProjectType.package =>
'Generate a shareable Flutter project containing modular Dart code.',
FlutterProjectType.plugin =>
'Generate a shareable Flutter project containing an API '
'in Dart code with a platform-specific implementation through method channels for Android, iOS, '
'Linux, macOS, Windows, web, or any combination of these.',
FlutterProjectType.pluginFfi =>
'Generate a shareable Flutter project containing an API '
'in Dart code with a platform-specific implementation through dart:ffi for Android, iOS, '
'Linux, macOS, Windows, or any combination of these.',
FlutterProjectType.module =>
'Generate a project to add a Flutter module to an existing Android or iOS application.',
};
static FlutterProjectType? fromCliName(String value) {
for (final FlutterProjectType type in FlutterProjectType.values) {
if (value == type.cliName) {
return type;
}
}
return null;
}
return result;
}
/// Verifies the expected yaml keys are present in the file.
@ -100,7 +115,7 @@ class FlutterProjectMetadata {
}
}
if (_validateMetadataMap(yamlRoot, <String, Type>{'project_type': String}, _logger)) {
_projectType = stringToProjectType(yamlRoot['project_type'] as String);
_projectType = FlutterProjectType.fromCliName(yamlRoot['project_type'] as String);
}
final Object? migrationYaml = yamlRoot['migration'];
if (migrationYaml is YamlMap) {
@ -163,7 +178,7 @@ version:
revision: $_versionRevision
channel: $_versionChannel
project_type: ${flutterProjectTypeToString(projectType)}
project_type: ${projectType == null ? '' : projectType!.cliName}
${migrateConfig.getOutputFileString()}''';
}

View File

@ -22,7 +22,6 @@ import '../base/terminal.dart';
import '../base/time.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../build_system/targets/web.dart';
import '../cache.dart';
import '../dart/language_version.dart';
import '../devfs.dart';
@ -37,6 +36,7 @@ import '../run_hot.dart';
import '../vmservice.dart';
import '../web/chrome.dart';
import '../web/compile.dart';
import '../web/file_generators/flutter_service_worker_js.dart';
import '../web/file_generators/main_dart.dart' as main_dart;
import '../web/web_device.dart';
import '../web/web_runner.dart';
@ -326,7 +326,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
flutterProject,
target,
debuggingOptions.buildInfo,
kNoneWorker,
ServiceWorkerStrategy.none,
compilerConfig: JsCompilerConfig.run(nativeNullAssertions: debuggingOptions.nativeNullAssertions)
);
}
@ -404,7 +404,7 @@ Please provide a valid TCP port (an integer between 0 and 65535, inclusive).
flutterProject,
target,
debuggingOptions.buildInfo,
kNoneWorker,
ServiceWorkerStrategy.none,
compilerConfig: JsCompilerConfig.run(nativeNullAssertions: debuggingOptions.nativeNullAssertions),
);
} on ToolExit {

View File

@ -59,7 +59,7 @@ class BuildableLinuxApp extends LinuxApp {
final String? binaryName = getCmakeExecutableName(project);
return globals.fs.path.join(
getLinuxBuildDirectory(),
getNameForBuildMode(buildMode),
buildMode.cliName,
'bundle',
binaryName,
);

View File

@ -67,7 +67,7 @@ Future<void> buildLinux(
'Building Linux application...',
);
try {
final String buildModeName = getNameForBuildMode(buildInfo.mode);
final String buildModeName = buildInfo.mode.cliName;
final Directory buildDirectory =
globals.fs.directory(getLinuxBuildDirectory(targetPlatform)).childDirectory(buildModeName);
await _runCmake(buildModeName, linuxProject.cmakeFile.parent, buildDirectory,

View File

@ -172,7 +172,7 @@ class BuildableMacOSApp extends MacOSApp {
}
String bundleDirectory(BuildInfo buildInfo) {
return sentenceCase(buildInfo.mode.name) + (buildInfo.flavor != null
return sentenceCase(buildInfo.mode.cliName) + (buildInfo.flavor != null
? ' ${sentenceCase(buildInfo.flavor!)}'
: '');
}

View File

@ -131,7 +131,7 @@ ${_projectMetadataInformation()}
final FlutterProjectMetadata metadata = FlutterProjectMetadata(project.metadataFile, _logger);
final FlutterProjectType? projectType = metadata.projectType;
final StringBuffer description = StringBuffer()
..writeln('**Type**: ${projectType == null ? 'malformed' : flutterProjectTypeToString(projectType)}')
..writeln('**Type**: ${projectType == null ? 'malformed' : projectType.cliName}')
..writeln('**Version**: ${manifest.appVersion}')
..writeln('**Material**: ${manifest.usesMaterialDesign}')
..writeln('**Android X**: ${manifest.usesAndroidX}')

View File

@ -82,18 +82,7 @@ class FlutterCommandResult {
final DateTime? endTimeOverride;
@override
String toString() {
switch (exitStatus) {
case ExitStatus.success:
return 'success';
case ExitStatus.warning:
return 'warning';
case ExitStatus.fail:
return 'fail';
case ExitStatus.killed:
return 'killed';
}
}
String toString() => exitStatus.name;
}
/// Common flutter command line options.
@ -130,7 +119,7 @@ abstract final class FlutterOptions {
}
/// flutter command categories for usage.
class FlutterCommandCategory {
abstract final class FlutterCommandCategory {
static const String sdk = 'Flutter SDK';
static const String project = 'Project';
static const String tools = 'Tools & Devices';
@ -641,7 +630,7 @@ abstract class FlutterCommand extends Command<void> {
defaultsTo: WebRendererMode.auto.name,
allowed: WebRendererMode.values.map((WebRendererMode e) => e.name),
help: 'The renderer implementation to use when building for the web.',
allowedHelp: Map<String, String>.fromEntries(WebRendererMode.values.map((WebRendererMode e) => MapEntry<String, String>(e.name, e.helpText)))
allowedHelp: CliEnum.allowedHelp(WebRendererMode.values)
);
}
@ -1422,7 +1411,7 @@ abstract class FlutterCommand extends Command<void> {
// Send timing.
final List<String?> labels = <String?>[
getEnumName(commandResult.exitStatus),
commandResult.exitStatus.name,
if (commandResult.timingLabelParts?.isNotEmpty ?? false)
...?commandResult.timingLabelParts,
];

View File

@ -9,6 +9,7 @@ import '../base/common.dart';
import '../base/file_system.dart';
import '../base/logger.dart';
import '../base/project_migrator.dart';
import '../base/utils.dart';
import '../build_info.dart';
import '../build_system/build_system.dart';
import '../build_system/targets/web.dart';
@ -21,6 +22,7 @@ import '../project.dart';
import '../reporting/reporting.dart';
import '../version.dart';
import 'compiler_config.dart';
import 'file_generators/flutter_service_worker_js.dart';
import 'migrations/scrub_generated_plugin_registrant.dart';
export 'compiler_config.dart';
@ -51,7 +53,7 @@ class WebBuilder {
FlutterProject flutterProject,
String target,
BuildInfo buildInfo,
String serviceWorkerStrategy, {
ServiceWorkerStrategy serviceWorkerStrategy, {
required WebCompilerConfig compilerConfig,
String? baseHref,
String? outputDirectoryPath,
@ -93,7 +95,7 @@ class WebBuilder {
kTargetFile: target,
kHasWebPlugins: hasWebPlugins.toString(),
if (baseHref != null) kBaseHref: baseHref,
kServiceWorkerStrategy: serviceWorkerStrategy,
kServiceWorkerStrategy: serviceWorkerStrategy.cliName,
...compilerConfig.toBuildSystemEnvironment(),
...buildInfo.toBuildSystemEnvironment(),
},
@ -133,7 +135,7 @@ class WebBuilder {
}
/// Web rendering backend mode.
enum WebRendererMode {
enum WebRendererMode implements CliEnum {
/// Auto detects which rendering backend to use.
auto,
@ -146,6 +148,10 @@ enum WebRendererMode {
/// Always use skwasm.
skwasm;
@override
String get cliName => snakeCase(name, '-');
@override
String get helpText => switch (this) {
auto =>
'Use the HTML renderer on mobile devices, and CanvasKit on desktop devices.',

View File

@ -2,16 +2,41 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import '../../base/utils.dart';
import '../../globals.dart' as globals;
/// The caching strategy for the generated service worker.
enum ServiceWorkerStrategy {
enum ServiceWorkerStrategy implements CliEnum {
/// Download the app shell eagerly and all other assets lazily.
/// Prefer the offline cached version.
offlineFirst,
/// Do not generate a service worker,
none,
none;
@override
String get cliName => snakeCase(name, '-');
static ServiceWorkerStrategy fromCliName(String? value) => value == null
? ServiceWorkerStrategy.offlineFirst
: values.singleWhere(
(ServiceWorkerStrategy element) => element.cliName == value,
orElse: () =>
throw ArgumentError.value(value, 'value', 'Not supported.'),
);
@override
String get helpText => switch (this) {
ServiceWorkerStrategy.offlineFirst =>
'Attempt to cache the application shell eagerly and then lazily '
'cache all subsequent assets as they are loaded. When making a '
'network request for an asset, the offline cache will be '
'preferred.',
ServiceWorkerStrategy.none =>
'Generate a service worker with no body. This is useful for local '
'testing or in cases where the service worker caching '
'functionality is not desirable'
};
}
/// Generate a service worker with an app-specific cache name a map of

View File

@ -113,7 +113,7 @@ class BuildableWindowsApp extends WindowsApp {
return globals.fs.path.join(
getWindowsBuildDirectory(),
'runner',
sentenceCase(getNameForBuildMode(buildMode)),
sentenceCase(buildMode.cliName),
'$binaryName.exe',
);
}

View File

@ -76,7 +76,7 @@ Future<void> buildWindows(WindowsProject windowsProject, BuildInfo buildInfo, {
'Please run `flutter doctor` for more details.');
}
final String buildModeName = getNameForBuildMode(buildInfo.mode);
final String buildModeName = buildInfo.mode.cliName;
final Directory buildDirectory = globals.fs.directory(getWindowsBuildDirectory());
final Status status = globals.logger.startProgress(
'Building Windows application...',

View File

@ -74,11 +74,11 @@ void main() {
expect(BuildMode.jitRelease.isPrecompiled, false);
expect(BuildMode.jitRelease.isJit, true);
expect(BuildMode.fromName('debug'), BuildMode.debug);
expect(BuildMode.fromName('profile'), BuildMode.profile);
expect(BuildMode.fromName('jit_release'), BuildMode.jitRelease);
expect(BuildMode.fromName('release'), BuildMode.release);
expect(() => BuildMode.fromName('foo'), throwsArgumentError);
expect(BuildMode.fromCliName('debug'), BuildMode.debug);
expect(BuildMode.fromCliName('profile'), BuildMode.profile);
expect(BuildMode.fromCliName('jit_release'), BuildMode.jitRelease);
expect(BuildMode.fromCliName('release'), BuildMode.release);
expect(() => BuildMode.fromCliName('foo'), throwsArgumentError);
});
});

View File

@ -39,7 +39,7 @@ void main() {
androidEnvironment = Environment.test(
fileSystem.currentDirectory,
defines: <String, String>{
kBuildMode: getNameForBuildMode(BuildMode.profile),
kBuildMode: BuildMode.profile.cliName,
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
},
inputs: <String, String>{},
@ -52,7 +52,7 @@ void main() {
iosEnvironment = Environment.test(
fileSystem.currentDirectory,
defines: <String, String>{
kBuildMode: getNameForBuildMode(BuildMode.profile),
kBuildMode: BuildMode.profile.cliName,
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios),
},
inputs: <String, String>{},
@ -266,7 +266,7 @@ void main() {
]);
await const KernelSnapshot().build(androidEnvironment
..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
..defines[kBuildMode] = BuildMode.debug.cliName
..defines[kTrackWidgetCreation] = 'false');
expect(processManager, hasNoRemainingExpectations);
@ -308,7 +308,7 @@ void main() {
await const KernelSnapshot().build(androidEnvironment
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin)
..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
..defines[kBuildMode] = BuildMode.debug.cliName
..defines[kTrackWidgetCreation] = 'false'
);
@ -322,7 +322,7 @@ void main() {
final Environment testEnvironment = Environment.test(
fileSystem.currentDirectory,
defines: <String, String>{
kBuildMode: getNameForBuildMode(BuildMode.debug),
kBuildMode: BuildMode.debug.cliName,
kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm),
},
processManager: processManager,
@ -553,7 +553,7 @@ void main() {
testUsingContext('kExtraGenSnapshotOptions passes values to gen_snapshot', () async {
androidEnvironment.defines[kExtraGenSnapshotOptions] = 'foo,bar,baz=2';
androidEnvironment.defines[kBuildMode] = getNameForBuildMode(BuildMode.profile);
androidEnvironment.defines[kBuildMode] = BuildMode.profile.cliName;
final String build = androidEnvironment.buildDir.path;
processManager.addCommands(<FakeCommand>[

View File

@ -371,7 +371,7 @@ class FakeDesktopDevice extends DesktopDevice {
if (nullExecutablePathForDevice) {
return null;
}
return getNameForBuildMode(buildInfo.mode);
return buildInfo.mode.cliName;
}
}

View File

@ -170,18 +170,12 @@ FlutterProject setUpFlutterProject(Directory directory) {
class FakeLinuxApp extends Fake implements LinuxApp {
@override
String executable(BuildMode buildMode) {
switch (buildMode) {
case BuildMode.debug:
return 'debug/executable';
case BuildMode.profile:
return 'profile/executable';
case BuildMode.release:
return 'release/executable';
default:
throw StateError('Invalid mode: $buildMode');
}
}
String executable(BuildMode buildMode) => switch (buildMode) {
BuildMode.debug => 'debug/executable',
BuildMode.profile => 'profile/executable',
BuildMode.release => 'release/executable',
_ => throw StateError('Invalid mode: $buildMode'),
};
}
class FakeOperatingSystemUtils extends Fake implements OperatingSystemUtils {
FakeOperatingSystemUtils({

View File

@ -1479,6 +1479,8 @@ TerminalHandler setUpTerminalHandler(List<FakeVmServiceRequest> requests, {
..isRunningDebug = false
..isRunningProfile = false
..isRunningRelease = true;
case _:
// NOOP
}
return TerminalHandler(
residentRunner,

View File

@ -11,6 +11,7 @@ import 'package:flutter_tools/src/project.dart';
import 'package:flutter_tools/src/reporting/reporting.dart';
import 'package:flutter_tools/src/version.dart';
import 'package:flutter_tools/src/web/compile.dart';
import 'package:flutter_tools/src/web/file_generators/flutter_service_worker_js.dart';
import '../../src/common.dart';
import '../../src/context.dart';
@ -44,7 +45,7 @@ void main() {
expect(environment.defines, <String, String>{
'TargetFile': 'target',
'HasWebPlugins': 'false',
'ServiceWorkerStrategy': 'serviceWorkerStrategy',
'ServiceWorkerStrategy': ServiceWorkerStrategy.offlineFirst.cliName,
'WasmOmitTypeChecks': 'false',
'BuildMode': 'debug',
'DartObfuscation': 'false',
@ -68,7 +69,7 @@ void main() {
flutterProject,
'target',
BuildInfo.debug,
'serviceWorkerStrategy',
ServiceWorkerStrategy.offlineFirst,
compilerConfig: const WasmCompilerConfig(omitTypeChecks: false),
);
@ -108,7 +109,7 @@ void main() {
flutterProject,
'target',
BuildInfo.debug,
'serviceWorkerStrategy',
ServiceWorkerStrategy.offlineFirst,
compilerConfig: const JsCompilerConfig.run(nativeNullAssertions: true),
),
throwsToolExit(message: 'Failed to compile application for the Web.'));

View File

@ -130,5 +130,5 @@ WindowsDevice setUpWindowsDevice({
class FakeWindowsApp extends Fake implements WindowsApp {
@override
String executable(BuildMode buildMode) => '${buildMode.name}/executable';
String executable(BuildMode buildMode) => '${buildMode.cliName}/executable';
}

View File

@ -76,7 +76,7 @@ void main() {
});
for (final BuildMode buildMode in <BuildMode>[BuildMode.debug, BuildMode.release]) {
group('build in ${buildMode.name} mode', () {
group('build in ${buildMode.cliName} mode', () {
late Directory outputPath;
late Directory outputApp;
late Directory frameworkDirectory;
@ -98,7 +98,7 @@ void main() {
'ios',
'--verbose',
'--no-codesign',
'--${buildMode.name}',
'--${buildMode.cliName}',
'--obfuscate',
'--split-debug-info=foo debug info/',
], workingDirectory: projectRoot);
@ -125,7 +125,7 @@ void main() {
projectRoot,
'build',
'ios',
'${sentenceCase(buildMode.name)}-iphoneos',
'${sentenceCase(buildMode.cliName)}-iphoneos',
));
buildAppFrameworkDsym = buildPath.childDirectory('App.framework.dSYM');