ビルドツール make / ant / maven / gradle ざっくり理解メモ


Spring Boot入門の本を読むと、Maven と Gradle でのビルドの方法が併記されているものが殆どだ。Gradle は Mavenとどう違うのだろうか? なんで Gradle 使わないといけないのか? とふつふつと疑問が湧いてくる。しかし、どちらのホームページを読んでも、非常に多くのことができることを漠然と書いてあるだけで、具体的な違いを簡単に理解させてもらえない。

そこで、動かしながら、それぞれの基本と両者の違いを確認したメモである。ついでに、make と ant も整理してみた。

make (メイク)

makeコマンドは、プログラムのビルド作業を自動化する。 コンパイル、リンク、インストール等のルールを記述したテキストファイル (Makefile) に従って、これらの作業を実行する。 C/C++言語だけでなく、Java言語にも適用することができる。

  • 構成ファイル: Makefile, makefile

ディレクトリ構造は、特に決まっていない。わかりやすくするために、実行モジュールごと、または、ライブラリごとで、ディレクトリが作成される。

.
├── HelloWorld.java
└── Makefile

makeの構成ファイルで、生成したいオブジェクト: ソースコード または 生成したいオブジェクト: 中間ファイル を書いて、2行目は TAB で字下げして、コンパイラを指定する。 その後のマクロ記号は ソースとなるコードで置き換えられる。

HelloWorld.class: HelloWorld.java
    javac $<

最もシンプルなJavaのソースコード

HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

コマンドの実行

材料となるファイルのリスト、コンパイル実行、プログラム実行を順番に実行した例である。

$ ls
HelloWorld.java Makefile

$ make
javac HelloWorld.java

$ java HelloWorld
Hello World

参考資料

Ant (アント)

AntはJavaで書かれたプログラムで、Javaアプリケーションのコンパイル、アセンブル、テスト、および実行を可能にする多数の組み込みタスクを提供する。AntはJavaクラスを使用して拡張できる。構成ファイルはXMLベースで、タスクを呼び出す。

  • 構成ファイル: build.xml
  • コマンドのインストール: $ brew install ant

ANT プロジェクトの作成

build.xml のディレクトリに src 下にパッケージ名のディレクトリを作成して、ソースコードのファイルを作成する。

.
├── build.xml
└── src
    └── tkr
        └── HelloWorld.java

構成ファイルでは、target としてタスクを記述、そして、前提となる depend を作成する。

build.xml
<project name="HelloWorld" basedir="." default="main">
    <property name="src.dir"     value="src"/>
    <property name="build.dir"   value="build"/>
    <property name="classes.dir" value="${build.dir}/classes"/>
    <property name="jar.dir"     value="${build.dir}/jar"/>
    <property name="main-class"  value="tkr.HelloWorld"/>

    <target name="clean">
        <delete dir="${build.dir}"/>
    </target>

    <target name="compile">
        <mkdir dir="${classes.dir}"/>
        <javac srcdir="${src.dir}" destdir="${classes.dir}"/>
    </target>

    <target name="jar" depends="compile">
        <mkdir dir="${jar.dir}"/>
        <jar destfile="${jar.dir}/${ant.project.name}.jar" basedir="${classes.dir}">
            <manifest>
                <attribute name="Main-Class" value="${main-class}"/>
            </manifest>
        </jar>
    </target>

    <target name="run" depends="jar">
        <java jar="${jar.dir}/${ant.project.name}.jar" fork="true"/>
    </target>
    <target name="clean-build" depends="clean,jar"/>
    <target name="main" depends="clean,run"/>

</project>

以下はJavaのソースコード

src/tkr/HelloWorld.java
package tkr;

public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World");
    }
}

ANTの実行

ant によるコンパイルの実行

$ ant compile
Buildfile: /Users/maho/web-apl/test-ant/build.xml

compile:
    [mkdir] Created dir: /Users/maho/web-apl/test-ant/build/classes
    [javac] /Users/maho/web-apl/test-ant/build.xml:14: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
    [javac] Compiling 1 source file to /Users/maho/web-apl/test-ant/build/classes

BUILD SUCCESSFUL
Total time: 0 seconds

ant によるJavaコードの実行

maho:test-ant maho$ ant run
Buildfile: /Users/maho/web-apl/test-ant/build.xml

