猫好きモバイルアプリケーション開発者記録

GradleでEclipseとIntellij IDEAの開発環境を作る

| Comments

ちょっと前まではMavenでプロジェクトを構築している人も多かったですが、 Android StudioがGradleを採用してからはGradleによるプロジェクト構築にシフトしている人も増えてきました。 とはいえ、Web業界で古いシステムを保守・運用している方だとまだまだMavenメインの利用者が多いかと思いますので、 Mavenからの移行も含めた観点で記載して行きます。

Mavenと比較したGradleの利点

Mavenと比較したGradleの利点を簡単にまとめると以下のような感じです。
(この記事を執筆時のGradleの最新バージョンは 1.11 です)


  • 記述量が圧倒的に少ない
  • 記述言語がGroovyなのでJavaも使える。そのため比較的なんでも記述できる
  • AntやMavenの機能も利用できる。特にAntはほぼすべての機能を利用可能。MavenはMavenプラグイン以外は殆ど利用可能
  • IDE(EclipseやIntellij IDEA)の設定も細かく記述でき、そこからIDEの設定ファイルを生成できる

まだまだありますが、代表的な利点はこんな感じです。


プロジェクトの準備

では早速ですが、各種設定をGradleで記述するための準備をしていきます。 今回の例では、以下のようなプロジェクト構成とします。


プロジェクト名 説明 コンテキストパス
base ベースロジックを含んだJavaプロジェクト なし
batch バッチシステム。baseプロジェクトを依存関係とする なし
admin 管理サイト。baseプロジェクトを依存関係とする /admin
front ユーザが閲覧するサイト。baseプロジェクトを依存関係とする /

また、各プロジェクトはMavenプロジェクトと同じで、以下のような一般的なWebアプリの構成にしておきます。

1
2
3
4
5
6
7
8
9
10
11
12
13
base - src - main - java
                  - resources

batch - src - main - java
                   - resources

admin - src - main - java
                   - resources
                   - webapp

front - src - main - java
                   - resources
                   - webapp

このディレクトリ構成にしておくと、余計な設定を記述する必要がないので便利です。
そして、Gradleにおいて設定ファイルを記述する場合、「build.gradle」というファイル名で設定ファイルを作成し、 この例のように複数プロジェクトがある場合は、Gradleのサブプロジェクトという機能を利用します。 サブプロジェクト機能を利用するには、「settings.gradle」というファイルを更に用意し、 build.gradleから定数値を外部ファイルから読み込みたい場合は、gradle.propertiesというファイルを用意します。 以上を踏まえた上で、ファイルとディレクトリ構成としては以下のようになります。

1
2
3
4
5
6
7
project - base
        - batch
        - admin
        - front
        - build.gradle
        - gradle.properties
        - settings.gradle

settings.gradle の記述

まず、settings.gralde を記述していきます。

1
2
3
4
include base
include batch
include admin
include front

これは project ディレクトリをメインのプロジェクトとしており、 その配下にある include で指定したディレクトリをサブプロジェクトとして扱うという指定になります。 以下のように指定することも可能です。

1
include base, batch, admin, front

gradle.properties の記述

以下の定数値を用意します。

1
2
3
4
5
6
7
8
# Mavenの自分サーバ用リポジトリ
project.maven.repository.url=http://192.168.1.200/maven

# Javaのバージョン
java.version=1.6

# Servletのバージョン
servlet.version=2.5

今回はこれらの値のみ外部ファイルへ記述します。

build.gradle の記述

それでは、メインとなる build.gradle の記述をしていきます。
以下のような内容になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
/**
 * Gradleによるプロジェクトビルドスクリプト。
 *
 * @author Kou
 */
apply plugin: 'eclipse'
apply plugin: 'idea'


/**
 * サブプロジェクト全体の共通設定。
 *
 */
