1. 概要

ソースセットは、Gradleプロジェクトでソースコードを構造化するための強力な方法を提供します。

このクイックチュートリアルでは、それらの使用方法を確認します。

2. デフォルトのソースセット

デフォルトにジャンプする前に、まずソースセットとは何かを説明しましょう。 名前が示すように、ソースセットはソースファイルの論理グループを表します。

Javaプロジェクトの構成について説明しますが、この概念は他のGradleプロジェクトタイプにも適用できます。

2.1. デフォルトのプロジェクトレイアウト

簡単なプロジェクト構造から始めましょう。

source-sets 
  ├── src 
  │    ├── main 
  │    │    └── java 
  │    │        ├── SourceSetsMain.java
  │    │        └── SourceSetsObject.java
  │    └── test 
  │         └── java 
  │             └── SourceSetsTest.java
  └── build.gradle 

それでは、build.gradleを見てみましょう。

apply plugin : "java"
description = "Source Sets example"
test {
    testLogging {
        events "passed", "skipped", "failed"
    }
}
dependencies {   
    implementation('org.apache.httpcomponents:httpclient:4.5.12')
    testImplementation('junit:junit:4.12')
}

Javaプラグインはデフォルトのソースディレクトリとしてのsrc/main/javaおよびsrc/test / java 。 

簡単なユーティリティタスクを作成しましょう。

task printSourceSetInformation(){
    doLast{
        sourceSets.each { srcSet ->
            println "["+srcSet.name+"]"
            print "-->Source directories: "+srcSet.allJava.srcDirs+"\n"
            print "-->Output directories: "+srcSet.output.classesDirs.files+"\n"
            println ""
        }
    }
}

ここでは、いくつかのソースセットプロパティを出力しています。 詳細については、いつでも完全なJavaDocを確認できます。

それを実行して、何が得られるかを見てみましょう。

$ ./gradlew printSourceSetInformation

> Task :source-sets:printSourceSetInformation
[main]
-->Source directories: [.../source-sets/src/main/java]
-->Output directories: [.../source-sets/build/classes/java/main]

[test]
-->Source directories: [.../source-sets/src/test/java]
-->Output directories: [.../source-sets/build/classes/java/test]

には、mainとtestの2つのデフォルトのソースセットがあることに注意してください。

2.2. デフォルト設定

Javaプラグインは、デフォルトのGradle構成も自動的に作成します

それらは特別な命名規則に従います:

これらを使用して、build.gradleで依存関係を宣言します。

dependencies { 
    implementation('org.apache.httpcomponents:httpclient:4.5.12') 
    testImplementation('junit:junit:4.12') 
}

mainImplementationの代わりにimplementationを指定していることに注意してください。 これは命名規則の例外です。

デフォルトでは、testImplementation構成は実装を拡張し、そのすべての依存関係と出力を継承します

ヘルパータスクを改善して、これが何であるかを見てみましょう。

task printSourceSetInformation(){

    doLast{
        sourceSets.each { srcSet ->
            println "["+srcSet.name+"]"
            print "-->Source directories: "+srcSet.allJava.srcDirs+"\n"
            print "-->Output directories: "+srcSet.output.classesDirs.files+"\n"
            print "-->Compile classpath:\n"
            srcSet.compileClasspath.files.each { 
                print "  "+it.path+"\n"
            }
            println ""
        }
    }
}

出力を見てみましょう:

[main]
// same output as before
-->Compile classpath:
  .../httpclient-4.5.12.jar
  .../httpcore-4.4.13.jar
  .../commons-logging-1.2.jar
  .../commons-codec-1.11.jar

[test]
// same output as before
-->Compile classpath:
  .../source-sets/build/classes/java/main
  .../source-sets/build/resources/main
  .../httpclient-4.5.12.jar
  .../junit-4.12.jar
  .../httpcore-4.4.13.jar
  .../commons-logging-1.2.jar
  .../commons-codec-1.11.jar
  .../hamcrest-core-1.3.jar

test ソースセットには、コンパイルクラスパスに main の出力が含まれ、依存関係も含まれています。

次に、単体テストを作成しましょう。

public class SourceSetsTest {

    @Test
    public void whenRun_ThenSuccess() {
        
        SourceSetsObject underTest = new SourceSetsObject("lorem","ipsum");
        
        assertThat(underTest.getUser(), is("lorem"));
        assertThat(underTest.getPassword(), is("ipsum"));
    }
}

ここでは、2つの値を格納する単純なPOJOをテストします。 メイン出力がテストクラスパスにあるため、直接使用できます。

次に、これをGradleから実行してみましょう。

./gradlew clean test

> Task :source-sets:test

com.baeldung.test.SourceSetsTest > whenRunThenSuccess PASSED

3. カスタムソースセット

これまでのところ、いくつかの賢明なデフォルトを見てきました。 ただし、実際には、特に統合テストの場合、カスタムソースセットが必要になることがよくあります。

これは、統合テストのクラスパスにのみ特定のテストライブラリを配置したい場合があるためです。 また、単体テストとは別に実行したい場合もあります。

3.1. カスタムソースセットの定義

統合テスト用に別のソースディレクトリを作成しましょう。

