mike-neckのブログ

Java or Groovy or Swift or Golang

Gradle3におけるJavaプロジェクトのビルド入門(1) #gradle

f:id:mike_neck:20151108133321p:plain

Gradleは元々の作りが非常にブラックボックスなため、設定されたオブジェクトのライフサイクルがわかりづらいなどの問題があり、Gradle3に向けて、それを修正している最中です。その中核となるのがRule Based Model Configurationという仕組みです。

その仕組については以前にもエントリーを書いているので、そちらを参照してください。

mike-neck.hatenadiary.com

基本的な考え方は

  1. 値オブジェクトの生成(プラグインが値オブジェクトを生成する)
  2. 値オブジェクトの設定(ユーザーがオブジェクトの中の値を変更できる/mutableなオブジェクト)
  3. 値の利用(ユーザーもプラグインも値を変更できない/immutableなオブジェクト)

という流れにそって設定を記述していくという考え方です。


では、Javaプロジェクトはどのように変わっていくかを今回は簡単に説明するとともに、簡単なプロジェクトを作ってみます。

プラグイン

これまではJavaプロジェクトの場合は次のように記述していました。

apply plugin: 'java'

あるいはGradle2.2以降であれば、次のようにも記述できました。

plugins {
    id 'java'
}

Gradle3以降のJavaプロジェクトではこの部分が次のように変わります。

plugins {
    id 'jvm-component'
    id 'java-lang'
}

jvm-componentプラグインjava-langプラグインの二つを利用します。

  • jvm-componentプラグインでは主にJVM系のプラグインに共通のディレクトリーセットなどをrule based modelオブジェクトに従って定義します。
  • java-langプラグインではJavaに特化したディレクトリーセットや成果物生成のタスクなどをrule based modelオブジェクトに従って定義します。

コンポーネント

上記の最後のほうで、「rule based modelオブジェクトに従って定義します」という言葉を何度も使いました。これはビルドスクリプトの中でmodel{}ブロック内で定義されるコンポーネント(components)というオブジェクトがプロジェクトのディレクトリー構造などを決定することを意味します。

そこで、我々もmodel{}ブロック内においてcomponentsオブジェクトに値を設定してみたいと思います。

model {
    components {
        main(JvmLibrarySpec)
    }
}

さて、この記述をした後で、componentsタスクを実行してみてください。

$ gradle components
:components

------------------------------------------------------------
Root project
------------------------------------------------------------

JVM library 'main'
------------------

Source sets
    Java source 'main:java'
        srcDir: src/main/java
    JVM resources 'main:resources'
        srcDir: src/main/resources

Binaries
    Jar 'mainJar'
        build using task: :mainJar
        targetPlatform: Java SE 8
        tool chain: JDK 8 (1.8)
        Jar file: build/jars/mainJar/main.jar

Note: currently not all plugins register their components, so some components may not be visible here.

BUILD SUCCESSFUL

Total time: 0.793 secs
$ 

この段階で、以下の情報がわかります。

  • ライブラリーの名前はmain
  • ソースはsrc/main/javaディレクトリー以下に配置される
  • リソースファイルはsrc/main/resourcesディレクトリー以下に配置される
  • 生成されるクラスファイルのターゲットプラットフォームはJava8
  • 作成されるjarのディレクトリーと名前はbuild/jars/mainJar/main.jar

コンポーネントはいくつでも作ることができます。例えば、次のように記述することでmainというライブラリーとsubというライブラリーが作成されます。

model {
    components {
        main(JvmLibrarySpec)
        sub(JvmLibrarySpec)
    }
}

この場合、それぞれのディレクトリーは次のようになります。

ライブラリー ソース リソース Jarファイル
main src/main/java src/main/resources build/jars/mainJar/main.jar
sub src/sub/java src/sub/resources build/jars/subJar/sub.jar

ビルド

さて、問題を簡単にするためにsubライブラリーには消えてもらうことにします。

src/main/javaおよびsrc/main/resourcesにファイルを作ってみましょう。

src/main/java/sample/Main.java

package sample;

import java.io.IOException;

public class Main {
    public static void main(String... args) throws IOException {
        Res res = new Res();
        System.out.println(res.getMessage("message"));
        System.out.println(res.getMessage("name"));
        System.out.println(res.getMessage("my"));
    }
}

src/main/java/sample/Res.java

package sample;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

public class Res {

    private final Properties prop;

    public Res() throws IOException {
        ClassLoader cl = getClass().getClassLoader();
        prop = new Properties();
        try(InputStream is = cl.getResourceAsStream("app.properties")) {
            InputStreamReader r = new InputStreamReader(is, StandardCharsets.UTF_8);
            prop.load(r);
        }
    }

    public String getMessage(String key) {
        return prop.getProperty(key, "<no-entry>");
    }
}

src/main/resources/app.properties

message=こんにちわ
name=mikeさん
my=僕はGradle

では、これをビルドします。ビルドコマンドはgradle buildです。

$ gradle clean build
:clean
:compileMainJarMainJava
:processMainJarMainResources
:createMainJar
:mainJar
:assemble
:check UP-TO-DATE
:build

BUILD SUCCESSFUL

Total time: 0.847 secs
$

というわけで、出来上がったようです。

成果物の位置も調べてみます。

$ tree build
build
├── classes
│   └── mainJar
│       ├── META-INF
│       ├── app.properties
│       └── sample
│           ├── Main.class
│           └── Res.class
├── jars
│   └── mainJar
│       └── main.jar
├── jvm-dep-cache
└── tmp
    ├── compileMainJarMainJava
    └── createMainJar
        └── MANIFEST.MF

10 directories, 5 files

build/jars/mainJar/main.jarに成果物が作成されています。

では、実行してみますが、ちょっと調べたのですが、MANIFEST.MFをいじるためのDSLが見当たりませんでした(´・ω・`)

そのため、-cpでjarを読み込んでメインクラスを指定して実行します。

$ java -cp build/jars/mainJar/main.jar sample.Main
こんにちわ
mikeさん
僕はGradle

はい、まあ、大したことのないプロジェクトですね。


以上がJVM component modelを用いたJavaプロジェクトの作成入門でした。

なお、今回作成したプロジェクトのサンプルは以下のレポジトリーから取得できます。

github.com

次回(あるかどうかは知らない)はJVM component modelとJava9 Project Jigsawの関係について言及できればいいなぁ…