subprojects {

    apply plugin: 'java'
    apply plugin: 'eclipse'
    apply plugin: 'eclipse-wtp'
    apply plugin: 'idea'

    repositories {

        maven {

            url getProperty('project.maven.repository.url')

        }
        mavenCentral()

    }

    configurations {

        // providedのコンパイル用ライブラリパス
        provided

    }

    // Javaのバージョンを指定
    sourceCompatibility = getProperty('java.version')
    targetCompatibility = getProperty('java.version')

    eclipse {

        classpath.defaultOutputDir = file('/target/classes')                // クラスファイル出力先

    }

}


/**
 * ベースロジック
 */
project(':base') {

    // クラスパスへ provided を追加
    sourceSets.main.compileClasspath     += configurations.provided
    sourceSets.test.compileClasspath     += configurations.provided
    sourceSets.test.runtimeClasspath     += configurations.provided
    eclipse.classpath.plusConfigurations += configurations.provided
    idea.module.scopes.PROVIDED.plus     += configurations.provided

    dependencies {

        // Java API
        provided(
                'javax.servlet:servlet-api:2.5',
                'javax.servlet.jsp:jsp-api:2.0',
        )

        // Struts
        compile(
                'taglibs:standard:1.1.2',
                'org.apache.struts:struts-core:1.3.10',
                'org.apache.struts:struts-tiles:1.3.10',
                'org.apache.struts:struts-taglib:1.3.10',
                'org.apache.struts:struts-el:1.3.10',
                'org.apache.struts:struts-extras:1.3.10',
        )

        // Solrj
        compile(
                'org.slf4j:slf4j-api:1.6.1',
                'org.slf4j:slf4j-log4j12:1.6.1',
                'org.apache.solr:solr-solrj:4.4.0',
        )

        // Solr Core
        compile('org.apache.solr:solr-core:4.4.0') {
            exclude group: 'org.slf4j', module: 'slf4j-jdk14'
        }

        // Test関係
        testCompile(
                'junit:junit:4.1.1',
        )

    }

}


/**
 * 管理サイト
 */
project(':admin') {

    apply plugin: 'war'

    // WARファイル名
    archivesBaseName = 'admin'

    // クラスパスへ provided を追加
    sourceSets.main.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.runtimeClasspath     += project(':base').configurations.provided
    eclipse.classpath.plusConfigurations += project(':base').configurations.provided
    idea.module.scopes.PROVIDED.plus     += project(':base').configurations.provided

    dependencies {

        compile project(':base')

    }

    eclipse {

        // Dynamic Web Projectの設定
        wtp {

            component {

                // EclipseのWTPで起動する場合のコンテキストパス
                contextPath = '/admin'

            }

            facet {

                facet name: 'java', version: getProperty('java.version')
                facet name: 'jst.web', version: getProperty('java.servlet.version')

            }

        }

    }

}


/**
 * バッチ
 */
project(':batch') {

    // クラスパスへ provided を追加
    sourceSets.main.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.runtimeClasspath     += project(':base').configurations.provided
    eclipse.classpath.plusConfigurations += project(':base').configurations.provided
    idea.module.scopes.PROVIDED.plus     += project(':base').configurations.provided

    dependencies {

        compile project(':base')

    }

}


/**
 * フロントサイト
 */
project(':front') {

    apply plugin: 'war'

    // WARファイル名
    archivesBaseName = 'ROOT'

    // クラスパスへ provided を追加
    sourceSets.main.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.compileClasspath     += project(':base').configurations.provided
    sourceSets.test.runtimeClasspath     += project(':base').configurations.provided
    eclipse.classpath.plusConfigurations += project(':base').configurations.provided
    idea.module.scopes.PROVIDED.plus     += project(':base').configurations.provided

    dependencies {

        compile project(':base')

    }

    eclipse {

        // Dynamic Web Projectの設定
        wtp {

            component {

                // EclipseのWTPで起動する場合のコンテキストパス
                contextPath = '/'

            }

            facet {

                facet name: 'java', version: getProperty('java.version')
                facet name: 'jst.web', version: getProperty('java.servlet.version')

            }

        }

    }

}

