如何从 Gradle 的“pluginManagement {}”块调用函数?

How can I call a function from Gradle's `pluginManagement {}` block?

提问人:Bartek Pacia 提问时间:4/2/2023 最后编辑:Bartek Pacia 更新时间:4/5/2023 访问量:401

问:

介绍

嗨,我正在尝试对在我的应用程序中找到的代码进行重复数据删除。这是我的文件目前的样子: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.sdklocal.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 设置插件可以在这里工作,但除非必要,否则我宁愿不这样做。

格拉德尔

评论


答:

2赞 RAI 4/4/2023 #1

pluginManagement 块应该只包含与插件相关的配置:声明插件版本、应用插件和配置插件存储库。它应该是第一个块的原因是它在项目中的其他 gradle 文件之前运行。此块的目的不是用于通用脚本或导入其他文件。

没有必要过度设计。要提取的功能只是从文件中获取变量。properties

使用 gradle.properties

在文件所在的项目根目录中创建一个文件,并添加:gradle.propertiessettings.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 属性的更多信息。 从高优先级到低优先级的层次结构以及它们如何覆盖这些值是:

  1. 命令行属性
  2. 系统属性
  3. 环境变量
  4. gradle.properties在主目录中
  5. gradle.properties在项目根目录中

简而言之,您只需要通过命令行将变量传递给您的命令gradle

举个简短的例子:

gradle -PflutterSdkPath=$(cat local.properties | grep flutterSdkPath | cut -d'=' -f2) <GRADLE_TASK>

评论

0赞 Bartek Pacia 4/4/2023
谢谢你的回答!问题在于现有工具生成文件,并且位于该文件中,而不是 .是否可以从非类似、无缝方式的 .properties 文件加载 Properties?local.propertiesflutter.sdkgradle.propertiesgradle.properties
0赞 RAI 4/5/2023
@BartekPacia,请看一下答案。我用命令行注入选项更新了它
0赞 Bartek Pacia 4/5/2023
感谢您更新答案。不幸的是,我认为在每次构建时手动将路径作为命令行标志传递对我们来说是不可行的。
0赞 Zachary 9/27/2023
但是,导入自我管理的 maven 需要对 进行额外的配置,尽管它与 中的完全相同。我想减少它们pluginManagementdependencyResolutionManagement
-1赞 alperkaraca_7 4/4/2023 #2

在项目的根目录中创建文件 gradle.properties

pluginManagement {
    includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")

    plugins {
        id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false
    }
}

评论

0赞 Community 4/18/2023
正如目前所写的那样,你的答案尚不清楚。请编辑以添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。您可以在帮助中心找到有关如何写出好答案的更多信息。
2赞 Vampire 4/5/2023 #3

不幸的是,它仍然不起作用,并且错误只会变得不那么有用:

它说找不到您尝试引用的属性,因为您将其作为方法时不再有此类属性。

如果您尝试调用该方法,它会告诉您找不到该方法。

我很确定它必须与 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()}")