Gradle 任务语法:如何从 Groovy 的角度解释它?

Ant*_*ony 4 dsl groovy gradle

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

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

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

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

我没有检查出像一些类似的问题,这一次却没有答案,其中明确或完全够我。

TL; 博士

  1. task myTask { doLast {} }在 Groovy 中如何解释令牌?
  2. Gradle 是否使用标准的 Groovy 解释器?
  3. myTask当它背后有task和没有def或类型时如何解释为标识符?
  4. 如果稍后在文件中我添加了myTask { dependsOn myOtherTask }如何解释?

lan*_*ava 9

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

  1. 如果方法的最后一个参数是闭包,则可以将闭包放在方法参数的右括号之后。
class MyClass {
   void doStuff(String name, Closure c) {
      c.call()
   } 
} 

def o = new MyClass() 
o.doStuff('x') {
   println "hello" 
} 
Run Code Online (Sandbox Code Playgroud)
  1. 您可以实现对象上缺少的方法。如果有人试图调用一个不存在的方法,你可以做一些事情
class MyClass {
    def methodMissing(String name, args) {
        println "You invoked ${name}(${args})" 
    }
} 
def o = new MyClass() {
   o.thisMethodDoesNotExist('foo')
}
Run Code Online (Sandbox Code Playgroud)
  1. 您可以在闭包上设置委托
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' 
} 
Run Code Online (Sandbox Code Playgroud)

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

所以,让我们分解你的片段

task myTask(type:Foo) { 
   doLast {...} 
}
Run Code Online (Sandbox Code Playgroud)

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

Closure c = { 
   doLast {...} 
}
project.task(project.myTask([type: Foo.class], c)) 
Run Code Online (Sandbox Code Playgroud)

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

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

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

参见TaskContainer.create(String, Class, Action)


Dan*_*ele 3

(免责声明,我不是一个出色的开发人员)

运行构建时(例如),将针对对象(由 Gradle 运行程序创建)评估gradle clean内容;请参阅API-Gradle 项目中的 Javadoc ;还要阅读整个摘要,因为它包含很多信息。在该页面中,他们澄清了这一点:build.gradleProject

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

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


因此,对于您的一些观点:

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

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