基本はこれだけでOKです。 Mavenの pom.xml と比較すると記述量が大分少なくなっていることがわかります。 また、Mavenの場合は、各プロジェクトのディレクトリ配下に pom.xml を記述する必要がありましたが、 Gradleでは1つのスクリプト内にすべての設定を記述できます。(敢えて分けて記述することも出来ます)

では、1つ1つ解説していきます。

サブプロジェクト全体の設定

各サブプロジェクト全体の共通設定は subprojects というブロック内で指定します。 ここから1つ1つ見て行きましょう。

01. プラグインの指定

先頭に apply plugin という指定がありますが、 これはどのプラグインを利用するかという指定です。 「Javaのimportのように単純に使いたいメソッドやクラスとかを呼び出すために定義しているだけだろう」と思われる方もいるかもしれませんが、 Gradleにおいては少し意味合いが違って、apply pluginを記述した段階でビルド動作に影響を与えます

1
2
3
4
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'eclipse-wtp'
apply plugin: 'idea'

この4つの指定では、各サブプロジェクトが
・「Javaプロジェクトである」
・「Eclipseプロジェクトである」
・「Eclipse WTPプロジェクトである」
・「Intellij IDEAプロジェクトである」
ということをGradle側へ伝えています。 この記述をすることで、各サブプロジェクトごとにEclipseやIntellij IDEAの設定ファイルを生成することが出来るようになります。

02. providedコンパイルの設定

「configurations」というブロックがあります。ここではプロジェクトで利用する設定パラメータを定義できます。 後述する依存関係の記述に関係しますが、providedコンパイルを実現するために、ここで provided というパラメータを宣言しています。 実は、Gradleでは Maven のような provided コンパイルの動作を標準ではサポートしていません。 とはいえ、簡単に同様の処理を実現することが出来ます。そのための準備として、ここにパラメータを宣言します。

03. Javaのバージョンの設定

「sourceCompatibility」「targetCompatibility」という記述がありますが、 これはビルド時に使用されるJavaのバージョン指定になります。 これを記述しない場合、インストールされているJDKのバージョンでビルドされます。 また、ここで指定したバージョンは、EclipseのJavaのバージョン指定にも利用されます。 基本的には環境依存がないようにバージョン指定をしておくことをオススメします。

04. Eclipseでコンパイルしたクラスファイルの出力先の設定

「eclipse」というブロック内に、「classpath.defaultOutputDir」というプロパティがあります。 ここに指定されたディレクトリにコンパイルしたクラスファイルが出力されます。 指定しない場合は、各プロジェクトのディレクトリ直下に「bin」というディレクトリが作成され、そこに出力されます。

依存関係の記述

今回の例では、baseプロジェクトに利用する依存関係をすべて定義し、 各サブプロジェクトにおいては base プロジェクトそのものを依存関係として定義しています。 これはGradleに詳しくない方でも上記スクリプトを見ても大体予想がついているかと思いますが、 「dependencies」という部分で定義します。 dependenciesではMavenリポジトリにあるライブラリを指定することが出来ます。 指定の形式としては、以下のようになります。

1
compile 'groupId:artifactId:version'

groupId、artifactId、versionをコロン(:)区切りで指定します。 しかし、Servlet APIのJARなど、中にはWARビルド時にWAR内へ含めたくないライブラリもあります。 その場合は以下のような指定が必要になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
configurations {

    // providedのコンパイル用ライブラリパス
    provided

}

dependencies {

    provided 'javax.servlet:servlet-api:2.5'

}

// クラスパスへ provided を追加
sourceSets.main.compileClasspath     += project(':base').configurations.provided
sourceSets.test.compileClasspath     += project(':base').configurations.provided
sourceSets.test.runtimeClasspath     += project(':base').configurations.provided
eclipse.classpath.plusConfigurations += project(':base').configurations.provided    // Eclipse用のクラスパス
idea.module.scopes.PROVIDED.plus     += project(':base').configurations.provided    // Intellij IDEA用の Provided クラスパス

