From e2dea95082e3a13d3084cacb6660d389449bab08 Mon Sep 17 00:00:00 2001 From: Gray Mackall <34871572+gmackall@users.noreply.github.com> Date: Tue, 8 Apr 2025 11:37:14 -0700 Subject: [PATCH] Fix warnings in FGP (#166727) Fixes/suppresses all warnings: 1. Fixes https://github.com/flutter/flutter/issues/162695 2. Suppresses warnings about the various `*Variant*` imports that we use being deprecated, with comments linking to https://github.com/flutter/flutter/issues/166550 to migrate to the variant api. 3. Fixes some unused elvis operators and unneeded types provided for declarations that AGP complains about, e.g. `val fooString: String = "a string"`, and use of deprecated string related methods. 4. Follows up on https://github.com/flutter/flutter/pull/166277, as we were getting a warning mentioned in this comment https://github.com/flutter/flutter/pull/166277#issuecomment-2780184486. 5. Suppresses some warnings about unused code where the analysis couldn't properly detect it being used (i.e., it isn't unused) (4) is more opinionated, let me know if you think it should be done in a follow up. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Gray Mackall --- .../bin/tasks/gradle_plugin_fat_apk_test.dart | 9 +- .../flutter_tools/gradle/build.gradle.kts | 10 -- .../gradle/src/main/kotlin/Deeplink.kt | 6 +- .../kotlin/FlutterAppPluginLoaderPlugin.kt | 1 + .../src/main/kotlin/FlutterExtension.kt | 1 + .../gradle/src/main/kotlin/FlutterPlugin.kt | 97 ++++++++++++------- .../src/main/kotlin/FlutterPluginConstants.kt | 5 +- .../src/main/kotlin/FlutterPluginUtils.kt | 55 +++++++---- .../gradle/src/main/kotlin/VersionUtils.kt | 4 +- .../src/main/kotlin/plugins/PluginHandler.kt | 2 +- .../src/main/kotlin/tasks/BaseFlutterTask.kt | 2 +- .../kotlin/tasks/BaseFlutterTaskHelper.kt | 18 +++- .../scripts/native_plugin_loader.gradle.kts | 8 +- .../kotlin/tasks/BaseFlutterTaskHelperTest.kt | 12 ++- 14 files changed, 143 insertions(+), 87 deletions(-) diff --git a/dev/devicelab/bin/tasks/gradle_plugin_fat_apk_test.dart b/dev/devicelab/bin/tasks/gradle_plugin_fat_apk_test.dart index b0cf6dd7e9..81e0da65e0 100644 --- a/dev/devicelab/bin/tasks/gradle_plugin_fat_apk_test.dart +++ b/dev/devicelab/bin/tasks/gradle_plugin_fat_apk_test.dart @@ -157,14 +157,7 @@ Future main() async { final String defaultPath = path.join(project.rootPath, 'android', 'app', '.cxx'); - final String modifiedPath = path.join( - project.rootPath, - 'build', - 'app', - 'intermediates', - 'flutter', - '.cxx', - ); + final String modifiedPath = path.join(project.rootPath, 'build', '.cxx'); if (Directory(defaultPath).existsSync()) { throw TaskResult.failure('Producing unexpected build artifacts in $defaultPath'); } diff --git a/packages/flutter_tools/gradle/build.gradle.kts b/packages/flutter_tools/gradle/build.gradle.kts index 0797f58275..d2a23ecfd4 100644 --- a/packages/flutter_tools/gradle/build.gradle.kts +++ b/packages/flutter_tools/gradle/build.gradle.kts @@ -19,16 +19,6 @@ tasks.validatePlugins { enableStricterValidation.set(true) } -// We need to compile Kotlin first so we can call it from Groovy. See https://stackoverflow.com/q/36214437/7009800 -tasks.withType { - dependsOn(tasks.compileKotlin) - classpath += files(tasks.compileKotlin.get().destinationDirectory) -} - -tasks.classes { - dependsOn(tasks.compileGroovy) -} - gradlePlugin { plugins { // The "flutterPlugin" name isn't used anywhere. diff --git a/packages/flutter_tools/gradle/src/main/kotlin/Deeplink.kt b/packages/flutter_tools/gradle/src/main/kotlin/Deeplink.kt index ead9c47d2a..d7eccebcab 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/Deeplink.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/Deeplink.kt @@ -10,10 +10,10 @@ import kotlinx.serialization.json.put // TODO(gmackall): Identify which of these can be val instead of var. class Deeplink( - var scheme: String?, - var host: String?, + private var scheme: String?, + private var host: String?, var path: String?, - var intentFilterCheck: IntentFilterCheck + private var intentFilterCheck: IntentFilterCheck ) { // TODO(gmackall): This behavior was kept identical to the original Groovy behavior as part of // the Groovy->Kotlin conversion, but should be changed once the conversion is complete. diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterAppPluginLoaderPlugin.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterAppPluginLoaderPlugin.kt index 2ebfb9ee89..081968af5a 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterAppPluginLoaderPlugin.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterAppPluginLoaderPlugin.kt @@ -22,6 +22,7 @@ private const val FLUTTER_SDK_PATH = "flutterSdkPath" * This plugin applies the native plugin loader plugin (../scripts/native_plugin_loader.gradle.kts) * and then configures the main project to `include` each of the loaded flutter plugins. */ +@Suppress("unused") // This class is used by packages/flutter_tools/gradle/build.gradle.kts. class FlutterAppPluginLoaderPlugin : Plugin { override fun apply(settings: Settings) { val flutterProjectRoot: File = settings.settingsDir.parentFile diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt index 7f41f08ad2..2bcfce6eda 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterExtension.kt @@ -17,6 +17,7 @@ import org.gradle.api.GradleException * Learn more about extensions in Gradle: * * https://docs.gradle.org/8.0.2/userguide/custom_plugins.html#sec:getting_input_from_the_build */ +@Suppress("unused") // The values in this class are used in Flutter developers app-level build.gradle file. open class FlutterExtension { /** Sets the compileSdkVersion used by default in Flutter app projects. */ val compileSdkVersion: Int = 35 diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt index d7b81902d2..b3d2a6ec6e 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt @@ -4,14 +4,10 @@ package com.flutter.gradle -import com.android.build.VariantOutput import com.android.build.api.dsl.ApplicationExtension import com.android.build.gradle.AbstractAppExtension import com.android.build.gradle.BaseExtension import com.android.build.gradle.LibraryExtension -import com.android.build.gradle.api.ApkVariantOutput -import com.android.build.gradle.api.BaseVariant -import com.android.build.gradle.api.BaseVariantOutput import com.android.build.gradle.tasks.PackageAndroidArtifact import com.android.build.gradle.tasks.ProcessAndroidResources import com.flutter.gradle.FlutterPluginUtils.readPropertiesIfExist @@ -27,6 +23,8 @@ import org.gradle.api.tasks.Copy import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.bundling.Jar import org.gradle.internal.os.OperatingSystem +import org.gradle.kotlin.dsl.support.serviceOf +import org.gradle.process.ExecOperations import java.io.File import java.nio.charset.StandardCharsets import java.nio.file.Paths @@ -54,7 +52,7 @@ class FlutterPlugin : Plugin { val flutterRootSystemVal: String? = System.getenv("FLUTTER_ROOT") val flutterRootPath: String = - resolveProperty("flutter.sdk", flutterRootSystemVal) + resolveFlutterSdkProperty(flutterRootSystemVal) ?: throw GradleException( "Flutter SDK not found. Define location with flutter.sdk in the " + "local.properties file or with a FLUTTER_ROOT environment variable." @@ -145,7 +143,7 @@ class FlutterPlugin : Plugin { isUniversalApk = false } } - val propDeferredComponentNames: String = "deferred-component-names" + val propDeferredComponentNames = "deferred-component-names" val deferredComponentNamesValue: String? = project.findProperty(propDeferredComponentNames) as? String if (deferredComponentNamesValue != null) { @@ -280,10 +278,8 @@ class FlutterPlugin : Plugin { private fun getExecutableNameForPlatform(baseExecutableName: String): String = if (OperatingSystem.current().isWindows) "$baseExecutableName.bat" else baseExecutableName - private fun resolveProperty( - propertyName: String, - defaultValue: String? - ): String? { + private fun resolveFlutterSdkProperty(defaultValue: String?): String? { + val propertyName = "flutter.sdk" if (localProperties == null) { localProperties = readPropertiesIfExist(File(project!!.projectDir.parentFile, "local.properties")) @@ -300,7 +296,8 @@ class FlutterPlugin : Plugin { rootProject.subprojects.forEach { subproject -> val gradlew: String = getExecutableNameForPlatform("${rootProject.projectDir}/gradlew") - rootProject.exec { + val execOps = rootProject.serviceOf() + execOps.exec { workingDir(rootProject.projectDir) executable(gradlew) args(":${subproject.name}:dependencies", "--write-locks") @@ -342,11 +339,18 @@ class FlutterPlugin : Plugin { } val copyFlutterAssetsTask: Task = addFlutterDeps(variant, flutterPlugin, targetPlatforms) - val variantOutput: BaseVariantOutput = variant.outputs.first() + + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + val variantOutput: com.android.build.gradle.api.BaseVariantOutput = variant.outputs.first() val processResources: ProcessAndroidResources = try { variantOutput.processResourcesProvider.get() } catch (e: UnknownTaskException) { + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") variantOutput.processResources } processResources.dependsOn(copyFlutterAssetsTask) @@ -361,19 +365,26 @@ class FlutterPlugin : Plugin { // * `build-mode` can be `release|debug|profile`. variant.outputs.forEach { output -> assembleTask.doLast { - output as ApkVariantOutput + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + output as com.android.build.gradle.api.ApkVariantOutput val packageApplicationProvider: PackageAndroidArtifact = variant.packageApplicationProvider.get() val outputDirectory: Directory = packageApplicationProvider.outputDirectory.get() val outputDirectoryStr: String = outputDirectory.toString() var filename = "app" - val abi = output.getFilter(VariantOutput.FilterType.ABI) + + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + val abi = output.getFilter(com.android.build.VariantOutput.FilterType.ABI) if (abi != null && abi.isNotEmpty()) { filename += "-$abi" } if (variant.flavorName != null && variant.flavorName.isNotEmpty()) { - filename += "-${variant.flavorName.toLowerCase()}" + filename += "-${FlutterPluginUtils.lowercase(variant.flavorName)}" } filename += "-${FlutterPluginUtils.buildModeFor(variant.buildType)}" projectToAddTasksTo.copy { @@ -395,13 +406,13 @@ class FlutterPlugin : Plugin { // This path is not flavor specific and must only be added once. // If support for flavors is added to native assets, then they must only be added // once per flavor; see https://github.com/dart-lang/native/issues/1359. - val nativeAssetsDir: String = + val nativeAssetsDir = "${projectToAddTasksTo.layout.buildDirectory.get()}/../native_assets/android/jniLibs/lib/" android.sourceSets .getByName("main") .jniLibs .srcDir(nativeAssetsDir) - getPluginHandler(projectToAddTasksTo!!).configurePlugins(engineVersion!!) + getPluginHandler(projectToAddTasksTo).configurePlugins(engineVersion!!) FlutterPluginUtils.detectLowCompileSdkVersionOrNdkVersion( projectToAddTasksTo, getPluginHandler(projectToAddTasksTo).getPluginList() @@ -469,10 +480,12 @@ class FlutterPlugin : Plugin { flutterPlugin, targetPlatforms ) + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 val mergeAssets = projectToAddTasksTo .tasks - .findByPath(":$hostAppProjectName:merge${appProjectVariant.name.capitalize()}Assets") + .findByPath(":$hostAppProjectName:merge${FlutterPluginUtils.capitalize(appProjectVariant.name)}Assets") check(mergeAssets != null) mergeAssets.dependsOn(copyFlutterAssetsTask) } @@ -502,7 +515,7 @@ class FlutterPlugin : Plugin { * be sure to change any instances of this string in symbols in the code below * to match. */ - const val FLUTTER_BUILD_PREFIX: String = "flutterBuild" + private const val FLUTTER_BUILD_PREFIX: String = "flutterBuild" /** * Finds a task by name, returning null if the task does not exist. @@ -517,8 +530,10 @@ class FlutterPlugin : Plugin { null } + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 private fun addFlutterDeps( - variant: BaseVariant, + @Suppress("DEPRECATION") variant: com.android.build.gradle.api.BaseVariant, flutterPlugin: FlutterPlugin, targetPlatforms: List ): Task { @@ -559,9 +574,15 @@ class FlutterPlugin : Plugin { if (FlutterPluginUtils.shouldProjectSplitPerAbi(project)) { variant.outputs.forEach { output -> // need to force this as the API does not return the right thing for our use. - output as ApkVariantOutput + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + output as com.android.build.gradle.api.ApkVariantOutput + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") val filterIdentifier: String = - output.getFilter(VariantOutput.FilterType.ABI) + output.getFilter(com.android.build.VariantOutput.FilterType.ABI) val abiVersionCode: Int? = FlutterPluginConstants.ABI_VERSION[filterIdentifier] if (abiVersionCode != null) { output.versionCodeOverride @@ -647,8 +668,8 @@ class FlutterPlugin : Plugin { project.layout.buildDirectory.dir("${FlutterPluginConstants.INTERMEDIATES_DIR}/flutter/${variant.name}/libs.jar") ) val packJniLibsTaskProvider: TaskProvider = - project.tasks.register( - "packJniLibs${FLUTTER_BUILD_PREFIX}${variant.name.capitalize()}", + project.tasks.register( + "packJniLibs${FLUTTER_BUILD_PREFIX}${FlutterPluginUtils.capitalize(variant.name)}", Jar::class.java ) { destinationDirectory.set(libJar.parentFile) @@ -663,9 +684,9 @@ class FlutterPlugin : Plugin { } // Copy the native assets created by build.dart and placed in build/native_assets by flutter assemble. // The `$project.layout.buildDirectory` is '.android/Flutter/build/' instead of 'build/'. - val buildDir: String = + val buildDir = "${FlutterPluginUtils.getFlutterSourceDirectory(project)}/build" - val nativeAssetsDir: String = + val nativeAssetsDir = "$buildDir/native_assets/android/jniLibs/lib" from("$nativeAssetsDir/$abi") { include("*.so") @@ -683,13 +704,14 @@ class FlutterPlugin : Plugin { ) val copyFlutterAssetsTaskProvider: TaskProvider = project.tasks.register( - "copyFlutterAssets${variant.name.capitalize()}", + "copyFlutterAssets${FlutterPluginUtils.capitalize(variant.name)}", Copy::class.java ) { dependsOn(compileTask) with(compileTask.assets) // TODO(gmackall): Replace with filePermissions.user.read/write = true once // minimum supported Gradle version is 8.3. + @Suppress("DEPRECATION") fileMode = 420 // corresponds to unix 0644 in base 8 if (isUsedAsSubproject) { // TODO(gmackall): above is always false, can delete @@ -701,20 +723,29 @@ class FlutterPlugin : Plugin { try { variant.mergeAssetsProvider.get() } catch (e: IllegalStateException) { + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") variant.mergeAssets } dependsOn(mergeAssets) - dependsOn("clean${mergeAssets.name.capitalize()}") - mergeAssets.mustRunAfter("clean${mergeAssets.name.capitalize()}") + dependsOn("clean${FlutterPluginUtils.capitalize(mergeAssets.name)}") + mergeAssets.mustRunAfter("clean${FlutterPluginUtils.capitalize(mergeAssets.name)}") into(mergeAssets.outputDir) } val copyFlutterAssetsTask: Task = copyFlutterAssetsTaskProvider.get() if (!isUsedAsSubproject) { - val variantOutput: BaseVariantOutput = variant.outputs.first() + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + val variantOutput: com.android.build.gradle.api.BaseVariantOutput = variant.outputs.first() val processResources = try { variantOutput.processResourcesProvider.get() } catch (e: IllegalStateException) { + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") variantOutput.processResources } processResources.dependsOn(copyFlutterAssetsTask) @@ -724,9 +755,9 @@ class FlutterPlugin : Plugin { // See https://docs.gradle.org/8.1/userguide/validation_problems.html#implicit_dependency. val tasksToCheck = listOf( - "compress${variant.name.capitalize()}Assets", - "bundle${variant.name.capitalize()}Aar", - "bundle${variant.name.capitalize()}LocalLintAar" + "compress${FlutterPluginUtils.capitalize(variant.name)}Assets", + "bundle${FlutterPluginUtils.capitalize(variant.name)}Aar", + "bundle${FlutterPluginUtils.capitalize(variant.name)}LocalLintAar" ) tasksToCheck.forEach { taskTocheck -> try { diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginConstants.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginConstants.kt index 8f8c0bda56..c49cfe356e 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginConstants.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginConstants.kt @@ -7,9 +7,6 @@ package com.flutter.gradle // TODO(gmackall): this should be collapsed back into the core FlutterPlugin once the Groovy to // kotlin conversion is complete. object FlutterPluginConstants { - // Strings that define project properties - const val PROP_PROCESS_RESOURCES_PROVIDER = "processResourcesProvider" - /** The platforms that can be passed to the `--Ptarget-platform` flag. */ private const val PLATFORM_ARM32 = "android-arm" private const val PLATFORM_ARM64 = "android-arm64" @@ -41,7 +38,7 @@ object FlutterPluginConstants { * Otherwise, the Play Store will complain that the APK variants have the same version. */ @JvmStatic val ABI_VERSION = - mapOf( + mapOf( ARCH_ARM32 to 1, ARCH_ARM64 to 2, ARCH_X86 to 3, diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt index fc1fd7a454..2b622591eb 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPluginUtils.kt @@ -6,14 +6,11 @@ package com.flutter.gradle import com.android.build.gradle.AbstractAppExtension import com.android.build.gradle.BaseExtension -import com.android.build.gradle.api.ApplicationVariant -import com.android.build.gradle.api.BaseVariantOutput import com.android.build.gradle.tasks.ProcessAndroidResources import com.android.builder.model.BuildType import com.flutter.gradle.plugins.PluginHandler import groovy.lang.Closure import groovy.util.Node -import groovy.util.XmlParser import org.gradle.api.GradleException import org.gradle.api.JavaVersion import org.gradle.api.Project @@ -62,6 +59,13 @@ object FlutterPluginUtils { @Suppress("DEPRECATION") internal fun capitalize(string: String): String = string.capitalize() + // Kotlin's toLowerCase function is deprecated, but the suggested replacement is not supported + // by the minimum version of Kotlin that we support. Centralize the use to one place, so that + // when our minimum version does support the replacement we can replace by changing a single + // line. + @Suppress("DEPRECATION") + internal fun lowercase(string: String): String = string.toLowerCase() + // compareTo implementation of version strings in the format of ints and periods // Will not crash on RC candidate strings but considers all RC candidates the same version. // Returns -1 if firstString < secondString, 0 if firstString == secondString, 1 if firstString > secondString @@ -513,11 +517,8 @@ object FlutterPluginUtils { getCompileSdkFromProject(project).toIntOrNull() ?: Int.MAX_VALUE var maxPluginCompileSdkVersion = projectCompileSdkVersion - // TODO(gmackall): This should be updated to reflect newer templates. - // The default for AGP 4.1.0 used in old templates. - val ndkVersionIfUnspecified = "21.1.6352462" val projectNdkVersion = - getAndroidExtension(project).ndkVersion ?: ndkVersionIfUnspecified + getAndroidExtension(project).ndkVersion var maxPluginNdkVersion = projectNdkVersion var numProcessedPlugins = pluginList.size val pluginsWithHigherSdkVersion = mutableListOf() @@ -543,14 +544,19 @@ object FlutterPluginUtils { ) } val pluginNdkVersion: String = - getAndroidExtension(pluginProject).ndkVersion ?: ndkVersionIfUnspecified + getAndroidExtension(pluginProject).ndkVersion maxPluginNdkVersion = VersionUtils.mostRecentSemanticVersion( pluginNdkVersion, maxPluginNdkVersion ) if (pluginNdkVersion != projectNdkVersion) { - pluginsWithDifferentNdkVersion.add(PluginVersionPair(pluginName, pluginNdkVersion)) + pluginsWithDifferentNdkVersion.add( + PluginVersionPair( + pluginName, + pluginNdkVersion + ) + ) } numProcessedPlugins-- @@ -614,7 +620,7 @@ object FlutterPluginUtils { // and rebuilt when running clean builds. gradleProjectAndroidExtension.externalNativeBuild.cmake.buildStagingDirectory( gradleProject.layout.buildDirectory - .dir("${FlutterPluginConstants.INTERMEDIATES_DIR}/flutter/.cxx") + .dir("../.cxx") .get() .asFile.path ) @@ -734,7 +740,10 @@ object FlutterPluginUtils { } } - private fun findProcessResources(baseVariantOutput: BaseVariantOutput): ProcessAndroidResources = + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + private fun findProcessResources(baseVariantOutput: com.android.build.gradle.api.BaseVariantOutput): ProcessAndroidResources = baseVariantOutput.processResourcesProvider?.get() ?: baseVariantOutput.processResources /** @@ -769,12 +778,15 @@ object FlutterPluginUtils { } android.applicationVariants.configureEach { val variant = this - project.tasks.register("output${FlutterPluginUtils.capitalize(variant.name)}AppLinkSettings") { + project.tasks.register("output${capitalize(variant.name)}AppLinkSettings") { val task: Task = this task.description = "stores app links settings for the given build variant of this Android project into a json file." variant.outputs.configureEach { - val baseVariantOutput: BaseVariantOutput = this + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") + val baseVariantOutput: com.android.build.gradle.api.BaseVariantOutput = this // Deeplinks are defined in AndroidManifest.xml and is only available after // processResourcesProvider. dependsOn(findProcessResources(baseVariantOutput)) @@ -800,18 +812,21 @@ object FlutterPluginUtils { * @param BaseVariantOutput The output of a specific build variant (e.g., debug, release). * @param variant The application variant being processed. */ + @Suppress("KDocUnresolvedReference") private fun createAppLinkSettings( - variant: ApplicationVariant, - baseVariantOutput: BaseVariantOutput + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") variant: com.android.build.gradle.api.ApplicationVariant, + @Suppress("DEPRECATION") baseVariantOutput: com.android.build.gradle.api.BaseVariantOutput ): AppLinkSettings { val appLinkSettings = AppLinkSettings(variant.applicationId) - // TODO https://github.com/flutter/flutter/issues/165881 - // Use import groovy.xml.XmlParser instead. + // XmlParser is not namespace aware because it makes querying nodes cumbersome. + // TODO(gmackall): Migrate to AGPs variant api. + // https://github.com/flutter/flutter/issues/166550 + @Suppress("DEPRECATION") val manifest: Node = - XmlParser(false, false).parse(findProcessResources(baseVariantOutput).manifestFile) - // The groovy.xml.XmlParser import would use getProperty like - // manifest.getProperty("application").let { applicationNode -> ... + groovy.xml.XmlParser(false, false).parse(findProcessResources(baseVariantOutput).manifestFile) val applicationNode: Node? = manifest.children().find { node -> node is Node && node.name() == "application" diff --git a/packages/flutter_tools/gradle/src/main/kotlin/VersionUtils.kt b/packages/flutter_tools/gradle/src/main/kotlin/VersionUtils.kt index 8f00e7ac8e..96a0923419 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/VersionUtils.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/VersionUtils.kt @@ -26,7 +26,7 @@ object VersionUtils { val v2Parts = version2.split(".", "-") val maxSize = max(v1Parts.size, v2Parts.size) - for (i in 0..maxSize - 1) { + for (i in 0 until maxSize) { val v1Part: String = v1Parts.getOrNull(i) ?: "0" val v2Part: String = v2Parts.getOrNull(i) ?: "0" @@ -42,7 +42,7 @@ object VersionUtils { return version1 // v1 is a number, v2 is not, so v1 is newer. v1Num == null && v2Num != null -> return version2 // v1 is not a number, v2 is, so v2 is newer. - v1Num == null && v2Num == null -> { // Both are not numbers (pre-release identifiers) + else -> { // Both are not numbers (pre-release identifiers) if (v1Part != v2Part) { return if (comparePreReleaseIdentifiers(v1Part, v2Part)) version1 else version2 } diff --git a/packages/flutter_tools/gradle/src/main/kotlin/plugins/PluginHandler.kt b/packages/flutter_tools/gradle/src/main/kotlin/plugins/PluginHandler.kt index b452435200..23cbcc5468 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/plugins/PluginHandler.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/plugins/PluginHandler.kt @@ -94,7 +94,7 @@ class PluginHandler( * This is only possible by taking all plugins into account, which * only appear on the `dependencyGraph` and in the `.flutter-plugins` file. * So in summary the plugins are currently selected from the `dependencyGraph` - * and filtered then with the [doesSupportAndroidPlatform] method instead of + * and filtered then with the [pluginSupportsAndroidPlatform] method instead of * just using the `plugins.android` list. */ private fun configureLegacyPluginEachProjects(engineVersionValue: String) { diff --git a/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTask.kt b/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTask.kt index cb9d45d892..6147809ea6 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTask.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTask.kt @@ -132,7 +132,7 @@ open class BaseFlutterTask : DefaultTask() { var flavor: String? = null /** - * Gets the dependency file(s) by calling [com.flutter.gradle.BaseFlutterTaskHelper.getDependenciesFiles]. + * Gets the dependency file(s) by calling [com.flutter.gradle.tasks.BaseFlutterTaskHelper.getDependenciesFiles]. * * @return the dependency file(s) based on the current intermediate directory path. */ diff --git a/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTaskHelper.kt b/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTaskHelper.kt index bfb0bcc5d3..edbfd888d0 100644 --- a/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTaskHelper.kt +++ b/packages/flutter_tools/gradle/src/main/kotlin/tasks/BaseFlutterTaskHelper.kt @@ -10,6 +10,8 @@ import org.gradle.api.GradleException import org.gradle.api.file.FileCollection import org.gradle.api.logging.LogLevel import org.gradle.api.tasks.OutputFiles +import org.gradle.kotlin.dsl.support.serviceOf +import org.gradle.process.ExecOperations import org.gradle.process.ExecSpec import java.nio.file.Paths @@ -72,6 +74,7 @@ object BaseFlutterTaskHelper { .map { "android_aot_deferred_components_bundle_${baseFlutterTask.buildMode}_$it" } + else -> baseFlutterTask.targetPlatformValues!!.map { "android_aot_bundle_${baseFlutterTask.buildMode}_$it" } } return ruleNames @@ -111,7 +114,17 @@ object BaseFlutterTaskHelper { if (!baseFlutterTask.fastStart!! || baseFlutterTask.buildMode != "debug") { args("-dTargetFile=${baseFlutterTask.targetPath}") } else { - args("-dTargetFile=${Paths.get(baseFlutterTask.flutterRoot!!.absolutePath, "examples", "splash", "lib", "main.dart")}") + args( + "-dTargetFile=${ + Paths.get( + baseFlutterTask.flutterRoot!!.absolutePath, + "examples", + "splash", + "lib", + "main.dart" + ) + }" + ) } args("-dTargetPlatform=android") args("-dBuildMode=${baseFlutterTask.buildMode}") @@ -157,6 +170,7 @@ object BaseFlutterTaskHelper { fun buildBundle(baseFlutterTask: BaseFlutterTask) { checkPreConditions(baseFlutterTask) baseFlutterTask.logging.captureStandardError(LogLevel.ERROR) - baseFlutterTask.project.exec(createExecSpecActionFromTask(baseFlutterTask = baseFlutterTask)) + val execOps = baseFlutterTask.project.serviceOf() + execOps.exec(createExecSpecActionFromTask(baseFlutterTask = baseFlutterTask)) } } diff --git a/packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts b/packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts index eab9482ed4..60753ee2f0 100644 --- a/packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts +++ b/packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts @@ -59,7 +59,10 @@ class NativePluginLoader { // of a federated plugin). val needsBuild = androidPlugin[NATIVE_BUILD_KEY] as? Boolean ?: true if (needsBuild) { - nativePlugins.add(androidPlugin as Map) // Safe cast when adding, assuming type is now validated + // Suppress the unchecked cast warning as we define the structure of the JSON in + // the tool and we have already mostly validated the structure. + @Suppress("UNCHECKED_CAST") + nativePlugins.add(androidPlugin as Map) } } return nativePlugins.toList() // Return immutable list @@ -136,6 +139,9 @@ class NativePluginLoader { if (pluginsDependencyFile.exists()) { val slurper = JsonSlurper() val readText = slurper.parseText(pluginsDependencyFile.readText()) + + // Suppress the unchecked cast warning as we define the structure of the JSON in the tool. + @Suppress("UNCHECKED_CAST") val parsedText = readText as? Map ?: error("Parsed JSON is not a Map: $readText") diff --git a/packages/flutter_tools/gradle/src/test/kotlin/tasks/BaseFlutterTaskHelperTest.kt b/packages/flutter_tools/gradle/src/test/kotlin/tasks/BaseFlutterTaskHelperTest.kt index 92565f11d6..88cca3d0fd 100644 --- a/packages/flutter_tools/gradle/src/test/kotlin/tasks/BaseFlutterTaskHelperTest.kt +++ b/packages/flutter_tools/gradle/src/test/kotlin/tasks/BaseFlutterTaskHelperTest.kt @@ -13,6 +13,8 @@ import org.gradle.api.GradleException import org.gradle.api.Project import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.logging.LoggingManager +import org.gradle.kotlin.dsl.support.serviceOf +import org.gradle.process.ExecOperations import org.gradle.process.ExecSpec import org.gradle.process.ProcessForkOptions import org.junit.jupiter.api.assertDoesNotThrow @@ -544,7 +546,9 @@ class BaseFlutterTaskHelperTest { val baseFlutterTask = mockk() val mockLoggingManager = mockk() val mockFile = mockk() - val mockProject = mockk() + // Mocking the serviceOf() extension below requires us to specify this internal type + // unfortunately. + val mockProject = mockk() // When baseFlutterTask.sourceDir is null, an exception is thrown. We mock its return value // before creating a BaseFlutterTaskHelper object. @@ -554,7 +558,11 @@ class BaseFlutterTaskHelperTest { every { baseFlutterTask.logging } returns mockLoggingManager every { mockLoggingManager.captureStandardError(any()) } returns mockLoggingManager every { baseFlutterTask.project } returns mockProject - every { mockProject.exec(any>()) } returns mockk() + val mockExecOperations = mockk() + every { + mockProject.serviceOf() + } returns mockExecOperations + every { mockExecOperations.exec(any>()) } returns mockk() BaseFlutterTaskHelper.buildBundle(baseFlutterTask) }