【问题标题】:Gradle task syntax: how is it explained from a Groovy perspective?Gradle 任务语法:如何从 Groovy 的角度进行解释?
【发布时间】:2019-07-05 16:23:41
【问题描述】:

我很难理解 Gradle 的 Groovy DSL 是如何工作的。

不幸的是,Gradle 是我在日常工作中遇到的 Groovy 的主要用例,而且我注意到对于许多开发人员来说,他们完全通过 Gradle 接触到 Groovy。因此,大多数 Gradle 用户对 Groovy 的掌握非常有限。

在我对 Groovy 的有限理解中,以下 sintax tokenA tokenB { tokenC } 其中所有标记都不是语言关键字,tokenA 将是我们使用参数 tokenB 调用的方法,最后一个参数是闭包。我想我是正确的,但我知道我错了,因为在 tokenB 之后可能需要一个逗号才能使分析正确。

正如您已经知道的那样,我绝不是 Groovy 开发人员,而且我认为在不学习 Groovy 基础知识的情况下使用 Gradle 是一件坏事,因为它限制了我充分利用其功能。但不幸的是,我唯一可行的选择是通过示例学习而不学习理论。

我确实检查了一些类似的问题,例如 this one,但没有对我来说足够清晰或完整的答案。

TL;DR

  1. 标记 task myTask { doLast {} } 在 Groovy 中是如何解释的?
  2. Gradle 是否使用标准的 Groovy 解释器?
  3. task 而不是def 或后面的类型时,myTask 如何解释为标识符?
  4. 如果稍后我在文件中添加了myTask { dependsOn myOtherTask },如何解释?

【问题讨论】:

    标签: gradle groovy dsl


    【解决方案1】:

    我相信这一切都很时髦,没有什么特别值得学习的。这是您需要了解的常规概念。

    1. 如果方法的最后一个参数是闭包,您可以将闭包放在方法参数的右括号之后。
    class MyClass {
       void doStuff(String name, Closure c) {
          c.call()
       } 
    } 
    
    def o = new MyClass() 
    o.doStuff('x') {
       println "hello" 
    } 
    
    1. 您可以在您的对象上实现method missing。如果有人试图调用不存在的方法,你可以做一些事情
    class MyClass {
        def methodMissing(String name, args) {
            println "You invoked ${name}(${args})" 
        }
    } 
    def o = new MyClass() {
       o.thisMethodDoesNotExist('foo')
    }
    
    1. 您可以set the delegate 关闭
    class MyBean {
       void include(String pattern) {...} 
       void exclude(String pattern) {...} 
    } 
    class MyClass {
       private MyBean myBean = new MyBean() 
       void doStuff(Closure c) {
          c.setDelegate(myBean)
          c.call()
       } 
    } 
    
    def o = new MyClass() 
    o.doStuff {
       include 'foo' 
       exclude 'bar' 
    } 
    

    这 3 个 groovy 特性几乎解释了 gradle 脚本中发生的让 Java 开发人员摸不着头脑的“神奇”行为。

    那么,让我们分解一下你的 sn-p

    task myTask(type:Foo) { 
       doLast {...} 
    }
    

    让我们添加一些括号并添加隐式项目引用。让我们也将闭包提取到一个变量中

    Closure c = { 
       doLast {...} 
    }
    project.task(project.myTask([type: Foo.class], c)) 
    

    project.myTask(...) 方法不存在,该行为最终通过methodMissing 功能实现。 Gradle 会将闭包上的委托设置为任务实例。因此闭包中的任何方法都将委托给新创建的任务。

    最终,这就是逻辑上所说的

    Action<? extends Task> action = { task ->
       task.doLast {...} 
    }
    project.tasks.create('myTask', Foo.class, action)
    

    TaskContainer.create(String, Class, Action)

    【讨论】:

    • 感谢您提供非常有用的答案。我不知道代表,这可以解释很多魔法。不过我还有一个问题。在task myTask { ... } 中考虑的task 是什么词,是像intMyClass 这样的类型,它是在委托中查找的方法吗?我们如何可以不带引号命名任务?我们怎么会有这样的语法task &lt;identifier&gt;&lt;arguments&gt;?这样的构造是标准的 Groovy 吗?我不知道如何仅使用闭包和委托来提出这样的 DSL。也许缺少的链接在我在我的问题中链接到的问题中?
    • 请重新阅读我的细分。它是对项目对象的方法调用。 project.task(project.myTask({ ... }))
    【解决方案2】:

    (免责声明,我不是一个优秀的开发者)

    运行构建时(例如gradle clean),build.gradle 的内容将根据Project 对象(由 Gradle 运行程序创建)进行评估;请参阅API-Gradle Project 的 Javadoc;还请阅读整个摘要,因为它包含很多信息。在该页面中,他们阐明:

    一个项目有 5 个方法“范围”,它会搜索方法:项目对象本身...构建文件...插件添加到项目的扩展...项目的任务 .. 为每个任务添加一个方法,使用任务名称作为方法名称...

    task myTask { } 应该等同于project.task('myTask')。它创建一个名为“myTask”的新任务并将该任务添加到当前项目中(参见 Javadoc)。然后,将一个属性添加到项目对象,以便它可以作为project.myTask 访问。 doLast {..} 在该任务对象上调用 doLast 方法;请参阅Task-doLast 的 Javadoc


    所以对于你的一些观点:

    1. project.task('myTask').doLast(..)(也许这里有更多关于闭包的信息)
    2. 确实如此(尝试从 github 构建);但有额外的处理;在运行构建之前,build.gradle 文件在 Project 实例中“注入”。加上许多其他步骤
    3. project.task('myTask')
    4. project.myTask.dependsOn(project.myOtherTask)(可能有一个额外的闭包,或者涉及到 Action 实例)。这是因为任务作为属性添加到项目中。

    还要注意显式语句,如project.myTask... 是有效的,它们可以在build.gradle 脚本中使用;但是它们很冗长,所以很少使用。

    【讨论】:

      猜你喜欢
      • 2015-02-19
      • 2012-10-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-06
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多