source-sets 
  ├── src 
  │    └── main 
  │         ├── java 
  │         │    ├── SourceSetsMain.java
  │         │    └── SourceSetsObject.java
  │         ├── test 
  │         │    └── SourceSetsTest.java
  │         └── itest 
  │              └── SourceSetsITest.java
  └── build.gradle 

次に、sourceSetsコンストラクトを使用してbuild.gradleで構成しましょう。

sourceSets {
    itest {
        java {
        }
    }
}
dependencies {
    implementation('org.apache.httpcomponents:httpclient:4.5.12')
    testImplementation('junit:junit:4.12')
}
// other declarations omitted

カスタムディレクトリを指定していないことに注意してください。 これは、フォルダが新しいソースセットの名前( itest )と一致するためです。

srcDirsプロパティに含まれるディレクトリをカスタマイズできます。

sourceSets{
    itest {
        java {
            srcDirs("src/itest")
        }
    }
}

最初からヘルパータスクを覚えていますか? 再実行して、何が出力されるかを見てみましょう。

$ ./gradlew printSourceSetInformation

> Task :source-sets:printSourceSetInformation
[itest]
-->Source directories: [.../source-sets/src/itest/java]
-->Output directories: [.../source-sets/build/classes/java/itest]
-->Compile classpath:
  .../source-sets/build/classes/java/main
  .../source-sets/build/resources/main

[main]
 // same output as before

[test]
 // same output as before

3.2. ソースセット固有の依存関係の割り当て

デフォルト設定を覚えていますか? itestソースセットの構成もいくつか取得します。

itestImplementationを使用して、新しい依存関係を割り当てましょう

dependencies {
    implementation('org.apache.httpcomponents:httpclient:4.5.12')
    testImplementation('junit:junit:4.12')
    itestImplementation('com.google.guava:guava:29.0-jre')
}

これは統合テストにのみ適用されます。

以前のテストを変更して、統合テストとして追加しましょう。

public class SourceSetsItest {

    @Test
    public void givenImmutableList_whenRun_ThenSuccess() {

        SourceSetsObject underTest = new SourceSetsObject("lorem", "ipsum");
        List someStrings = ImmutableList.of("Baeldung", "is", "cool");

        assertThat(underTest.getUser(), is("lorem"));
        assertThat(underTest.getPassword(), is("ipsum"));
        assertThat(someStrings.size(), is(3));
    }
}

で実行できるようにするには、コンパイルされた出力を使用するカスタムテストタスクを定義する必要があります。

// source sets declarations

// dependencies declarations 

task itest(type: Test) {
    description = "Run integration tests"
    group = "verification"
    testClassesDirs = sourceSets.itest.output.classesDirs
    classpath = sourceSets.itest.runtimeClasspath
}

これらの宣言は、構成フェーズで評価されます。 結果として、それらの順序は重要です

たとえば、これが宣言される前に、タスク本体に設定されたitestソースを参照することはできません。

テストを実行するとどうなるか見てみましょう。

$ ./gradlew clean itest

// some compilation issues

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':source-sets:compileItestJava'.
> Compilation failed; see the compiler error output for details.

前回の実行とは異なり、今回はコンパイルエラーが発生します。 どうしたの?

この新しいソースセットは、独立した構成を作成します。

つまり、 itestImplementationはJUnit依存関係を継承せず、mainの出力も取得しません。

Gradle構成でこれを修正しましょう:

sourceSets{
    itest {
        compileClasspath += sourceSets.main.output
        runtimeClasspath += sourceSets.main.output
        java {
        }
    }
}

// dependencies declaration
configurations {
    itestImplementation.extendsFrom(testImplementation)
    itestRuntimeOnly.extendsFrom(testRuntimeOnly)
}

それでは、統合テストを再実行しましょう。

$ ./gradlew clean itest

> Task :source-sets:itest

com.baeldung.itest.SourceSetsItest > givenImmutableList_whenRun_ThenSuccess PASSED

テストに合格します。

3.3. EclipseIDEの処理

これまで、Gradleを使用してソースセットを直接操作する方法を見てきました。 ただし、ほとんどの場合、IDE(Eclipseなど)を使用します。

プロジェクトをインポートすると、コンパイルの問題が発生します。

ただし、Gradleから統合テストを実行すると、エラーは発生しません。

$ ./gradlew clean itest

> Task :source-sets:itest

com.baeldung.itest.SourceSetsItest > givenImmutableList_whenRun_ThenSuccess PASSED

どうしたの? この場合、guavaの依存関係はitestImplementationに属します。

残念ながら、 EclipseBuildshipGradleプラグインはこれらのカスタム構成をうまく処理しません

これをbuild.gradle修正しましょう:

apply plugin: "eclipse"

// previous declarations

eclipse {
    classpath {
        plusConfigurations+=[configurations.itestCompileClasspath] 
    } 
}

ここで何をしたか説明しましょう。 構成をEclipseクラスパスに追加しました。

プロジェクトを更新すると、コンパイルの問題はなくなります。

ただし、このアプローチには欠点があります:IDEは構成を区別しません。

これは、テストソース(特に避けたかった)にグアバを簡単にインポートできることを意味します。

4. 結論

このチュートリアルでは、Gradleソースセットの基本について説明しました。

次に、カスタムソースセットがどのように機能するか、およびEclipseでそれらを使用する方法について説明しました。

いつものように、完全なソースコードはGitHubにあります。