提问人:Bartek Pacia 提问时间:4/2/2023 最后编辑:Bartek Pacia 更新时间:4/5/2023 访问量:401
如何从 Gradle 的“pluginManagement {}”块调用函数?
How can I call a function from Gradle's `pluginManagement {}` block?
问:
介绍
嗨,我正在尝试对在我的应用程序中找到的代码进行重复数据删除。这是我的文件目前的样子:settings.gradle
pluginManagement {
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ':app'
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
(你可以忽略特定于技术的事情,以及为什么它必须这样做——让我们假设它必须这样做。如果你对更多细节感兴趣,请看这个 Flutter 的 PR)
很容易看出,负责从文件加载属性的代码重复了两次。因此,我开始删除这种重复。flutter.sdk
local.properties
尝试 1(可变)
起初,我尝试了以下代码,但很快被 Gradle 提醒“pluginManagement {} 块必须出现在脚本中的任何其他语句之前”。
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
pluginManagement {
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ':app'
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
尝试 2(功能)
上一次尝试中的错误消息指出,在块之前不允许任何语句。我考虑过将代码移动到一个单独的函数中,因为根据我的(适度)理解,函数不是 Groovy 中的语句:pluginManagement {}
String flutterSdkPath() {
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
pluginManagement {
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ':app'
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
不幸的是,它仍然不起作用,并且错误只会变得不那么有用:
FAILURE: Build failed with an exception.
* Where:
Settings file '/Users/bartek/fvm/versions/master/examples/hello_world/android/settings.gradle' line: 20
* What went wrong:
A problem occurred evaluating settings 'android'.
> Could not get unknown property 'flutterSdkPath' for object of type org.gradle.plugin.management.internal.DefaultPluginManagementSpec.
我很确定它必须与 Gradle 以特殊方式处理的阻止有关(也就是说,在评估其余代码之前,首先对其进行评估)pluginManagement {}
settings.gradle
尝试 3(类中的静态方法)
我想也许块之前的所有代码都以某种方式被剥离了(?),所以我尝试创建一个带有静态方法的类:pluginManagement {}
pluginManagement {
includeBuild("${Flutter.flutterSdkPath}/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ':app'
apply from: "${Flutter.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle"
class Flutter {
String flutterSdkPath() {
def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
def properties = new Properties()
assert localPropertiesFile.exists()
localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}
}
这一次,错误消息就更没用了:
* Where:
Settings file '/Users/bartek/fvm/versions/master/examples/hello_world/android/settings.gradle' line: 11
* What went wrong:
A problem occurred evaluating settings 'android'.
> Flutter
* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
使用标志运行 Gradle 后,我注意到这一行:--stracktrace
... (<very long stack trace>)
Caused by: java.lang.NoClassDefFoundError: Flutter
...
看起来与我在尝试 2 中遇到的问题相同)。
关于如何将属性加载代码移动到一个地方并从块内部和外部调用它,我没有更多的想法。pluginManagement {}
我认为 Gradle 设置插件可以在这里工作,但除非必要,否则我宁愿不这样做。
答:
pluginManagement 块应该只包含与插件相关的配置:声明插件版本、应用插件和配置插件存储库。它应该是第一个块的原因是它在项目中的其他 gradle 文件之前运行。此块的目的不是用于通用脚本或导入其他文件。
没有必要过度设计。要提取的功能只是从文件中获取变量。properties
使用 gradle.properties
在文件所在的项目根目录中创建一个文件,并添加:gradle.properties
settings.gradle
flutterSdkPath=PATH_TO_YOUR_FLUTTER_SDK
在您的中,您可以使用它:settings.gradle
pluginManagement {
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
include ':app'
apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
有关如何使用 Kotlin 执行此操作的参考和更多信息,请阅读插件版本管理
使用命令行注入
有多种方法可以做到这一点。在这里,您可以阅读有关 Gradle、System、Environment 和 Project 属性的更多信息。 从高优先级到低优先级的层次结构以及它们如何覆盖这些值是:
- 命令行属性
- 系统属性
- 环境变量
gradle.properties
在主目录中gradle.properties
在项目根目录中
简而言之,您只需要通过命令行将变量传递给您的命令gradle
举个简短的例子:
gradle -PflutterSdkPath=$(cat local.properties | grep flutterSdkPath | cut -d'=' -f2) <GRADLE_TASK>
评论
local.properties
flutter.sdk
gradle.properties
gradle.properties
pluginManagement
dependencyResolutionManagement
在项目的根目录中创建文件 gradle.properties
pluginManagement {
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
plugins {
id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
}
}
评论
不幸的是,它仍然不起作用,并且错误只会变得不那么有用:
它说找不到您尝试引用的属性,因为您将其作为方法时不再有此类属性。
如果您尝试调用该方法,它会告诉您找不到该方法。
我很确定它必须与 Gradle 以特殊方式处理的阻止有关(也就是说,在评估其余代码之前,首先对其进行评估)
pluginManagement {}
settings.gradle
确切地说,它是单独提取和评估的。
该块可以定义插件存储库或包含带有设置插件的构建,因此要编译脚本的其余部分,需要首先单独评估它。
这也是您不能使用块外部的属性或方法的原因。
它必须是自给自足的。pluginManagement
我想也许块之前的所有代码都以某种方式被剥离了(?),所以我尝试创建一个带有静态方法的类:
pluginManagement {}
正如我所说,一切都是“剥离”的,否则您也可以简单地在块之后定义方法并调用它。
我认为 Gradle 设置插件可以在这里工作,但除非必要,否则我宁愿不这样做。
不,由于母鸡和鸡蛋的问题,这无济于事。
设置插件在块之后进行评估,因为该块可以定义包含的构建,其中包含设置插件或存储库,以便在何处查找设置插件,因此设置插件配置该块为时已晚。pluginManagement
PS 我也很乐意接受关于为什么我在尝试 1 的早期出现错误的解释,但在尝试 (2) 中没有。
不知道你说的“早期”是什么意思,这两个错误或多或少都会立即出现。 就在尝试 1 中,您在块之前有语句,这在 Groovy DSL 中是不允许的(在 Kotlin DSL 中是允许的,但在块内使用也不起作用),因此在解析脚本以提取块时出现错误,而在尝试 2 中,您在实际评估块时出现错误,因为它使用了不存在的属性。
您可以使用“额外”属性来执行要实现的操作。 使用它们通常是一种代码味道,只是无法正确执行某些操作的一种解决方法,但这可能是使用它可能是合适的罕见情况之一。
虽然该块是单独独立计算的,但它仍然可以修改模型,因此您可以添加闭包作为额外属性,然后该属性也可用于其余脚本:pluginManagment
pluginManagement {
settings.ext.foo = { "Foo" }
println("1: ${foo()}")
includeBuild("${foo()}")
}
println("2: ${foo()}")
评论