diff --git a/packages/flutter_tools/lib/src/android/application_package.dart b/packages/flutter_tools/lib/src/android/application_package.dart index 807f1ba918..7eb9c2b279 100644 --- a/packages/flutter_tools/lib/src/android/application_package.dart +++ b/packages/flutter_tools/lib/src/android/application_package.dart @@ -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()) { diff --git a/packages/flutter_tools/lib/src/android/build_validation.dart b/packages/flutter_tools/lib/src/android/build_validation.dart index 598a462e1b..c61d353bd6 100644 --- a/packages/flutter_tools/lib/src/android/build_validation.dart +++ b/packages/flutter_tools/lib/src/android/build_validation.dart @@ -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 .' ); } diff --git a/packages/flutter_tools/lib/src/artifacts.dart b/packages/flutter_tools/lib/src/artifacts.dart index a874b3c703..5eaf7220e8 100644 --- a/packages/flutter_tools/lib/src/artifacts.dart +++ b/packages/flutter_tools/lib/src/artifacts.dart @@ -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'); diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart index 8bcc97e70a..41c7180881 100644 --- a/packages/flutter_tools/lib/src/base/utils.dart +++ b/packages/flutter_tools/lib/src/base/utils.dart @@ -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 allowedHelp(List values) => + Map.fromEntries( + values.map( + (T e) => MapEntry(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'; diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index 95d0e35314..859d7664b6 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -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 { - 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 values = [ - debug, - profile, - release, - jitRelease, - ]; static const Set releaseModes = { 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. diff --git a/packages/flutter_tools/lib/src/build_system/targets/android.dart b/packages/flutter_tools/lib/src/build_system/targets/android.dart index e911f11c37..cfca44090c 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/android.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/android.dart @@ -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'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/assets.dart b/packages/flutter_tools/lib/src/build_system/targets/assets.dart index 6b20531349..b5d3925851 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/assets.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/assets.dart @@ -178,7 +178,7 @@ Future 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); diff --git a/packages/flutter_tools/lib/src/build_system/targets/common.dart b/packages/flutter_tools/lib/src/build_system/targets/common.dart index c3e6ea4d72..91c04b028a 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/common.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/common.dart @@ -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 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'; diff --git a/packages/flutter_tools/lib/src/build_system/targets/ios.dart b/packages/flutter_tools/lib/src/build_system/targets/ios.dart index 9146f0bfa1..7790e92d10 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/ios.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/ios.dart @@ -56,7 +56,7 @@ abstract class AotAssemblyBase extends Target { } final List 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'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/linux.dart b/packages/flutter_tools/lib/src/build_system/targets/linux.dart index f68b425130..9a83d1c486 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/linux.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/linux.dart @@ -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()) { diff --git a/packages/flutter_tools/lib/src/build_system/targets/macos.dart b/packages/flutter_tools/lib/src/build_system/targets/macos.dart index 9aaf236b3f..6cb4620259 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/macos.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/macos.dart @@ -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([ @@ -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'); diff --git a/packages/flutter_tools/lib/src/build_system/targets/web.dart b/packages/flutter_tools/lib/src/build_system/targets/web.dart index 65eb87b4f0..22d2c1adbe 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/web.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/web.dart @@ -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, [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( diff --git a/packages/flutter_tools/lib/src/build_system/targets/windows.dart b/packages/flutter_tools/lib/src/build_system/targets/windows.dart index f940ebc596..642284a9cc 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/windows.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/windows.dart @@ -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()) { diff --git a/packages/flutter_tools/lib/src/commands/build_aar.dart b/packages/flutter_tools/lib/src/commands/build_aar.dart index c1144118d7..445620c7d1 100644 --- a/packages/flutter_tools/lib/src/commands/build_aar.dart +++ b/packages/flutter_tools/lib/src/commands/build_aar.dart @@ -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, diff --git a/packages/flutter_tools/lib/src/commands/build_appbundle.dart b/packages/flutter_tools/lib/src/commands/build_appbundle.dart index 742e544818..000d2cc2f3 100644 --- a/packages/flutter_tools/lib/src/commands/build_appbundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_appbundle.dart @@ -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); diff --git a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart index 69ee07c960..3e71c91152 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios_framework.dart @@ -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 = [ ...globals.xcode!.xcrunCommand(), 'xcodebuild', diff --git a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart index e229533e22..eb1f5fa29a 100644 --- a/packages/flutter_tools/lib/src/commands/build_macos_framework.dart +++ b/packages/flutter_tools/lib/src/commands/build_macos_framework.dart @@ -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| diff --git a/packages/flutter_tools/lib/src/commands/build_web.dart b/packages/flutter_tools/lib/src/commands/build_web.dart index 91eb889f0b..b1b673da37 100644 --- a/packages/flutter_tools/lib/src/commands/build_web.dart +++ b/packages/flutter_tools/lib/src/commands/build_web.dart @@ -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: [ - kOfflineFirst, - kNoneWorker, - ], - allowedHelp: { - 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, diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index cddc9e3d18..106ba35c1a 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart @@ -36,25 +36,10 @@ class CreateCommand extends CreateBase { argParser.addOption( 'template', abbr: 't', - allowed: FlutterProjectType.values.map(flutterProjectTypeToString), + allowed: FlutterProjectType.values.map((FlutterProjectType e) => e.cliName), help: 'Specify the type of project to create.', valueHelp: 'type', - allowedHelp: { - 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 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, diff --git a/packages/flutter_tools/lib/src/commands/daemon.dart b/packages/flutter_tools/lib/src/commands/daemon.dart index 9e90e8f218..66db800266 100644 --- a/packages/flutter_tools/lib/src/commands/daemon.dart +++ b/packages/flutter_tools/lib/src/commands/daemon.dart @@ -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. diff --git a/packages/flutter_tools/lib/src/commands/drive.dart b/packages/flutter_tools/lib/src/commands/drive.dart index c67eddbdaa..dd71170b54 100644 --- a/packages/flutter_tools/lib/src/commands/drive.dart +++ b/packages/flutter_tools/lib/src/commands/drive.dart @@ -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: [ - 'android-chrome', - 'chrome', - 'edge', - 'firefox', - 'ios-safari', - 'safari', - ], - allowedHelp: { - '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', diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 12b2eb559d..99d8a89694 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart @@ -774,7 +774,7 @@ class RunCommand extends RunCommandBase { ExitStatus.success, timingLabelParts: [ if (hotMode) 'hot' else 'cold', - getModeName(getBuildMode()), + getBuildMode().cliName, if (devices!.length == 1) getNameForTargetPlatform(await devices![0].targetPlatform) else diff --git a/packages/flutter_tools/lib/src/compile.dart b/packages/flutter_tools/lib/src/compile.dart index 5b1422b8ba..0be3c958a6 100644 --- a/packages/flutter_tools/lib/src/compile.dart +++ b/packages/flutter_tools/lib/src/compile.dart @@ -165,37 +165,33 @@ class StdoutHandler { } /// List the preconfigured build options for a given build mode. -List buildModeOptions(BuildMode mode, List dartDefines) { - switch (mode) { - case BuildMode.debug: - return [ - // 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 buildModeOptions(BuildMode mode, List dartDefines) => + switch (mode) { + BuildMode.debug => [ + // 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 => [ + // 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 => [ '-Ddart.vm.profile=false', - if (!dartDefines.any((String define) => define.startsWith('dart.vm.product'))) - '-Ddart.vm.product=false', - '--enable-asserts', - ]; - case BuildMode.profile: - return [ - // 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 [ - '-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 { diff --git a/packages/flutter_tools/lib/src/drive/web_driver_service.dart b/packages/flutter_tools/lib/src/drive/web_driver_service.dart index 9564439fd7..445716b871 100644 --- a/packages/flutter_tools/lib/src/drive/web_driver_service.dart +++ b/packages/flutter_tools/lib/src/drive/web_driver_service.dart @@ -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 getDesiredCapabilities( bool? headless, { List webBrowserFlags = const [], String? chromeBinary, -}) { - switch (browser) { - case Browser.chrome: - return { - 'acceptInsecureCerts': true, - 'browserName': 'chrome', - 'goog:loggingPrefs': { - async_io.LogType.browser: 'INFO', - async_io.LogType.performance: 'ALL', - }, - 'goog:chromeOptions': { - if (chromeBinary != null) - 'binary': chromeBinary, - 'w3c': true, - 'args': [ - '--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': { - 'traceCategories': - 'devtools.timeline,' - 'v8,blink.console,benchmark,blink,' - 'blink.user_timing', +}) => + switch (browser) { + Browser.chrome => { + 'acceptInsecureCerts': true, + 'browserName': 'chrome', + 'goog:loggingPrefs': { + async_io.LogType.browser: 'INFO', + async_io.LogType.performance: 'ALL', + }, + 'goog:chromeOptions': { + if (chromeBinary != null) 'binary': chromeBinary, + 'w3c': true, + 'args': [ + '--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': { + 'traceCategories': 'devtools.timeline,' + 'v8,blink.console,benchmark,blink,' + 'blink.user_timing', + }, }, }, - }; - case Browser.firefox: - return { - 'acceptInsecureCerts': true, - 'browserName': 'firefox', - 'moz:firefoxOptions' : { - 'args': [ - if (headless!) '-headless', - ...webBrowserFlags, - ], - 'prefs': { - '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 => { + 'acceptInsecureCerts': true, + 'browserName': 'firefox', + 'moz:firefoxOptions': { + 'args': [ + if (headless!) '-headless', + ...webBrowserFlags, + ], + 'prefs': { + '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': {'level': 'trace'}, }, - 'log': {'level': 'trace'}, }, - }; - case Browser.edge: - return { - 'acceptInsecureCerts': true, - 'browserName': 'edge', - }; - case Browser.safari: - return { - 'browserName': 'safari', - }; - case Browser.iosSafari: - return { - 'platformName': 'ios', - 'browserName': 'safari', - 'safari:useSimulator': true, - }; - case Browser.androidChrome: - return { - 'browserName': 'chrome', - 'platformName': 'android', - 'goog:chromeOptions': { - 'androidPackage': 'com.android.chrome', - 'args': [ - '--disable-fullscreen', - ...webBrowserFlags, - ], + Browser.edge => { + '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 => { + 'browserName': 'safari', + }, + Browser.iosSafari => { + 'platformName': 'ios', + 'browserName': 'safari', + 'safari:useSimulator': true, + }, + Browser.androidChrome => { + 'browserName': 'chrome', + 'platformName': 'android', + 'goog:chromeOptions': { + 'androidPackage': 'com.android.chrome', + 'args': [ + '--disable-fullscreen', + ...webBrowserFlags, + ], + }, + }, + }; diff --git a/packages/flutter_tools/lib/src/flutter_project_metadata.dart b/packages/flutter_tools/lib/src/flutter_project_metadata.dart index 08e95daa06..c94693988b 100644 --- a/packages/flutter_tools/lib/src/flutter_project_metadata.dart +++ b/packages/flutter_tools/lib/src/flutter_project_metadata.dart @@ -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, {'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()}'''; } diff --git a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart index 565f140b87..a44b803320 100644 --- a/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart +++ b/packages/flutter_tools/lib/src/isolated/resident_web_runner.dart @@ -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 { diff --git a/packages/flutter_tools/lib/src/linux/application_package.dart b/packages/flutter_tools/lib/src/linux/application_package.dart index 918cf46568..c8ff3632cd 100644 --- a/packages/flutter_tools/lib/src/linux/application_package.dart +++ b/packages/flutter_tools/lib/src/linux/application_package.dart @@ -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, ); diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart index 735415e4a4..fb1b937ddf 100644 --- a/packages/flutter_tools/lib/src/linux/build_linux.dart +++ b/packages/flutter_tools/lib/src/linux/build_linux.dart @@ -67,7 +67,7 @@ Future 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, diff --git a/packages/flutter_tools/lib/src/macos/application_package.dart b/packages/flutter_tools/lib/src/macos/application_package.dart index f022cf0cfa..b2b993476f 100644 --- a/packages/flutter_tools/lib/src/macos/application_package.dart +++ b/packages/flutter_tools/lib/src/macos/application_package.dart @@ -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!)}' : ''); } diff --git a/packages/flutter_tools/lib/src/reporting/github_template.dart b/packages/flutter_tools/lib/src/reporting/github_template.dart index cbeca022e1..5a61b7b5fb 100644 --- a/packages/flutter_tools/lib/src/reporting/github_template.dart +++ b/packages/flutter_tools/lib/src/reporting/github_template.dart @@ -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}') diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index f2bb23f29a..959268e3fc 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -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 { 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.fromEntries(WebRendererMode.values.map((WebRendererMode e) => MapEntry(e.name, e.helpText))) + allowedHelp: CliEnum.allowedHelp(WebRendererMode.values) ); } @@ -1422,7 +1411,7 @@ abstract class FlutterCommand extends Command { // Send timing. final List labels = [ - getEnumName(commandResult.exitStatus), + commandResult.exitStatus.name, if (commandResult.timingLabelParts?.isNotEmpty ?? false) ...?commandResult.timingLabelParts, ]; diff --git a/packages/flutter_tools/lib/src/web/compile.dart b/packages/flutter_tools/lib/src/web/compile.dart index 2b09648b1a..70adfb66b6 100644 --- a/packages/flutter_tools/lib/src/web/compile.dart +++ b/packages/flutter_tools/lib/src/web/compile.dart @@ -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.', diff --git a/packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart b/packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart index 24ce814a0e..e70677a4df 100644 --- a/packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart +++ b/packages/flutter_tools/lib/src/web/file_generators/flutter_service_worker_js.dart @@ -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 diff --git a/packages/flutter_tools/lib/src/windows/application_package.dart b/packages/flutter_tools/lib/src/windows/application_package.dart index 8ec7f8d50c..bd38ece9cc 100644 --- a/packages/flutter_tools/lib/src/windows/application_package.dart +++ b/packages/flutter_tools/lib/src/windows/application_package.dart @@ -113,7 +113,7 @@ class BuildableWindowsApp extends WindowsApp { return globals.fs.path.join( getWindowsBuildDirectory(), 'runner', - sentenceCase(getNameForBuildMode(buildMode)), + sentenceCase(buildMode.cliName), '$binaryName.exe', ); } diff --git a/packages/flutter_tools/lib/src/windows/build_windows.dart b/packages/flutter_tools/lib/src/windows/build_windows.dart index a29b3a18b3..7e361d70d3 100644 --- a/packages/flutter_tools/lib/src/windows/build_windows.dart +++ b/packages/flutter_tools/lib/src/windows/build_windows.dart @@ -76,7 +76,7 @@ Future 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...', diff --git a/packages/flutter_tools/test/general.shard/build_info_test.dart b/packages/flutter_tools/test/general.shard/build_info_test.dart index d3bdbd550c..c4979b20ee 100644 --- a/packages/flutter_tools/test/general.shard/build_info_test.dart +++ b/packages/flutter_tools/test/general.shard/build_info_test.dart @@ -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); }); }); diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart index e128dc4aec..6adbd025df 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/common_test.dart @@ -39,7 +39,7 @@ void main() { androidEnvironment = Environment.test( fileSystem.currentDirectory, defines: { - kBuildMode: getNameForBuildMode(BuildMode.profile), + kBuildMode: BuildMode.profile.cliName, kTargetPlatform: getNameForTargetPlatform(TargetPlatform.android_arm), }, inputs: {}, @@ -52,7 +52,7 @@ void main() { iosEnvironment = Environment.test( fileSystem.currentDirectory, defines: { - kBuildMode: getNameForBuildMode(BuildMode.profile), + kBuildMode: BuildMode.profile.cliName, kTargetPlatform: getNameForTargetPlatform(TargetPlatform.ios), }, inputs: {}, @@ -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: { - 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([ diff --git a/packages/flutter_tools/test/general.shard/desktop_device_test.dart b/packages/flutter_tools/test/general.shard/desktop_device_test.dart index 582e50efd8..8967bd00a5 100644 --- a/packages/flutter_tools/test/general.shard/desktop_device_test.dart +++ b/packages/flutter_tools/test/general.shard/desktop_device_test.dart @@ -371,7 +371,7 @@ class FakeDesktopDevice extends DesktopDevice { if (nullExecutablePathForDevice) { return null; } - return getNameForBuildMode(buildInfo.mode); + return buildInfo.mode.cliName; } } diff --git a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart index 6abc4bf332..d560bdba71 100644 --- a/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart +++ b/packages/flutter_tools/test/general.shard/linux/linux_device_test.dart @@ -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({ diff --git a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart index 246865bd06..5992956b69 100644 --- a/packages/flutter_tools/test/general.shard/terminal_handler_test.dart +++ b/packages/flutter_tools/test/general.shard/terminal_handler_test.dart @@ -1479,6 +1479,8 @@ TerminalHandler setUpTerminalHandler(List requests, { ..isRunningDebug = false ..isRunningProfile = false ..isRunningRelease = true; + case _: + // NOOP } return TerminalHandler( residentRunner, diff --git a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart index 8e8fb227f9..fc6036889e 100644 --- a/packages/flutter_tools/test/general.shard/web/compile_web_test.dart +++ b/packages/flutter_tools/test/general.shard/web/compile_web_test.dart @@ -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, { '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.')); diff --git a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart index 1f38de3ac5..6128ec607c 100644 --- a/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart +++ b/packages/flutter_tools/test/general.shard/windows/windows_device_test.dart @@ -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'; } diff --git a/packages/flutter_tools/test/host_cross_arch.shard/ios_content_validation_test.dart b/packages/flutter_tools/test/host_cross_arch.shard/ios_content_validation_test.dart index fd2a7f723b..7894341635 100644 --- a/packages/flutter_tools/test/host_cross_arch.shard/ios_content_validation_test.dart +++ b/packages/flutter_tools/test/host_cross_arch.shard/ios_content_validation_test.dart @@ -76,7 +76,7 @@ void main() { }); for (final BuildMode buildMode in [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');