From 2c857b9370d43e5f32cd63c64bedeabb9538e615 Mon Sep 17 00:00:00 2001 From: Emmanuel Garcia Date: Fri, 13 Sep 2019 19:06:40 -0700 Subject: [PATCH] Enable R8 (#40453) --- .../complex_layout/android/gradle.properties | 1 + .../macrobenchmarks/android/gradle.properties | 1 + .../microbenchmarks/android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android_views/android/gradle.properties | 1 + .../channels/android/gradle.properties | 1 + .../codegen/android/gradle.properties | 1 + .../external_ui/android/gradle.properties | 1 + .../flavors/android/gradle.properties | 1 + .../image_loading/android/gradle.properties | 1 + .../named_isolates/android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android/gradle.properties | 2 +- .../ui/android/gradle.properties | 1 + examples/catalog/android/gradle.properties | 1 + .../flutter_gallery/android/gradle.properties | 1 + .../flutter_view/android/gradle.properties | 1 + .../hello_world/android/gradle.properties | 1 + examples/image_list/android/gradle.properties | 2 +- examples/layers/android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../android/gradle.properties | 1 + .../platform_view/android/gradle.properties | 1 + examples/stocks/android/gradle.properties | 1 + packages/flutter_tools/gradle/flutter.gradle | 6 +- .../flutter_tools/lib/src/android/gradle.dart | 53 ++++++++--- .../flutter_tools/lib/src/build_info.dart | 6 +- .../lib/src/commands/build_apk.dart | 12 +-- .../lib/src/commands/build_appbundle.dart | 9 +- .../lib/src/runner/flutter_command.dart | 13 +++ .../app/android.tmpl/gradle.properties.tmpl | 2 +- .../android/gradle/gradle.properties.tmpl | 2 +- .../android.tmpl/gradle.properties.tmpl | 2 +- .../general.shard/android/gradle_test.dart | 94 ++++++++++++++++++- .../application_package_test.dart | 4 + .../commands/build_apk_test.dart | 33 +++---- .../commands/build_appbundle_test.dart | 35 +++---- 40 files changed, 222 insertions(+), 78 deletions(-) diff --git a/dev/benchmarks/complex_layout/android/gradle.properties b/dev/benchmarks/complex_layout/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/benchmarks/complex_layout/android/gradle.properties +++ b/dev/benchmarks/complex_layout/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/benchmarks/macrobenchmarks/android/gradle.properties b/dev/benchmarks/macrobenchmarks/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/benchmarks/macrobenchmarks/android/gradle.properties +++ b/dev/benchmarks/macrobenchmarks/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/benchmarks/microbenchmarks/android/gradle.properties b/dev/benchmarks/microbenchmarks/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/benchmarks/microbenchmarks/android/gradle.properties +++ b/dev/benchmarks/microbenchmarks/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/android_semantics_testing/android/gradle.properties b/dev/integration_tests/android_semantics_testing/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/android_semantics_testing/android/gradle.properties +++ b/dev/integration_tests/android_semantics_testing/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/gradle.properties b/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/gradle.properties index 94adc3a3f97..a6738207fd1 100644 --- a/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/gradle.properties +++ b/dev/integration_tests/android_splash_screens/splash_screen_kitchen_sink/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/dev/integration_tests/android_splash_screens/splash_screen_load_rotate/android/gradle.properties b/dev/integration_tests/android_splash_screens/splash_screen_load_rotate/android/gradle.properties index 94adc3a3f97..a6738207fd1 100644 --- a/dev/integration_tests/android_splash_screens/splash_screen_load_rotate/android/gradle.properties +++ b/dev/integration_tests/android_splash_screens/splash_screen_load_rotate/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/dev/integration_tests/android_splash_screens/splash_screen_trans_rotate/android/gradle.properties b/dev/integration_tests/android_splash_screens/splash_screen_trans_rotate/android/gradle.properties index 94adc3a3f97..a6738207fd1 100644 --- a/dev/integration_tests/android_splash_screens/splash_screen_trans_rotate/android/gradle.properties +++ b/dev/integration_tests/android_splash_screens/splash_screen_trans_rotate/android/gradle.properties @@ -1,3 +1,4 @@ org.gradle.jvmargs=-Xmx1536M android.useAndroidX=true android.enableJetifier=true +android.enableR8=true diff --git a/dev/integration_tests/android_views/android/gradle.properties b/dev/integration_tests/android_views/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/android_views/android/gradle.properties +++ b/dev/integration_tests/android_views/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/channels/android/gradle.properties b/dev/integration_tests/channels/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/channels/android/gradle.properties +++ b/dev/integration_tests/channels/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/codegen/android/gradle.properties b/dev/integration_tests/codegen/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/codegen/android/gradle.properties +++ b/dev/integration_tests/codegen/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/external_ui/android/gradle.properties b/dev/integration_tests/external_ui/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/external_ui/android/gradle.properties +++ b/dev/integration_tests/external_ui/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/flavors/android/gradle.properties b/dev/integration_tests/flavors/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/flavors/android/gradle.properties +++ b/dev/integration_tests/flavors/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/image_loading/android/gradle.properties b/dev/integration_tests/image_loading/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/image_loading/android/gradle.properties +++ b/dev/integration_tests/image_loading/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/named_isolates/android/gradle.properties b/dev/integration_tests/named_isolates/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/named_isolates/android/gradle.properties +++ b/dev/integration_tests/named_isolates/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/platform_interaction/android/gradle.properties b/dev/integration_tests/platform_interaction/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/platform_interaction/android/gradle.properties +++ b/dev/integration_tests/platform_interaction/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/dev/integration_tests/release_smoke_test/android/gradle.properties b/dev/integration_tests/release_smoke_test/android/gradle.properties index 2bd6f4fda00..7be3d8b4684 100644 --- a/dev/integration_tests/release_smoke_test/android/gradle.properties +++ b/dev/integration_tests/release_smoke_test/android/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1536M - +android.enableR8=true diff --git a/dev/integration_tests/ui/android/gradle.properties b/dev/integration_tests/ui/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/dev/integration_tests/ui/android/gradle.properties +++ b/dev/integration_tests/ui/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/catalog/android/gradle.properties b/examples/catalog/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/catalog/android/gradle.properties +++ b/examples/catalog/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/flutter_gallery/android/gradle.properties b/examples/flutter_gallery/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/flutter_gallery/android/gradle.properties +++ b/examples/flutter_gallery/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/flutter_view/android/gradle.properties b/examples/flutter_view/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/flutter_view/android/gradle.properties +++ b/examples/flutter_view/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/hello_world/android/gradle.properties b/examples/hello_world/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/hello_world/android/gradle.properties +++ b/examples/hello_world/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/image_list/android/gradle.properties b/examples/image_list/android/gradle.properties index 2bd6f4fda00..7be3d8b4684 100644 --- a/examples/image_list/android/gradle.properties +++ b/examples/image_list/android/gradle.properties @@ -1,2 +1,2 @@ org.gradle.jvmargs=-Xmx1536M - +android.enableR8=true diff --git a/examples/layers/android/gradle.properties b/examples/layers/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/layers/android/gradle.properties +++ b/examples/layers/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/platform_channel/android/gradle.properties b/examples/platform_channel/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/platform_channel/android/gradle.properties +++ b/examples/platform_channel/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/platform_channel_swift/android/gradle.properties b/examples/platform_channel_swift/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/platform_channel_swift/android/gradle.properties +++ b/examples/platform_channel_swift/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/platform_view/android/gradle.properties b/examples/platform_view/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/platform_view/android/gradle.properties +++ b/examples/platform_view/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/examples/stocks/android/gradle.properties b/examples/stocks/android/gradle.properties index 8bd86f68051..7be3d8b4684 100644 --- a/examples/stocks/android/gradle.properties +++ b/examples/stocks/android/gradle.properties @@ -1 +1,2 @@ org.gradle.jvmargs=-Xmx1536M +android.enableR8=true diff --git a/packages/flutter_tools/gradle/flutter.gradle b/packages/flutter_tools/gradle/flutter.gradle index 7d2625940f5..e11d7ffbf87 100644 --- a/packages/flutter_tools/gradle/flutter.gradle +++ b/packages/flutter_tools/gradle/flutter.gradle @@ -159,8 +159,12 @@ class FlutterPlugin implements Plugin { "gradle", "flutter_proguard_rules.pro") project.android.buildTypes { release { + // Enables code shrinking, obfuscation, and optimization for only + // your project's release build type. minifyEnabled true - useProguard true + // Enables resource shrinking, which is performed by the + // Android Gradle plugin. + shrinkResources true // Fallback to `android/app/proguard-rules.pro`. // This way, custom Proguard rules can be configured as needed. proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro" diff --git a/packages/flutter_tools/lib/src/android/gradle.dart b/packages/flutter_tools/lib/src/android/gradle.dart index 049065aa7bd..c670388316b 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -306,6 +306,10 @@ Future _initializeGradle(FlutterProject project) async { final Status status = logger.startProgress('Initializing gradle...', timeout: timeoutConfiguration.slowOperation); + + // Update the project if needed. + // TODO(egarciad): https://github.com/flutter/flutter/issues/40460. + migrateToR8(android); injectGradleWrapperIfNeeded(android); final String gradle = _locateGradlewExecutable(android); @@ -335,6 +339,31 @@ Future _initializeGradle(FlutterProject project) async { return gradle; } +/// Migrates the Android's [directory] to R8. +/// https://developer.android.com/studio/build/shrink-code +@visibleForTesting +void migrateToR8(Directory directory) { + final File gradleProperties = directory.childFile('gradle.properties'); + if (!gradleProperties.existsSync()) { + throwToolExit('Expected file ${gradleProperties.path}.'); + } + final String propertiesContent = gradleProperties.readAsStringSync(); + if (propertiesContent.contains('android.enableR8')) { + printTrace('gradle.properties already sets `android.enableR8`'); + return; + } + printTrace('set `android.enableR8=true` in gradle.properties'); + try { + gradleProperties + .writeAsStringSync('android.enableR8=true\n', mode: FileMode.append); + } on FileSystemException { + throwToolExit( + 'The tool failed to add `android.enableR8=true` to ${gradleProperties.path}. ' + 'Please update the file manually and try this command again.' + ); + } +} + /// Injects the Gradle wrapper files if any of these files don't exist in [directory]. void injectGradleWrapperIfNeeded(Directory directory) { copyDirectorySync( @@ -754,8 +783,8 @@ Future _buildGradleProjectV2( if (androidBuildInfo.splitPerAbi) { command.add('-Psplit-per-abi=true'); } - if (androidBuildInfo.proguard) { - command.add('-Pproguard=true'); + if (androidBuildInfo.shrink) { + command.add('-Pshrink=true'); } if (androidBuildInfo.targetArchs.isNotEmpty) { final String targetPlatforms = androidBuildInfo.targetArchs @@ -772,7 +801,7 @@ Future _buildGradleProjectV2( } command.add(assembleTask); bool potentialAndroidXFailure = false; - bool potentialProguardFailure = false; + bool potentialR8Failure = false; final Stopwatch sw = Stopwatch()..start(); int exitCode = 1; try { @@ -789,10 +818,10 @@ Future _buildGradleProjectV2( if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) { potentialAndroidXFailure = true; } - // Proguard errors include this url. - if (!potentialProguardFailure && androidBuildInfo.proguard && - line.contains('http://proguard.sourceforge.net')) { - potentialProguardFailure = true; + // R8 errors include references to this package. + if (!potentialR8Failure && androidBuildInfo.shrink && + line.contains('com.android.tools.r8')) { + potentialR8Failure = true; } // Always print the full line in verbose mode. if (logger.isVerbose) { @@ -808,12 +837,12 @@ Future _buildGradleProjectV2( } if (exitCode != 0) { - if (potentialProguardFailure) { + if (potentialR8Failure) { final String exclamationMark = terminal.color('[!]', TerminalColor.red); - printStatus('$exclamationMark Proguard may have failed to optimize the Java bytecode.', emphasis: true); - printStatus('To disable proguard, pass the `--no-proguard` flag to this command.', indent: 4); - printStatus('To learn more about Proguard, see: https://flutter.dev/docs/deployment/android#enabling-proguard', indent: 4); - BuildEvent('proguard-failure').send(); + printStatus('$exclamationMark The shrinker may have failed to optimize the Java bytecode.', emphasis: true); + printStatus('To disable the shrinker, pass the `--no-shrink` flag to this command.', indent: 4); + printStatus('To learn more, see: https://developer.android.com/studio/build/shrink-code', indent: 4); + BuildEvent('r8-failure').send(); } else if (potentialAndroidXFailure) { printStatus('AndroidX incompatibilities may have caused this build to fail. See https://goo.gl/CP92wY.'); BuildEvent('android-x-failure').send(); diff --git a/packages/flutter_tools/lib/src/build_info.dart b/packages/flutter_tools/lib/src/build_info.dart index c40643892b7..57ee601e4fe 100644 --- a/packages/flutter_tools/lib/src/build_info.dart +++ b/packages/flutter_tools/lib/src/build_info.dart @@ -92,7 +92,7 @@ class AndroidBuildInfo { AndroidArch.arm64_v8a, ], this.splitPerAbi = false, - this.proguard = false, + this.shrink = false, }); // The build info containing the mode and flavor. @@ -105,8 +105,8 @@ class AndroidBuildInfo { /// will be produced. final bool splitPerAbi; - /// Whether to enable Proguard on release mode. - final bool proguard; + /// Whether to enable code shrinking on release mode. + final bool shrink; /// The target platforms for the build. final Iterable targetArchs; diff --git a/packages/flutter_tools/lib/src/commands/build_apk.dart b/packages/flutter_tools/lib/src/commands/build_apk.dart index a27d9f4160f..8936f0fbf0b 100644 --- a/packages/flutter_tools/lib/src/commands/build_apk.dart +++ b/packages/flutter_tools/lib/src/commands/build_apk.dart @@ -21,6 +21,7 @@ class BuildApkCommand extends BuildSubCommand { usesPubOption(); usesBuildNumberOption(); usesBuildNameOption(); + addShrinkingFlag(); argParser ..addFlag('split-per-abi', @@ -28,12 +29,6 @@ class BuildApkCommand extends BuildSubCommand { help: 'Whether to split the APKs per ABIs. ' 'To learn more, see: https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split', ) - ..addFlag('proguard', - negatable: true, - defaultsTo: false, - help: 'Whether to enable Proguard on release mode. ' - 'To learn more, see: https://flutter.dev/docs/deployment/android#enabling-proguard', - ) ..addMultiOption('target-platform', splitCommas: true, defaultsTo: ['android-arm', 'android-arm64'], @@ -83,10 +78,11 @@ class BuildApkCommand extends BuildSubCommand { @override Future runCommand() async { final BuildInfo buildInfo = getBuildInfo(); - final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(buildInfo, + final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo( + buildInfo, splitPerAbi: argResults['split-per-abi'], targetArchs: argResults['target-platform'].map(getAndroidArchForName), - proguard: argResults['proguard'], + shrink: argResults['shrink'], ); if (buildInfo.isRelease && !androidBuildInfo.splitPerAbi && androidBuildInfo.targetArchs.length > 1) { diff --git a/packages/flutter_tools/lib/src/commands/build_appbundle.dart b/packages/flutter_tools/lib/src/commands/build_appbundle.dart index 1a53b52f138..bfea24f1566 100644 --- a/packages/flutter_tools/lib/src/commands/build_appbundle.dart +++ b/packages/flutter_tools/lib/src/commands/build_appbundle.dart @@ -19,15 +19,10 @@ class BuildAppBundleCommand extends BuildSubCommand { usesPubOption(); usesBuildNumberOption(); usesBuildNameOption(); + addShrinkingFlag(); argParser ..addFlag('track-widget-creation', negatable: false, hide: !verboseHelp) - ..addFlag('proguard', - negatable: true, - defaultsTo: false, - help: 'Whether to enable Proguard on release mode. ' - 'To learn more, see: https://flutter.dev/docs/deployment/android#enabling-proguard', - ) ..addMultiOption('target-platform', splitCommas: true, defaultsTo: ['android-arm', 'android-arm64'], @@ -70,7 +65,7 @@ class BuildAppBundleCommand extends BuildSubCommand { Future runCommand() async { final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(getBuildInfo(), targetArchs: argResults['target-platform'].map(getAndroidArchForName), - proguard: argResults['proguard'], + shrink: argResults['shrink'], ); await androidBuilder.buildAab( project: FlutterProject.current(), diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index e22c6922955..597143e299a 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart @@ -277,6 +277,19 @@ abstract class FlutterCommand extends Command { help: 'Build a release version of your app${defaultToRelease ? ' (default mode)' : ''}.'); } + void addShrinkingFlag() { + argParser.addFlag('shrink', + negatable: true, + defaultsTo: true, + help: 'Whether to enable code shrinking on release mode.' + 'When enabling shrinking, you also benefit from obfuscation, ' + 'which shortens the names of your app’s classes and members, ' + 'and optimization, which applies more aggressive strategies to ' + 'further reduce the size of your app.' + 'To learn more, see: https://developer.android.com/studio/build/shrink-code' + ); + } + void usesFuchsiaOptions({ bool hide = false }) { argParser.addOption( 'target-model', diff --git a/packages/flutter_tools/templates/app/android.tmpl/gradle.properties.tmpl b/packages/flutter_tools/templates/app/android.tmpl/gradle.properties.tmpl index a2296f547b2..2a0d268db74 100644 --- a/packages/flutter_tools/templates/app/android.tmpl/gradle.properties.tmpl +++ b/packages/flutter_tools/templates/app/android.tmpl/gradle.properties.tmpl @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M - +android.enableR8=true {{#androidX}} android.useAndroidX=true android.enableJetifier=true diff --git a/packages/flutter_tools/templates/module/android/gradle/gradle.properties.tmpl b/packages/flutter_tools/templates/module/android/gradle/gradle.properties.tmpl index a2296f547b2..2a0d268db74 100644 --- a/packages/flutter_tools/templates/module/android/gradle/gradle.properties.tmpl +++ b/packages/flutter_tools/templates/module/android/gradle/gradle.properties.tmpl @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M - +android.enableR8=true {{#androidX}} android.useAndroidX=true android.enableJetifier=true diff --git a/packages/flutter_tools/templates/plugin/android.tmpl/gradle.properties.tmpl b/packages/flutter_tools/templates/plugin/android.tmpl/gradle.properties.tmpl index a2296f547b2..2a0d268db74 100644 --- a/packages/flutter_tools/templates/plugin/android.tmpl/gradle.properties.tmpl +++ b/packages/flutter_tools/templates/plugin/android.tmpl/gradle.properties.tmpl @@ -1,5 +1,5 @@ org.gradle.jvmargs=-Xmx1536M - +android.enableR8=true {{#androidX}} android.useAndroidX=true android.enableJetifier=true diff --git a/packages/flutter_tools/test/general.shard/android/gradle_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_test.dart index e583a46e82b..b650ab07665 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_test.dart @@ -3,7 +3,6 @@ // found in the LICENSE file. import 'dart:async'; -import 'dart:io'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/android/android_sdk.dart'; @@ -13,6 +12,7 @@ import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/artifacts.dart'; import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/io.dart'; import 'package:flutter_tools/src/base/os.dart'; import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/build_info.dart'; @@ -867,6 +867,13 @@ flutter: gradleWrapperDirectory .childFile(gradleBinary) .writeAsStringSync('irrelevant'); + fs.currentDirectory + .childDirectory('android') + .createSync(); + fs.currentDirectory + .childDirectory('android') + .childFile('gradle.properties') + .writeAsStringSync('irrelevant'); gradleWrapperDirectory .childDirectory('gradle') .childDirectory('wrapper') @@ -1072,6 +1079,82 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; }); }); + group('migrateToR8', () { + MemoryFileSystem memoryFileSystem; + + setUp(() { + memoryFileSystem = MemoryFileSystem(); + }); + + testUsingContext('throws ToolExit if gradle.properties doesn\'t exist', () { + final Directory sampleAppAndroid = fs.directory('/sample-app/android'); + sampleAppAndroid.createSync(recursive: true); + + expect(() { + migrateToR8(sampleAppAndroid); + }, throwsToolExit(message: 'Expected file ${sampleAppAndroid.path}')); + + }, overrides: { + FileSystem: () => memoryFileSystem, + }); + + testUsingContext('throws ToolExit if it cannot write gradle.properties', () { + final MockDirectory sampleAppAndroid = MockDirectory(); + final MockFile gradleProperties = MockFile(); + + when(gradleProperties.path).thenReturn('foo/gradle.properties'); + when(gradleProperties.existsSync()).thenReturn(true); + when(gradleProperties.readAsStringSync()).thenReturn(''); + when(gradleProperties.writeAsStringSync('android.enableR8=true\n', mode: FileMode.append)) + .thenThrow(const FileSystemException()); + + when(sampleAppAndroid.childFile('gradle.properties')) + .thenReturn(gradleProperties); + + expect(() { + migrateToR8(sampleAppAndroid); + }, + throwsToolExit(message: + 'The tool failed to add `android.enableR8=true` to foo/gradle.properties. ' + 'Please update the file manually and try this command again.')); + }); + + testUsingContext('does not update gradle.properties if it already uses R8', () { + final Directory sampleAppAndroid = fs.directory('/sample-app/android'); + sampleAppAndroid.createSync(recursive: true); + sampleAppAndroid.childFile('gradle.properties') + .writeAsStringSync('android.enableR8=true'); + + migrateToR8(sampleAppAndroid); + + expect(testLogger.traceText, + contains('gradle.properties already sets `android.enableR8`')); + expect(sampleAppAndroid.childFile('gradle.properties').readAsStringSync(), + equals('android.enableR8=true')); + }, overrides: { + FileSystem: () => memoryFileSystem, + }); + + testUsingContext('sets android.enableR8=true', () { + final Directory sampleAppAndroid = fs.directory('/sample-app/android'); + sampleAppAndroid.createSync(recursive: true); + sampleAppAndroid.childFile('gradle.properties') + .writeAsStringSync('org.gradle.jvmargs=-Xmx1536M\n'); + + migrateToR8(sampleAppAndroid); + + expect(testLogger.traceText, contains('set `android.enableR8=true` in gradle.properties')); + expect(sampleAppAndroid.childFile('gradle.properties').readAsStringSync(), + equals( + 'org.gradle.jvmargs=-Xmx1536M\n' + 'android.enableR8=true\n' + ) + ); + }, overrides: { + FileSystem: () => memoryFileSystem, + }); + }); + group('gradle build', () { MockAndroidSdk mockAndroidSdk; MockAndroidStudio mockAndroidStudio; @@ -1136,6 +1219,9 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; final File gradlew = fs.file('path/to/project/.android/gradlew'); gradlew.createSync(recursive: true); + fs.file('path/to/project/.android/gradle.properties') + .writeAsStringSync('irrelevant'); + when(mockProcessManager.run( ['/path/to/project/.android/gradlew', '-v'], workingDirectory: anyNamed('workingDirectory'), @@ -1198,9 +1284,11 @@ Platform fakePlatform(String name) { return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = name; } +class MockAndroidStudio extends Mock implements AndroidStudio {} +class MockDirectory extends Mock implements Directory {} +class MockFile extends Mock implements File {} +class MockGradleProject extends Mock implements GradleProject {} class MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {} class MockProcessManager extends Mock implements ProcessManager {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} -class MockGradleProject extends Mock implements GradleProject {} class MockitoAndroidSdk extends Mock implements AndroidSdk {} -class MockAndroidStudio extends Mock implements AndroidStudio {} diff --git a/packages/flutter_tools/test/general.shard/application_package_test.dart b/packages/flutter_tools/test/general.shard/application_package_test.dart index a374142df02..05855b0573f 100644 --- a/packages/flutter_tools/test/general.shard/application_package_test.dart +++ b/packages/flutter_tools/test/general.shard/application_package_test.dart @@ -103,6 +103,10 @@ void main() { platform.isWindows ? 'gradlew.bat' : 'gradlew', )..createSync(recursive: true); + project.android.hostAppGradleRoot + .childFile('gradle.properties') + .writeAsStringSync('irrelevant'); + final Directory gradleWrapperDir = fs.systemTempDirectory.createTempSync('gradle_wrapper.'); when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir); diff --git a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart index f3d19b8fec7..1c961ad3890 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_apk_test.dart @@ -137,7 +137,7 @@ void main() { tryToDelete(tempDir); }); - testUsingContext('proguard is enabled by default on release mode', () async { + testUsingContext('shrinking is enabled by default on release mode', () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app']); @@ -151,7 +151,7 @@ void main() { '-q', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptrack-widget-creation=false', - '-Pproguard=true', + '-Pshrink=true', '-Ptarget-platform=android-arm,android-arm64', 'assembleRelease', ], @@ -165,17 +165,16 @@ void main() { GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, }, - skip: true, timeout: allowForCreateFlutterProject); - testUsingContext('proguard is disabled when --no-proguard is passed', () async { + testUsingContext('shrinking is disabled when --no-shrink is passed', () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app']); await expectLater(() async { await runBuildApkCommand( projectPath, - arguments: ['--no-proguard'], + arguments: ['--no-shrink'], ); }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); @@ -198,10 +197,9 @@ void main() { GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, }, - skip: true, timeout: allowForCreateFlutterProject); - testUsingContext('guides the user when proguard fails', () async { + testUsingContext('guides the user when the shrinker fails', () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app']); @@ -211,22 +209,20 @@ void main() { '-q', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptrack-widget-creation=false', - '-Pproguard=true', + '-Pshrink=true', '-Ptarget-platform=android-arm,android-arm64', 'assembleRelease', ], workingDirectory: anyNamed('workingDirectory'), environment: anyNamed('environment'), )).thenAnswer((_) { - const String proguardStdoutWarning = - 'Warning: there were 6 unresolved references to program class members.' - 'Your input classes appear to be inconsistent.' - 'You may need to recompile the code.' - '(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)'; + const String r8StdoutWarning = + 'Execution failed for task \':app:transformClassesAndResourcesWithR8ForStageInternal\'.' + '> com.android.tools.r8.CompilationFailedException: Compilation failed to complete'; return Future.value( createMockProcess( exitCode: 1, - stdout: proguardStdoutWarning, + stdout: r8StdoutWarning, ) ); }); @@ -238,15 +234,15 @@ void main() { }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); expect(testLogger.statusText, - contains('Proguard may have failed to optimize the Java bytecode.')); + contains('The shrinker may have failed to optimize the Java bytecode.')); expect(testLogger.statusText, - contains('To disable proguard, pass the `--no-proguard` flag to this command.')); + contains('To disable the shrinker, pass the `--no-shrink` flag to this command.')); expect(testLogger.statusText, - contains('To learn more about Proguard, see: https://flutter.dev/docs/deployment/android#enabling-proguard')); + contains('To learn more, see: https://developer.android.com/studio/build/shrink-code')); verify(mockUsage.sendEvent( 'build-apk', - 'proguard-failure', + 'r8-failure', parameters: anyNamed('parameters'), )).called(1); }, @@ -257,7 +253,6 @@ void main() { ProcessManager: () => mockProcessManager, Usage: () => mockUsage, }, - skip: true, timeout: allowForCreateFlutterProject); }); } diff --git a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart index 4548e3ce172..4a7d98f7d11 100644 --- a/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart +++ b/packages/flutter_tools/test/general.shard/commands/build_appbundle_test.dart @@ -75,7 +75,7 @@ void main() { }, timeout: allowForCreateFlutterProject); }); - group('Flags', () { + group('Gradle', () { Directory tempDir; ProcessManager mockProcessManager; MockAndroidSdk mockAndroidSdk; @@ -122,7 +122,7 @@ void main() { tryToDelete(tempDir); }); - testUsingContext('proguard is enabled by default on release mode', () async { + testUsingContext('shrinking is enabled by default on release mode', () async { final String projectPath = await createProject( tempDir, arguments: ['--no-pub', '--template=app'], @@ -138,7 +138,7 @@ void main() { '-q', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptrack-widget-creation=false', - '-Pproguard=true', + '-Pshrink=true', '-Ptarget-platform=android-arm,android-arm64', 'bundleRelease', ], @@ -152,10 +152,9 @@ void main() { GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, }, - skip: true, timeout: allowForCreateFlutterProject); - testUsingContext('proguard is disabled when --no-proguard is passed', () async { + testUsingContext('shrinking is disabled when --no-shrink is passed', () async { final String projectPath = await createProject( tempDir, arguments: ['--no-pub', '--template=app'], @@ -164,7 +163,7 @@ void main() { await expectLater(() async { await runBuildAppBundleCommand( projectPath, - arguments: ['--no-proguard'], + arguments: ['--no-shrink'], ); }, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1')); @@ -187,10 +186,9 @@ void main() { GradleUtils: () => GradleUtils(), ProcessManager: () => mockProcessManager, }, - skip: true, timeout: allowForCreateFlutterProject); - testUsingContext('guides the user when proguard fails', () async { + testUsingContext('guides the user when the shrinker fails', () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app']); @@ -200,22 +198,20 @@ void main() { '-q', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptrack-widget-creation=false', - '-Pproguard=true', + '-Pshrink=true', '-Ptarget-platform=android-arm,android-arm64', 'bundleRelease', ], workingDirectory: anyNamed('workingDirectory'), environment: anyNamed('environment'), )).thenAnswer((_) { - const String proguardStdoutWarning = - 'Warning: there were 6 unresolved references to program class members.' - 'Your input classes appear to be inconsistent.' - 'You may need to recompile the code.' - '(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)'; + const String r8StdoutWarning = + 'Execution failed for task \':app:transformClassesAndResourcesWithR8ForStageInternal\'.' + '> com.android.tools.r8.CompilationFailedException: Compilation failed to complete'; return Future.value( createMockProcess( exitCode: 1, - stdout: proguardStdoutWarning, + stdout: r8StdoutWarning, ) ); }); @@ -227,15 +223,15 @@ void main() { }, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1')); expect(testLogger.statusText, - contains('Proguard may have failed to optimize the Java bytecode.')); + contains('The shrinker may have failed to optimize the Java bytecode.')); expect(testLogger.statusText, - contains('To disable proguard, pass the `--no-proguard` flag to this command.')); + contains('To disable the shrinker, pass the `--no-shrink` flag to this command.')); expect(testLogger.statusText, - contains('To learn more about Proguard, see: https://flutter.dev/docs/deployment/android#enabling-proguard')); + contains('To learn more, see: https://developer.android.com/studio/build/shrink-code')); verify(mockUsage.sendEvent( 'build-appbundle', - 'proguard-failure', + 'r8-failure', parameters: anyNamed('parameters'), )).called(1); }, @@ -246,7 +242,6 @@ void main() { ProcessManager: () => mockProcessManager, Usage: () => mockUsage, }, - skip: true, timeout: allowForCreateFlutterProject); }); }