Enable R8 (#40453)
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 e583a46..b650ab0 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/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 @@ 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 @@ }); }); + 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', () { MockAndroidSdk mockAndroidSdk; MockAndroidStudio mockAndroidStudio; @@ -1136,6 +1219,9 @@ 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( <String> ['/path/to/project/.android/gradlew', '-v'], workingDirectory: anyNamed('workingDirectory'), @@ -1198,9 +1284,11 @@ 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 a374142..05855b0 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 @@ 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 f3d19b8..1c961ad 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 @@ 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: <String>['--no-pub', '--template=app']); @@ -151,7 +151,7 @@ '-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 @@ 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: <String>['--no-pub', '--template=app']); await expectLater(() async { await runBuildApkCommand( projectPath, - arguments: <String>['--no-proguard'], + arguments: <String>['--no-shrink'], ); }, throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1')); @@ -198,10 +197,9 @@ 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: <String>['--no-pub', '--template=app']); @@ -211,22 +209,20 @@ '-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<Process>.value( createMockProcess( exitCode: 1, - stdout: proguardStdoutWarning, + stdout: r8StdoutWarning, ) ); }); @@ -238,15 +234,15 @@ }, 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 @@ 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 4548e3c..4a7d98f 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 @@ }, timeout: allowForCreateFlutterProject); }); - group('Flags', () { + group('Gradle', () { Directory tempDir; ProcessManager mockProcessManager; MockAndroidSdk mockAndroidSdk; @@ -122,7 +122,7 @@ 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: <String>['--no-pub', '--template=app'], @@ -138,7 +138,7 @@ '-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 @@ 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: <String>['--no-pub', '--template=app'], @@ -164,7 +163,7 @@ await expectLater(() async { await runBuildAppBundleCommand( projectPath, - arguments: <String>['--no-proguard'], + arguments: <String>['--no-shrink'], ); }, throwsToolExit(message: 'Gradle task bundleRelease failed with exit code 1')); @@ -187,10 +186,9 @@ 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: <String>['--no-pub', '--template=app']); @@ -200,22 +198,20 @@ '-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<Process>.value( createMockProcess( exitCode: 1, - stdout: proguardStdoutWarning, + stdout: r8StdoutWarning, ) ); }); @@ -227,15 +223,15 @@ }, 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 @@ ProcessManager: () => mockProcessManager, Usage: () => mockUsage, }, - skip: true, timeout: allowForCreateFlutterProject); }); }