このような指定をすることで、 依存関係をリポジトリから取得しつつも、WARには含めないようにすることが出来ます。 今回は base プロジェクトに宣言された provided な依存関係を base プロジェクトを依存関係とする各プロジェクトへ追加するため 「project(‘:base’).configurations.provided」を追加していますが、 そのプロジェクトのみで provided の依存関係を定義する場合は「configurations.provided」を各クラスパスへ += で追加するようにしてください。

WARの設定

今回はTomcatを利用することを前提としていますので、 その場合の設定です。 まず、EclipseでWTPを利用する場合の設定が以下になります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
eclipse {

    // Dynamic Web Projectの設定
    wtp {

        component {

            // EclipseのWTPで起動する場合のコンテキストパス
            contextPath = '/'

        }

        facet {

            facet name: 'java', version: getProperty('java.version')
            facet name: 'jst.web', version: getProperty('java.servlet.version')

        }

    }

}

各ブロック名を見れば勘のいい方は大体理解できるかと思います。 Eclipseの動的Webプロジェクトの必要な設定を上記の設定で記述しています。 また、実際のWARファイル名は以下の指定になります。

1
2
3
4
5
6
7
8
9
10
11
/**
 * フロントサイト
 */
project(':front') {

    apply plugin: 'war'

    // WARファイル名
    archivesBaseName = 'ROOT'

}

ここで指定された名前がWARファイル名になります。(拡張子は不要)
Tomcatの場合だと、server.xmlに記述をしない場合はWARファイル名がそのままコンテキストパスになりますので
(ROOTという名前は例外として / になる) ここでWARファイル名を指定します。

一見するとEclipseの方で設定しているからWARファイル名は勝手に設定してくれればいいと思う方もいるかもしれませんが、 Eclipseの設定はあくまでEclipseにおける設定なので、 実際にWARを作成時に使われる設定値ではありません。

実際にIDEで読み込んでみる

あとは実際にIDEでGradle Projectとしてインポートすれば完成です。
Gradle Projectとしてインポートするためには、Eclipseの場合はEclipse Marketplaceにある「Gradle Integration for Eclipse」プラグインをインストールしてください。
このプラグインを利用すると、リモートリポジトリから取得した依存関係の参照パスを ローカルPCのパスとしてEclipse設定ファイルへ記述しなくなるため Eclipseプロジェクトの設定ファイルを複数人で共有するプロジェクトにおいては最適です。

Gradle Integration for Eclipseプラグインをインストールしたら
このプラグインのEclipse設定からソースファイルのエンコーディングの設定(-Dfile.encoding=UTF-8)を追加するのを忘れないでください。
そのままだとWindowsのデフォルトエンコーディングがMS932のため問題が出ます。

Intellij IDEAにおいては、12以上のバージョンを利用している場合はJetGradleというIDEのエンジンが build.gradleの内容を解析してプロジェクトの作成をしてくれます。
ただし、Intellij IDEAのartifactsやfacetの設定はすべてを自動ではやってくれないので一部手動で行う必要があるのと、 上記で記述した provided 指定したライブラリについては、JetGradle側で export 設定を何故か行わないため
ここも手動でチェックをつけてあげる必要が出てきます。
13.1からは自動でartifactsやfacetも生成してくれますが、artifactsは複数プロジェクトに対応していないのかROOTコンテキストのプロジェクト分しか作成されません。
Gradle側のIntellij IDEA対応はあまり熱心ではないみたいなのでこの辺はいずれ対応してくれるであろうことを期待しましょう。

【2014/04/28追記】 Intellij IDEAでartifactsが作成されない不具合がEAP版の13.1 135.760で修正されました。 早々に試したい方はこちらからダウンロードできます。

まとめ

以上、早足で説明しましたが、 導入としてはこれくらいの知識があれば利用できます。 次回は実際にデプロイを行うときの手順を Mavenからの移行の観点を交えて説明していきたいと思います。

Comments