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

Viv*_*ath 7 java build-process build gradle

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

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

目前我的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"
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

apply plugin: 'my.gradle.plugin.PluginClass'
Run Code Online (Sandbox Code Playgroud)

当我这样做时,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'
Run Code Online (Sandbox Code Playgroud)

但这会导致另一个错误,其中 gradle 抱怨它无法libraryVersiongradle/buildscript.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"
    }
}
Run Code Online (Sandbox Code Playgroud)

这会导致另一个错误,其中gradle这个说有没有这样的属性extbuildscript。我知道这是因为确实没有“项目”可言,因为buildscript是单独编译的。然后我把我的buildscript块改build.gradle回:

buildscript {
    apply from: "gradle/buildscript.gradle"
}
Run Code Online (Sandbox Code Playgroud)

现在我没有收到ext错误消息,但我仍然收到一条错误消息,说它找不到具有指定 id 的插件。

我不能libraryVersion在里面硬编码buildscript,因为我需要它作为编译时依赖项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"
}
Run Code Online (Sandbox Code Playgroud)

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

import my.library.SomeThing

task customTask(type: DefaultTask) {
    //does something with SomeThing
}
Run Code Online (Sandbox Code Playgroud)

但是当我离开这个buildscriptbuild.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'
Run Code Online (Sandbox Code Playgroud)

我从 gradle 收到一个错误,说它无法解析 class my.library.SomeThing。我想我可以由具有共同解决这个问题,避免重复buildscript的文件,然后我可以在这两个应用build.gradleother.gradle

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

是否有关于此类行为的综合文档?这一切都违反了最小惊喜原则。当我将其移动到另一个文件时,我希望有一个buildscript用于build.gradle“正常工作”的块。

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

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

Viv*_*ath 7

这有点咆哮,但也有一个解决方案。我能够在不使用单独buildscript文件的情况下解决这个问题,但解决方法令人难以置信。我认为这是一个主要的缺点,你不能在外部脚本之间共享 buildscript 依赖项。

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

我知道可能存在技术限制,但是仅仅因为您将逻辑从一个文件移动到另一个文件而使构建中断是抽象泄漏,因为现在我需要知道这样做的细节和复杂性,超出了人们应该合理预期知道的范围. 我什至不介意是否明确和具体地提出这一点,以及解决语义差异的解决方案/变通方法。然而,当前关于组织构建逻辑的文档没有提到这些警告;它只记录快乐的道路。

/咆哮

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

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'
Run Code Online (Sandbox Code Playgroud)

然后在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(...)
}
Run Code Online (Sandbox Code Playgroud)