访问应用外部脚本中 buildscript 块中定义的类路径依赖项

Access classpath dependencies defined in buildscript block in applied external-script

我最初的目标是能够在 build.gradle 中使用 class 在 buildscript 中定义的路径依赖项,在使用 [= 导入到 build.gradle 的脚本中21=]。但是,外部脚本没有编译,因为 classes 无法解析。在研究了这个问题之后,我发现逻辑需要复制,所以我想我应该将 buildscript 提取到一个单独的文件中。然后我就可以在 build.gradle 以及外部脚本中应用它。

我什至没有成功应用来自 build.gradle 的外部构建脚本文件,更不用说从外部脚本应用它了。我尝试了很多事情,但无论我尝试什么,似乎我总是遇到两个问题之一:无法使用 gradle.properties 中的属性,或者找不到插件(即使 class已定义路径依赖。

目前我的 gradle/buildscript.gradle 文件如下所示:

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

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

libraryVersion 已在 gradle.properties 中定义。我的build.gradle如下:

buildscript {
    apply from: "gradle/buildscript.gradle"
}

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

当我这样做时,gradle 抱怨找不到 ID 为 my.gradle.plugin.PluginClass 的插件。我尝试删除引号,我还尝试 project.plugin.apply(...) 使用插件的 FQN 带引号和不带引号;这两个都导致 gradle 出错,并显示一条消息说它无法在根项目上找到 属性 my

我也试过:

buildscript {
    apply from: "gradle/buildscript.gradle", to: buildscript
}

apply plugin: 'my.gradle.PluginClass'

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

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 上没有这样的 属性 ext。我知道这是因为实际上还没有 "project" 可言,因为 buildscript 是单独编译的。然后我将 build.gradle 中的 buildscript 块改回:

buildscript {
    apply from: "gradle/buildscript.gradle"
}

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

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

这非常令人困惑和沮丧,因为以下 buildscript 块在 build.gradle 中可以正常工作:

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"
}

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

import my.library.SomeThing

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

但是当我将 buildscript 块留在 build.gradle 中并像这样应用另一个文件时:

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 收到一个错误,说它无法解析 class my.library.SomeThing。我想我可以解决这个问题并通过拥有一个通用的 buildscript 文件来避免重复,然后我可以在 build.gradleother.gradle.

中应用该文件

我在 buildSrc 中创建了一个自定义插件来按照我想要的方式配置项目,结果却以更复杂的方式失败并得到相同的结果。根本原因是相同的:无法将 class 路径依赖项公开给外部脚本。

是否有关于此类行为的全面文档?这一切都违反了最小惊喜原则。当我将它移动到另一个文件时,我希望在 build.gradle 到 "just work" 中使用的 buildscript 块。

apply 相对于 buildscript 块的语义不明确。此外,当 buildscript 出现在外部文件中时,它本身的语义也不清楚 - 行为有明显的变化,尤其是在插件和外部属性方面。

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

这个有点啰嗦,不过也是有解决办法的。我能够在不使用单独的 buildscript 文件的情况下解决这个问题,但解决方法是令人难以置信的骇人听闻。我认为您不能跨外部脚本共享构建脚本依赖项是一个主要缺点。

问题在于没有语义一致性,因为行为似乎取决于您如何决定 organize/modularize 您的构建逻辑。如果这是一个已知问题,则需要在某处的文档中特别指出 - 我能够找到提及这种令人惊讶的行为的唯一方法是来自 gradle 自己的论坛或 Whosebug .我不认为期望构建在单个文件中与构建逻辑的离散单元一起工作是不合理的,当这些离散单元被拆分到多个文件时 also 也能工作。构建逻辑不应根据您决定如何组织文件而有所不同,只要语义一致即可。

我知道可能存在技术限制,但是仅仅因为您将逻辑从一个文件移动到另一个文件而导致构建中断是抽象泄漏,因为现在我需要知道这样做的细节和复杂性,超出了人们应该知道的范围合理地 应该知道。我什至不介意是否明确和具体地指出这一点,以及 solutions/workarounds 以弥合语义上的差异。但是,当前关于组织构建逻辑的文档提到了这些注意事项 none;它只记录快乐的道路。

/咆哮

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

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(...)
}