[CP-stable][tool] Don't require a Flutter compile task when staging jniLibs (#189075)

This pull request is created by [automatic cherry pick workflow](https://github.com/flutter/flutter/blob/main/docs/releases/Flutter-Cherrypick-Process.md#automatically-creates-a-cherry-pick-request)
Please fill in the form below, and a flutter domain expert will evaluate this cherry pick request.

### Issue Link:
https://github.com/flutter/flutter/issues/189049
https://github.com/flutter/flutter/issues/188785

### Impact Description:
Fixes a compile time crash of [android instrumented tests](https://developer.android.com/training/testing/instrumented-tests) (e.g. `assembleDebugAndroidTest`, which is relied upon by our packages tests and some ecosystem members such as patrol).

### Changelog Description:
Fixes a breakage of [android instrumented tests](https://developer.android.com/training/testing/instrumented-tests) (e.g. `assembleDebugAndroidTest`, which is relied upon by our packages tests and some ecosystem members such as patrol).

< Replace with changelog description here >
[flutter/188805] Fixes a crash when running android instrumented tests.

### Workaround:
No.

### 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:
What are the steps to validate that this fix works?

Explained in detail in https://github.com/flutter/flutter/issues/188785
but in short, go to your flutter/packages checkout, cd to the camera example app and run
```
flutter build apk --debug --config-only
cd android
./gradlew app:assembleAndroidTest -Pverbose=true
```
diff --git a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt
index df6374b..35edcec 100644
--- a/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt
+++ b/packages/flutter_tools/gradle/src/main/kotlin/FlutterPlugin.kt
@@ -313,22 +313,28 @@
         val targetPlatformsList = targetPlatforms
         androidComponents.onVariants { variant ->
             val capitalizeVariantName = FlutterPluginUtils.capitalize(variant.name)
-            // Reference the Flutter compile task by name rather than by provider: this variant API
-            // callback runs before the legacy `applicationVariants` callback in addFlutterDeps that
-            // registers the task, so its provider does not exist yet here.
             val compileTaskName = flutterCompileTaskName(variant.name)
             val copyJniLibsTaskProvider: TaskProvider<CopyFlutterJniLibsTask> =
                 projectToAddTasksTo.tasks.register(
                     "copyJniLibs${FLUTTER_BUILD_PREFIX}$capitalizeVariantName",
                     CopyFlutterJniLibsTask::class.java
                 ) {
-                    dependsOn(compileTaskName)
-                    val compileTaskProvider = projectToAddTasksTo.tasks.named(compileTaskName, FlutterTask::class.java)
-                    val outputDirProvider =
-                        compileTaskProvider.flatMap { task ->
-                            projectToAddTasksTo.layout.dir(projectToAddTasksTo.provider { task.outputDirectory!! })
-                        }
-                    intermediateDir.set(outputDirProvider)
+                    // The Flutter compile task is registered later (in the legacy
+                    // `applicationVariants` callback in addFlutterDeps) and only for variants that
+                    // are actually built as a Flutter app. It is absent for e.g. an
+                    // `assembleAndroidTest` build, where `shouldConfigureFlutterTask` returns false.
+                    // Look it up tolerantly (findByName, not named) so this task degrades to a no-op
+                    // with empty output instead of failing to be created when there is no Flutter
+                    // build for the variant. See https://github.com/flutter/flutter/issues/188785.
+                    dependsOn(projectToAddTasksTo.tasks.matching { it.name == compileTaskName })
+                    intermediateDir.set(
+                        projectToAddTasksTo.layout.dir(
+                            projectToAddTasksTo.provider {
+                                val compileTask = projectToAddTasksTo.tasks.findByName(compileTaskName) as? FlutterTask
+                                compileTask?.outputDirectory
+                            }
+                        )
+                    )
                     this.targetPlatforms.set(targetPlatformsList)
                 }
             variant.sources.jniLibs?.addGeneratedSourceDirectory(
diff --git a/packages/flutter_tools/gradle/src/main/kotlin/tasks/CopyFlutterJniLibsTask.kt b/packages/flutter_tools/gradle/src/main/kotlin/tasks/CopyFlutterJniLibsTask.kt
index 4741fdf..6950e82 100644
--- a/packages/flutter_tools/gradle/src/main/kotlin/tasks/CopyFlutterJniLibsTask.kt
+++ b/packages/flutter_tools/gradle/src/main/kotlin/tasks/CopyFlutterJniLibsTask.kt
@@ -11,6 +11,7 @@
 import org.gradle.api.provider.ListProperty
 import org.gradle.api.tasks.Input
 import org.gradle.api.tasks.InputDirectory
+import org.gradle.api.tasks.Optional
 import org.gradle.api.tasks.OutputDirectory
 import org.gradle.api.tasks.PathSensitive
 import org.gradle.api.tasks.PathSensitivity
@@ -30,7 +31,14 @@
  * https://github.com/flutter/flutter/issues/187388.
  */
 abstract class CopyFlutterJniLibsTask : DefaultTask() {
-    /** The Flutter build output directory (the `flutter assemble` `--output` location). */
+    /**
+     * The Flutter build output directory (the `flutter assemble` `--output` location).
+     *
+     * Optional: it is absent when there is no Flutter compile task for the variant (e.g. an
+     * `assembleAndroidTest` build), in which case this task stages nothing. See
+     * https://github.com/flutter/flutter/issues/188785.
+     */
+    @get:Optional
     @get:InputDirectory
     @get:PathSensitive(PathSensitivity.RELATIVE)
     abstract val intermediateDir: DirectoryProperty
@@ -48,17 +56,21 @@
     fun copy() {
         fileSystemOperations.sync {
             into(destinationDir)
-            targetPlatforms.get().forEach { targetPlatform ->
-                val abi: String? = FlutterPluginConstants.PLATFORM_ARCH_MAP[targetPlatform]
-                from(intermediateDir.dir(abi ?: "null")) {
-                    include("*.so")
-                    rename { filename: String -> "lib$filename" }
-                    into(abi ?: "null")
-                }
-                val nativeAssetsDir = intermediateDir.dir("native_assets/jniLibs/lib/$abi")
-                from(nativeAssetsDir) {
-                    include("*.so")
-                    into(abi ?: "null")
+            // When there is no Flutter build for this variant (e.g. an assembleAndroidTest build),
+            // there is nothing to stage; the empty sync simply clears destinationDir.
+            if (intermediateDir.isPresent) {
+                targetPlatforms.get().forEach { targetPlatform ->
+                    val abi: String? = FlutterPluginConstants.PLATFORM_ARCH_MAP[targetPlatform]
+                    from(intermediateDir.dir(abi ?: "null")) {
+                        include("*.so")
+                        rename { filename: String -> "lib$filename" }
+                        into(abi ?: "null")
+                    }
+                    val nativeAssetsDir = intermediateDir.dir("native_assets/jniLibs/lib/$abi")
+                    from(nativeAssetsDir) {
+                        include("*.so")
+                        into(abi ?: "null")
+                    }
                 }
             }
         }
diff --git a/packages/flutter_tools/test/integration.shard/gradle_libapp_so_packaging_test.dart b/packages/flutter_tools/test/integration.shard/gradle_libapp_so_packaging_test.dart
index f76be44..d8119e7 100644
--- a/packages/flutter_tools/test/integration.shard/gradle_libapp_so_packaging_test.dart
+++ b/packages/flutter_tools/test/integration.shard/gradle_libapp_so_packaging_test.dart
@@ -254,6 +254,45 @@
     expect(libappContent.contains('probe-BBBB'), isTrue);
     expect(libappContent.contains('probe-AAAA'), isFalse);
   });
+
+  // Regression test for https://github.com/flutter/flutter/issues/188785.
+  //
+  // Building the androidTest variant directly (as flutter/packages' Firebase Test
+  // Lab tooling does) configures Gradle without a Flutter compile task: the single
+  // `assembleAndroidTest` CLI task makes `shouldConfigureFlutterTask` return false,
+  // so `compileFlutterBuild<Variant>` is never registered. The jniLibs copy task
+  // wired via the variant API must tolerate that (absent) compile task rather than
+  // failing with "Task with name 'compileFlutterBuildDebug' not found".
+  testWithoutContext(
+    'app:assembleAndroidTest builds when no Flutter compile task is configured',
+    () async {
+      final Directory appDir = _createApp(tempDir);
+
+      // Configure Gradle without running the Flutter build (no `flutter assemble`).
+      final ProcessResult configResult = processManager.runSync(<String>[
+        flutterBin,
+        'build',
+        'apk',
+        '--debug',
+        '--config-only',
+      ], workingDirectory: appDir.path);
+      expect(configResult, const ProcessResultMatcher());
+
+      final Directory androidDir = appDir.childDirectory('android');
+      final File gradlew = androidDir.childFile(Platform.isWindows ? 'gradlew.bat' : 'gradlew');
+      final ProcessResult assembleResult = processManager.runSync(<String>[
+        gradlew.path,
+        'app:assembleAndroidTest',
+        '-Pverbose=true',
+      ], workingDirectory: androidDir.path);
+      expect(
+        assembleResult.exitCode,
+        0,
+        reason:
+            'gradlew app:assembleAndroidTest failed:\n${assembleResult.stdout}\n${assembleResult.stderr}',
+      );
+    },
+  );
 }
 
 Directory _createApp(Directory workingDir) {