Groovy和DSL一: 模拟Gradle

Groovy是一种基于JVM,功能强大、类型可选、动态的,可与Java无缝衔接的编程语言,接触Groovy是因为Gradle。而Gradle好用是因为Groovy,到底谁成就了谁呢?

Groovy强大之处,在于创建DSL,Domain Specific Languages

我们看见的Gradle脚本中的书写规则,就是一种DSL语言

今天我们就来实现一个迷你版DSL语言,了解一下Groovy和DSL

我们把这个项目叫做Bradle吧

build.bradle

先创建一个build.bradle文件,和常见的gradle脚本一样:

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

dependencies {
    // Use JUnit test framework.
    testImplementation 'junit:junit:4.13.2'

    // This dependency is used by the application.
    implementation 'com.google.guava:guava:30.1.1-jre'
}

tasks.register("t1"){
    println "task t1 executed"
}

tasks.register("t2"){
    doLast{
        println "task t2 executed"
    }
}

脚本中的关键字,语法,以及嵌套关系,形成了一个微型的语法规则,这就是我们的DSL。

Script Class

现在我们创建一个类,来对应脚本中的各个关键字,实现DSL的解析,其中task是我们的重点,我们希望能够正确执行task中的语句。

Groovy创建的DSL中,大括号前面的关键字repositories和dependencies其实代表函数调用,后面的大括号,是一种参数类型,叫做Closure。

funcitionName{
   closure
}

除了函数调用,DSL中还包括属性,属性可以赋值,也可以调用属性对应的方法,咱们例子中,tasks就是属性,register是tasks对应的方法。

此外,Closure中的mavenCentral, testImplementation等,也是函数调用,我们一步步会涉及到

我们定义一个Class,文件名是Project.groovy:

class TaskContainer{
   void register(String name, Closure cl){
        println name + ":tasks.register get called"
   }

   void run(String taskName){
       println "Get to run task:" + taskName
   }
}

abstract class Project extends Script{
    TaskContainer tasks

    Project(){
        tasks = new TaskContainer()
    }

    void repositories(Closure cl){
        println "repositories get called"
    }
    void dependencies(Closure cl){
        println "dependencies get called"
    }
}

当我们在build.bradle脚本中写:

repositories {
    ...
}

我们希望是在调用Project.groovy中的函数

void repositories(Closure cl){
    ...
}

要实现这点,首先Project要继承于Script类。groovy中的脚本要属于Script类,才会被正确解析。其次我们需要把我们的Project类和build.bradle连接起来

Groovyshell

把Project类和build.bradle连接在一起的方式之一,是通过Groovyshell。

我们创建一个新的文件gshell.groovy,调用Groovyshell,串联脚本和对应类:

import org.codehaus.groovy.control.CompilerConfiguration
import Project

def config = new CompilerConfiguration()
//这里设置脚本对应的类
config.scriptBaseClass = 'Project'
def shell = new GroovyShell(this.class.classLoader, config)

//读取脚本
def script = new File("build.bradle")
//把脚本和Project类对应起来,返回Project的一个实例
def projInstance = shell.parse(script)
//Project中的run函数继承自Script,开始执行脚本
projInstance.run()

现在我们的目录结构如下:

$ tree .
.
├── Project.groovy
├── build.bradle
└── gshell.groovy

运行以下检查结果:

$ groovy gshell.groovy
repositories get called
dependencies get called
t1:tasks.register get called
t2:tasks.register get called

传递参数

我们还需要传递参数,我们调用gradle运行脚本的时候,格式是gradle taskname这样。我们朝这个方向前进。

首先在gshell.groovy中接受参数:

import org.codehaus.groovy.control.CompilerConfiguration
import Project

def config = new CompilerConfiguration()
config.scriptBaseClass = 'Project'
def shell = new GroovyShell(this.class.classLoader, config)

def script = new File("build.bradle")
def projInstance = shell.parse(script)
projInstance.run()

//处理命令行参数
taskName=this.args[0]
projInstance.tasks.run(taskName)

其次我们用一个脚本封装一下groovy命令,显得更像gradle。

我们创建一个bradle文件:

#!/bin/sh
groovy gshell.groovy $1

现在我们的文件目录结构如下:

.
├── Project.groovy
├── bradle
├── build.bradle
└── gshell.groovy

运行一下我们新鲜的bradle程序,随便执行一个任务:

$ ./bradle t1
repositories get called
dependencies get called
t1:tasks.register get called
t2:tasks.register get called
Get to run task:t1

小结

需要说明的是,本文只是一个示例,并非严谨的生产代码,很多异常情况并未检测,主要目的是为了利用Groovy描述创建DSL和脚本的解析流程。

本文中,所有的函数调用都没有具体实现 ,仅仅是输出了日志信息,下一节我们根据本文的框架,进一步完善Project.goovy类,阐述closure的执行,代理,让我们的task能够运行起来。

本文的例子放在了github上:

Ref

  • https://www.jorgemanrubia.com/2009/10/10/evaluating-code-dynamically-in-groovy/


《 “Groovy和DSL一: 模拟Gradle” 》 有 3 条评论

  1. Having an enlarged prostate is not believed to increase your risk of developing prostate cancer cost of generic cytotec without prescription Relation between hepatic expression of ATP binding cassette transporters G5 and G8 and biliary cholesterol secretion in mice

  2. 18 billion yuan, arecord high for land prices in Shenzhen priligy side effects There is little to be done at home when your cat is breathing heavily and having difficulty

回复 agodelo 取消回复

您的邮箱地址不会被公开。 必填项已用 * 标注

About Me

一位程序员,会弹吉他,喜欢读诗。
有一颗感恩的心,一位美丽的妻子,两个可爱的女儿
mail: geraldlee0825@gmail.com
github: https://github.com/lisuxiaoqi
medium: https://medium.com/@geraldlee0825