From Source to JAR - Part 3

In the first part of our series, we discussed how to compile Java code and create executable JAR files. The second part focused on Maven, detailing its Project Object Model (POM) and build lifecycle phases. In this article, we’ll explore Gradle’s core concepts, such as build scripts, tasks, and plugins, and how they work together to simplify and improve the build process.

Gradle Overview

Gradle uses a Groovy or Kotlin-based scripting language, which is more readable and easily customized than XML. This flexibility allows developers to tailor build processes to their specific requirements.

  • Example of a build.gradle file:
plugins {
    id 'java'
}

group 'org.alvesfc'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {

    testImplementation platform('org.junit:junit-bom:5.9.1')
    testImplementation 'org.junit.jupiter:junit-jupiter'
}

tasks.test {
    useJUnitPlatform()
}
  • Example of a build.gradle.kts file (using Kotlin DSL):
plugins {
    id("java")
}

group = "org.alvesfc"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

dependencies {
    testImplementation(platform("org.junit:junit-bom:5.9.1"))
    testImplementation("org.junit.jupiter:junit-jupiter")
}

tasks.test {
    useJUnitPlatform()
}

This example defines a simple Java project with a dependency on JUnit for testing.

  • Key Elements of a Gradle Build Script:

    • plugins: Specifies the plugins applied to the project. In this case, the java plugin is used to compile Java code.

    • group: Identifies the project’s group or organization.

    • version: Indicates the project’s version number.

    • repositories: Defines the repositories where dependencies are resolved. Here, mavenCentral() is used to fetch dependencies from the Maven Central repository.

    • dependencies: Lists the project’s dependencies. In this example, JUnit is used for testing.

    • tasks: Configures tasks in the build process. The test task is customized to use JUnit 5 for testing.

Gradle Lifecycle

The Gradle lifecycle is the sequence of phases and tasks that Gradle executes to build a project. Understanding the lifecycle helps in effectively organizing and configuring builds.

Here’s an overview of the Gradle lifecycle:

  1. Initialization Phase : Identify the projects involved in setting up the Gradle environment and building it.

    • Project Discovery: Gradle identifies which projects are included in the build, which is especially important in multi-project builds.

    • Gradle Settings: The settings.gradle (or settings.gradle.kts for Kotlin DSL) file is evaluated to configure the build and include any necessary projects.

    • Gradle Configuration: Global settings are applied, and the gradle.properties file is read.

  2. Configuration Phase: Create and configure the task graph based on the build scripts.

    • Script Evaluation: The build.gradle (or build.gradle.kts) scripts for all projects are evaluated. During this, tasks and their dependencies are declared.
    • Task Configuration: Gradle creates a directed acyclic graph (DAG) of tasks for execution. It’s important to note that Gradle only organizes the order of tasks for execution, and no actual work is performed.
  3. Execution Phase: Execute the tasks in the order defined by the task graph.

    • Task Execution: Gradle forcefully executes the tasks configured in the previous phase while respecting task dependencies to ensure they run in the correct order.
    • Incremental Builds: If the task outputs have not changed, Gradle will skip the task to save time using its incremental build capabilities.
    • Parallel Execution: When configured to do so, Gradle can execute independent tasks in parallel, effectively enhancing build performance.

Key Concepts:

  • Task Dependencies: Tasks can depend on other tasks, meaning a task will only run if its dependencies have run.
  • Lazy Configuration: Gradle uses lazy configuration to defer as much work as possible until it’s actually needed, optimizing the build process.
  • Build Script Plugins: Scripts can apply plugins to extend their capabilities, such as the Java plugin for Java projects.

Creating a Gradle Project

Ensure Gradle is installed and configured on your system. Verify installation by running:

gradle -v

If Gradle is not installed, follow the installation steps provided by official documentation.

Initialize the Gradle Project

  1. Create the Project Directory:
mkdir gradleSample
cd gradleSample
  1. Initialize the Project:

Run the following command to generate the basic project structure:

gradle init
  1. Select the project type (e.g., application, library, etc.) and configure the project settings.
  1: basic
  2: application
  3: library
  4: Gradle plugin
Enter selection (default: basic) [1..4] 3

Type 3: library (for a Java library project).

  1. Select implementation language:
  1: C++
  2: Groovy
  3: Java
  4: Kotlin
  5: Scala
  6: Swift
Enter selection (default: Java) [1..6] 3

Type 3: Java (for Java projects).

  1. Specify build script DSL (Groovy or Kotlin).
Select build script DSL:
 1: Groovy
 2: Kotlin
Enter selection (default: Groovy) [1..2] 2

Type 2: Kotlin (for Kotlin DSL).

  1. Generate build using new APIs and behavior (some features may change in the next minor release)? (default: no) [yes, no]

Type no (to not use the new APIs and behavior).

  1. Choose a test framework for your project.
Select test framework:
 1: JUnit 4
 2: TestNG
 3: Spock
 4: JUnit Jupiter
Enter selection (default: JUnit 4) [1..4] 4

Type 4: JUnit Jupiter (to use JUnit 5 as the testing framework).

  1. Enter the project name and source package when prompted. Here’s an example:
Project name (default: gradleSample): gradleSample
Source package (default: gradleSample): com.example.gradlesample

Once you’ve made all selections, Gradle will generate the project structure for you. This includes creating directories for your source code and test code, setting up a build script (build.gradle.kts if you chose Kotlin DSL), and other configuration files necessary for your project.

Build the Project

To build the project, navigate to the project directory and run the following command:

gradle build

Project Structure

After initialization, your project structure will look like this:

gradleSample/
├── gradle/
│   └── wrapper/
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
|── lib/
|   └── build.gradle.kts
|   ├── src/
|       ├── main/
|       |   ├── java/
|       |   |   └── com/
|       |   |       └── example/
|       |   |           └── gradlesample/
|       |   |                └── Library.java
|       |   ├── resources/
|       └── test/
|           ├── java/
|           |   └── com/
|           |       └── example/
|           |           └── gradlesample/
|           |               └── LibraryTest.java
|           ├── resources/
├── settings.gradle.kts
  • gradle/ : Contains the Gradle wrapper files.
  • lib/ : Contains the source code and resources.
  • build.gradle.kts: Contains the project configuration.
  • gradlew / gradlew.bat: Gradle wrapper scripts.
  • src/main/java: Source code directory.
  • src/main/resources: Resources directory.
  • src/test/java: Test source directory.
  • src/test/resources: Test resources directory.
  • settings.gradle.kts: Contains project settings.

Conclusion

This series has provided an overview of Java build systems, emphasizing Maven and Gradle. It explores compiling Java code, utilizing Maven’s Project Object Model, and leveraging Gradle’s flexible scripting capabilities. Whether you are a novice or an experienced developer, understanding these tools is crucial for optimizing your workflow. Setting up a Gradle project is straightforward and helps configure a tailored build process for efficiency.

References

Posts in this series