From 7cf1969ad476973dce04fa3649451c5a54de5925 Mon Sep 17 00:00:00 2001 From: Bartek Pacia Date: Tue, 7 May 2024 16:11:00 +0100 Subject: [PATCH] Improve Android SDK and NDK mistmatch warning message (#147809) This PR resolves #147806 - List plugin that want to be compiled against a higher Android SDK version - List plugins that depend on a different NDK version (we don't have a way to compare them) - Small formatting and wording improvements - Update syntax to work for both Groovy and Kotlin - If project uses `build.gradle.kts`, then it is mentioned in the warning message (previously always `build.gradle` was mentioned) demo --- .../gradle/src/main/groovy/flutter.groovy | 61 ++++++++++++++++--- ...lugin_compilesdkversion_mismatch_test.dart | 12 ++-- ...droid_plugin_ndkversion_mismatch_test.dart | 16 ++--- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy index 5cda294e63dc2..62e74a657a09c 100644 --- a/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy +++ b/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy @@ -693,7 +693,7 @@ class FlutterPlugin implements Plugin { if (pluginProject == null) { // Plugin was not included in `settings.gradle`, but is listed in `.flutter-plugins`. project.logger.error("Plugin project :${it.name} listed, but not found. Please fix your settings.gradle/settings.gradle.kts.") - } else if (doesSupportAndroidPlatform(pluginProject.projectDir.parentFile.path as String)) { + } else if (pluginSupportsAndroidPlatform(pluginProject)) { // Plugin has a functioning `android` folder and is included successfully, although it's not supported. // It must be configured nonetheless, to not throw an "Unresolved reference" exception. configurePluginProject(it) @@ -706,12 +706,23 @@ class FlutterPlugin implements Plugin { // TODO(54566): Can remove this function and its call sites once resolved. /** - * Returns `true` if the given path contains an `android` directory + * Returns `true` if the given project is a plugin project having an `android` directory * containing a `build.gradle` or `build.gradle.kts` file. */ - private Boolean doesSupportAndroidPlatform(String path) { - File buildGradle = new File(path, 'android' + File.separator + 'build.gradle') - File buildGradleKts = new File(path, 'android' + File.separator + 'build.gradle.kts') + private Boolean pluginSupportsAndroidPlatform(Project project) { + File buildGradle = new File(project.projectDir.parentFile, "android" + File.separator + "build.gradle") + File buildGradleKts = new File(project.projectDir.parentFile, "android" + File.separator + "build.gradle.kts") + return buildGradle.exists() || buildGradleKts.exists() + } + + /** + * Returns the Gradle build script for the build. When both Groovy and + * Kotlin variants exist, then Groovy (build.gradle) is preferred over + * Kotlin (build.gradle.kts). This is the same behavior as Gradle 8.5. + */ + private File buildGradleFile(Project project) { + File buildGradle = new File(project.projectDir.parentFile, "app" + File.separator + "build.gradle") + File buildGradleKts = new File(project.projectDir.parentFile, "app" + File.separator + "build.gradle.kts") if (buildGradle.exists() && buildGradleKts.exists()) { project.logger.error( "Both build.gradle and build.gradle.kts exist, so " + @@ -719,7 +730,7 @@ class FlutterPlugin implements Plugin { ) } - return buildGradle.exists() || buildGradleKts.exists() + return buildGradle.exists() ? buildGradle : buildGradleKts } /** @@ -837,6 +848,8 @@ class FlutterPlugin implements Plugin { String projectNdkVersion = project.android.ndkVersion ?: ndkVersionIfUnspecified String maxPluginNdkVersion = projectNdkVersion int numProcessedPlugins = getPluginList(project).size() + List> pluginsWithHigherSdkVersion = [] + List> pluginsWithDifferentNdkVersion = [] getPluginList(project).each { pluginObject -> assert(pluginObject.name instanceof String) @@ -851,17 +864,49 @@ class FlutterPlugin implements Plugin { if (getCompileSdkFromProject(pluginProject).isInteger()) { pluginCompileSdkVersion = getCompileSdkFromProject(pluginProject) as int } + maxPluginCompileSdkVersion = Math.max(pluginCompileSdkVersion, maxPluginCompileSdkVersion) + if (pluginCompileSdkVersion > projectCompileSdkVersion) { + pluginsWithHigherSdkVersion.add(new Tuple(pluginProject.name, pluginCompileSdkVersion)) + } + String pluginNdkVersion = pluginProject.android.ndkVersion ?: ndkVersionIfUnspecified maxPluginNdkVersion = mostRecentSemanticVersion(pluginNdkVersion, maxPluginNdkVersion) + if (pluginNdkVersion != projectNdkVersion) { + pluginsWithDifferentNdkVersion.add(new Tuple(pluginProject.name, pluginNdkVersion)) + } numProcessedPlugins-- if (numProcessedPlugins == 0) { if (maxPluginCompileSdkVersion > projectCompileSdkVersion) { - project.logger.error("One or more plugins require a higher Android SDK version.\nFix this issue by adding the following to ${project.projectDir}${File.separator}build.gradle:\nandroid {\n compileSdkVersion ${maxPluginCompileSdkVersion}\n ...\n}\n") + project.logger.error("Your project is configured to compile against Android SDK $projectCompileSdkVersion, but the following plugin(s) require to be compiled against a higher Android SDK version:") + for (Tuple2 pluginToCompileSdkVersion : pluginsWithHigherSdkVersion) { + project.logger.error("- ${pluginToCompileSdkVersion.first} compiles against Android SDK ${pluginToCompileSdkVersion.second}") + } + project.logger.error("""\ + Fix this issue by compiling against the highest Android SDK version (they are backward compatible). + Add the following to ${buildGradleFile(project).path}: + + android { + compileSdk = ${maxPluginCompileSdkVersion} + ... + } + """.stripIndent()) } if (maxPluginNdkVersion != projectNdkVersion) { - project.logger.error("One or more plugins require a higher Android NDK version.\nFix this issue by adding the following to ${project.projectDir}${File.separator}build.gradle:\nandroid {\n ndkVersion \"${maxPluginNdkVersion}\"\n ...\n}\n") + project.logger.error("Your project is configured with Android NDK $projectNdkVersion, but the following plugin(s) depend on a different Android NDK version:") + for (Tuple2 pluginToNdkVersion : pluginsWithDifferentNdkVersion) { + project.logger.error("- ${pluginToNdkVersion.first} requires Android NDK ${pluginToNdkVersion.second}") + } + project.logger.error("""\ + Fix this issue by using the highest Android NDK version (they are backward compatible). + Add the following to ${buildGradleFile(project).path}: + + android { + ndkVersion = \"${maxPluginNdkVersion}\" + ... + } + """.stripIndent()) } } } diff --git a/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart b/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart index 02335c423041e..9f753e6d278dc 100644 --- a/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart +++ b/packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart @@ -75,16 +75,20 @@ void main() { // Check error message is thrown expect( - result.stdout, + result.stderr, contains( - 'Warning: The plugin test_plugin requires Android SDK version 31 or higher.')); + 'Your project is configured to compile against Android SDK 30, but ' + 'the following plugin(s) require to be compiled against a higher Android SDK version:')); expect( result.stderr, - contains('One or more plugins require a higher Android SDK version.'), + contains('- test_plugin compiles against Android SDK 31'), ); expect( result.stderr, contains( - 'Fix this issue by adding the following to ${projectGradleFile.path}')); + 'Fix this issue by compiling against the highest Android SDK version (they are backward compatible).')); + expect( + result.stderr, + contains('Add the following to ${projectGradleFile.path}:')); }); } diff --git a/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart b/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart index d2eb640fa77fa..93e3cfd7445a2 100644 --- a/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart +++ b/packages/flutter_tools/test/integration.shard/android_plugin_ndkversion_mismatch_test.dart @@ -74,13 +74,15 @@ void main() { // Check that an error message is thrown. expect(result.stderr, contains(''' -One or more plugins require a higher Android NDK version. -Fix this issue by adding the following to ${projectGradleFile.path}: -android { - ndkVersion "21.4.7075529" - ... -} - +Your project is configured with Android NDK 21.1.6352462, but the following plugin(s) depend on a different Android NDK version: +- test_plugin requires Android NDK 21.4.7075529 +Fix this issue by using the highest Android NDK version (they are backward compatible). +Add the following to ${projectGradleFile.path}: + + android { + ndkVersion = "21.4.7075529" + ... + } ''')); }); }