compile:
    [javac] /Users/maho/web-apl/test-ant/build.xml:14: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds

jar:
    [mkdir] Created dir: /Users/maho/web-apl/test-ant/build/jar
      [jar] Building jar: /Users/maho/web-apl/test-ant/build/jar/HelloWorld.jar

run:
     [java] Hello World

BUILD SUCCESSFUL
Total time: 0 seconds

ant によって Jarファイルを作成して、実行する。

$ ant jar
Buildfile: /Users/maho/web-apl/test-ant/build.xml

compile:
    [javac] /Users/maho/web-apl/test-ant/build.xml:14: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds

jar:

BUILD SUCCESSFUL
Total time: 0 seconds

$ tree
.
├── build
│   ├── classes
│   │   └── tkr
│   │       └── HelloWorld.class
│   └── jar
│       └── HelloWorld.jar
├── build.xml
└── src
    └── tkr
        └── HelloWorld.java

6 directories, 4 files

$ java -jar build/jar/HelloWorld.jar 
Hello World

参考資料

Maven (メーベン / メイベン)

Mavenは Javaベースのプロジェクトの構築と管理に使用できるツールである。Apache Antに代わるものとして作られた。pom.xmlファイルは、Mavenでのプロジェクトの構成の中核である。プロジェクトをビルドするために必要な情報の大部分を含む単一の構成ファイルだ。pom.xml を効果的に使用するためにすべての複雑さを理解する必要はない。

Mavenのリポジトリは、ビルドアーティファクトと呼ばれる実行形式をビルドするために必要なアーティファクトを保存する領域で、ローカルとリモートの二つがある。ローカルリポジトリは、Mavenを実行しているコンピュータ上のディレクトリだ。これはリモートリポジトリからのダウンロードをキャッシュし、まだ、リリースされていない一時的なビルドアーティファクトを含む。リモートリポジトリは、サードパーティによって設定されたリポジトリである可能性もある。また、社内のファイルまたはHTTPサーバーに設定された内部リポジトリを設けることもできる。

Mavenには、ビルドとレポートの2種類のプラグインがある。ビルドプラグインは、ビルド中に実行され、要素で構成される。レポートプラグインは、サイトの生成中に実行され、要素で構成される。

  • 構成ファイル: pom.xml
  • コマンドのインストール: $ brew install maven

Maven プロジェクトの作成

次のコマンドで、雛形となるプロジェクトを作成できる。

$ mvn archetype:generate -DgroupId=org.tkr.app -DartifactId=hello-world -DarchetypeArtifactId=maven-archetype-quickstart -DarchetypeVersion=1.4 -DinteractiveMode=false

プロジェクトを作成すると、テンプレートになる pom.xmlsrc ディレクトリとソースコードを作成する。

hello-world/
├── pom.xml
└── src
    ├── main
    │   └── java
    │       └── org
    │           └── tkr
    │               └── app
    │                   └── App.java
    └── test
        └── java
            └── org
                └── tkr
                    └── app
                        └── AppTest.java

以下は、自動生成されたテンプレートのpom.xmlである。 Spring-Bootなどを利用する場合は、必要となるアーティファクトを追加する。

pom.xml
pom.xml 
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.tkr.app</groupId>
  <artifactId>hello-world</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>hello-world</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

Javaのソースコードは、自動生成される。

src/main/java/org/tkr/app/App.java
package org.tkr.app;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
    }
}

mvnコマンドの実行

mvn package コマンドを実行すると、中央リポジトリから、必要なアーティファクト(ライブラリ・ファイル)をダウンロードして、JavaのソースコードからパッケージとしてJavaを作成する。

