Gradle/Groovyの基礎

2018年7月13日

いきなりGradleをやろうとしてつまづくことは、この言語がどういう構造になっているのかもわからないのに、入門サイト・入門書では「これをやれ、あれをやれ」と言われることである。どんな言語もそうだが、それに従って使う分には良いのだが、そこからはみ出そうとすれば、途端に悩んでしまうことになる。

GradleがGroovy言語であること

Gradleのスクリプトは、完全にGroovy言語で記述される。応用のためにはGroovyがいかなる言語であるかを知らねばならないのだが、しかしGroovyを習いたいわけではない。そこで、Gradle用のスクリプトをあれこれいじりながら、Groovyも習うことにする。非常に参考になるページとして以下がある。Groovyを知らないとしたら(私も)、ここは必ず読む価値がある。とてもわかりやすくまとまっている。

他の資料としては以下がある。

「Groovyを知らない人のためのbuild.gradle読み書き入門」に書いてないこと

以下では、これを読みながら疑問を持ったところを追求してみる。

なぜcompile等は二つの記法が使えるのか?

以下の二つの書き方ができるが、なぜだろう?

  compile 'com.google.inject:guice:4.1.0'
compile group: 'com.google.inject', name: 'guice', version: '4.1.0'

おそらくはこういうこと、かなり単純にしてある。

def func(arg) {
  if (arg instanceof String) {
    println "string:" + arg           
    return;                    
  }
  println("map " + arg.a + "," + arg.b);
}
task sample {
  doLast {
    func(a:'123', b:'456')
    func('this is string')
  }
}

sampleを実行すると、以下の結果になる。つまり、文字列一つの場合には、コロンをセパレータとして解析していると思われる。

map 123,456
string:this is string

波括弧で囲まれた部分はクロージャ。。。とは限らない

波括弧で階層化のところだが、必ずしもすべての波括弧がクロージャとは限らないようだ。少なくともdefでメソッドを定義した場合、そのボディはクロージャとしてのスコープを持たない。

以下はエラーになる。funcのメソッドボディは変数Aのスコープを持ってない。つまり、クロージャではない。少なくともクロージャと言われるものと同じスコープではない。

def A = "abc";
def func() {
  println A // エラー
}
task (sample,{
  doLast {
    println A
    func();
  }
})

以下は正常実行できる。クロージャを作成し、それをfuncという変数に入れてるため。

def A = "abc";
def func = {
  println A
}

task (sample,{
  doLast {
    println A
    func();
  }
})

つまり、「波括弧で階層化」の例で、

def projectClosure = { name = 'SampleProject' }
def eclipseClosure = { project(projectClosure) }

eclipse(eclipseClosure)

の部分を誤って以下としてしまうと、動かなくなる。全く意味が違うので。

def projectClosure() { name = 'SampleProject' }
def eclipseClosure() { project(projectClosure) }

eclipse(eclipseClosure)

ところでGroovyの言語仕様はどうなっているのか?

プログラマであれば、そもそもの言語仕様の理解は欠かせないと思うのだが、まともにGroovyの言語仕様が議論されているところをみかけない。例えば、以下の場合、defもtaskもメソッドなのだろうか?

def func(arg) {
  if (arg instanceof String) {
    println "string:" + arg           
    return;                    
  }
  println("map " + arg.a + "," + arg.b);
}
task sample {
  doLast {
    func(a:'123', b:'456')
    func('this is string')
  }
}

taskについては、

task (sample,{
  doLast {
    func(a:'123', b:'456')
    func('this is string')
  }
})

とは書くことができるが、defについてはできなかった。実は、Groovy Language Documentationというものがあった。これを見ると、キーワード(予約語)は以下である。

as assert break case catch class const continue def default do else enum extends false finally
for goto if implements import in instanceof interface new null package return super switch
this throw throws trait true try while

これを見ると、Javaには無い特徴的なものとしては、as, def, in, traitといったところ。
つまり、defはメソッドではなく、キーワードであり、特別な処理が行われるわけだ。