[CP-Beta] Add Flutter Errors for Migrating to AGP 9 (#179765 and #180018) (#180038)
This is a manual cherry pick of https://github.com/flutter/flutter/pull/179765 and https://github.com/flutter/flutter/pull/180018 to beta. Combined into one CP PR because the second PR is a fix-forward.
### Issue Link:
What is the link to the issue this cherry-pick is addressing?
https://github.com/flutter/flutter/issues/179914
### Impact Description:
Impacted Users (Approximately who will hit this issue, ex. all Flutter devs, Windows developers, all end-customers, apps using X framework feature).
Flutter devs who ship to Android and are updating to AGP 9.0.0.
### Changelog Description:
Explain this cherry pick:
* In one line that is accessible to most Flutter developers.
* That describes the state prior to the fix.
* That includes which platforms are impacted.
See [best practices](https://github.com/flutter/flutter/blob/main/docs/releases/Hotfix-Documentation-Best-Practices.md) for examples.
Added Flutter Errors to direct users to AGP 9 Migration Flutter docs when upgrading to AGP 9.0.0.
### Workaround:
Is there a workaround for this issue?
Currently, you can address the errors by looking through the comments at this issue [here](https://github.com/flutter/flutter/issues/175688) or reading Android AGP 9 preview docs [here](https://developer.android.com/build/releases/agp-preview).
### Risk:
What is the risk level of this cherry-pick?
### Test Coverage:
Are you confident that your fix is well-tested by automated tests?
### Validation Steps:
Create or use an existing Flutter app. Update to AGP 9.0.0. Address the errors as instructed in the Flutter error, then run `flutter run` on the project.
diff --git a/packages/flutter_tools/lib/src/android/gradle_errors.dart b/packages/flutter_tools/lib/src/android/gradle_errors.dart
index 77f7f73..91122b1 100644
--- a/packages/flutter_tools/lib/src/android/gradle_errors.dart
+++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart
@@ -83,6 +83,8 @@
usageOfV1EmbeddingReferencesHandler,
jlinkErrorWithJava21AndSourceCompatibility,
missingNdkSourcePropertiesFile,
+ applyingKotlinAndroidPluginErrorHandler,
+ useNewAgpDslErrorHandler,
incompatibleKotlinVersionHandler, // This handler should always be last, as its key log output is sometimes in error messages with other root causes.
];
@@ -660,3 +662,52 @@
},
eventLabel: 'ndk-missing-source-properties-file',
);
+
+/// Handler when applying the kotlin-android plugin results in a build failure. This failure occurs when
+/// using AGP 9+ because built-in Kotlin has become the default behavior.
+@visibleForTesting
+final applyingKotlinAndroidPluginErrorHandler = GradleHandledError(
+ test: (String line) {
+ return line.contains(
+ "The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0",
+ );
+ },
+ handler:
+ ({required String line, required FlutterProject project, required bool usesAndroidX}) async {
+ final File appGradleFile = project.android.appGradleFile;
+ globals.printBox(
+ '''
+${globals.logger.terminal.warningMark} Starting AGP 9+, the default has become built-in Kotlin. This results in a build failure when applying the kotlin-android plugin at ${appGradleFile.path}.
+\nTo resolve this, migrate to built-in Kotlin. For instructions on how to migrate, see: https://docs.flutter.dev/release/breaking-changes/migrate-to-agp-9''',
+ title: _boxTitle,
+ );
+
+ return GradleBuildStatus.exit;
+ },
+ eventLabel: 'applying-kotlin-android-plugin-error',
+);
+
+/// Handler when using the new AGP DSL interfaces. Starting AGP 9+, only the new
+/// DSL interfaces are used. This results in a failure because we still depend
+/// on old DSL types.
+@visibleForTesting
+final useNewAgpDslErrorHandler = GradleHandledError(
+ test: _lineMatcher(const <String>[
+ "> Failed to apply plugin 'dev.flutter.flutter-gradle-plugin'",
+ '> java.lang.NullPointerException (no error message)',
+ ]),
+ handler:
+ ({required String line, required FlutterProject project, required bool usesAndroidX}) async {
+ final File appGradleFile = project.android.appGradleFile;
+ globals.printBox(
+ '''
+${globals.logger.terminal.warningMark} Starting AGP 9+, only the new DSL interface will be read. This results in a build failure when applying the Flutter Gradle plugin at ${appGradleFile.path}.
+\nTo resolve this update flutter or opt out of `android.newDsl`. For instructions on how to opt out, see: https://docs.flutter.dev/release/breaking-changes/migrate-to-agp-9
+\nIf you are not upgrading to AGP 9+, run `flutter analyze --suggestions` to check for incompatible dependencies.''',
+ title: _boxTitle,
+ );
+
+ return GradleBuildStatus.exit;
+ },
+ eventLabel: 'use-new-agp-dsl-error',
+);
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 31d611f..d0b3271 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
@@ -55,6 +55,8 @@
usageOfV1EmbeddingReferencesHandler,
jlinkErrorWithJava21AndSourceCompatibility,
missingNdkSourcePropertiesFile,
+ applyingKotlinAndroidPluginErrorHandler,
+ useNewAgpDslErrorHandler,
incompatibleKotlinVersionHandler,
]),
);
@@ -1621,6 +1623,86 @@
ProcessManager: () => processManager,
},
);
+
+ testUsingContext(
+ 'Failure to apply kotlin-android plugin',
+ () async {
+ const applyingKotlinAndroidPluginErrorExample = r'''
+FAILURE: Build failed with an exception.
+
+* Where:
+Build file '/Users/jesswon/Desktop/fresh_flutter_app/android/app/build.gradle.kts'
+
+* What went wrong:
+An exception occurred applying plugin request [id: 'kotlin-android']
+> Failed to apply plugin 'kotlin-android'.
+ > ⛔ Failed to apply plugin 'com.jetbrains.kotlin.android'
+ The 'org.jetbrains.kotlin.android' plugin is no longer required for Kotlin support since AGP 9.0.
+ Solution: Remove the 'org.jetbrains.kotlin.android' plugin from this project's build file: app/build.gradle.kts.
+ See https://issuetracker.google.com/438678642 for more details.
+ > java.lang.Throwable (no error message)
+ ''';
+
+ final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
+ await applyingKotlinAndroidPluginErrorHandler.handler(
+ line: applyingKotlinAndroidPluginErrorExample,
+ project: project,
+ usesAndroidX: true,
+ );
+
+ expect(
+ testLogger.statusText,
+ contains('Starting AGP 9+, the default has become built-in Kotlin.'),
+ );
+ expect(testLogger.statusText, contains('This results in a build failure'));
+ expect(testLogger.statusText, contains('when applying the kotlin-android plugin'));
+ },
+ overrides: <Type, Generator>{
+ GradleUtils: () => FakeGradleUtils(),
+ Platform: () => fakePlatform('android'),
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
+ },
+ );
+
+ testUsingContext(
+ 'Failure to apply kotlin-android plugin',
+ () async {
+ const useNewAgpDslErrorHandlerExample = r'''
+FAILURE: Build failed with an exception.
+
+* Where:
+Build file '/Users/jesswon/Desktop/fresh_flutter_app/android/app/build.gradle.kts'
+
+* What went wrong:
+An exception occurred applying plugin request [id: 'dev.flutter.flutter-gradle-plugin']
+> Failed to apply plugin 'dev.flutter.flutter-gradle-plugin'.
+ > java.lang.NullPointerException (no error message)
+ ''';
+
+ final FlutterProject project = FlutterProject.fromDirectoryTest(fileSystem.currentDirectory);
+ await useNewAgpDslErrorHandler.handler(
+ line: useNewAgpDslErrorHandlerExample,
+ project: project,
+ usesAndroidX: true,
+ );
+
+ expect(
+ testLogger.statusText,
+ contains('Starting AGP 9+, only the new DSL interface will be read.'),
+ );
+ expect(testLogger.statusText, contains('This results in a build failure'));
+ expect(testLogger.statusText, contains('when applying the Flutter Gradle plugin'));
+ expect(testLogger.statusText, contains('If you are not upgrading to AGP 9+'));
+ expect(testLogger.statusText, contains('run `flutter analyze --suggestions`'));
+ },
+ overrides: <Type, Generator>{
+ GradleUtils: () => FakeGradleUtils(),
+ Platform: () => fakePlatform('android'),
+ FileSystem: () => fileSystem,
+ ProcessManager: () => processManager,
+ },
+ );
}
bool formatTestErrorMessage(String errorMessage, GradleHandledError error) {