$ mvn package
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< org.tkr.app:hello-world >-----------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-resources-plugin/3.0.2/maven-resources-plugin-3.0.2.pom
<中略>
Downloading from central: https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar
Downloaded from central: https://repo.maven.apache.org/maven2/junit/junit/4.11/junit-4.11.jar (245 kB at 573 kB/s)
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:resources (default-resources) @ hello-world ---
Downloading from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.24/plexus-utils-3.0.24.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/codehaus/plexus/plexus-utils/3.0.24/plexus-utils-3.0.24.jar (247 kB at 800 kB/s)
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/maho/web-apl/test-maven/hello-world/src/main/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/maho/web-apl/test-maven/hello-world/target/classes
[INFO] 
[INFO] --- maven-resources-plugin:3.0.2:testResources (default-testResources) @ hello-world ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] skip non existing resourceDirectory /Users/maho/web-apl/test-maven/hello-world/src/test/resources
[INFO] 
[INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ hello-world ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 1 source file to /Users/maho/web-apl/test-maven/hello-world/target/test-classes
[INFO] 
[INFO] --- maven-surefire-plugin:2.22.1:test (default-test) @ hello-world ---
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/maven-surefire-common/2.22.1/maven-surefire-common-2.22.1.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/maven-surefire-common/2.22.1/maven-surefire-common-2.22.1.pom (11 kB at 44 kB/s)
<中略>
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.22.1/surefire-junit4-2.22.1.jar
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/surefire/surefire-junit4/2.22.1/surefire-junit4-2.22.1.jar (85 kB at 309 kB/s)
[INFO] 
[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running org.tkr.app.AppTest
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.02 s - in org.tkr.app.AppTest
[INFO] 
[INFO] Results:
[INFO] 
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO] 
[INFO] 
[INFO] --- maven-jar-plugin:3.0.2:jar (default-jar) @ hello-world ---
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-archiver/3.1.1/maven-archiver-3.1.1.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/maven/maven-archiver/3.1.1/maven-archiver-3.1.1.pom (4.3 kB at 17 kB/s)
<中略>
Downloaded from central: https://repo.maven.apache.org/maven2/org/tukaani/xz/1.5/xz-1.5.jar (100 kB at 188 kB/s)
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/commons/commons-compress/1.11/commons-compress-1.11.jar (426 kB at 368 kB/s)
[INFO] Building jar: /Users/maho/web-apl/test-maven/hello-world/target/hello-world-1.0-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  13.066 s
[INFO] Finished at: 2020-07-26T20:54:23+09:00
[INFO] ------------------------------------------------------------------------

中央リポジトリからダウンロードされたアーティアファクトは、ローカルリポジトリにダウンロードして保管される。

$ find .m2
.m2
.m2/repository
.m2/repository/commons-codec
.m2/repository/commons-codec/commons-codec
.m2/repository/commons-codec/commons-codec/1.6
.m2/repository/commons-codec/commons-codec/1.6/commons-codec-1.6.pom.sha1
.m2/repository/commons-codec/commons-codec/1.6/commons-codec-1.6.jar.sha1
.m2/repository/commons-codec/commons-codec/1.6/commons-codec-1.6.jar
.m2/repository/commons-codec/commons-codec/1.6/commons-codec-1.6.pom
.m2/repository/commons-codec/commons-codec/1.6/_remote.repositories

サンプルコードの実行

$ java -cp target/hello-world-1.0-SNAPSHOT.jar org.tkr.app.App
Hello World!

クリーンナップ

$ mvn clean
[INFO] Scanning for projects...
[INFO] 
[INFO] ----------------------< org.tkr.app:hello-world >-----------------------
[INFO] Building hello-world 1.0-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ hello-world ---
[INFO] Deleting /Users/maho/web-apl/test-maven/hello-world/target
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  0.211 s
[INFO] Finished at: 2020-07-26T21:07:00+09:00
[INFO] ------------------------------------------------------------------------

Dockerを使ったビルド方法

Mavenのコンテナを使い、次のDockerfileを編集して、コンテナ上でビルドすることができる。

Dockerfile
FROM maven:3.6.3-jdk-14 AS build
COPY src src
COPY pom.xml pom.xml
RUN  mvn clean package


FROM openjdk:8-jdk-alpine
RUN addgroup -S spring && adduser -S spring -G spring
USER spring:spring

ARG JAR_FILE=target/*.jar
COPY --from=build ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]

コンテナ上に Jar ファイルを作成することができる。 -v オプションでディレクトリを指定することで、パソコンにJavaやMavenをインストールすることなく、ビルドすることができる。

$ docker build -t hello-world:1.0 .

参考資料

Gradle (グラドル/グレードル)

Gradleは、ビルドの自動化と多言語開発のサポートに重点を置いたビルドツールだ。Gradleは、Java、Scala、Android、C/C++、Groovyなどの複数の言語とプラットフォームにわたるビルド自動化をサポートする、そして、IDEである Eclipse、IntelliJ、Jenkins などに統合されている。

  • 構成ファイル: pom.xml
  • コマンドのインストール: $ brew install gradle

Gradle プロジェクトの作成

対話型で番号を選んで、目的のプログジェクトを作成できる。

$ gradle init 
Starting a Gradle Daemon (subsequent builds will be faster)

Select type of project to generate:
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 2

Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Swift
Enter selection (default: Java) [1..5] 3

Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2] 1

Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 1

Project name (default: test-gradle2): hello-world
Source package (default: hello.world): 

> Task :init
Get more help with your project: https://docs.gradle.org/6.5.1/userguide/tutorial_java_projects.html

BUILD SUCCESSFUL in 36s
2 actionable tasks: 2 executed

プロジェクトディレクトリ構造

プロジェクトを作成することで、次のディレクトリを作成する。

.
├── build.gradle (1)
├── gradle 
│   └── wrapper
│       ├── gradle-wrapper.jar (2) 
│       └── gradle-wrapper.properties (3) 
├── gradlew (4)
├── gradlew.bat (5)
├── settings.gradle (6)
└── src
    ├── main
    │   ├── java
    │   │   └── hello
    │   │       └── world
    │   │           └── App.java (7)
    │   └── resources
    └── test
        ├── java
        │   └── hello
        │       └── world
        │           └── AppTest.java (8)
        └── resources

(1) 現在のプロジェクトを構成するためのGradleビルドスクリプト
(2) Gradle Wrapper実行可能JAR
(3) Gradleラッパー構成プロパティ
(4) Unixベースのシステム用のGradleラッパースクリプト
(5) Windows用Gradleラッパースクリプト
(6) Gradleビルドを構成するためのGradle設定スクリプト
(7) テンプレートソースコード
(8) テスト テンプレートソースコード

ここから、編集の必要があるファイルについて、内容を見ていく。

ルートプロジェクト名を書いておくファイルsettings.gradleである。Gradleのプロジェクトは、ルートの下に複数のサブプロジェクトが作れる。子プロジェクトのディレクトリや情報をこのファイルに記述する。詳細は参考資料を見て欲しい。

settings.gradle
rootProject.name = 'hello-world'

このファイルは、maven の pom.xml と近い。 プラグイン、リポジトリ URL、依存するアーティファクトなどを記述する。これも詳細は、参考資料を見てもらいたい。

build.gradle
plugins {
    id 'java'
    id 'application'
}

repositories {
    jcenter()
}

dependencies {
    implementation 'com.google.guava:guava:29.0-jre'
    testImplementation 'junit:junit:4.13'
}

application {
    mainClassName = 'hello.world.App'
}

自動生成されるアプリケーションのmainがあるJavaファイル

src/main/java/hello/world/App.java
package hello.world;
public class App {
    public String getGreeting() {
        return "Hello world.";
    }
    public static void main(String[] args) {
        System.out.println(new App().getGreeting());
    }
}

同様に生成されるテスト用コード

src/test/java/hello/world/AppTest.java
package hello.world;
import org.junit.Test;
import static org.junit.Assert.*;
public class AppTest {
    @Test public void testAppHasAGreeting() {
        App classUnderTest = new App();
        assertNotNull("app should have a greeting", classUnderTest.getGreeting());
    }
}

Gradle の実行方法

Gradleはタスクを追加できる。そして、次のコマンドは、タスクのリストは追加されたものを含めて表示する。

$ ./gradlew tasks
Starting a Gradle Daemon, 1 incompatible Daemon could not be reused, use --status for details

> Task :tasks

------------------------------------------------------------
Tasks runnable from root project
------------------------------------------------------------

Application tasks
-----------------
run - Runs this project as a JVM application

Build tasks
-----------
assemble - Assembles the outputs of this project.
build - Assembles and tests this project.
buildDependents - Assembles and tests this project and all projects that depend on it.
buildNeeded - Assembles and tests this project and all projects it depends on.
classes - Assembles main classes.
clean - Deletes the build directory.
jar - Assembles a jar archive containing the main classes.
testClasses - Assembles test classes.

Build Setup tasks
-----------------
init - Initializes a new Gradle build.
wrapper - Generates Gradle wrapper files.

Distribution tasks
------------------
assembleDist - Assembles the main distributions
distTar - Bundles the project as a distribution.
distZip - Bundles the project as a distribution.
installDist - Installs the project as a distribution as-is.

Documentation tasks
-------------------
javadoc - Generates Javadoc API documentation for the main source code.

Help tasks
----------
buildEnvironment - Displays all buildscript dependencies declared in root project 'hello-world'.
components - Displays the components produced by root project 'hello-world'. [incubating]
dependencies - Displays all dependencies declared in root project 'hello-world'.
dependencyInsight - Displays the insight into a specific dependency in root project 'hello-world'.
dependentComponents - Displays the dependent components of components in root project 'hello-world'. [incubating]
help - Displays a help message.
model - Displays the configuration model of root project 'hello-world'. [incubating]
outgoingVariants - Displays the outgoing variants of root project 'hello-world'.
projects - Displays the sub-projects of root project 'hello-world'.
properties - Displays the properties of root project 'hello-world'.
tasks - Displays the tasks runnable from root project 'hello-world'.

Verification tasks
------------------
check - Runs all checks.
test - Runs the unit tests.

Rules
-----
Pattern: clean<TaskName>: Cleans the output files of a task.
Pattern: build<ConfigurationName>: Assembles the artifacts of a configuration.
Pattern: upload<ConfigurationName>: Assembles and uploads the artifacts belonging to a configuration.

To see all tasks and more detail, run gradlew tasks --all

To see more detail about a task, run gradlew help --task <task>

BUILD SUCCESSFUL in 3s
1 actionable task: 1 executed

Javaプログラムのコンパイルと実行

$ ./gradlew run

> Task :run
Hello world.

BUILD SUCCESSFUL in 6s
2 actionable tasks: 2 executed

オプションに jar をつけることで、アプリケーションのjar ファイルを作成する。

$ ./gradlew jar

BUILD SUCCESSFUL in 506ms
2 actionable tasks: 1 executed, 1 up-to-date
maho:test-gradle2 maho$ tree
.
├── build
│   ├── classes
│   │   └── java
│   │       └── main
│   │           └── hello
│   │               └── world
│   │                   └── App.class
│   ├── generated
│   │   └── sources
│   │       ├── annotationProcessor
│   │       │   └── java
│   │       │       └── main
│   │       └── headers
│   │           └── java
│   │               └── main
│   ├── libs
│   │   └── hello-world.jar

コンテナを利用した gradle によるビルド

maven同様に、gradleのコンテナを利用することで、パソコンにJava SDKやGradleがインストールされていなくても、コンテナ上でGradleを利用してJava実行形式をビルドすることができる。

gradleプロジェクトのディレクトリに移動して、以下を実行するだけで良い。 それに加えて、gradle は、SDKのバージョンのタグが複数提供されているので、Docker Hub の Gradke のページを参照して、必要なJavaのバージョンを選ぶと良い。

docker run --rm -u gradle -v "$PWD":/home/gradle/project -w /home/gradle/project gradle gradle <gradle-task>

mavenと同じように、マルチステージ Dockerfile を作成して、コンパクトなコンテナイメージを作ることもできる。

参考資料

まとめ

make, ant, maven, gradle とビルドツールの歴史を追いかけながら違いを見てきた。makeは現在でも利用されるツールであるが、mavenやgradleとの違いは、依存するライブラリ(アーティファクト)がネットワーク上のリポジトリから得られるかどうかの違いだと思う。そして、makeはシェルやコマンドベースで依存関係を解決させることが基本となっており、antはJavaクラスで拡張できるようになっているものの、リポジトリからアーティファクトを入手できないのでライブラリをダウンロードしておく必要がある。それに比べると、mavenやgradleはリポジトリを使って他者のアーティファクトを再利用できる。maven と gradleの違いは、pom.xml と build.gradle といった構成ファイルの簡潔さや、足掛かりとなるコードの生成機能などが、gradleでは洗練度されているように思う。 コードを開発する作業は、より多くの開発者の成果物をベースに再利用を繰り返しながら、生産性を高め、開発のスピードを高める宿命がある。そのため、ビルドツールは、開発者の必要性を満たすために、進化していくのだと感じた。 mavenやgradleを利用して開発する場合、ネットワークからコードをダウンロードするために、脆弱性や悪意あるコードの混入から守る方法も同時に考えなければならない。