Lazily download artifacts (III) (#27903)
diff --git a/packages/flutter_tools/bin/fuchsia_attach.dart b/packages/flutter_tools/bin/fuchsia_attach.dart index eb91c2d..30840c4 100644 --- a/packages/flutter_tools/bin/fuchsia_attach.dart +++ b/packages/flutter_tools/bin/fuchsia_attach.dart
@@ -134,4 +134,7 @@ Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter'; return super.runCommand(); } + + @override + Future<void> updateCache() async {} }
diff --git a/packages/flutter_tools/bin/xcode_backend.sh b/packages/flutter_tools/bin/xcode_backend.sh index 1bf2310..89810c9 100755 --- a/packages/flutter_tools/bin/xcode_backend.sh +++ b/packages/flutter_tools/bin/xcode_backend.sh
@@ -84,7 +84,6 @@ local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}" - AssertExists "${framework_path}" AssertExists "${project_path}" local derived_dir="${SOURCE_ROOT}/Flutter" @@ -123,6 +122,12 @@ flutter_podspec="${FLUTTER_ENGINE}/out/${LOCAL_ENGINE}/Flutter.podspec" fi + # If the framework path does not exist, ensure that it is downloaded. + if [[ ! -e "$1" ]]; then + FLUTTER_ALREADY_LOCKED = "true" + RunCommand "${FLUTTER_ROOT}/bin/flutter" precache --suppress-analytics + fi + if [[ -e "${project_path}/.ios" ]]; then RunCommand rm -rf -- "${derived_dir}/engine" mkdir "${derived_dir}/engine"
diff --git a/packages/flutter_tools/lib/src/base/net.dart b/packages/flutter_tools/lib/src/base/net.dart index 742bd8b..788a0d4 100644 --- a/packages/flutter_tools/lib/src/base/net.dart +++ b/packages/flutter_tools/lib/src/base/net.dart
@@ -37,7 +37,8 @@ printTrace('Downloading: $url'); HttpClient httpClient; if (context[HttpClientFactory] != null) { - httpClient = (context[HttpClientFactory] as HttpClientFactory)(); // ignore: avoid_as + final HttpClientFactory httpClientFactory = context[HttpClientFactory]; + httpClient = httpClientFactory(); } else { httpClient = HttpClient(); } @@ -64,9 +65,9 @@ // If we're making a HEAD request, we're only checking to see if the URL is // valid. if (onlyHeaders) { - return (response.statusCode == 200) ? <int>[] : null; + return (response.statusCode == HttpStatus.ok) ? <int>[] : null; } - if (response.statusCode != 200) { + if (response.statusCode != HttpStatus.ok) { if (response.statusCode > 0 && response.statusCode < 500) { throwToolExit( 'Download failed.\n'
diff --git a/packages/flutter_tools/lib/src/cache.dart b/packages/flutter_tools/lib/src/cache.dart index 7f3aa7a..772a006 100644 --- a/packages/flutter_tools/lib/src/cache.dart +++ b/packages/flutter_tools/lib/src/cache.dart
@@ -13,6 +13,7 @@ import 'base/net.dart'; import 'base/os.dart'; import 'base/platform.dart'; +import 'build_info.dart'; import 'globals.dart'; /// A wrapper around the `bin/cache/` directory. @@ -187,7 +188,24 @@ return isOlderThanReference(entity: entity, referenceFile: flutterToolsStamp); } - bool isUpToDate() => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate()); + UpdateResult isUpToDate({ + List<BuildMode> buildModes = const <BuildMode>[], + List<TargetPlatform> targetPlatforms = const <TargetPlatform>[], + bool skipUnknown = true, + }) { + bool isUpToDate = true; + bool clobber = false; + for (CachedArtifact artifact in _artifacts) { + final UpdateResult result = artifact.isUpToDate( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + ); + isUpToDate &= result.isUpToDate; + clobber |= result.clobber; + } + return UpdateResult(isUpToDate: isUpToDate, clobber: clobber); + } Future<String> getThirdPartyFile(String urlStr, String serviceName) async { final Uri url = Uri.parse(urlStr); @@ -210,13 +228,40 @@ return cachedFile.path; } - Future<void> updateAll() async { - if (!_lockEnabled) + Future<void> updateAll({ + List<BuildMode> buildModes = const <BuildMode>[], + List<TargetPlatform> targetPlatforms = const <TargetPlatform>[], + bool skipUnknown = true, + bool clobber = false, + }) async { + if (!_lockEnabled) { return; + } try { for (CachedArtifact artifact in _artifacts) { - if (!artifact.isUpToDate()) - await artifact.update(); + bool localClobber = clobber; + if (localClobber) { + await artifact.update( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + clobber: localClobber, + ); + } + final UpdateResult result = artifact.isUpToDate( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + ); + localClobber |= result.clobber; + if (localClobber || !result.isUpToDate) { + await artifact.update( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + clobber: localClobber, + ); + } } } on SocketException catch (e) { if (_hostsBlockedInChina.contains(e.address?.host)) { @@ -232,6 +277,15 @@ } } +class UpdateResult { + const UpdateResult({this.isUpToDate, this.clobber = false}); + + /// Whether the artifact exists and is the correct version. + final bool isUpToDate; + /// Whether the artifact needs to be redownloaded. + final bool clobber; +} + /// An artifact managed by the cache. abstract class CachedArtifact { CachedArtifact(this.name, this.cache); @@ -248,19 +302,39 @@ /// starting from scratch. final List<File> _downloadedFiles = <File>[]; - bool isUpToDate() { - if (!location.existsSync()) - return false; - if (version != cache.getStampFor(name)) - return false; - return isUpToDateInner(); + @mustCallSuper + UpdateResult isUpToDate({ + List<BuildMode> buildModes = const <BuildMode>[], + List<TargetPlatform> targetPlatforms = const <TargetPlatform>[], + bool skipUnknown = true, + }) { + if (!location.existsSync()) { + return const UpdateResult(isUpToDate: false, clobber: false); + } + if (version != cache.getStampFor(name)) { + return const UpdateResult(isUpToDate: false, clobber: true); + } + return const UpdateResult(isUpToDate: true, clobber: false); } - Future<void> update() async { - if (location.existsSync()) + Future<void> update({ + List<BuildMode> buildModes, + List<TargetPlatform> targetPlatforms, + bool skipUnknown = true, + bool clobber = false, + }) async { + if (location.existsSync() && clobber) { location.deleteSync(recursive: true); - location.createSync(recursive: true); - await updateInner(); + } + if (!location.existsSync()) { + location.createSync(recursive: true); + } + await updateInner( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + clobber: clobber, + ); cache.setStampFor(name, version); _removeDownloadedFiles(); } @@ -268,6 +342,9 @@ /// Clear any zip/gzip files downloaded. void _removeDownloadedFiles() { for (File f in _downloadedFiles) { + if (!f.existsSync()) { + continue; + } f.deleteSync(); for (Directory d = f.parent; d.absolute.path != cache.getDownloadDir().absolute.path; d = d.parent) { if (d.listSync().isEmpty) { @@ -279,11 +356,13 @@ } } - /// Hook method for extra checks for being up-to-date. - bool isUpToDateInner() => true; - /// Template method to perform artifact update. - Future<void> updateInner(); + Future<void> updateInner({ + @required List<BuildMode> buildModes, + @required List<TargetPlatform> targetPlatforms, + @required bool skipUnknown, + @required bool clobber, + }); String get _storageBaseUrl { final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL']; @@ -351,9 +430,16 @@ MaterialFonts(Cache cache) : super('material_fonts', cache); @override - Future<void> updateInner() { + Future<void> updateInner({ + List<BuildMode> buildModes, + List<TargetPlatform> targetPlatforms, + bool skipUnknown, + bool clobber, + }) async { final Uri archiveUri = _toStorageUri(version); - return _downloadZipArchive('Downloading Material fonts...', archiveUri, location); + if (fs.directory(location).listSync().isEmpty || clobber) { + await _downloadZipArchive('Downloading Material fonts...', archiveUri, location); + } } } @@ -361,100 +447,387 @@ class FlutterEngine extends CachedArtifact { FlutterEngine(Cache cache) : super('engine', cache); - List<String> _getPackageDirs() => const <String>['sky_engine']; - - // Return a list of (cache directory path, download URL path) tuples. - List<List<String>> _getBinaryDirs() { - final List<List<String>> binaryDirs = <List<String>>[]; - - binaryDirs.add(<String>['common', 'flutter_patched_sdk.zip']); - - if (cache.includeAllPlatforms) - binaryDirs - ..addAll(_osxBinaryDirs) - ..addAll(_linuxBinaryDirs) - ..addAll(_windowsBinaryDirs) - ..addAll(_androidBinaryDirs) - ..addAll(_iosBinaryDirs) - ..addAll(_dartSdks); - else if (platform.isLinux) - binaryDirs - ..addAll(_linuxBinaryDirs) - ..addAll(_androidBinaryDirs); - else if (platform.isMacOS) - binaryDirs - ..addAll(_osxBinaryDirs) - ..addAll(_androidBinaryDirs) - ..addAll(_iosBinaryDirs); - else if (platform.isWindows) - binaryDirs - ..addAll(_windowsBinaryDirs) - ..addAll(_androidBinaryDirs); - - return binaryDirs; + // Return a list of [BinaryArtifact]s to download. + @visibleForTesting + List<BinaryArtifact> getBinaryDirs({ + @required List<BuildMode> buildModes, + @required List<TargetPlatform> targetPlatforms, + @required bool skipUnknown, + }) { + TargetPlatform hostPlatform; + if (cache.includeAllPlatforms) { + hostPlatform = null; + } if (platform.isMacOS) { + hostPlatform = TargetPlatform.darwin_x64; + } else if (platform.isLinux) { + hostPlatform = TargetPlatform.linux_x64; + } else if (platform.isWindows) { + hostPlatform = TargetPlatform.windows_x64; + } + final List<BinaryArtifact> results = _reduceEngineBinaries( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + hostPlatform: hostPlatform, + skipUnknown: skipUnknown, + ).toList(); + if (cache.includeAllPlatforms) { + return results + _dartSdks; + } + return results; } - List<List<String>> get _osxBinaryDirs => <List<String>>[ - <String>['darwin-x64', 'darwin-x64/artifacts.zip'], - <String>['android-arm-profile/darwin-x64', 'android-arm-profile/darwin-x64.zip'], - <String>['android-arm-release/darwin-x64', 'android-arm-release/darwin-x64.zip'], - <String>['android-arm64-profile/darwin-x64', 'android-arm64-profile/darwin-x64.zip'], - <String>['android-arm64-release/darwin-x64', 'android-arm64-release/darwin-x64.zip'], - <String>['android-arm-dynamic-profile/darwin-x64', 'android-arm-dynamic-profile/darwin-x64.zip'], - <String>['android-arm-dynamic-release/darwin-x64', 'android-arm-dynamic-release/darwin-x64.zip'], - <String>['android-arm64-dynamic-profile/darwin-x64', 'android-arm64-dynamic-profile/darwin-x64.zip'], - <String>['android-arm64-dynamic-release/darwin-x64', 'android-arm64-dynamic-release/darwin-x64.zip'], + Iterable<BinaryArtifact> _reduceEngineBinaries({ + List<BuildMode> buildModes, + List<TargetPlatform> targetPlatforms, + TargetPlatform hostPlatform, + bool skipUnknown, + }) { + return _binaries.where((BinaryArtifact engineBinary) { + if (hostPlatform != null && engineBinary.hostPlatform != null && engineBinary.hostPlatform != hostPlatform) { + return false; + } + // match if artifact has no restrictions. + if (engineBinary.skipChecks || engineBinary.buildMode == null && engineBinary.targetPlatform == null) { + return true; + } + final bool buildModeMatch = buildModes.any((BuildMode buildMode) => buildMode == engineBinary.buildMode); + final bool targetPlatformMatch = targetPlatforms.any((TargetPlatform targetPlatform) => targetPlatform == engineBinary.targetPlatform); + if (buildModeMatch && targetPlatformMatch // match if artifact exactly matches requiremnets. + || skipUnknown && buildModeMatch && targetPlatforms.isEmpty // match if build mode matches but target platform is unknown. + || skipUnknown && targetPlatformMatch && buildModes.isEmpty // match if target platform matches but build mode is null. + || !skipUnknown && targetPlatforms.isEmpty && buildModes.isEmpty) { // match if neither are provided but skipUnknown flag is provided. + return true; + } + return false; + }); + } + + List<BinaryArtifact> get _packages => const <BinaryArtifact>[ + BinaryArtifact( + name: 'sky_engine', + fileName: 'sky_engine' + ), ]; - List<List<String>> get _linuxBinaryDirs => <List<String>>[ - <String>['linux-x64', 'linux-x64/artifacts.zip'], - <String>['android-arm-profile/linux-x64', 'android-arm-profile/linux-x64.zip'], - <String>['android-arm-release/linux-x64', 'android-arm-release/linux-x64.zip'], - <String>['android-arm64-profile/linux-x64', 'android-arm64-profile/linux-x64.zip'], - <String>['android-arm64-release/linux-x64', 'android-arm64-release/linux-x64.zip'], - <String>['android-arm-dynamic-profile/linux-x64', 'android-arm-dynamic-profile/linux-x64.zip'], - <String>['android-arm-dynamic-release/linux-x64', 'android-arm-dynamic-release/linux-x64.zip'], - <String>['android-arm64-dynamic-profile/linux-x64', 'android-arm64-dynamic-profile/linux-x64.zip'], - <String>['android-arm64-dynamic-release/linux-x64', 'android-arm64-dynamic-release/linux-x64.zip'], + /// This lives separately since we only download it when includeAllPlatforms is true. + List<BinaryArtifact> get _dartSdks => const <BinaryArtifact>[ + BinaryArtifact( + name: 'darwin-x64', + fileName: 'dart-sdk-darwin-x64.zip', + ), + BinaryArtifact( + name: 'linux-x64', + fileName: 'dart-sdk-linux-x64.zip', + ), + BinaryArtifact( + name: 'windows-x64', + fileName: 'dart-sdk-windows-x64.zip', + ), ]; - List<List<String>> get _windowsBinaryDirs => <List<String>>[ - <String>['windows-x64', 'windows-x64/artifacts.zip'], - <String>['android-arm-profile/windows-x64', 'android-arm-profile/windows-x64.zip'], - <String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'], - <String>['android-arm64-profile/windows-x64', 'android-arm64-profile/windows-x64.zip'], - <String>['android-arm64-release/windows-x64', 'android-arm64-release/windows-x64.zip'], - <String>['android-arm-dynamic-profile/windows-x64', 'android-arm-dynamic-profile/windows-x64.zip'], - <String>['android-arm-dynamic-release/windows-x64', 'android-arm-dynamic-release/windows-x64.zip'], - <String>['android-arm64-dynamic-profile/windows-x64', 'android-arm64-dynamic-profile/windows-x64.zip'], - <String>['android-arm64-dynamic-release/windows-x64', 'android-arm64-dynamic-release/windows-x64.zip'], - ]; - - List<List<String>> get _androidBinaryDirs => <List<String>>[ - <String>['android-x86', 'android-x86/artifacts.zip'], - <String>['android-x64', 'android-x64/artifacts.zip'], - <String>['android-arm', 'android-arm/artifacts.zip'], - <String>['android-arm-profile', 'android-arm-profile/artifacts.zip'], - <String>['android-arm-release', 'android-arm-release/artifacts.zip'], - <String>['android-arm64', 'android-arm64/artifacts.zip'], - <String>['android-arm64-profile', 'android-arm64-profile/artifacts.zip'], - <String>['android-arm64-release', 'android-arm64-release/artifacts.zip'], - <String>['android-arm-dynamic-profile', 'android-arm-dynamic-profile/artifacts.zip'], - <String>['android-arm-dynamic-release', 'android-arm-dynamic-release/artifacts.zip'], - <String>['android-arm64-dynamic-profile', 'android-arm64-dynamic-profile/artifacts.zip'], - <String>['android-arm64-dynamic-release', 'android-arm64-dynamic-release/artifacts.zip'], - ]; - - List<List<String>> get _iosBinaryDirs => <List<String>>[ - <String>['ios', 'ios/artifacts.zip'], - <String>['ios-profile', 'ios-profile/artifacts.zip'], - <String>['ios-release', 'ios-release/artifacts.zip'], - ]; - - List<List<String>> get _dartSdks => <List<String>> [ - <String>['darwin-x64', 'dart-sdk-darwin-x64.zip'], - <String>['linux-x64', 'dart-sdk-linux-x64.zip'], - <String>['windows-x64', 'dart-sdk-windows-x64.zip'], + /// A set of all possible artifacts to download. + /// + /// Adding a new artifact: + /// + /// To ensure that we do not waste a user's time/data/storage, the flutter + /// tool should only binaries when they are required. These can be requested + /// in [FlutterCommand.updateCache]. + /// + /// An artifact should have the following features to prevent unecessary download: + /// + /// * `hostPlatform` should be one of `TargetPlatform.linux_x64`, + /// `TargetPlatform.darwin_x64`, or `TargetPlatfrom.windows_x64`. In the + /// case where there is no restriction it can be left as null. + /// * `buildMode` should be one of `BuildMode.debug`, `BuildMode.profile`, + /// `BuildMode.release`, `BuildMode.dynamicRelease`, or + /// `BuildMode.dynamicProfile`. In the case where it is required regardless + /// of buildMode, it can be left null. + /// * `targetPlatform` should be one of the supported target platforms. + /// * If, despite the restrictions above, the artifact should still be + /// downloaded, `skipChecks` can be set to true. + List<BinaryArtifact> get _binaries => const <BinaryArtifact>[ + BinaryArtifact( + name: 'common', + fileName: 'flutter_patched_sdk.zip', + ), + BinaryArtifact( + name: 'linux-x64', + fileName: 'linux-x64/artifacts.zip', + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm-profile/linux-x64', + fileName: 'android-arm-profile/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.profile, + hostPlatform: TargetPlatform.linux_x64, + skipChecks: true, + ), + BinaryArtifact( + name: 'android-arm-release/linux-x64', + fileName: 'android-arm-release/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.release, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm64-profile/linux-x64', + fileName: 'android-arm64-profile/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.profile, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm64-release/linux-x64', + fileName: 'android-arm64-release/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.release, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm-dynamic-profile/linux-x64', + fileName: 'android-arm-dynamic-profile/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.dynamicProfile, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm-dynamic-release/linux-x64', + fileName: 'android-arm-dynamic-release/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.dynamicRelease, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-profile/linux-x64', + fileName: 'android-arm64-dynamic-profile/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.dynamicProfile, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-release/linux-x64', + fileName: 'android-arm64-dynamic-release/linux-x64.zip', + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.dynamicRelease, + hostPlatform: TargetPlatform.linux_x64, + ), + BinaryArtifact( + name: 'windows-x64', + fileName: 'windows-x64/artifacts.zip', + hostPlatform: TargetPlatform.windows_x64, + ), + BinaryArtifact( + name: 'android-arm-profile/windows-x64', + fileName: 'android-arm-profile/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.profile, + skipChecks: true + ), + BinaryArtifact( + name: 'android-arm-release/windows-x64', + fileName: 'android-arm-release/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.release, + ), + BinaryArtifact( + name: 'android-arm64-profile/windows-x64', + fileName: 'android-arm64-profile/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.profile, + ), + BinaryArtifact( + name: 'android-arm64-release/windows-x64', + fileName: 'android-arm64-release/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.release, + ), + BinaryArtifact( + name: 'android-arm-dynamic-profile/windows-x64', + fileName: 'android-arm-dynamic-profile/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.dynamicProfile, + ), + BinaryArtifact( + name: 'android-arm-dynamic-release/windows-x64', + fileName: 'android-arm-dynamic-release/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm, + buildMode: BuildMode.dynamicRelease, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-profile/windows-x64', + fileName: 'android-arm64-dynamic-profile/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.dynamicProfile, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-release/windows-x64', + fileName: 'android-arm64-dynamic-release/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + targetPlatform: TargetPlatform.android_arm64, + buildMode: BuildMode.dynamicRelease, + ), + BinaryArtifact( + name: 'android-x86', + fileName: 'android-x86/artifacts.zip', + buildMode: BuildMode.debug, + targetPlatform: TargetPlatform.android_x86, + ), + BinaryArtifact( + name: 'android-x64', + fileName: 'android-x64/artifacts.zip', + buildMode: BuildMode.debug, + targetPlatform: TargetPlatform.android_x64, + ), + BinaryArtifact( + name: 'android-arm', + fileName: 'android-arm/artifacts.zip', + buildMode: BuildMode.debug, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-profile', + fileName: 'android-arm-profile/artifacts.zip', + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-release', + fileName: 'android-arm-release/artifacts.zip', + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm64', + fileName: 'android-arm64/artifacts.zip', + buildMode: BuildMode.debug, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm64-profile', + fileName: 'android-arm64-profile/artifacts.zip', + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm64-release', + fileName: 'android-arm64-release/artifacts.zip', + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm-dynamic-profile', + fileName: 'android-arm-dynamic-profile/artifacts.zip', + buildMode: BuildMode.dynamicProfile, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-dynamic-release', + fileName: 'android-arm-dynamic-release/artifacts.zip', + buildMode: BuildMode.dynamicRelease, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-profile', + fileName: 'android-arm64-dynamic-profile/artifacts.zip', + buildMode: BuildMode.dynamicProfile, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-release', + fileName: 'android-arm64-dynamic-release/artifacts.zip', + buildMode: BuildMode.dynamicRelease, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'ios', fileName: 'ios/artifacts.zip', + buildMode: BuildMode.debug, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'ios-profile', + fileName: 'ios-profile/artifacts.zip', + buildMode: BuildMode.profile, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'ios-release', + fileName: 'ios-release/artifacts.zip', + buildMode: BuildMode.release, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'darwin-x64', + fileName: 'darwin-x64/artifacts.zip', + hostPlatform: TargetPlatform.darwin_x64, + ), + BinaryArtifact( + name: 'android-arm-profile/darwin-x64', + fileName: 'android-arm-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + skipChecks: true, + ), + BinaryArtifact( + name: 'android-arm-release/darwin-x64', + fileName: 'android-arm-release/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm64-profile/darwin-x64', + fileName: 'android-arm64-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm64-release/darwin-x64', + fileName: 'android-arm64-release/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm-dynamic-profile/darwin-x64', + fileName: 'android-arm-dynamic-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.dynamicProfile, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-dynamic-release/darwin-x64', + fileName: 'android-arm-dynamic-release/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.dynamicRelease, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-profile/darwin-x64', + fileName: 'android-arm64-dynamic-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.dynamicProfile, + targetPlatform: TargetPlatform.android_arm64, + ), + BinaryArtifact( + name: 'android-arm64-dynamic-release/darwin-x64', + fileName: 'android-arm64-dynamic-release/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.dynamicRelease, + targetPlatform: TargetPlatform.android_arm64, + ), ]; // A list of cache directory paths to which the LICENSE file should be copied. @@ -466,52 +839,71 @@ } @override - bool isUpToDateInner() { + UpdateResult isUpToDate({ + List<BuildMode> buildModes = const <BuildMode>[], + List<TargetPlatform> targetPlatforms = const <TargetPlatform>[], + bool skipUnknown = true, + }) { + final UpdateResult parentResult = super.isUpToDate( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown + ); + if (!parentResult.isUpToDate || parentResult.clobber) { + return parentResult; + } final Directory pkgDir = cache.getCacheDir('pkg'); - for (String pkgName in _getPackageDirs()) { - final String pkgPath = fs.path.join(pkgDir.path, pkgName); - if (!fs.directory(pkgPath).existsSync()) - return false; + for (BinaryArtifact packageArtifact in _packages) { + final Directory packageDirectory = packageArtifact.artifactLocation(pkgDir); + if (!packageDirectory.existsSync()) { + return const UpdateResult(isUpToDate: false); + } } - - for (List<String> toolsDir in _getBinaryDirs()) { - final Directory dir = fs.directory(fs.path.join(location.path, toolsDir[0])); - if (!dir.existsSync()) - return false; + for (BinaryArtifact toolsArtifact in getBinaryDirs(buildModes: buildModes, targetPlatforms: targetPlatforms, skipUnknown: skipUnknown)) { + final Directory dir = toolsArtifact.artifactLocation(location); + if (!dir.existsSync()) { + return const UpdateResult(isUpToDate: false); + } } - - for (String licenseDir in _getLicenseDirs()) { - final File file = fs.file(fs.path.join(location.path, licenseDir, 'LICENSE')); - if (!file.existsSync()) - return false; - } - return true; + return const UpdateResult(isUpToDate: true); } @override - Future<void> updateInner() async { + Future<void> updateInner({ + @required List<BuildMode> buildModes, + @required List<TargetPlatform> targetPlatforms, + @required bool skipUnknown, + @required bool clobber, + }) async { final String url = '$_storageBaseUrl/flutter_infra/flutter/$version/'; - - final Directory pkgDir = cache.getCacheDir('pkg'); - for (String pkgName in _getPackageDirs()) { - final String pkgPath = fs.path.join(pkgDir.path, pkgName); + final Directory packageDirectory = cache.getCacheDir('pkg'); + for (BinaryArtifact rawArtifact in _packages) { + final String pkgPath = fs.path.join(packageDirectory.path, rawArtifact.name); final Directory dir = fs.directory(pkgPath); - if (dir.existsSync()) + final bool exists = dir.existsSync(); + if (exists) { + if (!clobber) { + continue; + } dir.deleteSync(recursive: true); - await _downloadZipArchive('Downloading package $pkgName...', Uri.parse(url + pkgName + '.zip'), pkgDir); + } + final Uri uri = Uri.parse('$url${rawArtifact.name}.zip'); + await _downloadZipArchive('Downloading package ${rawArtifact.name}...', uri, packageDirectory); } - for (List<String> toolsDir in _getBinaryDirs()) { - final String cacheDir = toolsDir[0]; - final String urlPath = toolsDir[1]; - final Directory dir = fs.directory(fs.path.join(location.path, cacheDir)); - await _downloadZipArchive('Downloading $cacheDir tools...', Uri.parse(url + urlPath), dir); + final List<BinaryArtifact> rawArtifacts = getBinaryDirs(buildModes: buildModes, targetPlatforms: targetPlatforms, skipUnknown: skipUnknown); + for (BinaryArtifact rawArtifact in rawArtifacts) { + final Directory artifactDirectory = rawArtifact.artifactLocation(location); + if (artifactDirectory.existsSync() && !clobber) { + continue; + } + final Uri uri = rawArtifact.artifactRemoteLocation(url); + await _downloadZipArchive('Downloading ${rawArtifact.name} tools...', uri, artifactDirectory); + _makeFilesExecutable(artifactDirectory); - _makeFilesExecutable(dir); - - final File frameworkZip = fs.file(fs.path.join(dir.path, 'Flutter.framework.zip')); + final File frameworkZip = fs.file(fs.path.join(artifactDirectory.path, 'Flutter.framework.zip')); if (frameworkZip.existsSync()) { - final Directory framework = fs.directory(fs.path.join(dir.path, 'Flutter.framework')); + final Directory framework = fs.directory(fs.path.join(artifactDirectory.path, 'Flutter.framework')); framework.createSync(); os.unzip(frameworkZip, framework); } @@ -520,45 +912,43 @@ final File licenseSource = fs.file(fs.path.join(Cache.flutterRoot, 'LICENSE')); for (String licenseDir in _getLicenseDirs()) { final String licenseDestinationPath = fs.path.join(location.path, licenseDir, 'LICENSE'); + // If the destination does not exist, we did not download the artifact to + // perform this operation. + if (!fs.directory(fs.path.join(location.path, licenseDir)).existsSync()) { + continue; + } await licenseSource.copy(licenseDestinationPath); } } + // Checks whether the remote artifacts for `engineVersion` are availible in storage. Future<bool> areRemoteArtifactsAvailable({ String engineVersion, bool includeAllPlatforms = true, }) async { - final bool includeAllPlatformsState = cache.includeAllPlatforms; + final bool includeAllPlatforms = cache.includeAllPlatforms; cache.includeAllPlatforms = includeAllPlatforms; - - Future<bool> checkForArtifacts(String engineVersion) async { - engineVersion ??= version; - final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/'; - - bool exists = false; - for (String pkgName in _getPackageDirs()) { - exists = await _doesRemoteExist('Checking package $pkgName is available...', - Uri.parse(url + pkgName + '.zip')); + engineVersion ??= version; + final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/'; + final bool result = await () async { + for (BinaryArtifact packageArtifact in _packages) { + final Uri uri = Uri.parse('$url${packageArtifact.name}.zip'); + final bool exists = await _doesRemoteExist('Checking package ${packageArtifact.name} is available...', uri); if (!exists) { return false; } } - - for (List<String> toolsDir in _getBinaryDirs()) { - final String cacheDir = toolsDir[0]; - final String urlPath = toolsDir[1]; - exists = await _doesRemoteExist('Checking $cacheDir tools are available...', - Uri.parse(url + urlPath)); + final List<BinaryArtifact> rawArtifacts = getBinaryDirs(buildModes: <BuildMode>[], targetPlatforms: <TargetPlatform>[], skipUnknown: false); + for (BinaryArtifact rawArtifact in rawArtifacts) { + final Uri uri = Uri.parse('$url${rawArtifact.fileName}'); + final bool exists = await _doesRemoteExist('Checking ${rawArtifact.name} tools are available...', uri); if (!exists) { return false; } } - return true; - } - - final bool result = await checkForArtifacts(engineVersion); - cache.includeAllPlatforms = includeAllPlatformsState; + }(); + cache.includeAllPlatforms = includeAllPlatforms; return result; } @@ -567,8 +957,9 @@ for (FileSystemEntity entity in dir.listSync()) { if (entity is File) { final String name = fs.path.basename(entity.path); - if (name == 'flutter_tester') + if (name == 'flutter_tester') { os.makeExecutable(entity); + } } } } @@ -583,30 +974,44 @@ String get _gradleWrapper => fs.path.join('gradle', 'wrapper', 'gradle-wrapper.jar'); @override - Future<void> updateInner() { + Future<void> updateInner({List<BuildMode> buildModes, List<TargetPlatform> targetPlatforms, bool skipUnknown, bool clobber}) async { final Uri archiveUri = _toStorageUri(version); - return _downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location).then<void>((_) { - // Delete property file, allowing templates to provide it. - fs.file(fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync(); - // Remove NOTICE file. Should not be part of the template. - fs.file(fs.path.join(location.path, 'NOTICE')).deleteSync(); - }); + if (fs.directory(location).listSync().isEmpty || clobber) { + await _downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location).then<void>((_) { + // Delete property file, allowing templates to provide it. + fs.file(fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync(); + // Remove NOTICE file. Should not be part of the template. + fs.file(fs.path.join(location.path, 'NOTICE')).deleteSync(); + }); + } } @override - bool isUpToDateInner() { + UpdateResult isUpToDate({ + List<BuildMode> buildModes = const <BuildMode>[], + List<TargetPlatform> targetPlatforms = const <TargetPlatform>[], + bool skipUnknown = true, + }) { + final UpdateResult parentResult = super.isUpToDate( + buildModes: buildModes, + targetPlatforms: targetPlatforms, + skipUnknown: skipUnknown, + ); + if (!parentResult.isUpToDate || parentResult.clobber) { + return parentResult; + } final Directory wrapperDir = cache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper')); if (!fs.directory(wrapperDir).existsSync()) - return false; + return const UpdateResult(isUpToDate: false); for (String scriptName in _gradleScripts) { final File scriptFile = fs.file(fs.path.join(wrapperDir.path, scriptName)); if (!scriptFile.existsSync()) - return false; + return const UpdateResult(isUpToDate: false); } final File gradleWrapperJar = fs.file(fs.path.join(wrapperDir.path, _gradleWrapper)); if (!gradleWrapperJar.existsSync()) - return false; - return true; + return const UpdateResult(isUpToDate: false); + return const UpdateResult(isUpToDate: true); } } @@ -661,3 +1066,34 @@ if (!directory.existsSync()) directory.createSync(recursive: true); } + +class BinaryArtifact { + const BinaryArtifact({ + this.targetPlatform, + this.buildMode, + @required this.name, + @required this.fileName, + this.hostPlatform, + this.skipChecks = false, + }); + + final TargetPlatform targetPlatform; + final TargetPlatform hostPlatform; + final BuildMode buildMode; + final String name; + final String fileName; + final bool skipChecks; + + /// The location where this artifact will be cached. + Directory artifactLocation(Directory location) { + return fs.directory(fs.path.join(location.path, name)); + } + + /// The remote location where this artifact can be downloaded. + Uri artifactRemoteLocation(String baseUrl) { + return Uri.parse('$baseUrl$fileName'); + } + + @override + String toString() => '$name/$fileName'; +}
diff --git a/packages/flutter_tools/lib/src/commands/build_ios.dart b/packages/flutter_tools/lib/src/commands/build_ios.dart index b90e332..8205c72 100644 --- a/packages/flutter_tools/lib/src/commands/build_ios.dart +++ b/packages/flutter_tools/lib/src/commands/build_ios.dart
@@ -49,9 +49,15 @@ final String description = 'Build an iOS application bundle (Mac OS X host only).'; @override - Future<FlutterCommandResult> runCommand() async { - final bool forSimulator = argResults['simulator']; + Future<void> validateCommand() async { defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release; + return super.validateCommand(); + } + + bool get forSimulator => argResults['simulator']; + + @override + Future<FlutterCommandResult> runCommand() async { if (getCurrentHostPlatform() != HostPlatform.darwin_x64) throwToolExit('Building for iOS is only supported on the Mac.');
diff --git a/packages/flutter_tools/lib/src/commands/create.dart b/packages/flutter_tools/lib/src/commands/create.dart index e3d1d0e..b786415 100644 --- a/packages/flutter_tools/lib/src/commands/create.dart +++ b/packages/flutter_tools/lib/src/commands/create.dart
@@ -212,8 +212,6 @@ throwToolExit('Neither the --flutter-root command line flag nor the FLUTTER_ROOT environment ' 'variable was specified. Unable to find package:flutter.', exitCode: 2); - await Cache.instance.updateAll(); - final String flutterRoot = fs.path.absolute(Cache.flutterRoot); final String flutterPackagesDirectory = fs.path.join(flutterRoot, 'packages');
diff --git a/packages/flutter_tools/lib/src/commands/ide_config.dart b/packages/flutter_tools/lib/src/commands/ide_config.dart index 2e14108..eaa4dd9 100644 --- a/packages/flutter_tools/lib/src/commands/ide_config.dart +++ b/packages/flutter_tools/lib/src/commands/ide_config.dart
@@ -223,8 +223,6 @@ throwToolExit('Currently, the only supported IDE is IntelliJ\n$usage', exitCode: 2); } - await Cache.instance.updateAll(); - if (argResults['update-templates']) { _handleTemplateUpdate(); return null;
diff --git a/packages/flutter_tools/lib/src/commands/precache.dart b/packages/flutter_tools/lib/src/commands/precache.dart index b1fc4dc..7137937 100644 --- a/packages/flutter_tools/lib/src/commands/precache.dart +++ b/packages/flutter_tools/lib/src/commands/precache.dart
@@ -4,6 +4,7 @@ import 'dart:async'; +import '../cache.dart'; import '../globals.dart'; import '../runner/flutter_command.dart'; @@ -11,6 +12,8 @@ PrecacheCommand() { argParser.addFlag('all-platforms', abbr: 'a', negatable: false, help: 'Precache artifacts for all platforms.'); + argParser.addFlag('force', abbr: 'f', negatable: false, + help: 'Force download of new cached artifacts'); } @override @@ -24,14 +27,18 @@ @override Future<FlutterCommandResult> runCommand() async { - if (argResults['all-platforms']) + if (argResults['all-platforms']) { cache.includeAllPlatforms = true; - - if (cache.isUpToDate()) + } + final UpdateResult result = cache.isUpToDate(skipUnknown: false); + if (result.isUpToDate && !result.clobber && !argResults['force']) { printStatus('Already up-to-date.'); - else - await cache.updateAll(); - - return null; + } else { + await cache.updateAll( + skipUnknown: false, + clobber: argResults['force'] || result.clobber, + ); + } + return const FlutterCommandResult(ExitStatus.success); } }
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index 2d93bfb..b90cdac 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -327,30 +327,43 @@ } BuildMode getBuildMode() { - final List<bool> modeFlags = <bool>[argResults['debug'], argResults['profile'], argResults['release']]; - if (modeFlags.where((bool flag) => flag).length > 1) + bool debug; + bool profile; + bool release; + if (argParser.options.containsKey('debug')) { + debug = argResults['debug']; + } else { + debug = _defaultBuildMode == BuildMode.debug; + } + if (argParser.options.containsKey('profile')) { + profile = argResults['profile']; + } else { + profile = _defaultBuildMode == BuildMode.profile; + } + if (argParser.options.containsKey('release')) { + release = argResults['release']; + } else { + release = _defaultBuildMode == BuildMode.release; + } + if (debug && profile || debug && release || release && profile) { throw UsageException('Only one of --debug, --profile, or --release can be specified.', null); + } final bool dynamicFlag = argParser.options.containsKey('dynamic') ? argResults['dynamic'] : false; - if (argResults['debug']) { - if (dynamicFlag) + if (debug) { + if (dynamicFlag) { throw ToolExit('Error: --dynamic requires --release or --profile.'); + } return BuildMode.debug; } - if (argResults['profile']) + if (profile) { return dynamicFlag ? BuildMode.dynamicProfile : BuildMode.profile; - if (argResults['release']) + } + if (release) { return dynamicFlag ? BuildMode.dynamicRelease : BuildMode.release; - - if (_defaultBuildMode == BuildMode.debug && dynamicFlag) - throw ToolExit('Error: --dynamic requires --release or --profile.'); - if (_defaultBuildMode == BuildMode.release && dynamicFlag) - return BuildMode.dynamicRelease; - if (_defaultBuildMode == BuildMode.profile && dynamicFlag) - return BuildMode.dynamicProfile; - + } return _defaultBuildMode; } @@ -388,7 +401,7 @@ '--patch-number (${argResults['patch-number']}) must be an int.', null); } - String extraFrontEndOptions = + List<String> extraFrontEndOptions = argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions) ? argResults[FlutterOptions.kExtraFrontEndOptions] : null; @@ -397,9 +410,9 @@ for (String expFlag in argResults[FlutterOptions.kEnableExperiment]) { final String flag = '--enable-experiment=' + expFlag; if (extraFrontEndOptions != null) { - extraFrontEndOptions += ',' + flag; + extraFrontEndOptions.add(flag); } else { - extraFrontEndOptions = flag; + extraFrontEndOptions = <String>[flag]; } } } @@ -425,9 +438,9 @@ baselineDir: argParser.options.containsKey('baseline-dir') ? argResults['baseline-dir'] : null, - extraFrontEndOptions: extraFrontEndOptions, + extraFrontEndOptions: extraFrontEndOptions?.join(', '), extraGenSnapshotOptions: argParser.options.containsKey(FlutterOptions.kExtraGenSnapshotOptions) - ? argResults[FlutterOptions.kExtraGenSnapshotOptions] + ? argResults[FlutterOptions.kExtraGenSnapshotOptions]?.join(', ') : null, buildSharedLibrary: argParser.options.containsKey('build-shared-library') ? argResults['build-shared-library'] @@ -517,6 +530,33 @@ ); } + /// A hook called to populate the cache with a particular target platform + /// or build mode. + /// + /// If a command requires specific artifacts, it is it's responsibility to + /// request them here. + Future<void> updateCache() async { + // Download all artifacts unless told otherwise. + await cache.updateAll( + buildModes: <BuildMode>[ + BuildMode.debug, + BuildMode.release, + BuildMode.profile, + BuildMode.dynamicProfile, + BuildMode.dynamicRelease, + ], + targetPlatforms: <TargetPlatform>[ + TargetPlatform.android_arm, + TargetPlatform.android_arm64, + TargetPlatform.android_x64, + TargetPlatform.android_x86, + TargetPlatform.ios, + ], + clobber: false, + skipUnknown: true, + ); + } + /// Perform validation then call [runCommand] to execute the command. /// Return a [Future] that completes with an exit code /// indicating whether execution was successful. @@ -527,11 +567,11 @@ @mustCallSuper Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async { await validateCommand(); - // Populate the cache. We call this before pub get below so that the sky_engine // package is available in the flutter cache for pub to find. - if (shouldUpdateCache) - await cache.updateAll(); + if (shouldUpdateCache) { + await updateCache(); + } if (shouldRunPub) { await pubGet(context: PubContext.getVerifyContext(name));
diff --git a/packages/flutter_tools/test/cache_test.dart b/packages/flutter_tools/test/cache_test.dart index 1e7916f..8b25990 100644 --- a/packages/flutter_tools/test/cache_test.dart +++ b/packages/flutter_tools/test/cache_test.dart
@@ -6,6 +6,7 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; +import 'package:flutter_tools/src/build_info.dart'; import 'package:mockito/mockito.dart'; import 'package:platform/platform.dart'; @@ -60,7 +61,8 @@ directory.createSync(recursive: true); fs.file(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper', 'gradle', 'wrapper', 'gradle-wrapper.jar')).createSync(recursive: true); when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); - expect(gradleWrapper.isUpToDateInner(), false); + when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); + expect(gradleWrapper.isUpToDate().isUpToDate, const UpdateResult(isUpToDate: false).isUpToDate); }, overrides: <Type, Generator>{ Cache: ()=> mockCache, FileSystem: () => fs, @@ -75,7 +77,8 @@ fs.file(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper', 'gradlew.bat')).createSync(recursive: true); when(mockCache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'))).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); - expect(gradleWrapper.isUpToDateInner(), true); + when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(fs.directory(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper'))); + expect(gradleWrapper.isUpToDate().isUpToDate, const UpdateResult(isUpToDate: true).isUpToDate); }, overrides: <Type, Generator>{ Cache: ()=> mockCache, FileSystem: () => fs, @@ -84,37 +87,37 @@ test('should not be up to date, if some cached artifact is not', () { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenReturn(true); - when(artifact2.isUpToDate()).thenReturn(false); + when(artifact1.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: true, clobber: false)); + when(artifact2.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: false, clobber: false)); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); - expect(cache.isUpToDate(), isFalse); + expect(cache.isUpToDate().isUpToDate, isFalse); }); test('should be up to date, if all cached artifacts are', () { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenReturn(true); - when(artifact2.isUpToDate()).thenReturn(true); + when(artifact1.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: true, clobber: false)); + when(artifact2.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: true, clobber: false)); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); - expect(cache.isUpToDate(), isTrue); + expect(cache.isUpToDate().isUpToDate, isTrue); }); test('should update cached artifacts which are not up to date', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenReturn(true); - when(artifact2.isUpToDate()).thenReturn(false); + when(artifact1.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: true, clobber: false)); + when(artifact2.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: false, clobber: false)); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); await cache.updateAll(); - verifyNever(artifact1.update()); - verify(artifact2.update()); + verifyNever(artifact1.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))); + verify(artifact2.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'), clobber: anyNamed('clobber'))); }); testUsingContext('failed storage.googleapis.com download shows China warning', () async { final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact(); - when(artifact1.isUpToDate()).thenReturn(false); - when(artifact2.isUpToDate()).thenReturn(false); + when(artifact1.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: false, clobber: false)); + when(artifact2.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: false, clobber: false)); final MockInternetAddress address = MockInternetAddress(); when(address.host).thenReturn('storage.googleapis.com'); - when(artifact1.update()).thenThrow(SocketException( + when(artifact1.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'), clobber: anyNamed('clobber'))).thenThrow(SocketException( 'Connection reset by peer', address: address, )); @@ -123,15 +126,203 @@ await cache.updateAll(); fail('Mock thrown exception expected'); } catch (e) { - verify(artifact1.update()); + verify(artifact1.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'), clobber: anyNamed('clobber'))); // Don't continue when retrieval fails. - verifyNever(artifact2.update()); + verifyNever(artifact2.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'), clobber: anyNamed('clobber'))); expect( testLogger.errorText, contains('https://flutter.io/community/china'), ); } }); + + final MockPlatform macos = MockPlatform(); + final MockPlatform windows = MockPlatform(); + final MockPlatform linux = MockPlatform(); + when(macos.isMacOS).thenReturn(true); + when(macos.isLinux).thenReturn(false); + when(macos.isWindows).thenReturn(false); + when(windows.isMacOS).thenReturn(false); + when(windows.isLinux).thenReturn(false); + when(windows.isWindows).thenReturn(true); + when(linux.isMacOS).thenReturn(false); + when(linux.isLinux).thenReturn(true); + when(linux.isWindows).thenReturn(false); + + testUsingContext('Engine cache filtering - macOS', () { + final FlutterEngine flutterEngine = FlutterEngine(MockCache()); + expect(flutterEngine.getBinaryDirs( + buildModes: <BuildMode>[BuildMode.release], + targetPlatforms: <TargetPlatform>[TargetPlatform.android_arm], + skipUnknown: true, + ), unorderedEquals(const <BinaryArtifact>[ + BinaryArtifact( + name: 'common', + fileName: 'flutter_patched_sdk.zip', + ), + BinaryArtifact( + name: 'android-arm-release', + fileName: 'android-arm-release/artifacts.zip', + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-profile/darwin-x64', + fileName: 'android-arm-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + skipChecks: true, + ), + BinaryArtifact( + name: 'android-arm-release/darwin-x64', + fileName: 'android-arm-release/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'darwin-x64', + fileName: 'darwin-x64/artifacts.zip', + hostPlatform: TargetPlatform.darwin_x64, + ), + ])); + }, overrides: <Type, Generator>{ + Platform: () => macos, + }); + + testUsingContext('Engine cache filtering - unknown mode - macOS', () { + final FlutterEngine flutterEngine = FlutterEngine(MockCache()); + expect(flutterEngine.getBinaryDirs( + buildModes: <BuildMode>[], + targetPlatforms: <TargetPlatform>[TargetPlatform.ios], + skipUnknown: true, + ), unorderedEquals(const <BinaryArtifact>[ + BinaryArtifact( + name: 'common', + fileName: 'flutter_patched_sdk.zip', + ), + BinaryArtifact( + name: 'android-arm-profile/darwin-x64', + fileName: 'android-arm-profile/darwin-x64.zip', + hostPlatform: TargetPlatform.darwin_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + skipChecks: true, + ), + BinaryArtifact( + name: 'ios', fileName: 'ios/artifacts.zip', + buildMode: BuildMode.debug, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'ios-profile', + fileName: 'ios-profile/artifacts.zip', + buildMode: BuildMode.profile, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'ios-release', + fileName: 'ios-release/artifacts.zip', + buildMode: BuildMode.release, + hostPlatform: TargetPlatform.darwin_x64, + targetPlatform: TargetPlatform.ios, + ), + BinaryArtifact( + name: 'darwin-x64', + fileName: 'darwin-x64/artifacts.zip', + hostPlatform: TargetPlatform.darwin_x64, + ), + ])); + }, overrides: <Type, Generator>{ + Platform: () => macos, + }); + + testUsingContext('Engine cache filtering - Windows', () { + final FlutterEngine flutterEngine = FlutterEngine(MockCache()); + expect(flutterEngine.getBinaryDirs( + buildModes: <BuildMode>[BuildMode.release], + targetPlatforms: <TargetPlatform>[TargetPlatform.android_arm], + skipUnknown: true, + ), unorderedEquals(const <BinaryArtifact>[ + BinaryArtifact( + name: 'common', + fileName: 'flutter_patched_sdk.zip', + ), + BinaryArtifact( + name: 'android-arm-release', + fileName: 'android-arm-release/artifacts.zip', + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-profile/windows-x64', + fileName: 'android-arm-profile/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + skipChecks: true, + ), + BinaryArtifact( + name: 'android-arm-release/windows-x64', + fileName: 'android-arm-release/windows-x64.zip', + hostPlatform: TargetPlatform.windows_x64, + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'windows-x64', + fileName: 'windows-x64/artifacts.zip', + hostPlatform: TargetPlatform.windows_x64, + ), + ])); + }, overrides: <Type, Generator>{ + Platform: () => windows, + }); + + testUsingContext('Engine cache filtering - linux', () { + final FlutterEngine flutterEngine = FlutterEngine(MockCache()); + expect(flutterEngine.getBinaryDirs( + buildModes: <BuildMode>[BuildMode.release], + targetPlatforms: <TargetPlatform>[TargetPlatform.android_arm], + skipUnknown: true, + ), unorderedEquals(const <BinaryArtifact>[ + BinaryArtifact( + name: 'common', + fileName: 'flutter_patched_sdk.zip', + ), + BinaryArtifact( + name: 'android-arm-release', + fileName: 'android-arm-release/artifacts.zip', + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'android-arm-profile/linux-x64', + fileName: 'android-arm-profile/linux-x64.zip', + hostPlatform: TargetPlatform.linux_x64, + buildMode: BuildMode.profile, + targetPlatform: TargetPlatform.android_arm, + skipChecks: true, + ), + BinaryArtifact( + name: 'android-arm-release/linux-x64', + fileName: 'android-arm-release/linux-x64.zip', + hostPlatform: TargetPlatform.linux_x64, + buildMode: BuildMode.release, + targetPlatform: TargetPlatform.android_arm, + ), + BinaryArtifact( + name: 'linux-x64', + fileName: 'linux-x64/artifacts.zip', + hostPlatform: TargetPlatform.linux_x64, + ), + ])); + }, overrides: <Type, Generator>{ + Platform: () => linux, + }); }); testUsingContext('flattenNameSubdirs', () { @@ -163,3 +354,4 @@ class MockCachedArtifact extends Mock implements CachedArtifact {} class MockInternetAddress extends Mock implements InternetAddress {} class MockCache extends Mock implements Cache {} +class MockPlatform extends Mock implements Platform {}
diff --git a/packages/flutter_tools/test/runner/flutter_command_test.dart b/packages/flutter_tools/test/runner/flutter_command_test.dart index f98371a..ea5bfa8 100644 --- a/packages/flutter_tools/test/runner/flutter_command_test.dart +++ b/packages/flutter_tools/test/runner/flutter_command_test.dart
@@ -42,7 +42,12 @@ testUsingContext('honors shouldUpdateCache true', () async { final DummyFlutterCommand flutterCommand = DummyFlutterCommand(shouldUpdateCache: true); await flutterCommand.run(); - verify(cache.updateAll()).called(1); + verify(cache.updateAll( + buildModes: anyNamed('buildModes'), + clobber: anyNamed('clobber'), + skipUnknown: anyNamed('skipUnknown'), + targetPlatforms: anyNamed('targetPlatforms') + )).called(1); }, overrides: <Type, Generator>{ Cache: () => cache,