Unverified Commit d71f324e authored by Jonah Williams's avatar Jonah Williams Committed by GitHub

Lazy cache 4 (#29785)

parent 6e50ccc8
...@@ -85,7 +85,6 @@ class AOTSnapshotter { ...@@ -85,7 +85,6 @@ class AOTSnapshotter {
if (fs.file('pubspec.yaml').existsSync()) { if (fs.file('pubspec.yaml').existsSync()) {
flutterProject = await FlutterProject.current(); flutterProject = await FlutterProject.current();
} }
final FlutterEngine engine = FlutterEngine(cache);
if (!_isValidAotPlatform(platform, buildMode)) { if (!_isValidAotPlatform(platform, buildMode)) {
printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.'); printError('${getNameForTargetPlatform(platform)} does not support AOT compilation.');
return 1; return 1;
...@@ -187,7 +186,7 @@ class AOTSnapshotter { ...@@ -187,7 +186,7 @@ class AOTSnapshotter {
'entryPoint': mainPath, 'entryPoint': mainPath,
'sharedLib': buildSharedLibrary.toString(), 'sharedLib': buildSharedLibrary.toString(),
'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '), 'extraGenSnapshotOptions': extraGenSnapshotOptions.join(' '),
'engineHash': engine.version, 'engineHash': Cache.instance.engineRevision,
'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}', 'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}',
}, },
depfilePaths: <String>[], depfilePaths: <String>[],
......
...@@ -15,6 +15,30 @@ import 'base/os.dart'; ...@@ -15,6 +15,30 @@ import 'base/os.dart';
import 'base/platform.dart'; import 'base/platform.dart';
import 'globals.dart'; import 'globals.dart';
/// A tag for a set of development artifacts that need to be cached.
enum DevelopmentArtifact {
/// Artifacts required for Android development.
android,
/// Artifacts required for iOS development.
iOS,
/// Artifacts required for web development,
web,
/// Artifacts required for desktop macOS.
macOS,
/// Artifacts required for desktop Windows.
windows,
/// Artifacts required for desktop linux.
linux,
/// Artifacts required by all developments.
universal,
}
/// A wrapper around the `bin/cache/` directory. /// A wrapper around the `bin/cache/` directory.
class Cache { class Cache {
/// [rootOverride] is configurable for testing. /// [rootOverride] is configurable for testing.
...@@ -22,9 +46,11 @@ class Cache { ...@@ -22,9 +46,11 @@ class Cache {
Cache({ Directory rootOverride, List<CachedArtifact> artifacts }) : _rootOverride = rootOverride { Cache({ Directory rootOverride, List<CachedArtifact> artifacts }) : _rootOverride = rootOverride {
if (artifacts == null) { if (artifacts == null) {
_artifacts.add(MaterialFonts(this)); _artifacts.add(MaterialFonts(this));
_artifacts.add(FlutterEngine(this)); _artifacts.add(AndroidEngineArtifacts(this));
_artifacts.add(IOSEngineArtifacts(this));
_artifacts.add(GradleWrapper(this)); _artifacts.add(GradleWrapper(this));
_artifacts.add(FlutterWebSdk(this)); _artifacts.add(FlutterWebSdk(this));
_artifacts.add(FlutterSdk(this));
} else { } else {
_artifacts.addAll(artifacts); _artifacts.addAll(artifacts);
} }
...@@ -194,7 +220,7 @@ class Cache { ...@@ -194,7 +220,7 @@ class Cache {
return isOlderThanReference(entity: entity, referenceFile: flutterToolsStamp); return isOlderThanReference(entity: entity, referenceFile: flutterToolsStamp);
} }
bool isUpToDate() => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate()); bool isUpToDate(Set<DevelopmentArtifact> requiredArtifacts) => _artifacts.every((CachedArtifact artifact) => artifact.isUpToDate(requiredArtifacts));
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);
...@@ -217,13 +243,16 @@ class Cache { ...@@ -217,13 +243,16 @@ class Cache {
return cachedFile.path; return cachedFile.path;
} }
Future<void> updateAll() async { /// Update the cache to contain all `requiredArtifacts`.
if (!_lockEnabled) Future<void> updateAll(Set<DevelopmentArtifact> requiredArtifacts) async {
if (!_lockEnabled) {
return; return;
}
try { try {
for (CachedArtifact artifact in _artifacts) { for (CachedArtifact artifact in _artifacts) {
if (!artifact.isUpToDate()) if (!artifact.isUpToDate(requiredArtifacts)) {
await artifact.update(); await artifact.update();
}
} }
} on SocketException catch (e) { } on SocketException catch (e) {
if (_hostsBlockedInChina.contains(e.address?.host)) { if (_hostsBlockedInChina.contains(e.address?.host)) {
...@@ -237,15 +266,34 @@ class Cache { ...@@ -237,15 +266,34 @@ class Cache {
rethrow; rethrow;
} }
} }
Future<bool> areRemoteArtifactsAvailable({
String engineVersion,
bool includeAllPlatforms = true,
}) async {
final bool includeAllPlatformsState = cache.includeAllPlatforms;
bool allAvailible = true;
cache.includeAllPlatforms = includeAllPlatforms;
for (CachedArtifact cachedArtifact in _artifacts) {
if (cachedArtifact is EngineCachedArtifact) {
allAvailible &= await cachedArtifact.checkForArtifacts(engineVersion);
}
}
cache.includeAllPlatforms = includeAllPlatformsState;
return allAvailible;
}
} }
/// 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, this.developmentArtifacts);
final String name; final String name;
final Cache cache; final Cache cache;
/// All development artifacts this cache provides.
final Set<DevelopmentArtifact> developmentArtifacts;
Directory get location => cache.getArtifactDirectory(name); Directory get location => cache.getArtifactDirectory(name);
String get version => cache.getVersionFor(name); String get version => cache.getVersionFor(name);
...@@ -255,18 +303,25 @@ abstract class CachedArtifact { ...@@ -255,18 +303,25 @@ abstract class CachedArtifact {
/// starting from scratch. /// starting from scratch.
final List<File> _downloadedFiles = <File>[]; final List<File> _downloadedFiles = <File>[];
bool isUpToDate() { bool isUpToDate(Set<DevelopmentArtifact> requiredArtifacts) {
if (!location.existsSync()) // If the set of required artifacts does not include any from this cache,
// then we can claim we are up to date to skip downloading.
if (!requiredArtifacts.any(developmentArtifacts.contains)) {
return true;
}
if (!location.existsSync()) {
return false; return false;
if (version != cache.getStampFor(name)) }
if (version != cache.getStampFor(name)) {
return false; return false;
}
return isUpToDateInner(); return isUpToDateInner();
} }
Future<void> update() async { Future<void> update() async {
if (location.existsSync()) if (!location.existsSync()) {
location.deleteSync(recursive: true); location.createSync(recursive: true);
location.createSync(recursive: true); }
await updateInner(); await updateInner();
cache.setStampFor(name, version); cache.setStampFor(name, version);
_removeDownloadedFiles(); _removeDownloadedFiles();
...@@ -355,7 +410,11 @@ void _maybeWarnAboutStorageOverride(String overrideUrl) { ...@@ -355,7 +410,11 @@ void _maybeWarnAboutStorageOverride(String overrideUrl) {
/// A cached artifact containing fonts used for Material Design. /// A cached artifact containing fonts used for Material Design.
class MaterialFonts extends CachedArtifact { class MaterialFonts extends CachedArtifact {
MaterialFonts(Cache cache) : super('material_fonts', cache); MaterialFonts(Cache cache) : super(
'material_fonts',
cache,
const <DevelopmentArtifact>{ DevelopmentArtifact.universal },
);
@override @override
Future<void> updateInner() { Future<void> updateInner() {
...@@ -369,7 +428,11 @@ class MaterialFonts extends CachedArtifact { ...@@ -369,7 +428,11 @@ class MaterialFonts extends CachedArtifact {
/// ///
/// This SDK references code within the regular Dart sdk to reduce download size. /// This SDK references code within the regular Dart sdk to reduce download size.
class FlutterWebSdk extends CachedArtifact { class FlutterWebSdk extends CachedArtifact {
FlutterWebSdk(Cache cache) : super('flutter_web_sdk', cache); FlutterWebSdk(Cache cache) : super(
'flutter_web_sdk',
cache,
const <DevelopmentArtifact>{ DevelopmentArtifact.web },
);
@override @override
Directory get location => cache.getWebSdkDirectory(); Directory get location => cache.getWebSdkDirectory();
...@@ -405,130 +468,37 @@ class FlutterWebSdk extends CachedArtifact { ...@@ -405,130 +468,37 @@ class FlutterWebSdk extends CachedArtifact {
} }
} }
/// A cached artifact containing the Flutter engine binaries. abstract class EngineCachedArtifact extends CachedArtifact {
class FlutterEngine extends CachedArtifact { EngineCachedArtifact(
FlutterEngine(Cache cache) : super('engine', cache); Cache cache,
Set<DevelopmentArtifact> requiredArtifacts,
List<String> _getPackageDirs() => const <String>['sky_engine']; ) : super('engine', cache, requiredArtifacts);
// 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;
}
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>>[ /// Return a list of (directory path, download URL path) tuples.
<String>['linux-x64', 'linux-x64/artifacts.zip'], List<List<String>> getBinaryDirs();
<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>>[ /// A list of cache directory paths to which the LICENSE file should be copied.
<String>['windows-x64', 'windows-x64/artifacts.zip'], List<String> getLicenseDirs();
<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>>[ /// A list of the dart package directories to download.
<String>['android-x86', 'android-x86/artifacts.zip'], List<String> getPackageDirs();
<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 list of cache directory paths to which the LICENSE file should be copied.
List<String> _getLicenseDirs() {
if (cache.includeAllPlatforms || platform.isMacOS) {
return const <String>['ios', 'ios-profile', 'ios-release'];
}
return const <String>[];
}
@override @override
bool isUpToDateInner() { bool isUpToDateInner() {
final Directory pkgDir = cache.getCacheDir('pkg'); final Directory pkgDir = cache.getCacheDir('pkg');
for (String pkgName in _getPackageDirs()) { for (String pkgName in getPackageDirs()) {
final String pkgPath = fs.path.join(pkgDir.path, pkgName); final String pkgPath = fs.path.join(pkgDir.path, pkgName);
if (!fs.directory(pkgPath).existsSync()) if (!fs.directory(pkgPath).existsSync())
return false; return false;
} }
for (List<String> toolsDir in _getBinaryDirs()) { for (List<String> toolsDir in getBinaryDirs()) {
final Directory dir = fs.directory(fs.path.join(location.path, toolsDir[0])); final Directory dir = fs.directory(fs.path.join(location.path, toolsDir[0]));
if (!dir.existsSync()) if (!dir.existsSync())
return false; return false;
} }
for (String licenseDir in _getLicenseDirs()) { for (String licenseDir in getLicenseDirs()) {
final File file = fs.file(fs.path.join(location.path, licenseDir, 'LICENSE')); final File file = fs.file(fs.path.join(location.path, licenseDir, 'LICENSE'));
if (!file.existsSync()) if (!file.existsSync())
return false; return false;
...@@ -541,15 +511,16 @@ class FlutterEngine extends CachedArtifact { ...@@ -541,15 +511,16 @@ class FlutterEngine extends CachedArtifact {
final String url = '$_storageBaseUrl/flutter_infra/flutter/$version/'; final String url = '$_storageBaseUrl/flutter_infra/flutter/$version/';
final Directory pkgDir = cache.getCacheDir('pkg'); final Directory pkgDir = cache.getCacheDir('pkg');
for (String pkgName in _getPackageDirs()) { for (String pkgName in getPackageDirs()) {
final String pkgPath = fs.path.join(pkgDir.path, pkgName); final String pkgPath = fs.path.join(pkgDir.path, pkgName);
final Directory dir = fs.directory(pkgPath); final Directory dir = fs.directory(pkgPath);
if (dir.existsSync()) if (dir.existsSync()) {
dir.deleteSync(recursive: true); dir.deleteSync(recursive: true);
}
await _downloadZipArchive('Downloading package $pkgName...', Uri.parse(url + pkgName + '.zip'), pkgDir); await _downloadZipArchive('Downloading package $pkgName...', Uri.parse(url + pkgName + '.zip'), pkgDir);
} }
for (List<String> toolsDir in _getBinaryDirs()) { for (List<String> toolsDir in getBinaryDirs()) {
final String cacheDir = toolsDir[0]; final String cacheDir = toolsDir[0];
final String urlPath = toolsDir[1]; final String urlPath = toolsDir[1];
final Directory dir = fs.directory(fs.path.join(location.path, cacheDir)); final Directory dir = fs.directory(fs.path.join(location.path, cacheDir));
...@@ -566,48 +537,35 @@ class FlutterEngine extends CachedArtifact { ...@@ -566,48 +537,35 @@ 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');
await licenseSource.copy(licenseDestinationPath); await licenseSource.copy(licenseDestinationPath);
} }
} }
Future<bool> areRemoteArtifactsAvailable({ Future<bool> checkForArtifacts(String engineVersion) async {
String engineVersion, engineVersion ??= version;
bool includeAllPlatforms = true, final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/';
}) async {
final bool includeAllPlatformsState = cache.includeAllPlatforms;
cache.includeAllPlatforms = includeAllPlatforms;
Future<bool> checkForArtifacts(String engineVersion) async {
engineVersion ??= version;
final String url = '$_storageBaseUrl/flutter_infra/flutter/$engineVersion/';
bool exists = false; bool exists = false;
for (String pkgName in _getPackageDirs()) { for (String pkgName in getPackageDirs()) {
exists = await _doesRemoteExist('Checking package $pkgName is available...', exists = await _doesRemoteExist('Checking package $pkgName is available...',
Uri.parse(url + pkgName + '.zip')); Uri.parse(url + pkgName + '.zip'));
if (!exists) { if (!exists) {
return false; return false;
}
} }
}
for (List<String> toolsDir in _getBinaryDirs()) { for (List<String> toolsDir in getBinaryDirs()) {
final String cacheDir = toolsDir[0]; final String cacheDir = toolsDir[0];
final String urlPath = toolsDir[1]; final String urlPath = toolsDir[1];
exists = await _doesRemoteExist('Checking $cacheDir tools are available...', exists = await _doesRemoteExist('Checking $cacheDir tools are available...',
Uri.parse(url + urlPath)); Uri.parse(url + urlPath));
if (!exists) { if (!exists) {
return false; return false;
}
} }
return true;
} }
return true;
final bool result = await checkForArtifacts(engineVersion);
cache.includeAllPlatforms = includeAllPlatformsState;
return result;
} }
...@@ -622,9 +580,123 @@ class FlutterEngine extends CachedArtifact { ...@@ -622,9 +580,123 @@ class FlutterEngine extends CachedArtifact {
} }
} }
/// A cached artifact containing the dart:ui source code.
class FlutterSdk extends EngineCachedArtifact {
FlutterSdk(Cache cache) : super(
cache,
const <DevelopmentArtifact>{ DevelopmentArtifact.universal },
);
@override
List<String> getPackageDirs() => const <String>['sky_engine'];
@override
List<List<String>> getBinaryDirs() {
final List<List<String>> binaryDirs = <List<String>>[
<String>['common', 'flutter_patched_sdk.zip'],
];
if (cache.includeAllPlatforms) {
binaryDirs.addAll(<List<String>>[
<String>['windows-x64', 'windows-x64/artifacts.zip'],
<String>['linux-x64', 'linux-x64/artifacts.zip'],
<String>['darwin-x64', 'darwin-x64/artifacts.zip'],
]);
} else if (platform.isWindows) {
binaryDirs.addAll(<List<String>>[
<String>['windows-x64', 'windows-x64/artifacts.zip'],
]);
} else if (platform.isMacOS) {
binaryDirs.addAll(<List<String>>[
<String>['darwin-x64', 'darwin-x64/artifacts.zip'],
]);
} else if (platform.isLinux) {
binaryDirs.addAll(<List<String>>[
<String>['linux-x64', 'linux-x64/artifacts.zip'],
]);
}
return binaryDirs;
}
@override
List<String> getLicenseDirs() => const <String>[];
}
class AndroidEngineArtifacts extends EngineCachedArtifact {
AndroidEngineArtifacts(Cache cache) : super(
cache,
const <DevelopmentArtifact>{ DevelopmentArtifact.android },
);
@override
List<String> getPackageDirs() => const <String>[];
@override
List<List<String>> getBinaryDirs() {
final List<List<String>> binaryDirs = <List<String>>[];
if (cache.includeAllPlatforms) {
binaryDirs
..addAll(_osxBinaryDirs)
..addAll(_linuxBinaryDirs)
..addAll(_windowsBinaryDirs)
..addAll(_androidBinaryDirs)
..addAll(_dartSdks);
} else if (platform.isWindows) {
binaryDirs
..addAll(_windowsBinaryDirs)
..addAll(_androidBinaryDirs);
} else if (platform.isMacOS) {
binaryDirs
..addAll(_osxBinaryDirs)
..addAll(_androidBinaryDirs);
} else if (platform.isLinux) {
binaryDirs
..addAll(_linuxBinaryDirs)
..addAll(_androidBinaryDirs);
}
return binaryDirs;
}
@override
List<String> getLicenseDirs() { return <String>[]; }
}
class IOSEngineArtifacts extends EngineCachedArtifact {
IOSEngineArtifacts(Cache cache) : super(
cache,
<DevelopmentArtifact>{ DevelopmentArtifact.iOS },
);
@override
List<List<String>> getBinaryDirs() {
final List<List<String>> binaryDirs = <List<String>>[];
if (platform.isMacOS || cache.includeAllPlatforms) {
binaryDirs.addAll(_iosBinaryDirs);
}
return binaryDirs;
}
@override
List<String> getLicenseDirs() {
if (cache.includeAllPlatforms || platform.isMacOS) {
return const <String>['ios', 'ios-profile', 'ios-release'];
}
return const <String>[];
}
@override
List<String> getPackageDirs() {
return <String>[];
}
}
/// A cached artifact containing Gradle Wrapper scripts and binaries. /// A cached artifact containing Gradle Wrapper scripts and binaries.
class GradleWrapper extends CachedArtifact { class GradleWrapper extends CachedArtifact {
GradleWrapper(Cache cache) : super('gradle_wrapper', cache); GradleWrapper(Cache cache) : super(
'gradle_wrapper',
cache,
const <DevelopmentArtifact>{ DevelopmentArtifact.android },
);
List<String> get _gradleScripts => <String>['gradlew', 'gradlew.bat']; List<String> get _gradleScripts => <String>['gradlew', 'gradlew.bat'];
...@@ -644,8 +716,9 @@ class GradleWrapper extends CachedArtifact { ...@@ -644,8 +716,9 @@ class GradleWrapper extends CachedArtifact {
@override @override
bool isUpToDateInner() { bool isUpToDateInner() {
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 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())
...@@ -706,6 +779,67 @@ Future<bool> _doesRemoteExist(String message, Uri url) async { ...@@ -706,6 +779,67 @@ Future<bool> _doesRemoteExist(String message, Uri url) async {
/// Create the given [directory] and parents, as necessary. /// Create the given [directory] and parents, as necessary.
void _ensureExists(Directory directory) { void _ensureExists(Directory directory) {
if (!directory.existsSync()) if (!directory.existsSync()) {
directory.createSync(recursive: true); directory.createSync(recursive: true);
}
} }
const List<List<String>> _osxBinaryDirs = <List<String>>[
<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'],
];
const List<List<String>> _linuxBinaryDirs = <List<String>>[
<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'],
];
const List<List<String>> _windowsBinaryDirs = <List<String>>[
<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'],
];
const List<List<String>> _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'],
];
const List<List<String>> _iosBinaryDirs = <List<String>>[
<String>['ios', 'ios/artifacts.zip'],
<String>['ios-profile', 'ios-profile/artifacts.zip'],
<String>['ios-release', 'ios-release/artifacts.zip'],
];
const List<List<String>> _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'],
];
...@@ -64,6 +64,11 @@ class AnalyzeCommand extends FlutterCommand { ...@@ -64,6 +64,11 @@ class AnalyzeCommand extends FlutterCommand {
@override @override
String get description => "Analyze the project's Dart code."; String get description => "Analyze the project's Dart code.";
@override
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
};
@override @override
bool get shouldRunPub { bool get shouldRunPub {
// If they're not analyzing the current project. // If they're not analyzing the current project.
......
...@@ -8,7 +8,7 @@ import '../base/common.dart'; ...@@ -8,7 +8,7 @@ import '../base/common.dart';
import '../base/logger.dart'; import '../base/logger.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../globals.dart'; import '../globals.dart';
import '../runner/flutter_command.dart' show FlutterCommandResult; import '../runner/flutter_command.dart' show DevelopmentArtifact, FlutterCommandResult;
import '../web/compile.dart'; import '../web/compile.dart';
import 'build.dart'; import 'build.dart';
...@@ -19,6 +19,12 @@ class BuildWebCommand extends BuildSubCommand { ...@@ -19,6 +19,12 @@ class BuildWebCommand extends BuildSubCommand {
defaultBuildMode = BuildMode.release; defaultBuildMode = BuildMode.release;
} }
@override
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.web,
};
@override @override
final String name = 'web'; final String name = 'web';
......
...@@ -245,7 +245,7 @@ class CreateCommand extends FlutterCommand { ...@@ -245,7 +245,7 @@ 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(<DevelopmentArtifact>{ DevelopmentArtifact.universal });
final String flutterRoot = fs.path.absolute(Cache.flutterRoot); final String flutterRoot = fs.path.absolute(Cache.flutterRoot);
......
...@@ -223,7 +223,7 @@ class IdeConfigCommand extends FlutterCommand { ...@@ -223,7 +223,7 @@ 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(); await Cache.instance.updateAll(<DevelopmentArtifact>{ DevelopmentArtifact.universal });
if (argResults['update-templates']) { if (argResults['update-templates']) {
_handleTemplateUpdate(); _handleTemplateUpdate();
......
...@@ -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,12 @@ class PrecacheCommand extends FlutterCommand { ...@@ -11,6 +12,12 @@ 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('android', negatable: true, defaultsTo: true,
help: 'Precache artifacts for Android development');
argParser.addFlag('ios', negatable: true, defaultsTo: true,
help: 'Precache artifacts for iOS developemnt');
argParser.addFlag('web', negatable: true, defaultsTo: false,
help: 'Precache artifacts for web development');
} }
@override @override
...@@ -24,14 +31,25 @@ class PrecacheCommand extends FlutterCommand { ...@@ -24,14 +31,25 @@ 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 Set<DevelopmentArtifact> requiredArtifacts = <DevelopmentArtifact>{ DevelopmentArtifact.universal };
if (argResults['android']) {
requiredArtifacts.add(DevelopmentArtifact.android);
}
if (argResults['ios']) {
requiredArtifacts.add(DevelopmentArtifact.iOS);
}
if (argResults['web']) {
requiredArtifacts.add(DevelopmentArtifact.web);
}
if (cache.isUpToDate(requiredArtifacts)) {
printStatus('Already up-to-date.'); printStatus('Already up-to-date.');
else } else {
await cache.updateAll(); await cache.updateAll(requiredArtifacts);
}
return null; return null;
} }
} }
...@@ -232,7 +232,6 @@ class KernelCompiler { ...@@ -232,7 +232,6 @@ class KernelCompiler {
if (fs.file('pubspec.yaml').existsSync()) { if (fs.file('pubspec.yaml').existsSync()) {
flutterProject = await FlutterProject.current(); flutterProject = await FlutterProject.current();
} }
final FlutterEngine engine = FlutterEngine(cache);
// TODO(cbracken): eliminate pathFilter. // TODO(cbracken): eliminate pathFilter.
// Currently the compiler emits buildbot paths for the core libs in the // Currently the compiler emits buildbot paths for the core libs in the
...@@ -246,7 +245,7 @@ class KernelCompiler { ...@@ -246,7 +245,7 @@ class KernelCompiler {
'entryPoint': mainPath, 'entryPoint': mainPath,
'trackWidgetCreation': trackWidgetCreation.toString(), 'trackWidgetCreation': trackWidgetCreation.toString(),
'linkPlatformKernelIn': linkPlatformKernelIn.toString(), 'linkPlatformKernelIn': linkPlatformKernelIn.toString(),
'engineHash': engine.version, 'engineHash': Cache.instance.engineRevision,
'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}', 'buildersUsed': '${flutterProject != null ? flutterProject.hasBuilders : false}',
}, },
depfilePaths: <String>[depFilePath], depfilePaths: <String>[depFilePath],
......
...@@ -170,9 +170,7 @@ class Doctor { ...@@ -170,9 +170,7 @@ class Doctor {
} }
Future<bool> checkRemoteArtifacts(String engineRevision) async { Future<bool> checkRemoteArtifacts(String engineRevision) async {
final Cache cache = Cache(); return Cache.instance.areRemoteArtifactsAvailable(engineVersion: engineRevision);
final FlutterEngine engine = FlutterEngine(cache);
return await engine.areRemoteArtifactsAvailable(engineVersion: engineRevision);
} }
/// Print information about the state of installed tooling. /// Print information about the state of installed tooling.
......
...@@ -19,6 +19,7 @@ import '../base/user_messages.dart'; ...@@ -19,6 +19,7 @@ import '../base/user_messages.dart';
import '../base/utils.dart'; import '../base/utils.dart';
import '../build_info.dart'; import '../build_info.dart';
import '../bundle.dart' as bundle; import '../bundle.dart' as bundle;
import '../cache.dart';
import '../dart/package_map.dart'; import '../dart/package_map.dart';
import '../dart/pub.dart'; import '../dart/pub.dart';
import '../device.dart'; import '../device.dart';
...@@ -28,6 +29,8 @@ import '../project.dart'; ...@@ -28,6 +29,8 @@ import '../project.dart';
import '../usage.dart'; import '../usage.dart';
import 'flutter_command_runner.dart'; import 'flutter_command_runner.dart';
export '../cache.dart' show DevelopmentArtifact;
enum ExitStatus { enum ExitStatus {
success, success,
warning, warning,
...@@ -530,8 +533,9 @@ abstract class FlutterCommand extends Command<void> { ...@@ -530,8 +533,9 @@ abstract class FlutterCommand extends Command<void> {
// 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 cache.updateAll(requiredArtifacts);
}
if (shouldRunPub) { if (shouldRunPub) {
await pubGet(context: PubContext.getVerifyContext(name)); await pubGet(context: PubContext.getVerifyContext(name));
...@@ -549,6 +553,16 @@ abstract class FlutterCommand extends Command<void> { ...@@ -549,6 +553,16 @@ abstract class FlutterCommand extends Command<void> {
return await runCommand(); return await runCommand();
} }
/// The set of development artifacts required for this command.
///
/// Defaults to [DevelopmentArtifact.universal],
/// [DevelopmentArtifact.android], and [DevelopmentArtifact.iOS].
Set<DevelopmentArtifact> get requiredArtifacts => const <DevelopmentArtifact>{
DevelopmentArtifact.universal,
DevelopmentArtifact.iOS,
DevelopmentArtifact.android,
};
/// Subclasses must implement this to execute the command. /// Subclasses must implement this to execute the command.
/// Optionally provide a [FlutterCommandResult] to send more details about the /// Optionally provide a [FlutterCommandResult] to send more details about the
/// execution for analytics. /// execution for analytics.
......
...@@ -84,34 +84,34 @@ void main() { ...@@ -84,34 +84,34 @@ 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(const <DevelopmentArtifact>{})).thenReturn(true);
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate(const <DevelopmentArtifact>{})).thenReturn(false);
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
expect(cache.isUpToDate(), isFalse); expect(cache.isUpToDate(const <DevelopmentArtifact>{}), 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(const <DevelopmentArtifact>{})).thenReturn(true);
when(artifact2.isUpToDate()).thenReturn(true); when(artifact2.isUpToDate(const <DevelopmentArtifact>{})).thenReturn(true);
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
expect(cache.isUpToDate(), isTrue); expect(cache.isUpToDate(const <DevelopmentArtifact>{}), 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(const <DevelopmentArtifact>{})).thenReturn(true);
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate(const <DevelopmentArtifact>{})).thenReturn(false);
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
await cache.updateAll(); await cache.updateAll(const <DevelopmentArtifact>{});
verifyNever(artifact1.update()); verifyNever(artifact1.update());
verify(artifact2.update()); verify(artifact2.update());
}); });
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(const <DevelopmentArtifact>{})).thenReturn(false);
when(artifact2.isUpToDate()).thenReturn(false); when(artifact2.isUpToDate(const <DevelopmentArtifact>{})).thenReturn(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()).thenThrow(SocketException(
...@@ -120,7 +120,7 @@ void main() { ...@@ -120,7 +120,7 @@ void main() {
)); ));
final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]); final Cache cache = Cache(artifacts: <CachedArtifact>[artifact1, artifact2]);
try { try {
await cache.updateAll(); await cache.updateAll(const <DevelopmentArtifact>{});
fail('Mock thrown exception expected'); fail('Mock thrown exception expected');
} catch (e) { } catch (e) {
verify(artifact1.update()); verify(artifact1.update());
......
...@@ -42,7 +42,7 @@ void main() { ...@@ -42,7 +42,7 @@ 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(any)).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