概要: Gradleのソースセットは、Javaプロジェクトのソースファイルとリソースを論理的にグループ化する仕組みです。本記事では、基本構造からカスタムソースセットの作成、Groovy・Scala・Kotlinなどの複数言語との併用方法を解説し、よくあるエラーの対処法も紹介します。
Gradleのソースセットとは?基本構造と主な役割
ソースセットの定義と2つの標準ソースセット
ソースセットとは、一連の論理的なソースファイルとリソースファイルの集まりです。GradleのJava Pluginを適用すると、デフォルトで「main」と「test」の2つのソースセットが自動的に作成されます。mainはアプリケーション本体の本番コード、testは単体テスト用のコードを格納する役割を持ちます。これにより、本番コードとテストコードを明確に分離して管理できます。
ソースセットにはJavaソースだけでなく、リソースファイルも含まれます。例えば設定ファイルやプロパティファイルなどは、Javaソースとは別のディレクトリに配置します。この分離構造により、コンパイル対象と実行時に必要なファイルを整理して扱えます。
既定のディレクトリ構成と出力先
Java Pluginの標準ディレクトリ構成は、以下のとおりです。
| ソースセット | ディレクトリ | 用途 |
|---|---|---|
| main | src/main/java | 本番Javaソース |
| main | src/main/resources | 本番リソース |
| test | src/test/java | テストJavaソース |
| test | src/test/resources | テスト用リソース |
コンパイル済みクラスの出力先は、デフォルトでbuild/classes/java/<ソースセット名>です。リソースはbuild/resources/<ソースセット名>にコピーされます。この規則を知っておくと、ビルド結果の確認やトラブルシューティングが容易になります。
ソースセットが持つ主要プロパティ
ソースセットには、ビルド処理に必要なさまざまな情報がプロパティとして定義されています。主なものを以下に示します。
- java:JavaソースのSourceDirectorySet。既定はsrc/<ソースセット名>/java
- resources:リソース出力にコピーされる非Javaファイル。既定はsrc/<ソースセット名>/resources
- allJava:ソースセット内の全Javaファイル(他言語プラグインが追加するJavaソースも含む)
- output:コンパイル済みクラスや処理済みリソースの出力コレクション
- compileClasspath:このソースセットのコンパイルに使用するクラスパス
- runtimeClasspath:このソースセットの実行に使用するクラスパス
これらのプロパティは、カスタムソースセットを作成する際に、他のソースセットとの連携や依存関係の設定で使用します。
要点:ソースセットはmainとtestが標準で用意され、src/<ソースセット名>/javaとresourcesが既定ディレクトリです。カスタマイズの前にこの基本構造を理解することが重要です。
カスタムソースセットの作り方とJavaプロジェクトへの適用
カスタムソースセットを作成する判断基準
カスタムソースセットは、統合テスト(intTest)や特殊なコード生成など、標準のmain/testとは別に管理したいコードがある場合に作成します。Gradle公式ドキュメントでは、①独自のクラスパスでコンパイルする必要がある、②mainやtestとは異なる扱いのクラスを生成する、③プロジェクトの自然な一部である——このうち、③と①または②の両方に該当する場合にカスタムソースセットが適切としています。
例えば統合テストは、単体テストとは実行タイミングや依存ライブラリが異なるため、testソースセットとは分離するのが一般的です。逆に、単なるディレクトリ整理のためだけなら、既存ソースセットにsrcDir()で追加するほうがシンプルです。目的を明確にしてから導入を検討しましょう。
カスタムソースセットの基本設定手順
統合テスト用のintTestソースセットを作成する手順を、以下に示します。
- sourceSetsブロックでintTestソースセットを定義する
- mainソースセットの出力をintTestのcompileClasspathとruntimeClasspathに追加する
- configurationsブロックでintTestImplementationをtestImplementationから継承させる
- dependenciesブロックでintTest用の依存関係を宣言する
- intTest用のTestタスクを登録する
特に重要なのは2番目の手順です。ソースセットはデフォルトでは互いのコードを参照できないため、mainのコンパイル結果を明示的にクラスパスへ追加する必要があります。これを行わないと、統合テストから本番コードを呼び出せません。
srcDir()とsrcDirsの違いに注意
ソースディレクトリをカスタマイズする際、srcDir()メソッドとsrcDirsプロパティの挙動の違いを理解することが重要です。
| 操作 | 挙動 | 既存ディレクトリ |
|---|---|---|
| srcDir(‘path’) | 既存ディレクトリに追加 | 維持される |
| srcDirs = [‘path’] | 既定ディレクトリを置換 | 削除される |
例えば、レガシープロジェクトで「src」を本番コードのディレクトリにしたい場合はsrcDirsで置換します。一方、既存のsrc/main/javaに加えてサードパーティのソースも含めたい場合はsrcDir()で追加します。誤って置換すると、標準ディレクトリが無効になりビルドエラーの原因になります。
要点:カスタムソースセットはmainの出力を明示的にクラスパスへ追加すること、Testタスクを自分で登録することが必須です。また、srcDir()は追加、srcDirsは置換という違いにも注意してください。
Groovy・Scala・Kotlinとの併用におけるソースセット設定
Groovyプラグインとの併用と共同コンパイル
Groovy Pluginを適用すると、既存のソースセットにgroovyプロパティ(GroovySourceDirectorySet)が追加されます。既定のGroovyソースディレクトリはsrc/<ソースセット名>/groovyです。例えばmainソースセットならsrc/main/groovy、testソースセットならsrc/test/groovyが使用されます。
Groovyの大きな特徴は、Javaとの共同コンパイル(joint compilation)に対応している点です。JavaコードとGroovyコードが相互に依存している場合でも、Groovyコンパイラが両方を同時に処理して解決できます。また、allJavaやallSourceプロパティには、Groovyソースディレクトリ内の.javaファイルも自動的に含まれます。これにより、既存のJavaプロジェクトにGroovyを段階的に導入できます。
Scalaプラグインとの併用と注意点
Scala Pluginを適用すると、ソースセットにscalaプロパティ(ScalaSourceDirectorySet)が追加されます。既定のScalaソースディレクトリはsrc/<ソースセット名>/scalaです。Scalaソースディレクトリには.scalaファイルと.javaファイルの両方を含められます。
ただし、Scalaの共同コンパイルはGroovyとは仕組みが異なります。ScalaコンパイラはJavaコンパイラを直接呼び出さず、まずScalaとJavaの型チェック用クラスファイルを生成し、その後JavaコンパイラがJavaソースをコンパイルする2段階のプロセスです。この違いにより、複雑な相互依存がある場合はビルド順序に注意が必要です。JavaとScalaの混在プロジェクトでは、この動作を理解したうえでソースディレクトリを設定しましょう。
Kotlinプラグインとの併用
Kotlin PluginはJavaベースのソースセットにKotlinソースディレクトリを追加します。既定ではsrc/<ソースセット名>/kotlinが使用されます。KotlinソースはJavaソースと同一プロジェクト内で共存可能です。JavaからKotlinを呼び出したり、KotlinからJavaを呼び出したりする相互運用が、追加設定なしで動作します。
なお、Kotlin Multiplatformでは階層構造のソースセット(hierarchical source sets)がサポートされていますが、これはKotlin Multiplatform固有の機能です。通常のJVMプロジェクトでKotlinを使用する場合は、Java Pluginのソースセット構造の上にKotlinソースディレクトリが追加されるという理解で十分です。各言語プラグインは、このように既存のソースセットを拡張する設計になっています。
要点:Groovy・Scala・Kotlinの各プラグインは、既存ソースセットに言語専用のソースディレクトリを追加します。Groovyはスムーズな共同コンパイル、Scalaは2段階コンパイルという違いを理解しておきましょう。
Gradleでよくあるソースセット関連のエラーとその対処法
カスタムソースセットからmainのコードを参照できない
カスタムソースセットを作成した直後に最もよく遭遇するのが、「シンボルを見つけられません」というコンパイルエラーです。これは、ソースセットがデフォルトで互いのコードを参照できないことが原因です。intTestソースセットからmainソースセットのクラスを呼び出すには、以下の設定が必要です。
- intTestのcompileClasspathにmainのoutputを追加する
- intTestのruntimeClasspathにmainのoutputを追加する
この設定を行わないと、統合テストのコンパイル時に本番コードのクラスが見つからずエラーになります。逆に、mainから別のソースセットを参照する場合も同様の設定が必要です。ソースセット間の依存関係は明示的に宣言するという原則を覚えておきましょう。
srcDirsによる意図しない既定ディレクトリの消失
ソースディレクトリをカスタマイズした際、突然「ソースファイルが見つからない」というエラーが発生することがあります。これは多くの場合、srcDirsプロパティへの代入によって既定ディレクトリが置換されてしまったことが原因です。
例えば、追加のつもりで「srcDirs = [‘thirdParty/src/main/java’]」と記述すると、src/main/javaが無効になります。既存ディレクトリを維持したまま追加するには、srcDir()メソッドを使用します。エラーが発生したら、まずbuild.gradleまたはbuild.gradle.ktsのソースセット設定を見直し、追加と置換のどちらの意図だったかを確認してください。この問題は、Gradleの設定に慣れていない開発者が特に陥りやすいポイントです。
カスタムソースセットのテストが実行されない
統合テスト用のソースセットを作成したのに、gradle testを実行してもテストが走らないという問題があります。これは、カスタムソースセットのテスト実行には、専用のTestタスクを自分で登録する必要があるためです。標準のtestタスクは、testソースセットしか対象にしません。
解決策として、intTestソースセット用のTestタスクを以下のように登録します。タスク内でtestClassesDirsにintTestの出力クラスディレクトリを、classpathにintTestのruntimeClasspathを指定します。これにより、gradle intTestというコマンドで統合テストを実行できるようになります。必要に応じて、buildタスクやcheckタスクに依存関係を追加して、ビルドプロセスに統合することも検討してください。
要点:よくあるエラーの原因は、ソースセット間の参照設定漏れ、srcDirsによる置換ミス、テストタスクの未登録の3つです。いずれも設定の見直しで解決できます。
GradleツーリングAPIとビルドスクリプト移行時の注意点
GradleツーリングAPIの役割とソースセット情報の取得
GradleツーリングAPIは、IDEや外部ツールがGradleビルドの情報をプログラム的に取得・操作するためのAPIです。IntelliJ IDEAやEclipseなどのIDEは、このAPIを通じてプロジェクトのソースセット構成や依存関係を読み取り、プロジェクトのインポートやコード補完に活用しています。ソースセットのディレクトリ構成を変更した場合、IDEとの連携にも影響があることを認識しておきましょう。
ツーリングAPIを使用すると、ソースセットのディレクトリ、コンパイルクラスパス、出力先などを外部から取得できます。ただし、ツーリングAPI自体の詳細な使用方法はGradle公式ドキュメントの「Gradle Tooling API」の章で確認する必要があります。本記事では、ビルドスクリプトの移行時にツーリングAPIとの互換性を意識することが重要という点に焦点を当てます。
Groovy DSLからKotlin DSLへの移行における注意点
ビルドスクリプトをGroovy DSL(build.gradle)からKotlin DSL(build.gradle.kts)へ移行する際、ソースセット設定の記述方法が変わります。代表的な違いを以下に示します。
| 操作 | Groovy DSL | Kotlin DSL |
|---|---|---|
| ディレクトリの置換 | srcDirs = [‘src’] | setSrcDirs(listOf(“src”)) |
| ディレクトリの追加 | srcDir ‘path’ | srcDir(“path”) |
| 依存関係の宣言 | testImplementation ‘…’ | testImplementation(“…”) |
移行時は、文字列のクォート、メソッド呼び出しの括弧、プロパティ代入とメソッド呼び出しの区別に注意が必要です。Kotlin DSLは型安全性が高いため、誤った設定はコンパイル時に検出されやすくなります。移行後は、gradle tasksやgradle buildを実行して、ソースセットが正しく認識されているか確認してください。
ソースセット移行時の検証ポイント
ビルドスクリプトを移行した後は、以下の点を確認してソースセット設定が正しく引き継がれているか検証します。
- カスタムソースセットのディレクトリ構成が意図どおりか
- srcDir()による追加とsrcDirsによる置換が正しく変換されているか
- ソースセット間のクラスパス依存関係(mainのoutput追加など)が維持されているか
- カスタムTestタスクが正しく登録され、実行できるか
移行作業では、一度にすべてを変換するのではなく、段階的に移行して各段階でビルドを検証することが推奨されます。特に、ソースセットのカスタマイズが多いプロジェクトほど、設定の変換漏れが発生しやすくなります。移行後はIDEの再インポートも行い、ソースディレクトリが正しく認識されているか確認しましょう。
要点:ツーリングAPIはIDE連携に影響するため、ソースセット変更時は再インポートが必要です。DSL移行では追加と置換の区別を正確に変換し、段階的に検証しながら進めましょう。
まとめ
よくある質問
Q: Gradleのソースセットとは何ですか?
A: ソースセットとは、一連の論理的なJavaソースファイルとリソースファイルの集まりです。Gradle Java Pluginはデフォルトでmain(本番コード)とtest(テストコード)の2つのソースセットを提供します。
Q: Gradleでカスタムソースセットを作成するにはどうすれば良いですか?
A: sourceSetsブロック内でintTestなどのブロックを定義します。カスタムソースセットには既定でsrc/〈ソースセット名〉/javaとsrc/〈ソースセット名〉/resourcesが作成されますが、mainの出力を参照するにはcompileClasspathやruntimeClasspathへの明示的な追加が必要です。
Q: srcDir()とsrcDirsの違いは何ですか?
A: srcDir()メソッドは既存のソースディレクトリに追加しますが、srcDirsプロパティへの代入(またはsetSrcDirs)は既定値を置き換えます。追加の意図でsrcDirs =を使うと既定ディレクトリが消えてしまうため注意が必要です。
Q: GradleではGroovyやScalaなど複数言語を併用できますか?
A: 可能です。Groovy PluginやScala Pluginは既存のソースセットに言語専用のSourceDirectorySet(groovyやscala)を追加します。GroovyとScalaはJavaとの共同コンパイルに対応しています。
Q: カスタムソースセットのテストを実行するにはどうすれば良いですか?
A: カスタムソースセットのテストは自動的にはtestタスクに組み込まれません。独自のTestタスクを登録し、testClassesDirsとclasspathを指定する必要があります。
