| // Copyright 2014 The Flutter Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import '../base/common.dart'; |
| import '../base/logger.dart'; |
| import '../base/platform.dart'; |
| import '../cache.dart'; |
| import '../features.dart'; |
| import '../runner/flutter_command.dart'; |
| |
| /// The flutter precache command allows downloading of cache artifacts without |
| /// the use of device/artifact autodetection. |
| class PrecacheCommand extends FlutterCommand { |
| PrecacheCommand({ |
| bool verboseHelp = false, |
| required Cache? cache, |
| required Platform platform, |
| required Logger logger, |
| required FeatureFlags featureFlags, |
| }) : _cache = cache, |
| _platform = platform, |
| _logger = logger, |
| _featureFlags = featureFlags { |
| argParser.addFlag('all-platforms', abbr: 'a', negatable: false, |
| help: 'Precache artifacts for all host platforms.'); |
| argParser.addFlag('force', abbr: 'f', negatable: false, |
| help: 'Force re-downloading of artifacts.'); |
| argParser.addFlag('android', |
| help: 'Precache artifacts for Android development.', |
| hide: !verboseHelp); |
| argParser.addFlag('android_gen_snapshot', |
| help: 'Precache gen_snapshot for Android development.', |
| hide: !verboseHelp); |
| argParser.addFlag('android_maven', |
| help: 'Precache Gradle dependencies for Android development.', |
| hide: !verboseHelp); |
| argParser.addFlag('android_internal_build', |
| help: 'Precache dependencies for internal Android development.', |
| hide: !verboseHelp); |
| argParser.addFlag('ios', |
| help: 'Precache artifacts for iOS development.'); |
| argParser.addFlag('web', |
| help: 'Precache artifacts for web development.'); |
| argParser.addFlag('linux', |
| help: 'Precache artifacts for Linux desktop development.'); |
| argParser.addFlag('windows', |
| help: 'Precache artifacts for Windows desktop development.'); |
| argParser.addFlag('macos', |
| help: 'Precache artifacts for macOS desktop development.'); |
| argParser.addFlag('fuchsia', |
| help: 'Precache artifacts for Fuchsia development.'); |
| argParser.addFlag('universal', defaultsTo: true, |
| help: 'Precache artifacts required for any development platform.'); |
| argParser.addFlag('flutter_runner', |
| help: 'Precache the flutter runner artifacts.', hide: !verboseHelp); |
| argParser.addFlag('use-unsigned-mac-binaries', |
| help: 'Precache the unsigned macOS binaries when available.', hide: !verboseHelp); |
| } |
| |
| final Cache? _cache; |
| final Logger _logger; |
| final Platform _platform; |
| final FeatureFlags _featureFlags; |
| |
| @override |
| final String name = 'precache'; |
| |
| @override |
| final String description = "Populate the Flutter tool's cache of binary artifacts.\n\n" |
| 'If no explicit platform flags are provided, this command will download the artifacts ' |
| 'for all currently enabled platforms'; |
| |
| @override |
| final String category = FlutterCommandCategory.sdk; |
| |
| @override |
| bool get shouldUpdateCache => false; |
| |
| /// Some flags are umbrella names that expand to include multiple artifacts. |
| static const Map<String, List<String>> _expandedArtifacts = <String, List<String>>{ |
| 'android': <String>[ |
| 'android_gen_snapshot', |
| 'android_maven', |
| 'android_internal_build', |
| ], |
| }; |
| |
| /// Returns a reverse mapping of _expandedArtifacts, from child artifact name |
| /// to umbrella name. |
| Map<String, String> _umbrellaForArtifactMap() { |
| return <String, String>{ |
| for (final MapEntry<String, List<String>> entry in _expandedArtifacts.entries) |
| for (final String childArtifactName in entry.value) |
| childArtifactName: entry.key, |
| }; |
| } |
| |
| /// Returns the name of all artifacts that were explicitly chosen via flags. |
| /// |
| /// If an umbrella is chosen, its children will be included as well. |
| Set<String> _explicitArtifactSelections() { |
| final Map<String, String> umbrellaForArtifact = _umbrellaForArtifactMap(); |
| final Set<String> selections = <String>{}; |
| bool explicitlySelected(String name) => boolArgDeprecated(name) && argResults!.wasParsed(name); |
| for (final DevelopmentArtifact artifact in DevelopmentArtifact.values) { |
| final String? umbrellaName = umbrellaForArtifact[artifact.name]; |
| if (explicitlySelected(artifact.name) || |
| (umbrellaName != null && explicitlySelected(umbrellaName))) { |
| selections.add(artifact.name); |
| } |
| } |
| return selections; |
| } |
| |
| @override |
| Future<void> validateCommand() { |
| _expandedArtifacts.forEach((String umbrellaName, List<String> childArtifactNames) { |
| if (!argResults!.arguments.contains('--no-$umbrellaName')) { |
| return; |
| } |
| for (final String childArtifactName in childArtifactNames) { |
| if (argResults!.arguments.contains('--$childArtifactName')) { |
| throwToolExit('--$childArtifactName requires --$umbrellaName'); |
| } |
| } |
| }); |
| |
| return super.validateCommand(); |
| } |
| |
| @override |
| Future<FlutterCommandResult> runCommand() async { |
| // Re-lock the cache. |
| if (_platform.environment['FLUTTER_ALREADY_LOCKED'] != 'true') { |
| await _cache!.lock(); |
| } |
| if (boolArgDeprecated('force')) { |
| _cache!.clearStampFiles(); |
| } |
| |
| final bool includeAllPlatforms = boolArgDeprecated('all-platforms'); |
| if (includeAllPlatforms) { |
| _cache!.includeAllPlatforms = true; |
| } |
| if (boolArgDeprecated('use-unsigned-mac-binaries')) { |
| _cache!.useUnsignedMacBinaries = true; |
| } |
| final Set<String> explicitlyEnabled = _explicitArtifactSelections(); |
| _cache!.platformOverrideArtifacts = explicitlyEnabled; |
| |
| // If the user did not provide any artifact flags, then download |
| // all artifacts that correspond to an enabled platform. |
| final bool downloadDefaultArtifacts = explicitlyEnabled.isEmpty; |
| final Map<String, String> umbrellaForArtifact = _umbrellaForArtifactMap(); |
| final Set<DevelopmentArtifact> requiredArtifacts = <DevelopmentArtifact>{}; |
| for (final DevelopmentArtifact artifact in DevelopmentArtifact.values) { |
| if (artifact.feature != null && !_featureFlags.isEnabled(artifact.feature!)) { |
| continue; |
| } |
| |
| final String argumentName = umbrellaForArtifact[artifact.name] ?? artifact.name; |
| if (includeAllPlatforms || boolArgDeprecated(argumentName) || downloadDefaultArtifacts) { |
| requiredArtifacts.add(artifact); |
| } |
| } |
| if (!await _cache!.isUpToDate()) { |
| await _cache!.updateAll(requiredArtifacts); |
| } else { |
| _logger.printStatus('Already up-to-date.'); |
| } |
| return FlutterCommandResult.success(); |
| } |
| } |