forked from firka/flutter
Convert AppPluginLoaderPlugin to Kotlin, and add NativePluginLoaderReflectionBridge to expose it in Kotlin (#166027)
Graph [stolen from Barteks comment](https://github.com/flutter/flutter/pull/161352#issuecomment-2611252732) documenting the existing (pre pr) state: ```mermaid graph TD; flutter.groovy -- import --> native_plugin_loader.groovy; flutter.groovy -- import --> BaseApplicationNameHandler.kt; module_plugin_loader.groovy -- "ext" --> native_plugin_loader.groovy; app_plugin_loader.groovy -- import --> native_plugin_loader.groovy; include_flutter.groovy -- "apply from: " --> module_plugin_loader.groovy; ``` 1. Converts the `app_plugin_loader.groovy` to kotlin source. 2. Converts the `module_plugin_loader.groovy` to kotlin script. This can't be changed to kotlin source yet, as we will need to instruct users to make a change to their host app-level gradle files before we can turn down script application of this separate gradle plugin. This is a breaking change, and will need a quarter at least of notice. 3. Unfortunately, the main Flutter Gradle plugin depends on being able to call methods of the `native_plugin_loader`, which we could do in groovy via wacky dynamic behavior, calling across the compiled plugin->script plugin barrier. We can't do this in Kotlin source, and we also can't fully convert `native_plugin_loader` to kotlin source yet because of (2), so I've added a `NativePluginLoaderReflectionBridge` that allows us to access the methods in the `native_plugin_loader.gradle.kts` from the Kotlin source files, calling across the compiled plugin->script plugin barrier as we were before. The plan here is 1. to follow up by adding a converted `native_plugin_loader.gradle.kts` in Kotlin source, and migrating all paths but the host-app using a module-as-source to use the converted approach (but not deleting the old way) 2. maintaining both ways for a release or two, with the script application printing a message notifying users to update to the non-script based application of the `module_plugin_loader`. 3. Then we can delete the script based apply, and also the reflection bridge. ## 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 `///`). - [ ] 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]. <!-- Links --> [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 <mackall@google.com>
This commit is contained in:
@@ -39,7 +39,7 @@ gradlePlugin {
|
||||
// The "flutterAppPluginLoaderPlugin" name isn't used anywhere.
|
||||
create("flutterAppPluginLoaderPlugin") {
|
||||
id = "dev.flutter.flutter-plugin-loader"
|
||||
implementationClass = "FlutterAppPluginLoaderPlugin"
|
||||
implementationClass = "com.flutter.gradle.FlutterAppPluginLoaderPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
import java.nio.file.Paths
|
||||
|
||||
File pathToThisDirectory = buildscript.sourceFile.parentFile
|
||||
apply from: Paths.get(pathToThisDirectory.absolutePath, "src", "main", "groovy", "native_plugin_loader.groovy")
|
||||
apply from: Paths.get(pathToThisDirectory.absolutePath, "src", "main", "scripts", "native_plugin_loader.gradle.kts")
|
||||
|
||||
def moduleProjectRoot = project(':flutter').projectDir.parentFile.parentFile
|
||||
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.initialization.Settings
|
||||
|
||||
import java.nio.file.Paths
|
||||
|
||||
apply plugin: FlutterAppPluginLoaderPlugin
|
||||
|
||||
class FlutterAppPluginLoaderPlugin implements Plugin<Settings> {
|
||||
@Override
|
||||
void apply(Settings settings) {
|
||||
def flutterProjectRoot = settings.settingsDir.parentFile
|
||||
|
||||
if(!settings.ext.hasProperty('flutterSdkPath')) {
|
||||
def properties = new Properties()
|
||||
def localPropertiesFile = new File(settings.rootProject.projectDir, "local.properties")
|
||||
localPropertiesFile.withInputStream { properties.load(it) }
|
||||
settings.ext.flutterSdkPath = properties.getProperty("flutter.sdk")
|
||||
assert settings.ext.flutterSdkPath != null, "flutter.sdk not set in local.properties"
|
||||
}
|
||||
|
||||
// Load shared gradle functions
|
||||
settings.apply from: Paths.get(settings.ext.flutterSdkPath, "packages", "flutter_tools", "gradle", "src", "main", "groovy", "native_plugin_loader.groovy")
|
||||
|
||||
List<Map<String, Object>> nativePlugins = settings.ext.nativePluginLoader.getPlugins(flutterProjectRoot)
|
||||
nativePlugins.each { androidPlugin ->
|
||||
def pluginDirectory = new File(androidPlugin.path as String, 'android')
|
||||
assert pluginDirectory.exists()
|
||||
settings.include(":${androidPlugin.name}")
|
||||
settings.project(":${androidPlugin.name}").projectDir = pluginDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.flutter.gradle.FlutterExtension
|
||||
import com.flutter.gradle.FlutterPluginConstants
|
||||
import com.flutter.gradle.FlutterTask
|
||||
import com.flutter.gradle.FlutterPluginUtils
|
||||
import com.flutter.gradle.NativePluginLoaderReflectionBridge
|
||||
import org.gradle.api.file.Directory
|
||||
|
||||
import java.nio.file.Paths
|
||||
@@ -114,7 +115,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
}
|
||||
|
||||
// Load shared gradle functions
|
||||
project.apply from: Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "src", "main", "groovy", "native_plugin_loader.groovy")
|
||||
project.apply from: Paths.get(flutterRoot.absolutePath, "packages", "flutter_tools", "gradle", "src", "main", "scripts", "native_plugin_loader.gradle.kts")
|
||||
|
||||
FlutterExtension extension = project.extensions.create("flutter", FlutterExtension)
|
||||
Properties localProperties = new Properties()
|
||||
@@ -298,7 +299,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
* and filtered then with the [doesSupportAndroidPlatform] method instead of
|
||||
* just using the `plugins.android` list.
|
||||
*/
|
||||
private void configureLegacyPluginEachProjects(Project project) {
|
||||
static private void configureLegacyPluginEachProjects(Project project) {
|
||||
try {
|
||||
// Read the contents of the settings.gradle file.
|
||||
// Remove block/line comments
|
||||
@@ -340,11 +341,11 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
*
|
||||
* The map value contains either the plugins `name` (String),
|
||||
* its `path` (String), or its `dependencies` (List<String>).
|
||||
* See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/groovy/native_plugin_loader.groovy
|
||||
* See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts
|
||||
*/
|
||||
private List<Map<String, Object>> getPluginList(Project project) {
|
||||
if (pluginList == null) {
|
||||
pluginList = project.ext.nativePluginLoader.getPlugins(FlutterPluginUtils.getFlutterSourceDirectory(project))
|
||||
pluginList = NativePluginLoaderReflectionBridge.getPlugins(project.ext, FlutterPluginUtils.getFlutterSourceDirectory(project))
|
||||
}
|
||||
return pluginList
|
||||
}
|
||||
@@ -354,7 +355,7 @@ class FlutterPlugin implements Plugin<Project> {
|
||||
/** Gets the plugins dependencies from `.flutter-plugins-dependencies`. */
|
||||
private List<Map<String, Object>> getPluginDependencies(Project project) {
|
||||
if (pluginDependencies == null) {
|
||||
Map meta = project.ext.nativePluginLoader.getDependenciesMetadata(FlutterPluginUtils.getFlutterSourceDirectory(project))
|
||||
Map meta = NativePluginLoaderReflectionBridge.getDependenciesMetadata(project.ext, FlutterPluginUtils.getFlutterSourceDirectory(project))
|
||||
if (meta == null) {
|
||||
pluginDependencies = []
|
||||
} else {
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
class NativePluginLoader {
|
||||
|
||||
// This string must match _kFlutterPluginsHasNativeBuildKey defined in
|
||||
// packages/flutter_tools/lib/src/flutter_plugins.dart.
|
||||
static final String nativeBuildKey = "native_build"
|
||||
static final String flutterPluginsDependenciesFile = ".flutter-plugins-dependencies"
|
||||
|
||||
/**
|
||||
* Gets the list of plugins that support the Android platform.
|
||||
* The list contains map elements with the following content:
|
||||
* {
|
||||
* "name": "plugin-a",
|
||||
* "path": "/path/to/plugin-a",
|
||||
* "dependencies": ["plugin-b", "plugin-c"],
|
||||
* "native_build": true
|
||||
* "dev_dependency": false
|
||||
* }
|
||||
*
|
||||
* Therefore the map value can either be a `String`, a `List<String>` or a `Boolean`.
|
||||
*/
|
||||
List<Map<String, Object>> getPlugins(File flutterSourceDirectory) {
|
||||
List<Map<String, Object>> nativePlugins = []
|
||||
def meta = getDependenciesMetadata(flutterSourceDirectory)
|
||||
if (meta == null) {
|
||||
return nativePlugins
|
||||
}
|
||||
|
||||
assert(meta.plugins instanceof Map<String, Object>)
|
||||
def androidPlugins = meta.plugins.android
|
||||
assert(androidPlugins instanceof List<Map>)
|
||||
// Includes the Flutter plugins that support the Android platform.
|
||||
androidPlugins.each { Map<String, Object> androidPlugin ->
|
||||
// The property types can be found in _filterPluginsByPlatform defined in
|
||||
// packages/flutter_tools/lib/src/flutter_plugins.dart.
|
||||
assert(androidPlugin.name instanceof String)
|
||||
assert(androidPlugin.path instanceof String)
|
||||
assert(androidPlugin.dependencies instanceof List<String>)
|
||||
assert(androidPlugin.dev_dependency instanceof Boolean)
|
||||
// Skip plugins that have no native build (such as a Dart-only implementation
|
||||
// of a federated plugin).
|
||||
def needsBuild = androidPlugin.containsKey(nativeBuildKey) ? androidPlugin[nativeBuildKey] : true
|
||||
if (needsBuild) {
|
||||
nativePlugins.add(androidPlugin)
|
||||
}
|
||||
}
|
||||
return nativePlugins
|
||||
}
|
||||
|
||||
|
||||
private Map<String, Object> parsedFlutterPluginsDependencies
|
||||
|
||||
/**
|
||||
* Parses <project-src>/.flutter-plugins-dependencies
|
||||
*/
|
||||
Map<String, Object> getDependenciesMetadata(File flutterSourceDirectory) {
|
||||
// Consider a `.flutter-plugins-dependencies` file with the following content:
|
||||
// {
|
||||
// "plugins": {
|
||||
// "android": [
|
||||
// {
|
||||
// "name": "plugin-a",
|
||||
// "path": "/path/to/plugin-a",
|
||||
// "dependencies": ["plugin-b", "plugin-c"],
|
||||
// "native_build": true
|
||||
// "dev_dependency": false
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-b",
|
||||
// "path": "/path/to/plugin-b",
|
||||
// "dependencies": ["plugin-c"],
|
||||
// "native_build": true
|
||||
// "dev_dependency": false
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-c",
|
||||
// "path": "/path/to/plugin-c",
|
||||
// "dependencies": [],
|
||||
// "native_build": true
|
||||
// "dev_dependency": false
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-d",
|
||||
// "path": "/path/to/plugin-d",
|
||||
// "dependencies": [],
|
||||
// "native_build": true
|
||||
// "dev_dependency": true
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// "dependencyGraph": [
|
||||
// {
|
||||
// "name": "plugin-a",
|
||||
// "dependencies": ["plugin-b","plugin-c"]
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-b",
|
||||
// "dependencies": ["plugin-c"]
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-c",
|
||||
// "dependencies": []
|
||||
// },
|
||||
// {
|
||||
// "name": "plugin-d",
|
||||
// "dependencies": []
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// This means, `plugin-a` depends on `plugin-b` and `plugin-c`.
|
||||
// `plugin-b` depends on `plugin-c`.
|
||||
// `plugin-c` doesn't depend on anything.
|
||||
// `plugin-d` also doesn't depend on anything, but it is a dev
|
||||
// dependency to the Flutter project, so it is marked as such.
|
||||
if (parsedFlutterPluginsDependencies) {
|
||||
return parsedFlutterPluginsDependencies
|
||||
}
|
||||
File pluginsDependencyFile = new File(flutterSourceDirectory, flutterPluginsDependenciesFile)
|
||||
if (pluginsDependencyFile.exists()) {
|
||||
def object = new JsonSlurper().parseText(pluginsDependencyFile.text)
|
||||
assert(object instanceof Map<String, Object>)
|
||||
parsedFlutterPluginsDependencies = object
|
||||
return object
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(135392): Remove and use declarative form when migrated
|
||||
ext {
|
||||
nativePluginLoader = new NativePluginLoader()
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import com.android.build.api.dsl.ApplicationExtension
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.initialization.Settings
|
||||
import org.jetbrains.kotlin.gradle.plugin.extraProperties
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import java.util.Properties
|
||||
|
||||
private const val FLUTTER_SDK_PATH = "flutterSdkPath"
|
||||
|
||||
// Integration tests that cover this class include
|
||||
// - packages/flutter_tools/test/integration.shard/android_gradle_daemon_cache_test.dart
|
||||
// - packages/flutter_tools/test/integration.shard/android_plugin_compilesdkversion_mismatch_test.dart
|
||||
// And can be run by following the README in packages/flutter_tools/.
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
class FlutterAppPluginLoaderPlugin : Plugin<Settings> {
|
||||
override fun apply(settings: Settings) {
|
||||
val flutterProjectRoot: File = settings.settingsDir.parentFile
|
||||
|
||||
if (!settings.extraProperties.has(FLUTTER_SDK_PATH)) {
|
||||
val properties = Properties()
|
||||
val localPropertiesFile = File(settings.rootProject.projectDir, "local.properties")
|
||||
localPropertiesFile.inputStream().use { properties.load(it) }
|
||||
settings.extraProperties.set(FLUTTER_SDK_PATH, properties.getProperty("flutter.sdk"))
|
||||
assert(
|
||||
settings.extraProperties.get(FLUTTER_SDK_PATH) != null
|
||||
) { "flutter.sdk not set in local.properties" }
|
||||
}
|
||||
|
||||
settings.apply {
|
||||
from(
|
||||
Paths.get(
|
||||
settings.extraProperties.get(FLUTTER_SDK_PATH) as String,
|
||||
"packages",
|
||||
"flutter_tools",
|
||||
"gradle",
|
||||
"src",
|
||||
"main",
|
||||
"scripts",
|
||||
"native_plugin_loader.gradle.kts"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
NativePluginLoaderReflectionBridge
|
||||
.getPlugins(settings.extraProperties, flutterProjectRoot)
|
||||
.forEach { androidPlugin ->
|
||||
val pluginDirectory = File(androidPlugin["path"] as String, "android")
|
||||
check(
|
||||
pluginDirectory.exists()
|
||||
) { "Plugin directory does not exist: ${pluginDirectory.absolutePath}" }
|
||||
val pluginName = androidPlugin["name"] as String
|
||||
settings.include(":$pluginName")
|
||||
settings.project(":$pluginName").projectDir = pluginDirectory
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import org.gradle.api.GradleException
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
// TODO(gmackall): this should be collapsed back into the core FlutterPlugin once the Groovy to
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import com.android.build.gradle.AbstractAppExtension
|
||||
@@ -704,7 +708,7 @@ object FlutterPluginUtils {
|
||||
*
|
||||
* The map value contains either the plugins `name` (String),
|
||||
* its `path` (String), or its `dependencies` (List<String>).
|
||||
* See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/groovy/native_plugin_loader.groovy
|
||||
* See [NativePluginLoader#getPlugins] in packages/flutter_tools/gradle/src/main/scripts/native_plugin_loader.gradle.kts
|
||||
*/
|
||||
private fun getPluginListWithoutDevDependencies(pluginList: List<Map<String?, Any?>>): List<Map<String?, Any?>> =
|
||||
pluginList.filter { pluginObject -> pluginObject["dev_dependency"] == false }
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import org.gradle.api.file.CopySpec
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package com.flutter.gradle
|
||||
|
||||
import org.gradle.api.plugins.ExtraPropertiesExtension
|
||||
import java.io.File
|
||||
|
||||
// TODO(gmackall): Remove reflection after migrating to plugin style application in
|
||||
// https://github.com/flutter/flutter/issues/166461.
|
||||
// New methods should not be added.
|
||||
|
||||
/**
|
||||
* Class to hide from Kotlin source the dangerous reflection being used to call methods defined
|
||||
* in script gradle plugins.
|
||||
*/
|
||||
|
||||
object NativePluginLoaderReflectionBridge {
|
||||
private var nativePluginLoader: Any? = null
|
||||
|
||||
/**
|
||||
* An abstraction to hide reflection from calling sites. See ../scripts/native_plugin_loader.gradle.kts.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getPlugins(
|
||||
extraProperties: ExtraPropertiesExtension,
|
||||
flutterProjectRoot: File
|
||||
): List<Map<String, Any>> {
|
||||
nativePluginLoader = extraProperties.get("nativePluginLoader")!!
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val pluginList: List<Map<String, Any>> =
|
||||
nativePluginLoader!!::class
|
||||
.members
|
||||
.firstOrNull { it.name == "getPlugins" }
|
||||
?.call(nativePluginLoader, flutterProjectRoot) as List<Map<String, Any>>
|
||||
|
||||
return pluginList
|
||||
}
|
||||
|
||||
/**
|
||||
* An abstraction to hide reflection from calling sites. See ../scripts/native_plugin_loader.gradle.kts.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun getDependenciesMetadata(
|
||||
extraProperties: ExtraPropertiesExtension,
|
||||
flutterProjectRoot: File
|
||||
): Map<String, Any> {
|
||||
nativePluginLoader = extraProperties.get("nativePluginLoader")!!
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val dependenciesMetadata: Map<String, Any> =
|
||||
nativePluginLoader!!::class
|
||||
.members
|
||||
.firstOrNull { it.name == "dependenciesMetadata" }
|
||||
?.call(nativePluginLoader, flutterProjectRoot) as Map<String, Any>
|
||||
|
||||
return dependenciesMetadata
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import groovy.json.JsonSlurper
|
||||
import java.io.File
|
||||
|
||||
// When changing the names of either
|
||||
// 1. this file or
|
||||
// 2. the names of the methods on this class
|
||||
// be sure to also modify the corresponding values in ../kotlin/NativePluginLoaderReflectionBridge.kt
|
||||
class NativePluginLoader {
|
||||
companion object {
|
||||
// This string must match _kFlutterPluginsHasNativeBuildKey defined in
|
||||
// packages/flutter_tools/lib/src/flutter_plugins.dart.
|
||||
const val NATIVE_BUILD_KEY = "native_build"
|
||||
const val FLUTTER_PLUGINS_DEPENDENCIES_FILE = ".flutter-plugins-dependencies"
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the list of plugins that support the Android platform.
|
||||
* The list contains map elements with the following content:
|
||||
* {
|
||||
* "name": "plugin-a",
|
||||
* "path": "/path/to/plugin-a",
|
||||
* "dependencies": ["plugin-b", "plugin-c"],
|
||||
* "native_build": true
|
||||
* "dev_dependency": false
|
||||
* }
|
||||
*
|
||||
* Therefore the map value can either be a `String`, a `List<String>` or a `Boolean`.
|
||||
*/
|
||||
fun getPlugins(flutterSourceDirectory: File): List<Map<String, Any>> {
|
||||
val nativePlugins = mutableListOf<Map<String, Any>>()
|
||||
val meta = getDependenciesMetadata(flutterSourceDirectory)
|
||||
if (meta == null) {
|
||||
return nativePlugins
|
||||
}
|
||||
|
||||
val pluginsMap: Map<*, *> = (meta["plugins"] as? Map<*, *>) ?: error("Metadata 'plugins' is not a Map: $meta")
|
||||
val androidPluginsUntyped = pluginsMap["android"]
|
||||
if (androidPluginsUntyped == null) {
|
||||
return nativePlugins // Return empty list if android plugins are not found
|
||||
}
|
||||
val androidPlugins = androidPluginsUntyped as? List<*> ?: error("Metadata 'plugins.android' is not a List: $meta")
|
||||
|
||||
// Includes the Flutter plugins that support the Android platform.
|
||||
androidPlugins.forEach { androidPluginUntyped ->
|
||||
val androidPlugin = androidPluginUntyped as? Map<*, *> ?: error("androidPlugin is not a Map: $androidPluginUntyped")
|
||||
|
||||
// The property types can be found in _filterPluginsByPlatform defined in
|
||||
// packages/flutter_tools/lib/src/flutter_plugins.dart.
|
||||
check(androidPlugin["name"] is String) { "androidPlugin 'name' is not a String: $androidPlugin" }
|
||||
check(androidPlugin["path"] is String) { "androidPlugin 'path' is not a String: $androidPlugin" }
|
||||
check(androidPlugin["dependencies"] is List<*>) { "androidPlugin 'dependencies' is not a List: $androidPlugin" }
|
||||
check(androidPlugin["dev_dependency"] is Boolean) { "androidPlugin 'dev_dependency' is not a Boolean: $androidPlugin" }
|
||||
|
||||
// Skip plugins that have no native build (such as a Dart-only implementation
|
||||
// of a federated plugin).
|
||||
val needsBuild = androidPlugin[NATIVE_BUILD_KEY] as? Boolean ?: true
|
||||
if (needsBuild) {
|
||||
nativePlugins.add(androidPlugin as Map<String, Any>) // Safe cast when adding, assuming type is now validated
|
||||
}
|
||||
}
|
||||
return nativePlugins.toList() // Return immutable list
|
||||
}
|
||||
|
||||
private var parsedFlutterPluginsDependencies: Map<String, Any>? = null
|
||||
|
||||
/**
|
||||
* Parses <project-src>/.flutter-plugins-dependencies
|
||||
*/
|
||||
fun getDependenciesMetadata(flutterSourceDirectory: File): Map<String, Any>? {
|
||||
// Consider a `.flutter-plugins-dependencies` file with the following content:
|
||||
// { ... (example content as in the original Groovy code) ... }
|
||||
// This means, `plugin-a` depends on `plugin-b` and `plugin-c`.
|
||||
// ... (rest of the comment as in the original Groovy code) ...
|
||||
if (parsedFlutterPluginsDependencies != null) {
|
||||
return parsedFlutterPluginsDependencies
|
||||
}
|
||||
val pluginsDependencyFile = File(flutterSourceDirectory, FLUTTER_PLUGINS_DEPENDENCIES_FILE)
|
||||
if (pluginsDependencyFile.exists()) {
|
||||
val slurper = JsonSlurper()
|
||||
val readText = slurper.parseText(pluginsDependencyFile.readText())
|
||||
val parsedText =
|
||||
readText as? Map<String, Any>
|
||||
?: error("Parsed JSON is not a Map<String, Any>: $readText")
|
||||
parsedFlutterPluginsDependencies = parsedText
|
||||
return parsedText
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
extra["nativePluginLoader"] = NativePluginLoader()
|
||||
Reference in New Issue
Block a user