Unverified Commit 2c857b93 authored by Emmanuel Garcia's avatar Emmanuel Garcia Committed by GitHub

Enable R8 (#40453)

parent e3c4609a
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
...@@ -159,8 +159,12 @@ class FlutterPlugin implements Plugin<Project> { ...@@ -159,8 +159,12 @@ class FlutterPlugin implements Plugin<Project> {
"gradle", "flutter_proguard_rules.pro") "gradle", "flutter_proguard_rules.pro")
project.android.buildTypes { project.android.buildTypes {
release { release {
// Enables code shrinking, obfuscation, and optimization for only
// your project's release build type.
minifyEnabled true minifyEnabled true
useProguard true // Enables resource shrinking, which is performed by the
// Android Gradle plugin.
shrinkResources true
// Fallback to `android/app/proguard-rules.pro`. // Fallback to `android/app/proguard-rules.pro`.
// This way, custom Proguard rules can be configured as needed. // This way, custom Proguard rules can be configured as needed.
proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro" proguardFiles project.android.getDefaultProguardFile("proguard-android.txt"), flutterProguardRules, "proguard-rules.pro"
......
...@@ -306,6 +306,10 @@ Future<String> _initializeGradle(FlutterProject project) async { ...@@ -306,6 +306,10 @@ Future<String> _initializeGradle(FlutterProject project) async {
final Status status = logger.startProgress('Initializing gradle...', final Status status = logger.startProgress('Initializing gradle...',
timeout: timeoutConfiguration.slowOperation); timeout: timeoutConfiguration.slowOperation);
// Update the project if needed.
// TODO(egarciad): https://github.com/flutter/flutter/issues/40460.
migrateToR8(android);
injectGradleWrapperIfNeeded(android); injectGradleWrapperIfNeeded(android);
final String gradle = _locateGradlewExecutable(android); final String gradle = _locateGradlewExecutable(android);
...@@ -335,6 +339,31 @@ Future<String> _initializeGradle(FlutterProject project) async { ...@@ -335,6 +339,31 @@ Future<String> _initializeGradle(FlutterProject project) async {
return gradle; 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]. /// Injects the Gradle wrapper files if any of these files don't exist in [directory].
void injectGradleWrapperIfNeeded(Directory directory) { void injectGradleWrapperIfNeeded(Directory directory) {
copyDirectorySync( copyDirectorySync(
...@@ -754,8 +783,8 @@ Future<void> _buildGradleProjectV2( ...@@ -754,8 +783,8 @@ Future<void> _buildGradleProjectV2(
if (androidBuildInfo.splitPerAbi) { if (androidBuildInfo.splitPerAbi) {
command.add('-Psplit-per-abi=true'); command.add('-Psplit-per-abi=true');
} }
if (androidBuildInfo.proguard) { if (androidBuildInfo.shrink) {
command.add('-Pproguard=true'); command.add('-Pshrink=true');
} }
if (androidBuildInfo.targetArchs.isNotEmpty) { if (androidBuildInfo.targetArchs.isNotEmpty) {
final String targetPlatforms = androidBuildInfo.targetArchs final String targetPlatforms = androidBuildInfo.targetArchs
...@@ -772,7 +801,7 @@ Future<void> _buildGradleProjectV2( ...@@ -772,7 +801,7 @@ Future<void> _buildGradleProjectV2(
} }
command.add(assembleTask); command.add(assembleTask);
bool potentialAndroidXFailure = false; bool potentialAndroidXFailure = false;
bool potentialProguardFailure = false; bool potentialR8Failure = false;
final Stopwatch sw = Stopwatch()..start(); final Stopwatch sw = Stopwatch()..start();
int exitCode = 1; int exitCode = 1;
try { try {
...@@ -789,10 +818,10 @@ Future<void> _buildGradleProjectV2( ...@@ -789,10 +818,10 @@ Future<void> _buildGradleProjectV2(
if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) { if (!isAndroidXPluginWarning && androidXFailureRegex.hasMatch(line)) {
potentialAndroidXFailure = true; potentialAndroidXFailure = true;
} }
// Proguard errors include this url. // R8 errors include references to this package.
if (!potentialProguardFailure && androidBuildInfo.proguard && if (!potentialR8Failure && androidBuildInfo.shrink &&
line.contains('http://proguard.sourceforge.net')) { line.contains('com.android.tools.r8')) {
potentialProguardFailure = true; potentialR8Failure = true;
} }
// Always print the full line in verbose mode. // Always print the full line in verbose mode.
if (logger.isVerbose) { if (logger.isVerbose) {
...@@ -808,12 +837,12 @@ Future<void> _buildGradleProjectV2( ...@@ -808,12 +837,12 @@ Future<void> _buildGradleProjectV2(
} }
if (exitCode != 0) { if (exitCode != 0) {
if (potentialProguardFailure) { if (potentialR8Failure) {
final String exclamationMark = terminal.color('[!]', TerminalColor.red); final String exclamationMark = terminal.color('[!]', TerminalColor.red);
printStatus('$exclamationMark Proguard may have failed to optimize the Java bytecode.', emphasis: true); printStatus('$exclamationMark The shrinker 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 disable the shrinker, pass the `--no-shrink` flag to this command.', indent: 4);
printStatus('To learn more about Proguard, see: https://flutter.dev/docs/deployment/android#enabling-proguard', indent: 4); printStatus('To learn more, see: https://developer.android.com/studio/build/shrink-code', indent: 4);
BuildEvent('proguard-failure').send(); BuildEvent('r8-failure').send();
} else if (potentialAndroidXFailure) { } else if (potentialAndroidXFailure) {
printStatus('AndroidX incompatibilities may have caused this build to fail. See https://goo.gl/CP92wY.'); printStatus('AndroidX incompatibilities may have caused this build to fail. See https://goo.gl/CP92wY.');
BuildEvent('android-x-failure').send(); BuildEvent('android-x-failure').send();
......
...@@ -92,7 +92,7 @@ class AndroidBuildInfo { ...@@ -92,7 +92,7 @@ class AndroidBuildInfo {
AndroidArch.arm64_v8a, AndroidArch.arm64_v8a,
], ],
this.splitPerAbi = false, this.splitPerAbi = false,
this.proguard = false, this.shrink = false,
}); });
// The build info containing the mode and flavor. // The build info containing the mode and flavor.
...@@ -105,8 +105,8 @@ class AndroidBuildInfo { ...@@ -105,8 +105,8 @@ class AndroidBuildInfo {
/// will be produced. /// will be produced.
final bool splitPerAbi; final bool splitPerAbi;
/// Whether to enable Proguard on release mode. /// Whether to enable code shrinking on release mode.
final bool proguard; final bool shrink;
/// The target platforms for the build. /// The target platforms for the build.
final Iterable<AndroidArch> targetArchs; final Iterable<AndroidArch> targetArchs;
......
...@@ -21,6 +21,7 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -21,6 +21,7 @@ class BuildApkCommand extends BuildSubCommand {
usesPubOption(); usesPubOption();
usesBuildNumberOption(); usesBuildNumberOption();
usesBuildNameOption(); usesBuildNameOption();
addShrinkingFlag();
argParser argParser
..addFlag('split-per-abi', ..addFlag('split-per-abi',
...@@ -28,12 +29,6 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -28,12 +29,6 @@ class BuildApkCommand extends BuildSubCommand {
help: 'Whether to split the APKs per ABIs. ' help: 'Whether to split the APKs per ABIs. '
'To learn more, see: https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split', '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', ..addMultiOption('target-platform',
splitCommas: true, splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64'], defaultsTo: <String>['android-arm', 'android-arm64'],
...@@ -83,10 +78,11 @@ class BuildApkCommand extends BuildSubCommand { ...@@ -83,10 +78,11 @@ class BuildApkCommand extends BuildSubCommand {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final BuildInfo buildInfo = getBuildInfo(); final BuildInfo buildInfo = getBuildInfo();
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(buildInfo, final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(
buildInfo,
splitPerAbi: argResults['split-per-abi'], splitPerAbi: argResults['split-per-abi'],
targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName), targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName),
proguard: argResults['proguard'], shrink: argResults['shrink'],
); );
if (buildInfo.isRelease && !androidBuildInfo.splitPerAbi && androidBuildInfo.targetArchs.length > 1) { if (buildInfo.isRelease && !androidBuildInfo.splitPerAbi && androidBuildInfo.targetArchs.length > 1) {
......
...@@ -19,15 +19,10 @@ class BuildAppBundleCommand extends BuildSubCommand { ...@@ -19,15 +19,10 @@ class BuildAppBundleCommand extends BuildSubCommand {
usesPubOption(); usesPubOption();
usesBuildNumberOption(); usesBuildNumberOption();
usesBuildNameOption(); usesBuildNameOption();
addShrinkingFlag();
argParser argParser
..addFlag('track-widget-creation', negatable: false, hide: !verboseHelp) ..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', ..addMultiOption('target-platform',
splitCommas: true, splitCommas: true,
defaultsTo: <String>['android-arm', 'android-arm64'], defaultsTo: <String>['android-arm', 'android-arm64'],
...@@ -70,7 +65,7 @@ class BuildAppBundleCommand extends BuildSubCommand { ...@@ -70,7 +65,7 @@ class BuildAppBundleCommand extends BuildSubCommand {
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(getBuildInfo(), final AndroidBuildInfo androidBuildInfo = AndroidBuildInfo(getBuildInfo(),
targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName), targetArchs: argResults['target-platform'].map<AndroidArch>(getAndroidArchForName),
proguard: argResults['proguard'], shrink: argResults['shrink'],
); );
await androidBuilder.buildAab( await androidBuilder.buildAab(
project: FlutterProject.current(), project: FlutterProject.current(),
......
...@@ -277,6 +277,19 @@ abstract class FlutterCommand extends Command<void> { ...@@ -277,6 +277,19 @@ abstract class FlutterCommand extends Command<void> {
help: 'Build a release version of your app${defaultToRelease ? ' (default mode)' : ''}.'); 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 }) { void usesFuchsiaOptions({ bool hide = false }) {
argParser.addOption( argParser.addOption(
'target-model', 'target-model',
......
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
{{#androidX}} {{#androidX}}
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
......
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
{{#androidX}} {{#androidX}}
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
......
org.gradle.jvmargs=-Xmx1536M org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
{{#androidX}} {{#androidX}}
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
......
...@@ -3,7 +3,6 @@ ...@@ -3,7 +3,6 @@
// found in the LICENSE file. // found in the LICENSE file.
import 'dart:async'; import 'dart:async';
import 'dart:io';
import 'package:file/memory.dart'; import 'package:file/memory.dart';
import 'package:flutter_tools/src/android/android_sdk.dart'; import 'package:flutter_tools/src/android/android_sdk.dart';
...@@ -13,6 +12,7 @@ import 'package:flutter_tools/src/base/logger.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/artifacts.dart';
import 'package:flutter_tools/src/base/common.dart'; import 'package:flutter_tools/src/base/common.dart';
import 'package:flutter_tools/src/base/file_system.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/os.dart';
import 'package:flutter_tools/src/base/platform.dart'; import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/build_info.dart'; import 'package:flutter_tools/src/build_info.dart';
...@@ -867,6 +867,13 @@ flutter: ...@@ -867,6 +867,13 @@ flutter:
gradleWrapperDirectory gradleWrapperDirectory
.childFile(gradleBinary) .childFile(gradleBinary)
.writeAsStringSync('irrelevant'); .writeAsStringSync('irrelevant');
fs.currentDirectory
.childDirectory('android')
.createSync();
fs.currentDirectory
.childDirectory('android')
.childFile('gradle.properties')
.writeAsStringSync('irrelevant');
gradleWrapperDirectory gradleWrapperDirectory
.childDirectory('gradle') .childDirectory('gradle')
.childDirectory('wrapper') .childDirectory('wrapper')
...@@ -1072,6 +1079,82 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; ...@@ -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: <Type, Generator>{
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: <Type, Generator>{
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: <Type, Generator>{
FileSystem: () => memoryFileSystem,
});
});
group('gradle build', () { group('gradle build', () {
MockAndroidSdk mockAndroidSdk; MockAndroidSdk mockAndroidSdk;
MockAndroidStudio mockAndroidStudio; MockAndroidStudio mockAndroidStudio;
...@@ -1136,6 +1219,9 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)'''; ...@@ -1136,6 +1219,9 @@ at org.gradle.wrapper.GradleWrapperMain.main(GradleWrapperMain.java:61)''';
final File gradlew = fs.file('path/to/project/.android/gradlew'); final File gradlew = fs.file('path/to/project/.android/gradlew');
gradlew.createSync(recursive: true); gradlew.createSync(recursive: true);
fs.file('path/to/project/.android/gradle.properties')
.writeAsStringSync('irrelevant');
when(mockProcessManager.run( when(mockProcessManager.run(
<String> ['/path/to/project/.android/gradlew', '-v'], <String> ['/path/to/project/.android/gradlew', '-v'],
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
...@@ -1198,9 +1284,11 @@ Platform fakePlatform(String name) { ...@@ -1198,9 +1284,11 @@ Platform fakePlatform(String name) {
return FakePlatform.fromPlatform(const LocalPlatform())..operatingSystem = 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 MockLocalEngineArtifacts extends Mock implements LocalEngineArtifacts {}
class MockProcessManager extends Mock implements ProcessManager {} class MockProcessManager extends Mock implements ProcessManager {}
class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {} class MockXcodeProjectInterpreter extends Mock implements XcodeProjectInterpreter {}
class MockGradleProject extends Mock implements GradleProject {}
class MockitoAndroidSdk extends Mock implements AndroidSdk {} class MockitoAndroidSdk extends Mock implements AndroidSdk {}
class MockAndroidStudio extends Mock implements AndroidStudio {}
...@@ -103,6 +103,10 @@ void main() { ...@@ -103,6 +103,10 @@ void main() {
platform.isWindows ? 'gradlew.bat' : 'gradlew', platform.isWindows ? 'gradlew.bat' : 'gradlew',
)..createSync(recursive: true); )..createSync(recursive: true);
project.android.hostAppGradleRoot
.childFile('gradle.properties')
.writeAsStringSync('irrelevant');
final Directory gradleWrapperDir = fs.systemTempDirectory.createTempSync('gradle_wrapper.'); final Directory gradleWrapperDir = fs.systemTempDirectory.createTempSync('gradle_wrapper.');
when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir); when(mockCache.getArtifactDirectory('gradle_wrapper')).thenReturn(gradleWrapperDir);
......
...@@ -137,7 +137,7 @@ void main() { ...@@ -137,7 +137,7 @@ void main() {
tryToDelete(tempDir); 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, final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=app']); arguments: <String>['--no-pub', '--template=app']);
...@@ -151,7 +151,7 @@ void main() { ...@@ -151,7 +151,7 @@ void main() {
'-q', '-q',
'-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Ptrack-widget-creation=false', '-Ptrack-widget-creation=false',
'-Pproguard=true', '-Pshrink=true',
'-Ptarget-platform=android-arm,android-arm64', '-Ptarget-platform=android-arm,android-arm64',
'assembleRelease', 'assembleRelease',
], ],
...@@ -165,17 +165,16 @@ void main() { ...@@ -165,17 +165,16 @@ void main() {
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}, },
skip: true,
timeout: allowForCreateFlutterProject); 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, final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=app']); arguments: <String>['--no-pub', '--template=app']);
await expectLater(() async { await expectLater(() async {
await runBuildApkCommand( await runBuildApkCommand(
projectPath, projectPath,
arguments: <String>['--no-proguard'], arguments: <String>['--no-shrink'],
); );
}, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1'));
...@@ -198,10 +197,9 @@ void main() { ...@@ -198,10 +197,9 @@ void main() {
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}, },
skip: true,
timeout: allowForCreateFlutterProject); 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, final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=app']); arguments: <String>['--no-pub', '--template=app']);
...@@ -211,22 +209,20 @@ void main() { ...@@ -211,22 +209,20 @@ void main() {
'-q', '-q',
'-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Ptrack-widget-creation=false', '-Ptrack-widget-creation=false',
'-Pproguard=true', '-Pshrink=true',
'-Ptarget-platform=android-arm,android-arm64', '-Ptarget-platform=android-arm,android-arm64',
'assembleRelease', 'assembleRelease',
], ],
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), environment: anyNamed('environment'),
)).thenAnswer((_) { )).thenAnswer((_) {
const String proguardStdoutWarning = const String r8StdoutWarning =
'Warning: there were 6 unresolved references to program class members.' 'Execution failed for task \':app:transformClassesAndResourcesWithR8ForStageInternal\'.'
'Your input classes appear to be inconsistent.' '> com.android.tools.r8.CompilationFailedException: Compilation failed to complete';
'You may need to recompile the code.'
'(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)';
return Future<Process>.value( return Future<Process>.value(
createMockProcess( createMockProcess(
exitCode: 1, exitCode: 1,
stdout: proguardStdoutWarning, stdout: r8StdoutWarning,
) )
); );
}); });
...@@ -238,15 +234,15 @@ void main() { ...@@ -238,15 +234,15 @@ void main() {
}, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1'));
expect(testLogger.statusText, 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, 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, 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( verify(mockUsage.sendEvent(
'build-apk', 'build-apk',
'proguard-failure', 'r8-failure',
parameters: anyNamed('parameters'), parameters: anyNamed('parameters'),
)).called(1); )).called(1);
}, },
...@@ -257,7 +253,6 @@ void main() { ...@@ -257,7 +253,6 @@ void main() {
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Usage: () => mockUsage, Usage: () => mockUsage,
}, },
skip: true,
timeout: allowForCreateFlutterProject); timeout: allowForCreateFlutterProject);
}); });
} }
......
...@@ -75,7 +75,7 @@ void main() { ...@@ -75,7 +75,7 @@ void main() {
}, timeout: allowForCreateFlutterProject); }, timeout: allowForCreateFlutterProject);
}); });
group('Flags', () { group('Gradle', () {
Directory tempDir; Directory tempDir;
ProcessManager mockProcessManager; ProcessManager mockProcessManager;
MockAndroidSdk mockAndroidSdk; MockAndroidSdk mockAndroidSdk;
...@@ -122,7 +122,7 @@ void main() { ...@@ -122,7 +122,7 @@ void main() {
tryToDelete(tempDir); 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( final String projectPath = await createProject(
tempDir, tempDir,
arguments: <String>['--no-pub', '--template=app'], arguments: <String>['--no-pub', '--template=app'],
...@@ -138,7 +138,7 @@ void main() { ...@@ -138,7 +138,7 @@ void main() {
'-q', '-q',
'-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Ptrack-widget-creation=false', '-Ptrack-widget-creation=false',
'-Pproguard=true', '-Pshrink=true',
'-Ptarget-platform=android-arm,android-arm64', '-Ptarget-platform=android-arm,android-arm64',
'bundleRelease', 'bundleRelease',
], ],
...@@ -152,10 +152,9 @@ void main() { ...@@ -152,10 +152,9 @@ void main() {
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}, },
skip: true,
timeout: allowForCreateFlutterProject); 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( final String projectPath = await createProject(
tempDir, tempDir,
arguments: <String>['--no-pub', '--template=app'], arguments: <String>['--no-pub', '--template=app'],
...@@ -164,7 +163,7 @@ void main() { ...@@ -164,7 +163,7 @@ void main() {
await expectLater(() async { await expectLater(() async {
await runBuildAppBundleCommand( await runBuildAppBundleCommand(
projectPath, projectPath,
arguments: <String>['--no-proguard'], arguments: <String>['--no-shrink'],
); );
}, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1')); }, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1'));
...@@ -187,10 +186,9 @@ void main() { ...@@ -187,10 +186,9 @@ void main() {
GradleUtils: () => GradleUtils(), GradleUtils: () => GradleUtils(),
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
}, },
skip: true,
timeout: allowForCreateFlutterProject); 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, final String projectPath = await createProject(tempDir,
arguments: <String>['--no-pub', '--template=app']); arguments: <String>['--no-pub', '--template=app']);
...@@ -200,22 +198,20 @@ void main() { ...@@ -200,22 +198,20 @@ void main() {
'-q', '-q',
'-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', '-Ptarget=${fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}',
'-Ptrack-widget-creation=false', '-Ptrack-widget-creation=false',
'-Pproguard=true', '-Pshrink=true',
'-Ptarget-platform=android-arm,android-arm64', '-Ptarget-platform=android-arm,android-arm64',
'bundleRelease', 'bundleRelease',
], ],
workingDirectory: anyNamed('workingDirectory'), workingDirectory: anyNamed('workingDirectory'),
environment: anyNamed('environment'), environment: anyNamed('environment'),
)).thenAnswer((_) { )).thenAnswer((_) {
const String proguardStdoutWarning = const String r8StdoutWarning =
'Warning: there were 6 unresolved references to program class members.' 'Execution failed for task \':app:transformClassesAndResourcesWithR8ForStageInternal\'.'
'Your input classes appear to be inconsistent.' '> com.android.tools.r8.CompilationFailedException: Compilation failed to complete';
'You may need to recompile the code.'
'(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)';
return Future<Process>.value( return Future<Process>.value(
createMockProcess( createMockProcess(
exitCode: 1, exitCode: 1,
stdout: proguardStdoutWarning, stdout: r8StdoutWarning,
) )
); );
}); });
...@@ -227,15 +223,15 @@ void main() { ...@@ -227,15 +223,15 @@ void main() {
}, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1')); }, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1'));
expect(testLogger.statusText, 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, 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, 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( verify(mockUsage.sendEvent(
'build-appbundle', 'build-appbundle',
'proguard-failure', 'r8-failure',
parameters: anyNamed('parameters'), parameters: anyNamed('parameters'),
)).called(1); )).called(1);
}, },
...@@ -246,7 +242,6 @@ void main() { ...@@ -246,7 +242,6 @@ void main() {
ProcessManager: () => mockProcessManager, ProcessManager: () => mockProcessManager,
Usage: () => mockUsage, Usage: () => mockUsage,
}, },
skip: true,
timeout: allowForCreateFlutterProject); timeout: allowForCreateFlutterProject);
}); });
} }
......
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