Working with source sets

The Java plugin also adds a new concept to our project-source sets. A source set is a collection of source files that are compiled and executed together. The files can be Java source files or resource files. Source sets can be used to group files together with a certain meaning in our project, without having to create a separate project. For example, we can separate the location of source files that describe the API of our Java project in a source set, and run tasks that only apply to the files in this source set.

Without any configuration, we already have the main and test source sets, which are added by the Java plugin. For each source set, the plugin also adds the following three tasks: compile<SourceSet>Javaprocess<SourceSet>Resources, and <SourceSet>Classes. When the source set is named main, we don't have to provide the source set name when we execute a task. For example, compileJava applies to the main source test, but compileTestJava applies to the test source set.

Each source set also has some properties to access the directories and files that make up the source set. The following table shows the properties that we can access in a source set:

Source set property

Type

Description

java

SourceDirectorySet

These are the Java source files for this project. Only files with the .java extension are in this collection.

allJava

SourceDirectorySet

By default, this is the same as the Java property, so it contains all the Java source files. Other plugins can add extra source files to this collection.

resources

SourceDirectorySet

These are all the resource files for this source set. This contains all the files in the resources source directory, excluding any files with the .java extension.

allSource

SourceDirectorySet

By default, this is the combination of the resources and Java properties. This includes all the source files of this source set, both resource and Java source files.

output

SourceSetOutput

These are the output files for the source files in the source set. This contains the compiled classes and processed resources.

java.srcDirs

Set<File>

These are the directories with Java source files.

resources.srcDirs

Set<File>

These are the directories with the resource files for this source set.

output.classesDir

File

This is the output directory with the compiled class files for the Java source files in this source set.

output.resourcesDir

File

This is the output directory with the processed resource files from the resources in this source set.

name

String

This is the read-only value with the name of the source set.

We can access these properties via the sourceSets property of our project. In the following example, we will create a new task to display values for several properties:

apply plugin: 'java' 
task sourceSetJavaProperties << { 
    sourceSets { 
        main { 
            println "java.srcDirs = ${java.srcDirs}" 
            println "resources.srcDirs = ${resources.srcDirs}" 
            println "java.files = ${java.files.name}" 
            println "allJava.files = ${allJava.files.name}" 
            println "resources.files = ${resources.files.name}" 
            println "allSource.files = ${allSource.files.name}" 
            println "output.classesDir = ${output.classesDir}" 
            println "output.resourcesDir = ${output.resourcesDir}" 
            println "output.files = ${output.files}" 
      } 
    } 
} 

When we run the sourceSetJavaproperties task, we get the following output:

$ gradle sourceSetJavaproperties
:sourceSetJavaProperties
java.srcDirs = [/gradle-book/Chapter4/Code_Files/sourcesets/src/main/java]
resources.srcDirs = [/gradle-book/Chapter4/Code_Files/sourcesets/src/main/resources]
java.files = [Sample.java]
allJava.files = [Sample.java]
resources.files = [messages.properties]
allSource.files = [messages.properties, Sample.java]
output.classesDir = /gradle-book/Chapter4/Code_Files/sourcesets/build/classes/main
output.resourcesDir = /gradle-book/Chapter4/Code_Files/sourcesets/build/resources/main
output.files = [/gradle-book/Chapter4/Code_Files/sourcesets/build/classes/main, /gradle-book/Chapter4/Code_Files/sourcesets/build/resources/main]
BUILD SUCCESSFUL
Total time: 0.594 secs

Creating a new source set

We can create our own source set in a project. A source set contains all the source files that are related to each other. In our example, we will add a new source set to include a Java interface. Our Sample class will then implement the interface; however, as we use a separate source set, we can use this later to create a separate JAR file with only the compiled interface class. We will name the source set api as the interface is actually the API of our example project, which we can share with other projects.

To define this source set, we only have to put the name in the sourceSets property of the project, as follows:

apply plugin: 'java' 
 
sourceSets { 
    api 
} 

Gradle will create three new tasks based on this source set—apiClasses, compileApiJava, and processApiResources. We can see these tasks after we execute the tasks command:

$ gradle tasks --all
...
Build tasks
-----------
apiClasses - Assembles api classes.
compileApiJava - Compiles api Java source.
processApiResources - Processes api resources.
...

We have created our Java interface in the src/api/java directory, which is the source directory for the Java source files for the api source set. The following code allows us to see the Java interface:

// File: src/api/java/gradle/sample/ReadWelcomeMessage.java 
package gradle.sample; 
 
/** 
* Read welcome message from source and return value. 
*/ 
public interface ReadWelcomeMessage { 
 
    /** 
    * @return Welcome message 
    */ 
    String getWelcomeMessage(); 
 
} 

To compile the source file, we can execute the compileApiJava or apiClasses task:

$ gradle apiClasses
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
BUILD SUCCESSFUL
Total time: 0.595 secs

The source file is compiled in the build/classes/api directory.

We will now change the source code of our Sample class and implement the ReadWelcomeMessage interface, as shown in the following code:

// File: src/main/java/gradle/sample/Sample.java 
package gradle.sample; 
 
import java.util.ResourceBundle; 
 
/** 
* Read welcome message from external properties file 
* <code>messages.properties</code>. 
*/ 
public class Sample implements ReadWelcomeMessage { 
 
    public Sample() { 
    } 
 
    /** 
    * Get <code>messages.properties</code> file 
    * and read the value for <em>welcome</em> key. 
    * 
    * @return Value for <em>welcome</em> key 
    *         from <code>messages.properties</code> 
    */ 
    public String getWelcomeMessage() { 
      final ResourceBundle resourceBundle =           ResourceBundle.getBundle("messages"); 
      final String message = resourceBundle.getString("welcome"); 
      return message; 
    } 
 
} 

Next, we run the classes task to recompile our changed Java source file:

$ gradle classes
:compileJava
/gradle-book/Chapter4/src/main/java/gradle/sample/Sample.java:10: error: cannot find symbol
public class Sample implements ReadWelcomeMessage {
                              ^
symbol: class ReadWelcomeMessage
1 error
:compileJava FAILED
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':compileJava'.
> Compilation failed; see the compiler error output for details.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
Total time: 0.608 secs

We get a compilation error! The Java compiler cannot find the ReadWelcomeMessage interface. However, we just ran the apiClasses task and compiled the interface without errors.

To fix this, we must define a dependency between the classes and apiClasses tasks. The classes task is dependent on the apiClasses tasks. First, the interface must be compiled and then the class that implements the interface must be compiled .

Next, we must add the output directory with the compiled interface class file to the compileClasspath property of the main source set. Once we have done this, we know for sure that the Java compiler picks up the compiled class file to compile the Sample class.

To do this, we will change the build file and add the task dependency between the two tasks and the main source set configuration, as follows:

apply plugin: 'java' 
n 
sourceSets { 
    api 
    main { 
      compileClasspath += files(api.output.classesDir) 
    } 
} 
 
classes.dependsOn apiClasses 

Now we can run the classes task again, without errors:

$ gradle classes
:compileApiJava
:processApiResources UP-TO-DATE
:apiClasses
:compileJava
:processResources
:classes
BUILD SUCCESSFUL
Total time: 0.648 secs

Custom configuration

If we use Gradle for an existing project, we might have a different directory structure than the default structure defined by Gradle, or it may be that we want to have a different structure for another reason. We can account for this by configuring the source sets and using different values for the source directories.

Consider that we have a project with the following source directory structure:

. 
├── resources 
│   ├── java 
│   └── test 
├── src 
│   └── java 
├── test 
│   ├── integration 
│   │   └── java 
│   └── unit 
│       └── java 
└── tree.txt 

We will need to reconfigure the main and test source sets, but we must also add a new integration-test source set. The following code reflects the directory structure for the source sets:

apply plugin: 'java'

sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'resources/java'
        }
    }
    test {
        java {
            srcDir 'test/unit/java'
        }
        resources {
            srcDir 'resources/test'
        }
    }

    'integeration-test' {
        java {
            srcDir 'test/integration/java'
        }
        resources {
            srcDir 'resources/test'
        }
    }
}

Notice how we must put the name of the integration-test source set in quotes; this is because we use a hyphen in the name. Gradle then converts the name of the source set into integrationTest (without the hyphen and with a capital T). To compile, for example, the source files of the integration test source set, we use the compileIntegrationTestJava task.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.208.97