私は Java コードを書くのに Eclipse を使っています。以前はプロジェクト形式として標準の Java Project を選択していましたが、 現在は Gradle プロジェクト形式を選択するようにしています。
Gradle の素晴らしさを知ったのは Android アプリケーションの開発で Android Studio に触れた折でした (Android Studio のプロジェクト形式は Gradle プロジェクト形式だったのです)。以来、 Android 以外の Java アプリケーション開発 (Web サーバーアプリや JavaFX アプリ) でも Gradle プロジェクト形式を採用するようになりました。
今回は Gradle プロジェクトの雛形をカスタマイズする方法を紹介します。
それでも私はEclipseを続けるよ!
ちなみに、 Android Studio は IntelliJ IDEA がベースになっています。Android 以外の Java アプリケーション開発環境も Eclipse から IntelliJ IDEA に変えてしまったほうが楽かな?と考えたりもしたのですが、 悩んだ末に、 長年愛用している Eclipse を使い続けることにしました。
IntelliJ IDEA に乗り換えなかった理由は製品が有償だったことです。IntelliJ IDEA には無償で使えるオープンソースの Community 版もあります。Community 版は個人利用だけでなく商用利用も認められています。それでも、 私は、 有償の Ultimate 版と無償の Community 版の 2 つが存在していることが気になりました。IntelliJ IDEA Community 版では一部の機能が使えないという差別化が図られています。それがどうしても好きになれなかったのです。
Buildship
話を戻しましょう。
2015 年にリリースされた Eclipse 4.5 MARS から Buildship という Gradle プラグインが標準で含まれるようになりました。すぐに Gradle プロジェクトを使い始めることができます。
Eclipse の標準プロジェクト形式 (Java Project) を作成する場合は New → Java Project を選択しますよね。
Gradle プロジェクト形式を作成する場合は Java Project ではなく Project... を選択します。
ウィザードが表示されるので、 Gradle Project
を選択して Next > を押します。
ウィザードを最後まで進めていくと、 プロジェクトの雛形が自動的に作成されます。
この自動的に作成される Gradle プロジェクトは私の好みではないです。
build.gradle
で指定されているプラグインがjava-library
になっている。ライブラリよりアプリケーションを開発することのほうが多いのでjava
になっていて欲しい。build.gradle
の依存ライブラリにguava
が入っている。Google 推しやめてください。サンプルとして初期配置されている
Library.java
クラス、 すぐに消してしまうだけなので、 最初からないほうがいいです。test
フォルダーも要らないです。(こんなこと言ったらテストコード書いてないのか!と怒られそうですが…。)
もっと自分好みの雛形を出力して欲しい! そう思いますよね?
Gradleプロジェクトの雛形をカスタマイズする
というわけで、 自動作成される Gradle プロジェクトの雛形をカスタマイズすることにしました。
Gradle プロジェクトの雛形は Eclipse の Buildship プラグインが出力しているわけではなく、 Gradle 自身が出力しています。Buildship を使わずにコマンドラインで gradle init
を実行した場合も同じ雛形が出力されます。Buildship が特別なことをしているわけではないので、 Gradle 自体の機能でプロジェクトの雛形をカスタマイズする方法を考えます。
init.gradle
Gradle にはビルドプロセスをカスタマイズするための init.gradle
という仕組みが用意されています。ユーザーフォルダー直下の .gradle
フォルダーに init.gradle
というファイル名で Gradle ファイルを置いておくと、 ビルドプロセスの前に init.gradle
が実行されるようになります。
- Windows の場合、 配置パスは
%USERPROFILE%\.gradle\init.gradle
になります。
この仕組みを使って、 あらかじめ用意しておいた雛形ファイル群がプロジェクトにコピーされるようにします。
init.gradlebuildFinished {
Task init = rootProject.tasks.findByName('init')
if(init && init.state.executed && !init.state.skipped) {
if(init.type.contains('java')) {
//delete files
["src/test", "src/main/java/${rootProject.name}"]
.each {
println "delete ${it}"
delete rootProject.file(it)
}
}
//copy files
copy {
from "templates/${init.type}"
into rootProject.file('.')
}
}
if(rootProject.tasks.findByName('nothing')) {
copy {
from "templates/.gitignore"
into rootProject.file('.')
}
}
}
私は、 このような init.gradle
スクリプトを用意しました。
init.gradle
はビルドプロセスで常に実行されることに注意してください。新規プロジェクトの作成時 (init
タスク実行時) だけ雛形のコピーがおこなわれるように if 文の条件を init.state.executed
と !init.state.skipped
としています。
この init.gradle
スクリプトでは以下の 3 つの処理をおこなっています。
java
プロジェクトだったらtest
フォルダーを削除します。(怒らないで!)init.gradle
と同じ場所にあるtemplates/${init.type}
フォルダー内のファイルをプロジェクトにコピーします。${init.type}
の部分はgradle init
の--type
引数で指定された値 (プロジェクトのタイプ名) に置き換えられます。Buildship からプロジェクトを作成した場合はjava-library
が指定されるので、templates/java-library
配下のファイルがプロジェクトにコピーされます。nothing
タスクが実行された場合にtemplates/.gitignore
をプロジェクトにコピーします。この処理の詳細については後述します。
templates/java-library
フォルダーには build.gradle
、 javadoc.css
、 lib
を置きました。
- %USERPROFILE%
- .gradle
- init.gradle
- templates
- .gitignore
- java-library
- build.gradle
- javadoc.css
- lib
- .gradle
私が雛形として使っている build.gradle
がこれです。
build.gradleapply plugin: 'java'
def defaultEncoding = 'UTF-8'
tasks.withType(AbstractCompile).each { it.options.encoding = defaultEncoding }
tasks.withType(GroovyCompile).each { it.groovyOptions.encoding = defaultEncoding }
javadoc {
options.charSet = defaultEncoding
options.encoding = defaultEncoding
options.stylesheetFile = new File(rootDir, 'javadoc.css')
}
repositories {
jcenter()
}
dependencies {
compile fileTree(dir: 'lib',
includes: ['**/*.jar'],
excludes: ['**/*-sources.jar', '**/*-javadoc.jar'])
}
文字コードの指定、 Javadoc スタイルシートの指定、 lib
フォルダー内の JAR ファイルを参照ライブラリとして追加するようにしています。
init.gradle
と雛形ファイル一式は以下のリンクからダウンロードできます。
BuildshipからGradleプロジェクトを作成してみる
ユーザーフォルダー直下の .gradle
フォルダーに init.gradle
と templates
をコピーした状態で、 Eclipse で Gradle プロジェクトを新規作成してみます。
今度は以下のような構成でプロジェクトが作成されました。
test
フォルダーやLibrary.java
が存在していない。javadoc.css
が配置されている。build.gradle
の中身がカスタマイズ版に置き換わっている。.gitignore
がカスタマイズ版に置き換わっている。(Eclipse のエクスプローラービューには表示されていません。)
これで、 Gradle プロジェクト新規作成後の定型的なカスタマイズ作業を減らすことができますね。ぜひ、 自分好みのプロジェクトの雛形にカスタマイズしてみてください!
.gitignore
- Git を使っていない人は読み飛ばして構いません。
.gitigonore
は Git リポジトリーへの登録を除外するファイルやフォルダーを記述するリストです。このファイルに Eclipse 固有の管理ファイル .classpath
、 .project
、 .settings
を追加しておけば、 Git リポジトリーに Eclipse 固有の管理ファイルが格納されることはなくなり、 純粋な Gradle プロジェクトのみを Git リポジトリーに格納することができます。
Eclipse の Buildship プラグインはとても賢く build.gradle
の dependencies
設定から自動的に Eclipse のライブラリ参照を構成してくれるので、 Eclipse のプロジェクト管理ファイルを大事に守る必要はありません。Buildship プラグインなら、 いつでも Gradle プロジェクトから Eclipse のプロジェクトを再構成できます。
とても便利な .gitignore
ファイルですが、 このファイルは Gradle でも特別扱いされており、 templates/java-library
に .gitignore
を配置しておいても新規プロジェクトにコピーされません。
なぜかというと、 init.gradle
の buildFinished
リスナーで .gitignore
をコピーしても、 その後で Gradle によって .gitignore
が勝手に上書きされてしまうからです。この強制上書きは GitIgnoreGenerator というクラスでおこなわれています。
GitIgnoreGenerator.java のソースコード、 結構ひどいコードだと思います。
GitIgnoreGenerator.javapublic class GitIgnoreGenerator implements BuildContentGenerator {
@Override
public void generate(InitSettings settings) {
File file = fileResolver.resolve(".gitignore");
try {
PrintWriter writer = new PrintWriter(new FileWriter(file));
try {
writer.println("# Ignore Gradle project-specific cache directory");
writer.println(".gradle");
writer.println();
writer.println("# Ignore Gradle build output directory");
writer.println("build");
} finally {
writer.close();
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
}
外部リソースを元に .gitignore
を作るのではなく、 ソースコードに .gitignore
に書き込む中身がハードコーディングされているという…。さらに、 この BuildContentGenerator
インターフェースの呼び出し工程も追いかけてみましたが、 ファクトリーパターンになっているわけでもなく、 GitIgnoreGenerator
の振る舞いを抑制したり差し替えたりするのは困難に見えました。
Gradle のエンジニアさんでも、 こんなテキトーなコード書くんですね…。
というわけで、 gradle init
でプロジェクトを新規作成したときに、 独自の .gitignore
に置き換えることは (通常の方法では) できませんでした。
しかし、 幸運なことに Eclipse から Buildship で Gradle プロジェクトを作成した場合は gradle init
だけでなく、 そのあとで gradle nothing
というビルドプロセスも呼び出されるようなのです。
そこで init.gradle
に以下のコードを追加しました。
if(rootProject.tasks.findByName('nothing')) {
copy {
from "templates/.gitignore"
into rootProject.file('.')
}
}
このコードは前述した GitIgnoreGenerator よりも後で呼び出されるため、 Gradle によって強制上書きされた .gitignore
をさらに上書きできます。上書き返し大成功です。
この templates/.gitignore
のコピーは Buildship から Gradle プロジェクトを作成した場合のみ有効です。コマンドラインから gradle init
を実行した場合は、 デフォルトの .gitignore
のままになってしまうのでご注意ください。
gradle init
で自動作成されるプロジェクトの雛形をカスタマイズする方法の紹介は以上です。