diff --git a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy index fa2dee6138..96190686d1 100644 --- a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy +++ b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy @@ -381,7 +381,7 @@ class FlutterPlugin implements Plugin { shrinkResources(isBuiltAsApp(project)) // 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") + proguardFiles(project.android.getDefaultProguardFile("proguard-android-optimize.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 d7219d89cd..e740ebd40b 100644 --- a/packages/flutter_tools/lib/src/android/gradle.dart +++ b/packages/flutter_tools/lib/src/android/gradle.dart @@ -440,7 +440,12 @@ class AndroidGradleBuilder implements AndroidBuilder { GradleHandledError? detectedGradleError; String? detectedGradleErrorLine; String? consumeLog(String line) { - if (detectedGradleError != null) { + // The log lines that trigger incompatibleKotlinVersionHandler don't + // always indicate an error, and there are times that that handler + // covers up a more important error handler. Uniquely set it to be + // the lowest priority handler by allowing it to be overridden. + if (detectedGradleError != null + && detectedGradleError != incompatibleKotlinVersionHandler) { // Pipe stdout/stderr from Gradle. return line; } diff --git a/packages/flutter_tools/lib/src/android/gradle_errors.dart b/packages/flutter_tools/lib/src/android/gradle_errors.dart index 4c65bac364..81de7798a6 100644 --- a/packages/flutter_tools/lib/src/android/gradle_errors.dart +++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart @@ -67,11 +67,10 @@ final List gradleErrors = [ networkErrorHandler, permissionDeniedErrorHandler, flavorUndefinedHandler, - r8FailureHandler, + r8DexingBugInAgp73Handler, minSdkVersionHandler, transformInputIssueHandler, lockFileDepMissingHandler, - incompatibleKotlinVersionHandler, minCompileSdkVersionHandler, jvm11RequiredHandler, outdatedGradleHandler, @@ -81,6 +80,7 @@ final List gradleErrors = [ remoteTerminatedHandshakeHandler, couldNotOpenCacheDirectoryHandler, incompatibleCompileSdk35AndAgpVersionHandler, + incompatibleKotlinVersionHandler, // This handler should always be last, as its key log output is sometimes in error messages with other root causes. ]; const String _boxTitle = 'Flutter Fix'; @@ -198,28 +198,6 @@ final GradleHandledError zipExceptionHandler = GradleHandledError( eventLabel: 'zip-exception', ); -// R8 failure. -@visibleForTesting -final GradleHandledError r8FailureHandler = GradleHandledError( - test: _lineMatcher(const [ - 'com.android.tools.r8', - ]), - handler: ({ - required String line, - required FlutterProject project, - required bool usesAndroidX, - }) async { - globals.printBox( - '${globals.logger.terminal.warningMark} The shrinker may have failed to optimize the Java bytecode.\n' - 'To disable the shrinker, pass the `--no-shrink` flag to this command.\n' - 'To learn more, see: https://developer.android.com/studio/build/shrink-code', - title: _boxTitle, - ); - return GradleBuildStatus.exit; - }, - eventLabel: 'r8', -); - /// Handle Gradle error thrown when Gradle needs to download additional /// Android SDK components (e.g. Platform Tools), and the license /// for that component has not been accepted. @@ -429,7 +407,8 @@ final GradleHandledError lockFileDepMissingHandler = GradleHandledError( eventLabel: 'lock-dep-issue', ); -@visibleForTesting +// This handler is made visible in other files so that we can uniquely set it +// to be the lowest priority error. final GradleHandledError incompatibleKotlinVersionHandler = GradleHandledError( test: _lineMatcher(const [ 'was compiled with an incompatible version of Kotlin', @@ -642,6 +621,17 @@ final GradleHandledError couldNotOpenCacheDirectoryHandler = GradleHandledError( eventLabel: 'could-not-open-cache-directory', ); + +String _getAgpLocation(FlutterProject project) { + return ''' + The version of AGP that your project uses is likely defined in: +${project.android.settingsGradleFile.path}, +in the 'plugins' closure. + Alternatively, if your project was created with an older version of the templates, it is likely +in the buildscript.dependencies closure of the top-level build.gradle: +${project.android.hostAppGradleFile.path}.'''; +} + @visibleForTesting final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHandledError( test: (String line) => line.contains('RES_TABLE_TYPE_TYPE entry offsets overlap actual entry data'), @@ -652,10 +642,7 @@ final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHa }) async { globals.printBox( '${globals.logger.terminal.warningMark} Using compileSdk 35 requires Android Gradle Plugin (AGP) 8.1.0 or higher.' - ' \n Please upgrade to a newer AGP version. The version of AGP that your project uses is likely' - " defined in:\n${project.android.settingsGradleFile.path},\nin the 'plugins' closure. \n Alternatively, if your " - 'project was created with an older version of the templates, it is likely \nin the buildscript.dependencies ' - 'closure of the top-level build.gradle:\n${project.android.hostAppGradleFile.path}.\n\n Finally, if you have a' + ' \n Please upgrade to a newer AGP version.${_getAgpLocation(project)}\n\n Finally, if you have a' ' strong reason to avoid upgrading AGP, you can temporarily lower the compileSdk version in the following file:\n${project.android.appGradleFile.path}', title: _boxTitle, ); @@ -664,3 +651,24 @@ final GradleHandledError incompatibleCompileSdk35AndAgpVersionHandler = GradleHa }, eventLabel: 'incompatible-compile-sdk-and-agp', ); + +@visibleForTesting +final GradleHandledError r8DexingBugInAgp73Handler = GradleHandledError( + test: (String line) => line.contains('com.android.tools.r8.internal') && line.contains(': Unused argument with users'), + handler: ({ + required String line, + required FlutterProject project, + required bool usesAndroidX, + }) async { + globals.printBox(''' +${globals.logger.terminal.warningMark} Version 7.3 of the Android Gradle Plugin (AGP) uses a version of R8 that contains a bug which causes this error (see more info at https://issuetracker.google.com/issues/242308990). +To fix this error, update to a newer version of AGP (at least 7.4.0). + +${_getAgpLocation(project)}''', + title: _boxTitle, + ); + + return GradleBuildStatus.exit; + }, + eventLabel: 'r8-dexing-bug-in-AGP-7.3' +); diff --git a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart index 7cce991dcf..bc4dae2a71 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/build_apk_test.dart @@ -426,61 +426,6 @@ void main() { AndroidStudio: () => FakeAndroidStudio(), }); - testUsingContext('guides the user when the shrinker fails', () async { - final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app', '--platform=android']); - const String r8StdoutWarning = - "Execution failed for task ':app:transformClassesAndResourcesWithR8ForStageInternal'.\n" - '> com.android.tools.r8.CompilationFailedException: Compilation failed to complete'; - processManager.addCommand(FakeCommand( - command: [ - gradlew, - '-q', - '-Ptarget-platform=android-arm,android-arm64,android-x64', - '-Ptarget=${globals.fs.path.join(tempDir.path, 'flutter_project', 'lib', 'main.dart')}', - '-Pbase-application-name=android.app.Application', - '-Pdart-obfuscation=false', - '-Ptrack-widget-creation=true', - '-Ptree-shake-icons=true', - 'assembleRelease', - ], - exitCode: 1, - stdout: r8StdoutWarning, - )); - - await expectLater( - () => runBuildApkCommand( - projectPath, - ), - throwsToolExit(message: 'Gradle task assembleRelease failed with exit code 1'), - ); - expect( - testLogger.statusText, allOf( - containsIgnoringWhitespace('The shrinker may have failed to optimize the Java bytecode.'), - containsIgnoringWhitespace('To disable the shrinker, pass the `--no-shrink` flag to this command.'), - containsIgnoringWhitespace('To learn more, see: https://developer.android.com/studio/build/shrink-code'), - ) - ); - - expect( - analytics.sentEvents, - contains( - Event.flutterBuildInfo( - label: 'gradle-r8-failure', - buildType: 'gradle', - ), - ), - ); - expect(processManager, hasNoRemainingExpectations); - }, - overrides: { - AndroidSdk: () => mockAndroidSdk, - Java: () => null, - FlutterProjectFactory: () => FakeFlutterProjectFactory(tempDir), - ProcessManager: () => processManager, - Analytics: () => analytics, - AndroidStudio: () => FakeAndroidStudio(), - }); - testUsingContext("reports when the app isn't using AndroidX", () async { final String projectPath = await createProject(tempDir, arguments: ['--no-pub', '--template=app', '--platform=android']); // Simulate a non-androidx project. diff --git a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart index 4185169dd2..9e5da629c2 100644 --- a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart +++ b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart @@ -38,11 +38,10 @@ void main() { networkErrorHandler, permissionDeniedErrorHandler, flavorUndefinedHandler, - r8FailureHandler, + r8DexingBugInAgp73Handler, minSdkVersionHandler, transformInputIssueHandler, lockFileDepMissingHandler, - incompatibleKotlinVersionHandler, minCompileSdkVersionHandler, jvm11RequiredHandler, outdatedGradleHandler, @@ -52,6 +51,7 @@ void main() { remoteTerminatedHandshakeHandler, couldNotOpenCacheDirectoryHandler, incompatibleCompileSdk35AndAgpVersionHandler, + incompatibleKotlinVersionHandler, ]) ); }); @@ -1320,7 +1320,6 @@ Execution failed for task ':app:bundleReleaseResources'. '│ /android/settings.gradle, │\n' "│ in the 'plugins' closure. │\n" '│ Alternatively, if your project was created with an older version of the templates, it is likely │\n' - '│ │\n' '│ in the buildscript.dependencies closure of the top-level build.gradle: │\n' '│ /android/build.gradle. │\n' '│ │\n' @@ -1337,6 +1336,43 @@ Execution failed for task ':app:bundleReleaseResources'. FileSystem: () => fileSystem, ProcessManager: () => processManager, }); + + testUsingContext('AGP 7.3.0 R8 bug', () async { + const String errorExample = r''' +ERROR:/Users/mackall/.gradle/caches/transforms-3/bd2c84591857c6d4c308221ffece862e/transformed/jetified-media3-exoplayer-dash-1.4.0-runtime.jar: R8: com.android.tools.r8.internal.Y10: Unused argument with users in androidx + '''; + + await r8DexingBugInAgp73Handler.handler( + line: errorExample, + project: FlutterProject.fromDirectoryTest(fileSystem.currentDirectory), + usesAndroidX: true, + ); + + expect( + testLogger.statusText, + contains( + '\n' + '┌─ Flutter Fix ────────────────────────────────────────────────────────────────────────────────────┐\n' + '│ [!] Version 7.3 of the Android Gradle Plugin (AGP) uses a version of R8 that contains a bug │\n' + '│ which causes this error (see more info at https://issuetracker.google.com/issues/242308990). │\n' + '│ To fix this error, update to a newer version of AGP (at least 7.4.0). │\n' + '│ │\n' + '│ The version of AGP that your project uses is likely defined in: │\n' + '│ /android/settings.gradle, │\n' + "│ in the 'plugins' closure. │\n" + '│ Alternatively, if your project was created with an older version of the templates, it is likely │\n' + '│ in the buildscript.dependencies closure of the top-level build.gradle: │\n' + '│ /android/build.gradle. │\n' + '└──────────────────────────────────────────────────────────────────────────────────────────────────┘\n' + '' + ) + ); + }, overrides: { + GradleUtils: () => FakeGradleUtils(), + Platform: () => fakePlatform('android'), + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + }); } bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {