我的最初目标是能够在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.gradle
和buildSrc
中。我在
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(...)
}