Unverified Commit 76061c4f authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Lazily download artifacts (Part II) (#27735)

parent 16fd0eb5
...@@ -134,4 +134,7 @@ class _FuchsiaAttachCommand extends AttachCommand { ...@@ -134,4 +134,7 @@ class _FuchsiaAttachCommand extends AttachCommand {
Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter'; Cache.flutterRoot = '$originalWorkingDirectory/third_party/dart-pkg/git/flutter';
return super.runCommand(); return super.runCommand();
} }
@override
Future<void> updateCache() async {}
} }
...@@ -84,7 +84,6 @@ BuildApp() { ...@@ -84,7 +84,6 @@ BuildApp() {
local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}" local framework_path="${FLUTTER_ROOT}/bin/cache/artifacts/engine/${artifact_variant}"
AssertExists "${framework_path}"
AssertExists "${project_path}" AssertExists "${project_path}"
local derived_dir="${SOURCE_ROOT}/Flutter" local derived_dir="${SOURCE_ROOT}/Flutter"
...@@ -118,6 +117,12 @@ BuildApp() { ...@@ -118,6 +117,12 @@ BuildApp() {
flutter_podspec="${LOCAL_ENGINE}/Flutter.podspec" flutter_podspec="${LOCAL_ENGINE}/Flutter.podspec"
fi 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 if [[ -e "${project_path}/.ios" ]]; then
RunCommand rm -rf -- "${derived_dir}/engine" RunCommand rm -rf -- "${derived_dir}/engine"
mkdir "${derived_dir}/engine" mkdir "${derived_dir}/engine"
......
...@@ -37,7 +37,8 @@ Future<List<int>> _attempt(Uri url, {bool onlyHeaders = false}) async { ...@@ -37,7 +37,8 @@ Future<List<int>> _attempt(Uri url, {bool onlyHeaders = false}) async {
printTrace('Downloading: $url'); printTrace('Downloading: $url');
HttpClient httpClient; HttpClient httpClient;
if (context[HttpClientFactory] != null) { if (context[HttpClientFactory] != null) {
httpClient = (context[HttpClientFactory] as HttpClientFactory)(); // ignore: avoid_as final HttpClientFactory httpClientFactory = context[HttpClientFactory];
httpClient = httpClientFactory();
} else { } else {
httpClient = HttpClient(); httpClient = HttpClient();
} }
...@@ -64,9 +65,9 @@ Future<List<int>> _attempt(Uri url, {bool onlyHeaders = false}) async { ...@@ -64,9 +65,9 @@ Future<List<int>> _attempt(Uri url, {bool onlyHeaders = false}) async {
// If we're making a HEAD request, we're only checking to see if the URL is // If we're making a HEAD request, we're only checking to see if the URL is
// valid. // valid.
if (onlyHeaders) { 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) { if (response.statusCode > 0 && response.statusCode < 500) {
throwToolExit( throwToolExit(
'Download failed.\n' 'Download failed.\n'
......
...@@ -13,6 +13,7 @@ import 'base/logger.dart'; ...@@ -13,6 +13,7 @@ import 'base/logger.dart';
import 'base/net.dart'; import 'base/net.dart';
import 'base/os.dart'; import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'build_info.dart';
import 'globals.dart'; import 'globals.dart';
/// A wrapper around the `bin/cache/` directory. /// A wrapper around the `bin/cache/` directory.
...@@ -187,7 +188,24 @@ class Cache { ...@@ -187,7 +188,24 @@ class Cache {
return isOlderThanReference(entity: entity, referenceFile: flutterToolsStamp); 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 { Future<String> getThirdPartyFile(String urlStr, String serviceName) async {
final Uri url = Uri.parse(urlStr); final Uri url = Uri.parse(urlStr);
...@@ -210,13 +228,40 @@ class Cache { ...@@ -210,13 +228,40 @@ class Cache {
return cachedFile.path; return cachedFile.path;
} }
Future<void> updateAll() async { Future<void> updateAll({
if (!_lockEnabled) List<BuildMode> buildModes = const <BuildMode>[],
List<TargetPlatform> targetPlatforms = const <TargetPlatform>[],
bool skipUnknown = true,
bool clobber = false,
}) async {
if (!_lockEnabled) {
return; return;
}
try { try {
for (CachedArtifact artifact in _artifacts) { for (CachedArtifact artifact in _artifacts) {
if (!artifact.isUpToDate()) bool localClobber = clobber;
await artifact.update(); 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) { } on SocketException catch (e) {
if (_hostsBlockedInChina.contains(e.address?.host)) { if (_hostsBlockedInChina.contains(e.address?.host)) {
...@@ -232,6 +277,15 @@ class Cache { ...@@ -232,6 +277,15 @@ class Cache {
} }
} }
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. /// An artifact managed by the cache.
abstract class CachedArtifact { abstract class CachedArtifact {
CachedArtifact(this.name, this.cache); CachedArtifact(this.name, this.cache);
...@@ -248,19 +302,39 @@ abstract class CachedArtifact { ...@@ -248,19 +302,39 @@ abstract class CachedArtifact {
/// starting from scratch. /// starting from scratch.
final List<File> _downloadedFiles = <File>[]; final List<File> _downloadedFiles = <File>[];
bool isUpToDate() { @mustCallSuper
if (!location.existsSync()) UpdateResult isUpToDate({
return false; List<BuildMode> buildModes = const <BuildMode>[],
if (version != cache.getStampFor(name)) List<TargetPlatform> targetPlatforms = const <TargetPlatform>[],
return false; bool skipUnknown = true,
return isUpToDateInner(); }) {
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 { Future<void> update({
if (location.existsSync()) List<BuildMode> buildModes,
List<TargetPlatform> targetPlatforms,
bool skipUnknown = true,
bool clobber = false,
}) async {
if (location.existsSync() && clobber) {
location.deleteSync(recursive: true); 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); cache.setStampFor(name, version);
_removeDownloadedFiles(); _removeDownloadedFiles();
} }
...@@ -268,6 +342,9 @@ abstract class CachedArtifact { ...@@ -268,6 +342,9 @@ abstract class CachedArtifact {
/// Clear any zip/gzip files downloaded. /// Clear any zip/gzip files downloaded.
void _removeDownloadedFiles() { void _removeDownloadedFiles() {
for (File f in _downloadedFiles) { for (File f in _downloadedFiles) {
if (!f.existsSync()) {
continue;
}
f.deleteSync(); f.deleteSync();
for (Directory d = f.parent; d.absolute.path != cache.getDownloadDir().absolute.path; d = d.parent) { for (Directory d = f.parent; d.absolute.path != cache.getDownloadDir().absolute.path; d = d.parent) {
if (d.listSync().isEmpty) { if (d.listSync().isEmpty) {
...@@ -279,11 +356,13 @@ abstract class CachedArtifact { ...@@ -279,11 +356,13 @@ abstract class CachedArtifact {
} }
} }
/// Hook method for extra checks for being up-to-date.
bool isUpToDateInner() => true;
/// Template method to perform artifact update. /// 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 { String get _storageBaseUrl {
final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL']; final String overrideUrl = platform.environment['FLUTTER_STORAGE_BASE_URL'];
...@@ -351,9 +430,16 @@ class MaterialFonts extends CachedArtifact { ...@@ -351,9 +430,16 @@ class MaterialFonts extends CachedArtifact {
MaterialFonts(Cache cache) : super('material_fonts', cache); MaterialFonts(Cache cache) : super('material_fonts', cache);
@override @override
Future<void> updateInner() { Future<void> updateInner({
List<BuildMode> buildModes,
List<TargetPlatform> targetPlatforms,
bool skipUnknown,
bool clobber,
}) async {
final Uri archiveUri = _toStorageUri(version); 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 MaterialFonts extends CachedArtifact { ...@@ -361,100 +447,387 @@ class MaterialFonts extends CachedArtifact {
class FlutterEngine extends CachedArtifact { class FlutterEngine extends CachedArtifact {
FlutterEngine(Cache cache) : super('engine', cache); FlutterEngine(Cache cache) : super('engine', cache);
List<String> _getPackageDirs() => const <String>['sky_engine']; // Return a list of [BinaryArtifact]s to download.
@visibleForTesting
// Return a list of (cache directory path, download URL path) tuples. List<BinaryArtifact> getBinaryDirs({
List<List<String>> _getBinaryDirs() { @required List<BuildMode> buildModes,
final List<List<String>> binaryDirs = <List<String>>[]; @required List<TargetPlatform> targetPlatforms,
@required bool skipUnknown,
binaryDirs.add(<String>['common', 'flutter_patched_sdk.zip']); }) {
TargetPlatform hostPlatform;
if (cache.includeAllPlatforms) if (cache.includeAllPlatforms) {
binaryDirs hostPlatform = null;
..addAll(_osxBinaryDirs) } if (platform.isMacOS) {
..addAll(_linuxBinaryDirs) hostPlatform = TargetPlatform.darwin_x64;
..addAll(_windowsBinaryDirs) } else if (platform.isLinux) {
..addAll(_androidBinaryDirs) hostPlatform = TargetPlatform.linux_x64;
..addAll(_iosBinaryDirs) } else if (platform.isWindows) {
..addAll(_dartSdks); hostPlatform = TargetPlatform.windows_x64;
else if (platform.isLinux) }
binaryDirs final List<BinaryArtifact> results = _reduceEngineBinaries(
..addAll(_linuxBinaryDirs) buildModes: buildModes,
..addAll(_androidBinaryDirs); targetPlatforms: targetPlatforms,
else if (platform.isMacOS) hostPlatform: hostPlatform,
binaryDirs skipUnknown: skipUnknown,
..addAll(_osxBinaryDirs) ).toList();
..addAll(_androidBinaryDirs) if (cache.includeAllPlatforms) {
..addAll(_iosBinaryDirs); return results + _dartSdks;
else if (platform.isWindows) }
binaryDirs return results;
..addAll(_windowsBinaryDirs) }
..addAll(_androidBinaryDirs);
return binaryDirs;
}
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'],
];
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'],
];
List<List<String>> get _windowsBinaryDirs => <List<String>>[ Iterable<BinaryArtifact> _reduceEngineBinaries({
<String>['windows-x64', 'windows-x64/artifacts.zip'], List<BuildMode> buildModes,
<String>['android-arm-profile/windows-x64', 'android-arm-profile/windows-x64.zip'], List<TargetPlatform> targetPlatforms,
<String>['android-arm-release/windows-x64', 'android-arm-release/windows-x64.zip'], TargetPlatform hostPlatform,
<String>['android-arm64-profile/windows-x64', 'android-arm64-profile/windows-x64.zip'], bool skipUnknown,
<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'], return _binaries.where((BinaryArtifact engineBinary) {
<String>['android-arm-dynamic-release/windows-x64', 'android-arm-dynamic-release/windows-x64.zip'], if (hostPlatform != null && engineBinary.hostPlatform != null && engineBinary.hostPlatform != hostPlatform) {
<String>['android-arm64-dynamic-profile/windows-x64', 'android-arm64-dynamic-profile/windows-x64.zip'], return false;
<String>['android-arm64-dynamic-release/windows-x64', 'android-arm64-dynamic-release/windows-x64.zip'], }
]; // 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<List<String>> get _androidBinaryDirs => <List<String>>[ List<BinaryArtifact> get _packages => const <BinaryArtifact>[
<String>['android-x86', 'android-x86/artifacts.zip'], BinaryArtifact(
<String>['android-x64', 'android-x64/artifacts.zip'], name: 'sky_engine',
<String>['android-arm', 'android-arm/artifacts.zip'], fileName: 'sky_engine'
<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>>[ /// This lives separately since we only download it when includeAllPlatforms is true.
<String>['ios', 'ios/artifacts.zip'], List<BinaryArtifact> get _dartSdks => const <BinaryArtifact>[
<String>['ios-profile', 'ios-profile/artifacts.zip'], BinaryArtifact(
<String>['ios-release', 'ios-release/artifacts.zip'], 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 _dartSdks => <List<String>> [ /// A set of all possible artifacts to download.
<String>['darwin-x64', 'dart-sdk-darwin-x64.zip'], ///
<String>['linux-x64', 'dart-sdk-linux-x64.zip'], /// Adding a new artifact:
<String>['windows-x64', 'dart-sdk-windows-x64.zip'], ///
/// To ensure that we do not waste a user's time/data/storage, the flutter
/// tool should only download 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. // A list of cache directory paths to which the LICENSE file should be copied.
...@@ -466,52 +839,71 @@ class FlutterEngine extends CachedArtifact { ...@@ -466,52 +839,71 @@ class FlutterEngine extends CachedArtifact {
} }
@override @override
bool isUpToDateInner() { UpdateResult isUpToDate({
final Directory pkgDir = cache.getCacheDir('pkg'); List<BuildMode> buildModes = const <BuildMode>[],
for (String pkgName in _getPackageDirs()) { List<TargetPlatform> targetPlatforms = const <TargetPlatform>[],
final String pkgPath = fs.path.join(pkgDir.path, pkgName); bool skipUnknown = true,
if (!fs.directory(pkgPath).existsSync()) }) {
return false; 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 (List<String> toolsDir in _getBinaryDirs()) { for (BinaryArtifact packageArtifact in _packages) {
final Directory dir = fs.directory(fs.path.join(location.path, toolsDir[0])); final Directory packageDirectory = packageArtifact.artifactLocation(pkgDir);
if (!dir.existsSync()) if (!packageDirectory.existsSync()) {
return false; return const UpdateResult(isUpToDate: false);
}
} }
for (BinaryArtifact toolsArtifact in getBinaryDirs(buildModes: buildModes, targetPlatforms: targetPlatforms, skipUnknown: skipUnknown)) {
for (String licenseDir in _getLicenseDirs()) { final Directory dir = toolsArtifact.artifactLocation(location);
final File file = fs.file(fs.path.join(location.path, licenseDir, 'LICENSE')); if (!dir.existsSync()) {
if (!file.existsSync()) return const UpdateResult(isUpToDate: false);
return false; }
} }
return true; return const UpdateResult(isUpToDate: true);
} }
@override @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 String url = '$_storageBaseUrl/flutter_infra/flutter/$version/';
final Directory packageDirectory = cache.getCacheDir('pkg');
final Directory pkgDir = cache.getCacheDir('pkg'); for (BinaryArtifact rawArtifact in _packages) {
for (String pkgName in _getPackageDirs()) { final String pkgPath = fs.path.join(packageDirectory.path, rawArtifact.name);
final String pkgPath = fs.path.join(pkgDir.path, pkgName);
final Directory dir = fs.directory(pkgPath); final Directory dir = fs.directory(pkgPath);
if (dir.existsSync()) final bool exists = dir.existsSync();
if (exists) {
if (!clobber) {
continue;
}
dir.deleteSync(recursive: true); 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 List<BinaryArtifact> rawArtifacts = getBinaryDirs(buildModes: buildModes, targetPlatforms: targetPlatforms, skipUnknown: skipUnknown);
final String cacheDir = toolsDir[0]; for (BinaryArtifact rawArtifact in rawArtifacts) {
final String urlPath = toolsDir[1]; final Directory artifactDirectory = rawArtifact.artifactLocation(location);
final Directory dir = fs.directory(fs.path.join(location.path, cacheDir)); if (artifactDirectory.existsSync() && !clobber) {
await _downloadZipArchive('Downloading $cacheDir tools...', Uri.parse(url + urlPath), dir); continue;
}
_makeFilesExecutable(dir); final Uri uri = rawArtifact.artifactRemoteLocation(url);
await _downloadZipArchive('Downloading ${rawArtifact.name} tools...', uri, artifactDirectory);
_makeFilesExecutable(artifactDirectory);
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()) { 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(); framework.createSync();
os.unzip(frameworkZip, framework); os.unzip(frameworkZip, framework);
} }
...@@ -520,43 +912,43 @@ class FlutterEngine extends CachedArtifact { ...@@ -520,43 +912,43 @@ class FlutterEngine extends CachedArtifact {
final File licenseSource = fs.file(fs.path.join(Cache.flutterRoot, 'LICENSE')); final File licenseSource = fs.file(fs.path.join(Cache.flutterRoot, 'LICENSE'));
for (String licenseDir in _getLicenseDirs()) { for (String licenseDir in _getLicenseDirs()) {
final String licenseDestinationPath = fs.path.join(location.path, licenseDir, 'LICENSE'); 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); await licenseSource.copy(licenseDestinationPath);
} }
} }
Future<bool> areRemoteArtifactsAvailable({String engineVersion, // Checks whether the remote artifacts for `engineVersion` are availible in storage.
bool includeAllPlatforms = true}) async { Future<bool> areRemoteArtifactsAvailable({
final bool includeAllPlatformsState = cache.includeAllPlatforms; String engineVersion,
bool includeAllPlatforms = true,
}) async {
final bool includeAllPlatforms = cache.includeAllPlatforms;
cache.includeAllPlatforms = includeAllPlatforms; cache.includeAllPlatforms = includeAllPlatforms;
engineVersion ??= version;
Future<bool> checkForArtifacts(String engineVersion) async { final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/';
engineVersion ??= version; final bool result = await () async {
final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/'; for (BinaryArtifact packageArtifact in _packages) {
final Uri uri = Uri.parse('$url${packageArtifact.name}.zip');
bool exists = false; final bool exists = await _doesRemoteExist('Checking package ${packageArtifact.name} is available...', uri);
for (String pkgName in _getPackageDirs()) {
exists = await _doesRemoteExist('Checking package $pkgName is available...',
Uri.parse(url + pkgName + '.zip'));
if (!exists) { if (!exists) {
return false; return false;
} }
} }
final List<BinaryArtifact> rawArtifacts = getBinaryDirs(buildModes: <BuildMode>[], targetPlatforms: <TargetPlatform>[], skipUnknown: false);
for (List<String> toolsDir in _getBinaryDirs()) { for (BinaryArtifact rawArtifact in rawArtifacts) {
final String cacheDir = toolsDir[0]; final Uri uri = Uri.parse('$url${rawArtifact.fileName}');
final String urlPath = toolsDir[1]; final bool exists = await _doesRemoteExist('Checking ${rawArtifact.name} tools are available...', uri);
exists = await _doesRemoteExist('Checking $cacheDir tools are available...',
Uri.parse(url + urlPath));
if (!exists) { if (!exists) {
return false; return false;
} }
} }
return true; return true;
} }();
cache.includeAllPlatforms = includeAllPlatforms;
final bool result = await checkForArtifacts(engineVersion);
cache.includeAllPlatforms = includeAllPlatformsState;
return result; return result;
} }
...@@ -565,8 +957,9 @@ class FlutterEngine extends CachedArtifact { ...@@ -565,8 +957,9 @@ class FlutterEngine extends CachedArtifact {
for (FileSystemEntity entity in dir.listSync()) { for (FileSystemEntity entity in dir.listSync()) {
if (entity is File) { if (entity is File) {
final String name = fs.path.basename(entity.path); final String name = fs.path.basename(entity.path);
if (name == 'flutter_tester') if (name == 'flutter_tester') {
os.makeExecutable(entity); os.makeExecutable(entity);
}
} }
} }
} }
...@@ -581,30 +974,44 @@ class GradleWrapper extends CachedArtifact { ...@@ -581,30 +974,44 @@ class GradleWrapper extends CachedArtifact {
String get _gradleWrapper => fs.path.join('gradle', 'wrapper', 'gradle-wrapper.jar'); String get _gradleWrapper => fs.path.join('gradle', 'wrapper', 'gradle-wrapper.jar');
@override @override
Future<void> updateInner() { Future<void> updateInner({List<BuildMode> buildModes, List<TargetPlatform> targetPlatforms, bool skipUnknown, bool clobber}) async {
final Uri archiveUri = _toStorageUri(version); final Uri archiveUri = _toStorageUri(version);
return _downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location).then<void>((_) { if (fs.directory(location).listSync().isEmpty || clobber) {
// Delete property file, allowing templates to provide it. await _downloadZippedTarball('Downloading Gradle Wrapper...', archiveUri, location).then<void>((_) {
fs.file(fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync(); // Delete property file, allowing templates to provide it.
// Remove NOTICE file. Should not be part of the template. fs.file(fs.path.join(location.path, 'gradle', 'wrapper', 'gradle-wrapper.properties')).deleteSync();
fs.file(fs.path.join(location.path, 'NOTICE')).deleteSync(); // Remove NOTICE file. Should not be part of the template.
}); fs.file(fs.path.join(location.path, 'NOTICE')).deleteSync();
});
}
} }
@override @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')); final Directory wrapperDir = cache.getCacheDir(fs.path.join('artifacts', 'gradle_wrapper'));
if (!fs.directory(wrapperDir).existsSync()) if (!fs.directory(wrapperDir).existsSync())
return false; return const UpdateResult(isUpToDate: false);
for (String scriptName in _gradleScripts) { for (String scriptName in _gradleScripts) {
final File scriptFile = fs.file(fs.path.join(wrapperDir.path, scriptName)); final File scriptFile = fs.file(fs.path.join(wrapperDir.path, scriptName));
if (!scriptFile.existsSync()) if (!scriptFile.existsSync())
return false; return const UpdateResult(isUpToDate: false);
} }
final File gradleWrapperJar = fs.file(fs.path.join(wrapperDir.path, _gradleWrapper)); final File gradleWrapperJar = fs.file(fs.path.join(wrapperDir.path, _gradleWrapper));
if (!gradleWrapperJar.existsSync()) if (!gradleWrapperJar.existsSync())
return false; return const UpdateResult(isUpToDate: false);
return true; return const UpdateResult(isUpToDate: true);
} }
} }
...@@ -659,3 +1066,34 @@ void _ensureExists(Directory directory) { ...@@ -659,3 +1066,34 @@ void _ensureExists(Directory directory) {
if (!directory.existsSync()) if (!directory.existsSync())
directory.createSync(recursive: true); 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';
}
...@@ -4,6 +4,8 @@ ...@@ -4,6 +4,8 @@
import 'dart:async'; import 'dart:async';
import '../build_info.dart';
import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
import 'build_aot.dart'; import 'build_aot.dart';
import 'build_apk.dart'; import 'build_apk.dart';
...@@ -36,4 +38,19 @@ abstract class BuildSubCommand extends FlutterCommand { ...@@ -36,4 +38,19 @@ abstract class BuildSubCommand extends FlutterCommand {
BuildSubCommand() { BuildSubCommand() {
requiresPubspecYaml(); requiresPubspecYaml();
} }
@override
Future<void> updateCache() async {
final BuildInfo buildInfo = getBuildInfo();
bool skipUnknown = false;
if (buildInfo.mode == null || buildInfo.targetPlatform == null) {
skipUnknown = true;
}
await cache.updateAll(
buildModes: buildInfo.mode != null ? <BuildMode>[buildInfo.mode] : <BuildMode>[],
targetPlatforms: buildInfo.targetPlatform != null ? <TargetPlatform>[buildInfo.targetPlatform] : <TargetPlatform>[],
clobber: false,
skipUnknown: skipUnknown,
);
}
} }
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'dart:async'; import 'dart:async';
import '../android/apk.dart'; import '../android/apk.dart';
import '../build_info.dart';
import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart'; import 'build.dart';
...@@ -49,4 +51,23 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -49,4 +51,23 @@ class BuildApkCommand extends BuildSubCommand {
); );
return null; return null;
} }
@override
Future<void> updateCache() async {
final BuildInfo buildInfo = getBuildInfo();
await cache.updateAll(
buildModes: <BuildMode>[
buildInfo.mode,
BuildMode.debug,
],
targetPlatforms: <TargetPlatform>[
TargetPlatform.android_arm,
TargetPlatform.android_arm64,
TargetPlatform.android_x64,
TargetPlatform.android_x86
],
clobber: false,
skipUnknown: true,
);
}
} }
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
import 'dart:async'; import 'dart:async';
import '../android/app_bundle.dart'; import '../android/app_bundle.dart';
import '../build_info.dart';
import '../globals.dart';
import '../project.dart'; import '../project.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show FlutterCommandResult;
import 'build.dart'; import 'build.dart';
...@@ -47,4 +49,20 @@ class BuildAppBundleCommand extends BuildSubCommand { ...@@ -47,4 +49,20 @@ class BuildAppBundleCommand extends BuildSubCommand {
); );
return null; return null;
} }
@override
Future<void> updateCache() async {
final BuildInfo buildInfo = getBuildInfo();
await cache.updateAll(
buildModes: <BuildMode>[
buildInfo.mode,
],
targetPlatforms: <TargetPlatform>[
TargetPlatform.android_arm,
TargetPlatform.android_arm64,
],
clobber: false,
skipUnknown: true,
);
}
} }
...@@ -49,9 +49,15 @@ class BuildIOSCommand extends BuildSubCommand { ...@@ -49,9 +49,15 @@ class BuildIOSCommand extends BuildSubCommand {
final String description = 'Build an iOS application bundle (Mac OS X host only).'; final String description = 'Build an iOS application bundle (Mac OS X host only).';
@override @override
Future<FlutterCommandResult> runCommand() async { Future<void> validateCommand() async {
final bool forSimulator = argResults['simulator'];
defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release; defaultBuildMode = forSimulator ? BuildMode.debug : BuildMode.release;
return super.validateCommand();
}
bool get forSimulator => argResults['simulator'];
@override
Future<FlutterCommandResult> runCommand() async {
if (getCurrentHostPlatform() != HostPlatform.darwin_x64) if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
throwToolExit('Building for iOS is only supported on the Mac.'); throwToolExit('Building for iOS is only supported on the Mac.');
......
...@@ -211,7 +211,10 @@ class CreateCommand extends FlutterCommand { ...@@ -211,7 +211,10 @@ class CreateCommand extends FlutterCommand {
throwToolExit('Neither the --flutter-root command line flag nor the FLUTTER_ROOT environment ' throwToolExit('Neither the --flutter-root command line flag nor the FLUTTER_ROOT environment '
'variable was specified. Unable to find package:flutter.', exitCode: 2); 'variable was specified. Unable to find package:flutter.', exitCode: 2);
await Cache.instance.updateAll(); await Cache.instance.updateAll(
skipUnknown: true,
clobber: false,
);
final String flutterRoot = fs.path.absolute(Cache.flutterRoot); final String flutterRoot = fs.path.absolute(Cache.flutterRoot);
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import '../base/common.dart'; import '../base/common.dart';
import '../doctor.dart'; import '../doctor.dart';
import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
class DoctorCommand extends FlutterCommand { class DoctorCommand extends FlutterCommand {
...@@ -48,4 +49,12 @@ class DoctorCommand extends FlutterCommand { ...@@ -48,4 +49,12 @@ class DoctorCommand extends FlutterCommand {
final bool success = await doctor.diagnose(androidLicenses: argResults['android-licenses'], verbose: verbose); final bool success = await doctor.diagnose(androidLicenses: argResults['android-licenses'], verbose: verbose);
return FlutterCommandResult(success ? ExitStatus.success : ExitStatus.warning); return FlutterCommandResult(success ? ExitStatus.success : ExitStatus.warning);
} }
@override
Future<void> updateCache() async {
await cache.updateAll(
clobber: false,
skipUnknown: true,
);
}
} }
...@@ -223,8 +223,6 @@ class IdeConfigCommand extends FlutterCommand { ...@@ -223,8 +223,6 @@ class IdeConfigCommand extends FlutterCommand {
throwToolExit('Currently, the only supported IDE is IntelliJ\n$usage', exitCode: 2); throwToolExit('Currently, the only supported IDE is IntelliJ\n$usage', exitCode: 2);
} }
await Cache.instance.updateAll();
if (argResults['update-templates']) { if (argResults['update-templates']) {
_handleTemplateUpdate(); _handleTemplateUpdate();
return null; return null;
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
import 'dart:async'; import 'dart:async';
import '../cache.dart';
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command.dart'; import '../runner/flutter_command.dart';
...@@ -11,6 +12,8 @@ class PrecacheCommand extends FlutterCommand { ...@@ -11,6 +12,8 @@ class PrecacheCommand extends FlutterCommand {
PrecacheCommand() { PrecacheCommand() {
argParser.addFlag('all-platforms', abbr: 'a', negatable: false, argParser.addFlag('all-platforms', abbr: 'a', negatable: false,
help: 'Precache artifacts for all platforms.'); help: 'Precache artifacts for all platforms.');
argParser.addFlag('force', abbr: 'f', negatable: false,
help: 'Force download of new cached artifacts');
} }
@override @override
...@@ -24,14 +27,18 @@ class PrecacheCommand extends FlutterCommand { ...@@ -24,14 +27,18 @@ class PrecacheCommand extends FlutterCommand {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
if (argResults['all-platforms']) if (argResults['all-platforms']) {
cache.includeAllPlatforms = true; 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.'); printStatus('Already up-to-date.');
else } else {
await cache.updateAll(); await cache.updateAll(
skipUnknown: false,
return null; clobber: argResults['force'] || result.clobber,
);
}
return const FlutterCommandResult(ExitStatus.success);
} }
} }
...@@ -61,6 +61,34 @@ abstract class RunCommandBase extends FlutterCommand { ...@@ -61,6 +61,34 @@ abstract class RunCommandBase extends FlutterCommand {
bool get traceStartup => argResults['trace-startup']; bool get traceStartup => argResults['trace-startup'];
String get route => argResults['route']; String get route => argResults['route'];
@override
bool get shouldUpdateCache => true;
@override
Future<void> updateCache() async {
final BuildInfo buildInfo = getBuildInfo();
final BuildMode buildMode = buildInfo.mode ?? BuildMode.debug;
final Set<TargetPlatform> targetPlatforms = Set<TargetPlatform>();
if (buildInfo.targetPlatform != null) {
targetPlatforms.add(buildInfo.targetPlatform);
}
await for (Device device in deviceManager.getAllConnectedDevices()) {
targetPlatforms.add(await device.targetPlatform);
}
if (targetPlatforms.contains(TargetPlatform.android_arm) || targetPlatforms.contains(TargetPlatform.android_arm64)) {
targetPlatforms.add(TargetPlatform.android_x64);
targetPlatforms.add(TargetPlatform.android_x86);
targetPlatforms.add(TargetPlatform.android_arm);
targetPlatforms.add(TargetPlatform.android_arm64);
}
await cache.updateAll(
buildModes: <BuildMode>[buildMode],
targetPlatforms: targetPlatforms.toList(),
clobber: false,
skipUnknown: false,
);
}
} }
class RunCommand extends RunCommandBase { class RunCommand extends RunCommandBase {
......
...@@ -323,30 +323,43 @@ abstract class FlutterCommand extends Command<void> { ...@@ -323,30 +323,43 @@ abstract class FlutterCommand extends Command<void> {
} }
BuildMode getBuildMode() { BuildMode getBuildMode() {
final List<bool> modeFlags = <bool>[argResults['debug'], argResults['profile'], argResults['release']]; bool debug;
if (modeFlags.where((bool flag) => flag).length > 1) 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); throw UsageException('Only one of --debug, --profile, or --release can be specified.', null);
}
final bool dynamicFlag = argParser.options.containsKey('dynamic') final bool dynamicFlag = argParser.options.containsKey('dynamic')
? argResults['dynamic'] ? argResults['dynamic']
: false; : false;
if (argResults['debug']) { if (debug) {
if (dynamicFlag) if (dynamicFlag) {
throw ToolExit('Error: --dynamic requires --release or --profile.'); throw ToolExit('Error: --dynamic requires --release or --profile.');
}
return BuildMode.debug; return BuildMode.debug;
} }
if (argResults['profile']) if (profile) {
return dynamicFlag ? BuildMode.dynamicProfile : BuildMode.profile; return dynamicFlag ? BuildMode.dynamicProfile : BuildMode.profile;
if (argResults['release']) }
if (release) {
return dynamicFlag ? BuildMode.dynamicRelease : BuildMode.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; return _defaultBuildMode;
} }
...@@ -384,7 +397,7 @@ abstract class FlutterCommand extends Command<void> { ...@@ -384,7 +397,7 @@ abstract class FlutterCommand extends Command<void> {
'--patch-number (${argResults['patch-number']}) must be an int.', null); '--patch-number (${argResults['patch-number']}) must be an int.', null);
} }
String extraFrontEndOptions = List<String> extraFrontEndOptions =
argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions) argParser.options.containsKey(FlutterOptions.kExtraFrontEndOptions)
? argResults[FlutterOptions.kExtraFrontEndOptions] ? argResults[FlutterOptions.kExtraFrontEndOptions]
: null; : null;
...@@ -393,9 +406,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -393,9 +406,9 @@ abstract class FlutterCommand extends Command<void> {
for (String expFlag in argResults[FlutterOptions.kEnableExperiment]) { for (String expFlag in argResults[FlutterOptions.kEnableExperiment]) {
final String flag = '--enable-experiment=' + expFlag; final String flag = '--enable-experiment=' + expFlag;
if (extraFrontEndOptions != null) { if (extraFrontEndOptions != null) {
extraFrontEndOptions += ',' + flag; extraFrontEndOptions.add(flag);
} else { } else {
extraFrontEndOptions = flag; extraFrontEndOptions = <String>[flag];
} }
} }
} }
...@@ -421,9 +434,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -421,9 +434,9 @@ abstract class FlutterCommand extends Command<void> {
baselineDir: argParser.options.containsKey('baseline-dir') baselineDir: argParser.options.containsKey('baseline-dir')
? argResults['baseline-dir'] ? argResults['baseline-dir']
: null, : null,
extraFrontEndOptions: extraFrontEndOptions, extraFrontEndOptions: extraFrontEndOptions?.join(', '),
extraGenSnapshotOptions: argParser.options.containsKey(FlutterOptions.kExtraGenSnapshotOptions) extraGenSnapshotOptions: argParser.options.containsKey(FlutterOptions.kExtraGenSnapshotOptions)
? argResults[FlutterOptions.kExtraGenSnapshotOptions] ? argResults[FlutterOptions.kExtraGenSnapshotOptions]?.join(', ')
: null, : null,
buildSharedLibrary: argParser.options.containsKey('build-shared-library') buildSharedLibrary: argParser.options.containsKey('build-shared-library')
? argResults['build-shared-library'] ? argResults['build-shared-library']
...@@ -513,6 +526,19 @@ abstract class FlutterCommand extends Command<void> { ...@@ -513,6 +526,19 @@ abstract class FlutterCommand extends Command<void> {
); );
} }
/// 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 {
// Only download the minimum set of binaries.
await cache.updateAll(
clobber: false,
skipUnknown: true,
);
}
/// Perform validation then call [runCommand] to execute the command. /// Perform validation then call [runCommand] to execute the command.
/// Return a [Future] that completes with an exit code /// Return a [Future] that completes with an exit code
/// indicating whether execution was successful. /// indicating whether execution was successful.
...@@ -523,11 +549,11 @@ abstract class FlutterCommand extends Command<void> { ...@@ -523,11 +549,11 @@ abstract class FlutterCommand extends Command<void> {
@mustCallSuper @mustCallSuper
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async { Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
await validateCommand(); await validateCommand();
// Populate the cache. We call this before pub get below so that the sky_engine // 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. // package is available in the flutter cache for pub to find.
if (shouldUpdateCache) if (shouldUpdateCache) {
await cache.updateAll(); await updateCache();
}
if (shouldRunPub) { if (shouldRunPub) {
await pubGet(context: PubContext.getVerifyContext(name)); await pubGet(context: PubContext.getVerifyContext(name));
......
...@@ -6,6 +6,7 @@ import 'dart:async'; ...@@ -6,6 +6,7 @@ import 'dart:async';
import 'package:file/file.dart'; import 'package:file/file.dart';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/build_info.dart';
import 'package:mockito/mockito.dart'; import 'package:mockito/mockito.dart';
import 'package:platform/platform.dart'; import 'package:platform/platform.dart';
...@@ -60,7 +61,8 @@ void main() { ...@@ -60,7 +61,8 @@ void main() {
directory.createSync(recursive: true); directory.createSync(recursive: true);
fs.file(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper', 'gradle', 'wrapper', 'gradle-wrapper.jar')).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'))); 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>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: ()=> mockCache,
FileSystem: () => fs FileSystem: () => fs
...@@ -75,7 +77,8 @@ void main() { ...@@ -75,7 +77,8 @@ void main() {
fs.file(fs.path.join(directory.path, 'artifacts', 'gradle_wrapper', 'gradlew.bat')).createSync(recursive: true); 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'))); 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>{ }, overrides: <Type, Generator>{
Cache: ()=> mockCache, Cache: ()=> mockCache,
FileSystem: () => fs FileSystem: () => fs
...@@ -84,37 +87,37 @@ void main() { ...@@ -84,37 +87,37 @@ void main() {
test('should not be up to date, if some cached artifact is not', () { test('should not be up to date, if some cached artifact is not', () {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.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()).thenReturn(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]); 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', () { test('should be up to date, if all cached artifacts are', () {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.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()).thenReturn(true); 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]); 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 { test('should update cached artifacts which are not up to date', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.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()).thenReturn(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]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
await cache.updateAll(); await cache.updateAll();
verifyNever(artifact1.update()); verifyNever(artifact1.update(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown')));
verify(artifact2.update()); 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 { testUsingContext('failed storage.googleapis.com download shows China warning', () async {
final CachedArtifact artifact1 = MockCachedArtifact(); final CachedArtifact artifact1 = MockCachedArtifact();
final CachedArtifact artifact2 = MockCachedArtifact(); final CachedArtifact artifact2 = MockCachedArtifact();
when(artifact1.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()).thenReturn(false); when(artifact2.isUpToDate(buildModes: anyNamed('buildModes'), targetPlatforms: anyNamed('targetPlatforms'), skipUnknown: anyNamed('skipUnknown'))).thenReturn(const UpdateResult(isUpToDate: false, clobber: false));
final MockInternetAddress address = MockInternetAddress(); final MockInternetAddress address = MockInternetAddress();
when(address.host).thenReturn('storage.googleapis.com'); 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', 'Connection reset by peer',
address: address, address: address,
)); ));
...@@ -123,15 +126,203 @@ void main() { ...@@ -123,15 +126,203 @@ void main() {
await cache.updateAll(); await cache.updateAll();
fail('Mock thrown exception expected'); fail('Mock thrown exception expected');
} catch (e) { } 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. // 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( expect(
testLogger.errorText, testLogger.errorText,
contains('https://flutter.io/community/china'), 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', () { testUsingContext('flattenNameSubdirs', () {
...@@ -162,4 +353,5 @@ class MockFile extends Mock implements File { ...@@ -162,4 +353,5 @@ class MockFile extends Mock implements File {
class MockRandomAccessFile extends Mock implements RandomAccessFile {} class MockRandomAccessFile extends Mock implements RandomAccessFile {}
class MockCachedArtifact extends Mock implements CachedArtifact {} class MockCachedArtifact extends Mock implements CachedArtifact {}
class MockInternetAddress extends Mock implements InternetAddress {} class MockInternetAddress extends Mock implements InternetAddress {}
class MockCache extends Mock implements Cache {} class MockCache extends Mock implements Cache {}
\ No newline at end of file class MockPlatform extends Mock implements Platform {}
...@@ -42,7 +42,12 @@ void main() { ...@@ -42,7 +42,12 @@ void main() {
testUsingContext('honors shouldUpdateCache true', () async { testUsingContext('honors shouldUpdateCache true', () async {
final DummyFlutterCommand flutterCommand = DummyFlutterCommand(shouldUpdateCache: true); final DummyFlutterCommand flutterCommand = DummyFlutterCommand(shouldUpdateCache: true);
await flutterCommand.run(); 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>{ overrides: <Type, Generator>{
Cache: () => cache, Cache: () => cache,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment