Kumpulan Sumber Gradle

1. Ikhtisar

Kumpulan sumber memberi kami cara yang ampuh untuk menyusun kode sumber dalam proyek Gradle kami.

Dalam tutorial singkat ini, kita akan melihat bagaimana menggunakannya.

2. Set Sumber Default

Sebelum beralih ke default, pertama mari kita jelaskan apa itu set sumber. Sesuai dengan namanya, kumpulan sumber mewakili pengelompokan logis dari file sumber .

Kami akan membahas konfigurasi proyek Java, tetapi konsepnya juga berlaku untuk jenis proyek Gradle lainnya.

2.1. Tata Letak Proyek Default

Mari kita mulai dengan struktur proyek sederhana:

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

Sekarang mari kita lihat 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') }

Plugin Java menganggap src / main / java dan src / test / java sebagai direktori sumber default .

Mari membuat tugas utilitas sederhana:

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 "" } } }

Kami hanya mencetak beberapa properti kumpulan sumber di sini. Kami selalu dapat memeriksa JavaDoc lengkap untuk informasi lebih lanjut.

Mari kita jalankan dan lihat apa yang kita dapatkan:

$ ./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] 

Perhatikan kami memiliki dua set sumber default: main dan test .

2.2. Konfigurasi Default

Plugin Java juga secara otomatis membuat beberapa konfigurasi Gradle default untuk kita .

Mereka mengikuti konvensi penamaan khusus: .

Kami menggunakannya untuk mendeklarasikan dependensi di build.gradle :

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

Perhatikan bahwa kita menentukan implementasi, bukan mainImplementation . Ini adalah pengecualian untuk konvensi penamaan.

Secara default, konfigurasi testImplementation memperluas implementasi dan mewarisi semua dependensi dan outputnya .

Mari tingkatkan tugas pembantu kita dan lihat tentang apa ini:

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 "" } } }

Mari kita lihat hasilnya:

[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

Set sumber pengujian berisi keluaran main dalam classpath kompilasi dan juga menyertakan dependensinya.

Selanjutnya, mari buat pengujian unit kami:

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

Di sini kami menguji POJO sederhana yang menyimpan dua nilai. Kita bisa menggunakannya secara langsung karena yang utama output di kami uji classpath .

Selanjutnya, mari kita jalankan ini dari Gradle:

./gradlew clean test > Task :source-sets:test com.baeldung.test.SourceSetsTest > whenRunThenSuccess PASSED 

3. Kumpulan Sumber Kustom

Sejauh ini, kami telah melihat beberapa default yang masuk akal. Namun, dalam praktiknya, kami sering membutuhkan kumpulan sumber khusus, terutama untuk pengujian integrasi.

Itu karena kita mungkin ingin memiliki pustaka pengujian khusus hanya pada jalur kelas pengujian integrasi. Kami juga mungkin ingin menjalankannya secara independen dari pengujian unit.

3.1. Menentukan Kumpulan Sumber Kustom

Mari buat direktori sumber terpisah untuk pengujian integrasi kita:

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

Selanjutnya, mari kita konfigurasikan di build.gradle kita menggunakan konstruksi sourceSets :

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

Perhatikan bahwa kami tidak menentukan direktori kustom apa pun. Itu karena folder kita cocok dengan nama set sumber baru ( itest ).

Kita dapat menyesuaikan direktori apa yang disertakan dengan properti srcDirs :

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

Ingat tugas pembantu kita dari awal? Mari kita jalankan kembali dan lihat apa yang dicetaknya:

$ ./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. Menetapkan Ketergantungan Khusus Kumpulan Sumber

Ingat konfigurasi default? Kami sekarang mendapatkan beberapa konfigurasi untuk set sumber itest juga.

Mari kita gunakan itestImplementation untuk menetapkan dependensi baru :

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

Yang ini hanya berlaku untuk pengujian integrasi.

Mari ubah pengujian sebelumnya dan tambahkan sebagai pengujian integrasi:

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)); } }

Untuk dapat menjalankannya , kita perlu mendefinisikan tugas pengujian khusus yang menggunakan keluaran yang dikompilasi :

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

Deklarasi ini dievaluasi selama fase konfigurasi . Akibatnya, urutan mereka menjadi penting .

For example, we cannot reference the itest source set in the task body before this is declared.

Let's see what happens if we run the test:

$ ./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.

Unlike the previous run, we get a compilation error this time. So what happened?

This new source set creates an independent configuration.

In other words, itestImplementation does not inherit the JUnit dependency, nor does it get the outputs of main.

Let's fix this in our Gradle configuration:

sourceSets{ itest { compileClasspath += sourceSets.main.output runtimeClasspath += sourceSets.main.output java { } } } // dependencies declaration configurations { itestImplementation.extendsFrom(testImplementation) itestRuntimeOnly.extendsFrom(testRuntimeOnly) }

Now let's rerun our integration test:

$ ./gradlew clean itest > Task :source-sets:itest com.baeldung.itest.SourceSetsItest > givenImmutableList_whenRun_ThenSuccess PASSED

The test passes.

3.3. Eclipse IDE Handling

We've seen so far how to work with source sets directly with Gradle. However, most of the time, we'll be using an IDE (such as Eclipse).

When we import the project, we get some compilation issues:

However, if we run the integrations test from Gradle, we get no errors:

$ ./gradlew clean itest > Task :source-sets:itest com.baeldung.itest.SourceSetsItest > givenImmutableList_whenRun_ThenSuccess PASSED

So what happened? In this case, the guava dependency belongs to itestImplementation.

Unfortunately, the Eclipse Buildship Gradle plugin does not handle these custom configurations very well.

Let's fix this in our build.gradle:

apply plugin: "eclipse" // previous declarations eclipse { classpath { plusConfigurations+=[configurations.itestCompileClasspath] } } 

Let's explain what we did here. We appended our configuration to the Eclipse classpath.

If we refresh the project, the compilation issues are gone.

However, there's a drawback to this approach: The IDE does not distinguish between configurations.

This means we can easily import guava in our test sources (which we specifically wanted to avoid).

4. Conclusion

In this tutorial, we covered the basics of Gradle source sets.

Then we explained how custom source sets work and how to use them in Eclipse.

As usual, we can find the complete source code over on GitHub.