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