Gradle の inputs.property にクロージャ渡したときと変数渡したとき

完全に体調を崩してしまいました。まいったなあ…。

ところで Gradle って名前は有名な割に、ブログ記事、 stackoverflow のポスト、少ない。つまり、わかってる人たちだけが使ってる感じなので、そうそうそれ俺も思ってた!みたいな疑問が表に出て来てない。予想以上に辛い。しかし更新検知というか更新判断?の仕組みは好きだし、 Groovy も好きなので便利です。

タスクへの入力定義のほうで、ファイルじゃ無くて値が使えるんですが、変数渡したり、クロージャ渡したり出来るみたいなんです。でも変数だと思ったように動かせなくて、下のような感じで書いて動かしてみたところ

task hoge {
  ext.myProp = 'original'
  doLast {
    myProp = 'rewrited'
  }
}

task fuga(dependsOn: hoge) {
  inputs.properties([
    'closure': {
      println '==inputs.properties closure evaluated.'
      hoge.myProp
    },
    'var': hoge.myProp
  ])
  outputs.file 'test'
  doFirst {
    println '[fuga.doFirst]'
    println fuga.inputs.getProperties()
  }
  doLast {
    println '[fuga.doLast]'
    println fuga.inputs.getProperties()
  }
}

println '[root]'
println fuga.inputs.getProperties()
~/tmp ❯❯❯ gradle fuga
[root]
==inputs.properties closure evaluated.
{var=original, closure=original}
:hoge
:fuga
==inputs.properties closure evaluated.
[fuga.doFirst]
==inputs.properties closure evaluated.
{var=original, closure=rewrited}
[fuga.doLast]
==inputs.properties closure evaluated.
{var=original, closure=rewrited}

BUILD SUCCESSFUL

Total time: 2.929 secs

こんな感じでした。 fuga.inputs.getProperties() を呼ぶたびにクロージャが実行されてることが分かりますが、変数はその時の値になるんじゃなくて、ずっと Configuration Phase で評価した値です。クロージャは Execution Phase 及び取りにいったときに毎回評価されている模様です。

Docker の import とか push みたいに結果がファイルに出ない場合( stdout をファイルに出しても良いけど…)、一つ前のタスクの結果を次のタスクの inputs.property として使いたい。という場合があって、

task importBuild(type: Exec, dependsOn: build) {
  inputs.file  'output/image.tar'
  outputs.upToDateWhen {
    ( importedRevision == revisions.imported )
  }
  commandLine  'sudo', 'bash', '-c', "cat output/image.tar | docker import - oogatta/hoge"

  ext.importedRevision = revisions.imported
  standardOutput = new ByteArrayOutputStream()

  doLast {
    importedRevision = standardOutput.toString().trim()

    ant.propertyfile(file: revisionsFile) {
      entry(key: 'imported', value: importedRevision)
    }
  }
}

task pushBuild(type: DockerPushImage, dependsOn: importBuild) {
  inputs.property 'importedRevision', { importBuild.importedRevision }
  outputs.upToDateWhen {
    def proc = ['sudo', 'bash', '-c', "docker history --no-trunc oogatta/hoge:latest | awk 'FNR>1{print \$1;}'"].execute()
    proc.waitFor()

    def imageId = proc.in.text.trim()

    ( imageId == importBuild.importedRevision )
  }
  imageId = "oogatta/hoge"
}

例えばこういう感じで、クロージャ助かりました。でもなんか、きっともっと良い方法があるに違いありません。なので、これはあくまで inputs.property についての話です。

Gradle は公式のドキュメントの向いてる方向が何やらエンタープライズ感があって、自分みたいな目的には、ツール自体は適応しているのだけど、周辺環境の意味で辛いです。今時のツールみたいにふわっとした感じでお願いしたい!。