@@ -10,7 +10,6 @@ import com.android.build.gradle.api.ApplicationVariant
10
10
import com.android.build.gradle.api.BaseVariantOutput
11
11
import com.android.build.gradle.tasks.ProcessAndroidResources
12
12
import com.android.builder.model.BuildType
13
- import com.flutter.gradle.plugins.PluginHandler
14
13
import groovy.lang.Closure
15
14
import groovy.util.Node
16
15
import groovy.util.XmlParser
@@ -140,6 +139,19 @@ object FlutterPluginUtils {
140
139
141
140
// TODO(54566): Can remove this function and its call sites once resolved.
142
141
142
+ /* *
143
+ * Returns `true` if the given project is a plugin project having an `android` directory
144
+ * containing a `build.gradle` or `build.gradle.kts` file.
145
+ */
146
+ @JvmStatic
147
+ @JvmName(" pluginSupportsAndroidPlatform" )
148
+ internal fun pluginSupportsAndroidPlatform (project : Project ): Boolean {
149
+ val buildGradle = File (File (project.projectDir.parentFile, " android" ), " build.gradle" )
150
+ val buildGradleKts =
151
+ File (File (project.projectDir.parentFile, " android" ), " build.gradle.kts" )
152
+ return buildGradle.exists() || buildGradleKts.exists()
153
+ }
154
+
143
155
/* *
144
156
* Returns the Gradle settings script for the build. When both Groovy and
145
157
* Kotlin variants exist, then Groovy (settings.gradle) is preferred over
@@ -394,7 +406,7 @@ object FlutterPluginUtils {
394
406
return project.property(PROP_LOCAL_ENGINE_BUILD_MODE ) == flutterBuildMode
395
407
}
396
408
397
- internal fun getAndroidExtension (project : Project ): BaseExtension {
409
+ private fun getAndroidExtension (project : Project ): BaseExtension {
398
410
// Common supertype of the android extension types.
399
411
// But maybe this should be https://developer.android.com/reference/tools/gradle-api/8.7/com/android/build/api/dsl/TestedExtension.
400
412
return project.extensions.findByType(BaseExtension ::class .java)!!
@@ -599,7 +611,7 @@ object FlutterPluginUtils {
599
611
600
612
// Otherwise, point to an empty CMakeLists.txt, and ignore associated warnings.
601
613
gradleProjectAndroidExtension.externalNativeBuild.cmake.path(
602
- " $flutterSdkRootPath /packages/flutter_tools/gradle/src/main/scripts /CMakeLists.txt"
614
+ " $flutterSdkRootPath /packages/flutter_tools/gradle/src/main/groovy /CMakeLists.txt"
603
615
)
604
616
605
617
// AGP defaults to outputting build artifacts in `android/app/.cxx`. This directory is a
@@ -644,7 +656,7 @@ object FlutterPluginUtils {
644
656
internal fun addFlutterDependencies (
645
657
project : Project ,
646
658
buildType : BuildType ,
647
- pluginHandler : PluginHandler ,
659
+ pluginList : List < Map < String ?, Any ?>> ,
648
660
engineVersion : String
649
661
) {
650
662
val flutterBuildMode: String = buildModeFor(buildType)
@@ -664,9 +676,11 @@ object FlutterPluginUtils {
664
676
// embedding.
665
677
val pluginsThatIncludeFlutterEmbeddingAsTransitiveDependency: List <Map <String ?, Any ?>> =
666
678
if (flutterBuildMode == " release" ) {
667
- pluginHandler.getPluginListWithoutDevDependencies()
679
+ getPluginListWithoutDevDependencies(
680
+ pluginList
681
+ )
668
682
} else {
669
- pluginHandler.getPluginList()
683
+ pluginList
670
684
}
671
685
672
686
if (! isFlutterAppProject(project) || pluginsThatIncludeFlutterEmbeddingAsTransitiveDependency.isEmpty()) {
@@ -688,6 +702,143 @@ object FlutterPluginUtils {
688
702
}
689
703
}
690
704
705
+ /* *
706
+ * Gets the list of plugins (as map) that support the Android platform and are dependencies of the
707
+ * Android project excluding dev dependencies.
708
+ *
709
+ * The map value contains either the plugins `name` (String),
710
+ * its `path` (String), or its `dependencies` (List<String>).
711
+ * See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts
712
+ */
713
+ private fun getPluginListWithoutDevDependencies (pluginList : List <Map <String ?, Any ?>>): List <Map <String ?, Any ?>> =
714
+ pluginList.filter { pluginObject -> pluginObject[" dev_dependency" ] == false }
715
+
716
+ /* *
717
+ * Add the dependencies on other plugin projects to the plugin project.
718
+ * A plugin A can depend on plugin B. As a result, this dependency must be surfaced by
719
+ * making the Gradle plugin project A depend on the Gradle plugin project B.
720
+ */
721
+ @JvmStatic
722
+ @JvmName(" configurePluginDependencies" )
723
+ internal fun configurePluginDependencies (
724
+ project : Project ,
725
+ pluginObject : Map <String ?, Any ?>
726
+ ) {
727
+ val pluginName: String =
728
+ requireNotNull(pluginObject[" name" ] as ? String ) {
729
+ " Missing valid \" name\" property for plugin object: $pluginObject "
730
+ }
731
+ val pluginProject: Project = project.rootProject.findProject(" :$pluginName " ) ? : return
732
+
733
+ getAndroidExtension(project).buildTypes.forEach { buildType ->
734
+ val flutterBuildMode: String = buildModeFor(buildType)
735
+ if (flutterBuildMode == " release" && (pluginObject[" dev_dependency" ] as ? Boolean == true )) {
736
+ // This plugin is a dev dependency will not be included in the
737
+ // release build, so no need to add its dependencies.
738
+ return @forEach
739
+ }
740
+ val dependencies = requireNotNull(pluginObject[" dependencies" ] as ? List <* >)
741
+ dependencies.forEach innerForEach@{ pluginDependencyName ->
742
+ check(pluginDependencyName is String )
743
+ if (pluginDependencyName.isEmpty()) {
744
+ return @innerForEach
745
+ }
746
+
747
+ val dependencyProject =
748
+ project.rootProject.findProject(" :$pluginDependencyName " ) ? : return @innerForEach
749
+ pluginProject.afterEvaluate {
750
+ pluginProject.dependencies.add(" implementation" , dependencyProject)
751
+ }
752
+ }
753
+ }
754
+ }
755
+
756
+ /* *
757
+ * Performs configuration related to the plugin's Gradle [Project], including
758
+ * 1. Adding the plugin itself as a dependency to the main project.
759
+ * 2. Adding the main project's build types to the plugin's build types.
760
+ * 3. Adding a dependency on the Flutter embedding to the plugin.
761
+ *
762
+ * Should only be called on plugins that support the Android platform.
763
+ */
764
+ @JvmStatic
765
+ @JvmName(" configurePluginProject" )
766
+ internal fun configurePluginProject (
767
+ project : Project ,
768
+ pluginObject : Map <String ?, Any ?>,
769
+ engineVersion : String
770
+ ) {
771
+ // TODO(gmackall): should guard this with a pluginObject.contains().
772
+ val pluginName =
773
+ requireNotNull(pluginObject[" name" ] as ? String ) { " Plugin name must be a string for plugin object: $pluginObject " }
774
+ val pluginProject: Project = project.rootProject.findProject(" :$pluginName " ) ? : return
775
+
776
+ // Apply the "flutter" Gradle extension to plugins so that they can use it's vended
777
+ // compile/target/min sdk values.
778
+ pluginProject.extensions.create(" flutter" , FlutterExtension ::class .java)
779
+
780
+ // Add plugin dependency to the app project. We only want to add dependency
781
+ // for dev dependencies in non-release builds.
782
+ project.afterEvaluate {
783
+ getAndroidExtension(project).buildTypes.forEach { buildType ->
784
+ if (! (pluginObject[" dev_dependency" ] as Boolean ) || buildType.name != " release" ) {
785
+ project.dependencies.add(" ${buildType.name} Api" , pluginProject)
786
+ }
787
+ }
788
+ }
789
+
790
+ // Wait until the Android plugin loaded.
791
+ pluginProject.afterEvaluate {
792
+ // Checks if there is a mismatch between the plugin compileSdkVersion and the project compileSdkVersion.
793
+ val projectCompileSdkVersion: String = getCompileSdkFromProject(project)
794
+ val pluginCompileSdkVersion: String = getCompileSdkFromProject(pluginProject)
795
+ // TODO(gmackall): This is doing a string comparison, which is odd and also can be wrong
796
+ // when comparing preview versions (against non preview, and also in the
797
+ // case of alphabet reset which happened with "Baklava".
798
+ if (pluginCompileSdkVersion > projectCompileSdkVersion) {
799
+ project.logger.quiet(" Warning: The plugin $pluginName requires Android SDK version $pluginCompileSdkVersion or higher." )
800
+ project.logger.quiet(
801
+ " For more information about build configuration, see ${FlutterPluginConstants .WEBSITE_DEPLOYMENT_ANDROID_BUILD_CONFIG } ."
802
+ )
803
+ }
804
+
805
+ getAndroidExtension(project).buildTypes.forEach { buildType ->
806
+ addEmbeddingDependencyToPlugin(project, pluginProject, buildType, engineVersion)
807
+ }
808
+ }
809
+ }
810
+
811
+ private fun addEmbeddingDependencyToPlugin (
812
+ project : Project ,
813
+ pluginProject : Project ,
814
+ buildType : BuildType ,
815
+ engineVersion : String
816
+ ) {
817
+ val flutterBuildMode: String = buildModeFor(buildType)
818
+ // TODO(gmackall): this should be safe to remove, as the minimum required AGP is well above
819
+ // 3.5. We should try to remove it.
820
+ // In AGP 3.5, the embedding must be added as an API implementation,
821
+ // so java8 features are desugared against the runtime classpath.
822
+ // For more, see https://github.com/flutter/flutter/issues/40126
823
+ if (! supportsBuildMode(pluginProject, flutterBuildMode)) {
824
+ return
825
+ }
826
+ if (! pluginProject.hasProperty(" android" )) {
827
+ return
828
+ }
829
+
830
+ // Copy build types from the app to the plugin.
831
+ // This allows to build apps with plugins and custom build types or flavors.
832
+ getAndroidExtension(pluginProject).buildTypes.addAll(getAndroidExtension(project).buildTypes)
833
+
834
+ // The embedding is API dependency of the plugin, so the AGP is able to desugar
835
+ // default method implementations when the interface is implemented by a plugin.
836
+ //
837
+ // See https://issuetracker.google.com/139821726, and
838
+ // https://github.com/flutter/flutter/issues/72185 for more details.
839
+ addApiDependencies(pluginProject, buildType.name, " io.flutter:flutter_embedding_$flutterBuildMode :$engineVersion " )
840
+ }
841
+
691
842
// ------------------ Task adders (a subset of the above category)
692
843
693
844
// Add a task that can be called on flutter projects that prints the Java version used in Gradle.
0 commit comments