mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Provide user feedback on slow Gradle operations (#11015)
This commit is contained in:
parent
a106f504c7
commit
030abfd49a
@ -360,7 +360,7 @@ class AndroidDevice extends Device {
|
||||
);
|
||||
// Package has been built, so we can get the updated application ID and
|
||||
// activity name from the .apk.
|
||||
package = new AndroidApk.fromCurrentDirectory();
|
||||
package = await AndroidApk.fromCurrentDirectory();
|
||||
}
|
||||
|
||||
printTrace("Stopping app '${package.name}' on $name.");
|
||||
|
@ -25,6 +25,7 @@ const String gradleAppOutDirV1 = 'android/app/build/outputs/apk';
|
||||
const String gradleVersion = '3.3';
|
||||
|
||||
String _cachedGradleAppOutDirV2;
|
||||
String _cachedGradleExecutable;
|
||||
|
||||
enum FlutterPluginVersion {
|
||||
none,
|
||||
@ -57,7 +58,7 @@ FlutterPluginVersion get flutterPluginVersion {
|
||||
return FlutterPluginVersion.none;
|
||||
}
|
||||
|
||||
String getGradleAppOut() {
|
||||
Future<String> getGradleAppOut() async {
|
||||
switch (flutterPluginVersion) {
|
||||
case FlutterPluginVersion.none:
|
||||
// Fall through. Pretend we're v1, and just go with it.
|
||||
@ -66,27 +67,29 @@ String getGradleAppOut() {
|
||||
case FlutterPluginVersion.managed:
|
||||
// Fall through. The managed plugin matches plugin v2 for now.
|
||||
case FlutterPluginVersion.v2:
|
||||
return '${getGradleAppOutDirV2()}/app.apk';
|
||||
return '${await _getGradleAppOutDirV2()}/app.apk';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String getGradleAppOutDirV2() {
|
||||
_cachedGradleAppOutDirV2 ??= _calculateGradleAppOutDirV2();
|
||||
Future<String> _getGradleAppOutDirV2() async {
|
||||
_cachedGradleAppOutDirV2 ??= await _calculateGradleAppOutDirV2();
|
||||
return _cachedGradleAppOutDirV2;
|
||||
}
|
||||
|
||||
// Note: this call takes about a second to complete.
|
||||
String _calculateGradleAppOutDirV2() {
|
||||
final String gradle = ensureGradle();
|
||||
// Note: Dependencies are resolved and possibly downloaded as a side-effect
|
||||
// of calculating the app properties using Gradle. This may take minutes.
|
||||
Future<String> _calculateGradleAppOutDirV2() async {
|
||||
final String gradle = await _ensureGradle();
|
||||
updateLocalProperties();
|
||||
try {
|
||||
final String properties = runCheckedSync(
|
||||
final Status status = logger.startProgress('Resolving dependencies...', expectSlowOperation: true);
|
||||
final RunResult runResult = await runCheckedAsync(
|
||||
<String>[gradle, 'app:properties'],
|
||||
workingDirectory: 'android',
|
||||
hideStdout: true,
|
||||
environment: _gradleEnv,
|
||||
);
|
||||
final String properties = runResult.stdout.trim();
|
||||
String buildDir = properties
|
||||
.split('\n')
|
||||
.firstWhere((String s) => s.startsWith('buildDir: '))
|
||||
@ -97,6 +100,7 @@ String _calculateGradleAppOutDirV2() {
|
||||
// Relativize path, snip current directory + separating '/'.
|
||||
buildDir = buildDir.substring(currentDirectory.length + 1);
|
||||
}
|
||||
status.stop();
|
||||
return '$buildDir/outputs/apk';
|
||||
} catch (e) {
|
||||
printError('Error running gradle: $e');
|
||||
@ -105,7 +109,7 @@ String _calculateGradleAppOutDirV2() {
|
||||
return gradleAppOutDirV1;
|
||||
}
|
||||
|
||||
String locateProjectGradlew({ bool ensureExecutable: true }) {
|
||||
String _locateProjectGradlew({ bool ensureExecutable: true }) {
|
||||
final String path = fs.path.join(
|
||||
'android', platform.isWindows ? 'gradlew.bat' : 'gradlew'
|
||||
);
|
||||
@ -120,12 +124,27 @@ String locateProjectGradlew({ bool ensureExecutable: true }) {
|
||||
}
|
||||
}
|
||||
|
||||
String ensureGradle() {
|
||||
String gradle = locateProjectGradlew();
|
||||
Future<String> _ensureGradle() async {
|
||||
_cachedGradleExecutable ??= await _initializeGradle();
|
||||
return _cachedGradleExecutable;
|
||||
}
|
||||
|
||||
// Note: Gradle may be bootstrapped and possibly downloaded as a side-effect
|
||||
// of validating the Gradle executable. This may take several seconds.
|
||||
Future<String> _initializeGradle() async {
|
||||
final Status status = logger.startProgress('Initializing Gradle...', expectSlowOperation: true);
|
||||
String gradle = _locateProjectGradlew();
|
||||
if (gradle == null) {
|
||||
_injectGradleWrapper();
|
||||
gradle = locateProjectGradlew();
|
||||
gradle = _locateProjectGradlew();
|
||||
}
|
||||
if (gradle == null)
|
||||
throwToolExit('Unable to locate gradlew script');
|
||||
printTrace('Using gradle from $gradle.');
|
||||
// Validates the Gradle executable by asking for its version.
|
||||
// Makes Gradle Wrapper download and install Gradle distribution, if needed.
|
||||
await runCheckedAsync(<String>[gradle, '-v'], environment: _gradleEnv);
|
||||
status.stop();
|
||||
return gradle;
|
||||
}
|
||||
|
||||
@ -183,24 +202,24 @@ Future<Null> buildGradleProject(BuildMode buildMode, String target, String kerne
|
||||
|
||||
injectPlugins();
|
||||
|
||||
final String gradle = ensureGradle();
|
||||
final String gradle = await _ensureGradle();
|
||||
|
||||
switch (flutterPluginVersion) {
|
||||
case FlutterPluginVersion.none:
|
||||
// Fall through. Pretend it's v1, and just go for it.
|
||||
case FlutterPluginVersion.v1:
|
||||
return buildGradleProjectV1(gradle);
|
||||
return _buildGradleProjectV1(gradle);
|
||||
case FlutterPluginVersion.managed:
|
||||
// Fall through. Managed plugin builds the same way as plugin v2.
|
||||
case FlutterPluginVersion.v2:
|
||||
return buildGradleProjectV2(gradle, buildModeName, target, kernelPath);
|
||||
return _buildGradleProjectV2(gradle, buildModeName, target, kernelPath);
|
||||
}
|
||||
}
|
||||
|
||||
Future<Null> buildGradleProjectV1(String gradle) async {
|
||||
// Run 'gradle build'.
|
||||
final Status status = logger.startProgress('Running \'gradle build\'...', expectSlowOperation: true);
|
||||
final int exitcode = await runCommandAndStreamOutput(
|
||||
Future<Null> _buildGradleProjectV1(String gradle) async {
|
||||
// Run 'gradlew build'.
|
||||
final Status status = logger.startProgress('Running \'gradlew build\'...', expectSlowOperation: true);
|
||||
final int exitCode = await runCommandAndStreamOutput(
|
||||
<String>[fs.file(gradle).absolute.path, 'build'],
|
||||
workingDirectory: 'android',
|
||||
allowReentrantFlutter: true,
|
||||
@ -208,8 +227,8 @@ Future<Null> buildGradleProjectV1(String gradle) async {
|
||||
);
|
||||
status.stop();
|
||||
|
||||
if (exitcode != 0)
|
||||
throwToolExit('Gradle build failed: $exitcode', exitCode: exitcode);
|
||||
if (exitCode != 0)
|
||||
throwToolExit('Gradle build failed: $exitCode', exitCode: exitCode);
|
||||
|
||||
final File apkFile = fs.file(gradleAppOutV1);
|
||||
printStatus('Built $gradleAppOutV1 (${getSizeAsMB(apkFile.lengthSync())}).');
|
||||
@ -226,10 +245,10 @@ File findApkFile(String buildDirectory, String buildModeName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Future<Null> buildGradleProjectV2(String gradle, String buildModeName, String target, String kernelPath) async {
|
||||
Future<Null> _buildGradleProjectV2(String gradle, String buildModeName, String target, String kernelPath) async {
|
||||
final String assembleTask = "assemble${toTitleCase(buildModeName)}";
|
||||
|
||||
// Run 'gradle assemble<BuildMode>'.
|
||||
// Run 'gradlew assemble<BuildMode>'.
|
||||
final Status status = logger.startProgress('Running \'gradlew $assembleTask\'...', expectSlowOperation: true);
|
||||
final String gradlePath = fs.file(gradle).absolute.path;
|
||||
final List<String> command = <String>[gradlePath];
|
||||
@ -258,7 +277,7 @@ Future<Null> buildGradleProjectV2(String gradle, String buildModeName, String ta
|
||||
if (exitcode != 0)
|
||||
throwToolExit('Gradle build failed: $exitcode', exitCode: exitcode);
|
||||
|
||||
final String buildDirectory = getGradleAppOutDirV2();
|
||||
final String buildDirectory = await _getGradleAppOutDirV2();
|
||||
final File apkFile = findApkFile(buildDirectory, buildModeName);
|
||||
if (apkFile == null)
|
||||
throwToolExit('Gradle build failed to produce an Android package.');
|
||||
|
@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:meta/meta.dart' show required;
|
||||
import 'package:xml/xml.dart' as xml;
|
||||
|
||||
@ -78,21 +80,21 @@ class AndroidApk extends ApplicationPackage {
|
||||
}
|
||||
|
||||
/// Creates a new AndroidApk based on the information in the Android manifest.
|
||||
factory AndroidApk.fromCurrentDirectory() {
|
||||
static Future<AndroidApk> fromCurrentDirectory() async {
|
||||
String manifestPath;
|
||||
String apkPath;
|
||||
|
||||
if (isProjectUsingGradle()) {
|
||||
if (fs.file(getGradleAppOut()).existsSync()) {
|
||||
final String apkPath = await getGradleAppOut();
|
||||
if (fs.file(apkPath).existsSync()) {
|
||||
// Grab information from the .apk. The gradle build script might alter
|
||||
// the application Id, so we need to look at what was actually built.
|
||||
return new AndroidApk.fromApk(getGradleAppOut());
|
||||
return new AndroidApk.fromApk(apkPath);
|
||||
}
|
||||
// The .apk hasn't been built yet, so we work with what we have. The run
|
||||
// command will grab a new AndroidApk after building, to get the updated
|
||||
// IDs.
|
||||
manifestPath = gradleManifestPath;
|
||||
apkPath = getGradleAppOut();
|
||||
} else {
|
||||
manifestPath = fs.path.join('android', 'AndroidManifest.xml');
|
||||
apkPath = fs.path.join(getAndroidBuildDirectory(), 'app.apk');
|
||||
@ -251,15 +253,15 @@ class PrebuiltIOSApp extends IOSApp {
|
||||
String get _bundlePath => bundleDir.path;
|
||||
}
|
||||
|
||||
ApplicationPackage getApplicationPackageForPlatform(TargetPlatform platform, {
|
||||
Future<ApplicationPackage> getApplicationPackageForPlatform(TargetPlatform platform, {
|
||||
String applicationBinary
|
||||
}) {
|
||||
}) async {
|
||||
switch (platform) {
|
||||
case TargetPlatform.android_arm:
|
||||
case TargetPlatform.android_x64:
|
||||
case TargetPlatform.android_x86:
|
||||
return applicationBinary == null
|
||||
? new AndroidApk.fromCurrentDirectory()
|
||||
? await AndroidApk.fromCurrentDirectory()
|
||||
: new AndroidApk.fromApk(applicationBinary);
|
||||
case TargetPlatform.ios:
|
||||
return applicationBinary == null
|
||||
@ -281,12 +283,12 @@ class ApplicationPackageStore {
|
||||
|
||||
ApplicationPackageStore({ this.android, this.iOS });
|
||||
|
||||
ApplicationPackage getPackageForPlatform(TargetPlatform platform) {
|
||||
Future<ApplicationPackage> getPackageForPlatform(TargetPlatform platform) async {
|
||||
switch (platform) {
|
||||
case TargetPlatform.android_arm:
|
||||
case TargetPlatform.android_x64:
|
||||
case TargetPlatform.android_x86:
|
||||
android ??= new AndroidApk.fromCurrentDirectory();
|
||||
android ??= await AndroidApk.fromCurrentDirectory();
|
||||
return android;
|
||||
case TargetPlatform.ios:
|
||||
iOS ??= new IOSApp.fromCurrentDirectory();
|
||||
|
@ -45,7 +45,7 @@ class BuildIOSCommand extends BuildSubCommand {
|
||||
if (getCurrentHostPlatform() != HostPlatform.darwin_x64)
|
||||
throwToolExit('Building for iOS is only supported on the Mac.');
|
||||
|
||||
final BuildableIOSApp app = applicationPackages.getPackageForPlatform(TargetPlatform.ios);
|
||||
final BuildableIOSApp app = await applicationPackages.getPackageForPlatform(TargetPlatform.ios);
|
||||
|
||||
if (app == null)
|
||||
throwToolExit('Application not configured for iOS');
|
||||
|
@ -229,7 +229,7 @@ Future<LaunchResult> _startApp(DriveCommand command) async {
|
||||
await appStopper(command);
|
||||
|
||||
printTrace('Installing application package.');
|
||||
final ApplicationPackage package = command.applicationPackages
|
||||
final ApplicationPackage package = await command.applicationPackages
|
||||
.getPackageForPlatform(await command.device.targetPlatform);
|
||||
if (await command.device.isAppInstalled(package))
|
||||
await command.device.uninstallApp(package);
|
||||
@ -304,7 +304,7 @@ void restoreAppStopper() {
|
||||
|
||||
Future<bool> _stopApp(DriveCommand command) async {
|
||||
printTrace('Stopping application.');
|
||||
final ApplicationPackage package = command.applicationPackages.getPackageForPlatform(await command.device.targetPlatform);
|
||||
final ApplicationPackage package = await command.applicationPackages.getPackageForPlatform(await command.device.targetPlatform);
|
||||
final bool stopped = await command.device.stopApp(package);
|
||||
await command._deviceLogSubscription?.cancel();
|
||||
return stopped;
|
||||
|
@ -31,7 +31,7 @@ class InstallCommand extends FlutterCommand {
|
||||
|
||||
@override
|
||||
Future<Null> runCommand() async {
|
||||
final ApplicationPackage package = applicationPackages.getPackageForPlatform(await device.targetPlatform);
|
||||
final ApplicationPackage package = await applicationPackages.getPackageForPlatform(await device.targetPlatform);
|
||||
|
||||
Cache.releaseLockEarly();
|
||||
|
||||
|
@ -32,7 +32,7 @@ class StopCommand extends FlutterCommand {
|
||||
@override
|
||||
Future<Null> runCommand() async {
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
final ApplicationPackage app = applicationPackages.getPackageForPlatform(targetPlatform);
|
||||
final ApplicationPackage app = await applicationPackages.getPackageForPlatform(targetPlatform);
|
||||
if (app == null) {
|
||||
final String platformName = getNameForTargetPlatform(targetPlatform);
|
||||
throwToolExit('No Flutter application for $platformName found in the current directory.');
|
||||
|
@ -179,7 +179,7 @@ void _writeIOSPluginRegistry(String directory, List<Plugin> plugins) {
|
||||
}
|
||||
|
||||
/// Finds Flutter plugins in the pubspec.yaml, creates platform injection
|
||||
/// registries classes and add them to the build depedencies.
|
||||
/// registries classes and add them to the build dependencies.
|
||||
///
|
||||
/// Returns whether any Flutter plugins are added.
|
||||
bool injectPlugins({String directory}) {
|
||||
|
@ -210,7 +210,7 @@ class FlutterDevice {
|
||||
printStatus('Launching ${getDisplayPath(hotRunner.mainPath)} on ${device.name} in $modeName mode...');
|
||||
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
package = getApplicationPackageForPlatform(
|
||||
package = await getApplicationPackageForPlatform(
|
||||
targetPlatform,
|
||||
applicationBinary: hotRunner.applicationBinary
|
||||
);
|
||||
@ -261,7 +261,7 @@ class FlutterDevice {
|
||||
bool shouldBuild: true,
|
||||
}) async {
|
||||
final TargetPlatform targetPlatform = await device.targetPlatform;
|
||||
package = getApplicationPackageForPlatform(
|
||||
package = await getApplicationPackageForPlatform(
|
||||
targetPlatform,
|
||||
applicationBinary: coldRunner.applicationBinary
|
||||
);
|
||||
|
Loading…
Reference in New Issue
Block a user