我的最初目标是能够在buildscript中的build.gradle中的build.gradle中定义的类路径依赖项中使用脚本,该脚本已使用apply from:导入到buildscript中。但是,由于无法解析类,因此外部脚本无法编译。在研究了这个问题之后,我发现逻辑需要重复,因此我想将build.gradle提取到一个单独的文件中。然后,我将能够在build.gradle以及外部脚本中应用它。

我什至没有成功地从gradle.properties应用外部buildscript文件,更不用说从外部脚本应用它了。我已经尝试了多种方法,但是无论如何尝试,似乎总是会遇到两个问题之一:不能使用gradle/buildscript.gradle的属性,或者找不到插件(即使已经定义了classpath依赖项) 。

目前,我的libraryVersion文件如下所示:

buildscript {
    repositories {
        maven { url "http://some.url.com" }
    }

    dependencies {
        classpath "my.gradle.plugin:gradle-plugin:1.0.0"
        classpath "my.library:my-library:$libraryVersion"
    }
}
gradle.properties已在build.gradle中定义。我的my.gradle.plugin.PluginClass如下:
buildscript {
    apply from: "gradle/buildscript.gradle"
}

apply plugin: 'my.gradle.plugin.PluginClass'

当我这样做时,gradle抱怨找不到ID为project.plugin.apply(...)的插件。我尝试删除引号,也尝试使用插件的FQN(带引号和不带引号)进行my;两者均导致gradle错误并显示一条消息,提示它在根项目上找不到libraryVersion属性。

我也尝试过:
buildscript {
    apply from: "gradle/buildscript.gradle", to: buildscript
}

apply plugin: 'my.gradle.PluginClass'

但这会导致另一个错误,其中gradle抱怨无法解析gradle/buildscript.gradle中的ext。所以我尝试了这个:
buildscript {
    ext.libraryVersion = "1.0.1"

    repositories {
        maven { url "http://some.url.com" }
    }

    dependencies {
        classpath "my.gradle.plugin:gradle-plugin:1.0.0"
        classpath "my.library:my-library:$libraryVersion"
    }
}

这会导致另一个错误,其中gradle表示buildscript上没有此类属性buildscript。我知道这是因为buildscript是单独编译的,因此确实没有可说的“项目”。然后,我将build.gradle中的ext块改回了:
buildscript {
    apply from: "gradle/buildscript.gradle"
}

现在,我没有收到libraryVersion错误,但仍然收到一条错误消息,提示它找不到具有指定ID的插件。

我无法在buildscript内对build.gradle进行硬编码,因为我需要将它作为buildscript中的编译时依赖项,而我宁愿不必在两个地方进行维护。

这非常令人困惑和沮丧,因为以下build.gradle块在buildscript中本身可以正常工作:
buildscript {
    ext.libraryVersion = "1.0.1"

    repositories {
        maven { url "http://some.url.com" }
    }

    dependencies {
        classpath "my.gradle.plugin:gradle-plugin:1.0.0"
        classpath "my.library:my-library:$libraryVersion"
    }
}

apply plugin: 'my-plugin-id' //No need to use FQN

dependencies {
    compile "my.library:library-version:$libraryVersion"
}

我尝试拆分other.gradle块的原因是因为我有一个文件my.library,其中包含一些使用buildscript中的类的自定义任务:
import my.library.SomeThing

task customTask(type: DefaultTask) {
    //does something with SomeThing
}

但是,当我将build.gradle块保留在my.library.SomeThing中并应用其他文件时,如下所示:
buildscript {
    ext.libraryVersion = "1.0.1"

    repositories {
        maven { url "http://some.url.com" }
    }

    dependencies {
        classpath "my.gradle.plugin:gradle-plugin:1.0.0"
        classpath "my.library:my-library:$libraryVersion"
    }
}

apply plugin: 'my-plugin-id' //No need to use FQN

dependencies {
    compile "my.library:my-library:$libraryVersion"
}

apply from: 'gradle/other.gradle'

我从gradle中得到一个错误,说它无法解析buildscript类。我想我可以解决这个问题,并通过有一个通用的build.gradle文件来避免重复,然后再将其应用于other.gradlebuildSrc中。

我在buildscript内创建了一个自定义插件,以按我希望的方式配置项目,但最终以更复杂的方式失败,导致结果相同。根本原因是相同的:无法将类路径依赖项暴露给外部脚本。

是否有关于此类行为的综合文档?关于此的一切都违反了最小惊讶原则。我希望将build.gradle中使用的apply块移到另一个文件时“可以正常工作”。

关于buildscript块的buildscript的语义尚不清楚。此外,ojit_code本身在外部文件中出现时的语义也不明确-行为存在明显差异,尤其是在插件和外部属性方面。

处理此问题的最佳方法是什么?

最佳答案

这有点麻烦,但是也有解决方案。我能够在不使用单独的buildscript文件的情况下解决此问题,但是解决方法令人难以置信地令人讨厌。我认为这是一个主要缺点,您无法在外部脚本之间共享buildscript依赖关系。

问题在于没有语义上的一致性,因为行为似乎取决于您决定如何组织/模块化构建逻辑。如果这是一个已知问题,则需要在文档中的某个地方特别注明-我能够找到提及这种令人惊讶行为的唯一方法是来自gradle自己的论坛或StackOverflow。我认为期望可以在单个文件中使用构建逻辑的离散单元的构建并在将这些离散单元拆分为多个文件时也可以运行是不合理的。只要语义是一致的,构建逻辑就不会根据您决定组织文件的方式而有所不同。

我知道这可能会有技术限制,但是由于您将逻辑从一个文件移动到另一个文件而导致构建中断是抽象泄漏,因为现在我必须知道这样做的细节和复杂性,超出了应该合理预期的范围。我什至都不介意是否明确,明确地指出这一点,以及弥合语义差异的解决方案/变通办法。但是,有关组织构建逻辑的最新文档没有提到这些警告。它只记录了幸福的道路。

/ rant

所以这是解决方案。我通过使用扩展保存了对类本身的引用:

import my.library.SomeThing
import my.library.SomeOtherThing

buildscript {
    ext.libraryVersion = "1.0.1"

    repositories {
        maven { url "http://some.url.com" }
    }

    dependencies {
        classpath "my.gradle.plugin:gradle-plugin:1.0.0"
        classpath "my.library:my-library:$libraryVersion"
    }
}

apply plugin: 'my-plugin-id' //No need to use FQN

ext.SomeThing = SomeThing
ext.SomeOtherThing = SomeOtherThing

dependencies {
    compile "my.library:my-library:$libraryVersion"
}

apply from: 'gradle/other.gradle'

然后在other.gradle中:
// Necessary; you can't just use ext.SomeThing in the task later because
// it is available at compile-time, but apparently not at runtime. Although
// it does work if you use project.ext.SomeThing. However, I just found this
// to be more convenient.
def SomeThing = ext.SomeThing
def SomeOtherThing = ext.SomeOtherThing

task someTask(type: DefaultTask) {
    // You have to use def; you cannot use the actual type because
    // it is not available at compile-time. Also, since you only
    // have a class object, you cannot use "new" directly; you have to
    // create a new instance by calling newInstance() on the class object
    def someThing = SomeThing.newInstance(...)

    // If you are calling static methods you can invoke them directly
    // on the class object. Again, you have to use def if the return
    // type is something defined within my-library.
    def foo = SomeOtherThing.staticMethod(...)
}

07-24 09:46
查看更多