Extremely Serious

Category: Gradle

Understanding Gradle Build Phases: Initialization, Configuration, and Execution

Gradle, a powerful build automation tool, follows a structured process to build and configure projects. This process involves distinct phases, each playing a crucial role in the overall build lifecycle. In this article, we will explore the Initialization, Configuration, and Execution phases of a Gradle build and provide examples to illustrate each phase.

Initialization Phase

The Initialization Phase is the starting point of the Gradle build process. During this phase, Gradle constructs the Project instance, and sets up the build environment. The settings.gradle file is a key component executed during this phase.

Example:

// settings.gradle
rootProject.name = 'gradleBuildPhases'
println "Initialization Phase: This is executed during initialization"

In this example, the Initialization Phase prints a message when the settings.gradle file is executed.

Configuration Phase

The Configuration Phase follows the Initialization Phase and involves configuring the project and the tasks. During this phase, Gradle evaluates build scripts to set up the tasks and their dependencies.

Example:

// build.gradle
println 'Configuration Phase: Outside any task configuration.'

task myTask {
    println "Configuration Phase: Inside task configuration"
}

In this example, a task named myTask is defined in the build script. All the println statements will be performed during the Configuration Phase. Notice that there is a println statement outside the task, it will be executed as part of this phase. Moreover, this is also the phase where the task graph is created for all the requested tasks.

Execution Phase

The Execution Phase is where the actual tasks are executed based on their dependencies. Gradle ensures that tasks are executed in the correct order to fulfill their dependencies.

Example:

task myTask {

    doFirst {
        println 'Execution Phase: This is executed first.'
    }

    doLast {
        println 'Execution Phase: This is execute last.'
    }

    println "Configuration Phase: Inside task configuration"
}

Updating myTask task from the previous section. When executing it, Gradle automatically executes the action specified in the doFirst closure first, and the actions specified in the doLast closures will be performed last. Gradle will follow the task graph generated by the configuration phase.

Conclusion

Understanding the flow through Initialization, Configuration, and Execution phases is essential for effective project configuration and task execution in Gradle. By leveraging these phases, developers can structure their builds, manage dependencies, and define tasks to create a robust and efficient build process.

In conclusion, Gradle's build phases provide a systematic approach to building and configuring projects. Utilizing the Initialization Phase to set up the build environment, the Configuration Phase to define tasks, and the Execution Phase to carry out actions ensures a well-organized and reliable build process.

Gradle Binary to Package with the Wrapper

  1. Download the gradle binary from the following address:

    https://services.gradle.org/distributions/

    For example gradle-7.4-all.zip.

  2. Place the downloaded gradle binary to the gradle/wrapper directory of the gradle project.

  3. Update the wrapper configuration (i.e. gradle/wrapper/gradle-wrapper.properties) like the following:

    If you've downloaded gradle-7.4-all.zip binary from step 1.

    distributionBase=PROJECT
    distributionPath=wrapper/dists
    distributionUrl=gradle-7.4-all.zip
    zipStoreBase=PROJECT
    zipStorePath=wrapper/dists

Downloading Dependencies with Gradle Script

  1. Create settings.gradle file with the following content:

    rootProject.name = 'download-dependecies'
  2. Create build.gradle file with the following content:

    plugins {
        id 'application'
    }
    
    group 'xyz.ronella.gradle'
    version '1.0.0'
    
    repositories {
        mavenCentral()
    }
    
    dependencies {
        //Add all the target dependencies here
        implementation 'org.apache.poi:poi:3.5-FINAL'
    }
    
    task download(group: 'resolver') {
        doFirst {
            ant.delete(dir: 'libs')
            ant.mkdir(dir: 'libs')
        }
        doLast {
            copy {
                from sourceSets.main.runtimeClasspath
                into 'libs/'
            }
            def outputFile = "${project.buildDir}\\${project.name}-${version}.zip"
            ant.zip(basedir: "${file('libs')}", destfile: "${outputFile}", excludes: '*.class')
            println "Output: ${outputFile}"
            ant.delete(dir: 'libs')
        }
    }
  3. Run the following gradle command:

    gradle download

    If you are using the gradle wrapper, use the following command:

    gradlew download

Maven Scope to Gradle Configuration Mapping

Maven Gradle Comment
compile api if the dependency should be exposed to consumers.
implementation if the dependency should not be exposed to consumers.
provided compileOnly Maven provided is also available at runtime. Gradle compileOnly is limited to compile only.
runtime runtimeOnly
test testImplementation

Using Embedded Derby in Java 9 with Gradle

  1. Add the following dependencies to your build.gradle file:
    compile group: 'org.apache.derby', name: 'derby', version: '10.15.1.3'
    compile group: 'org.apache.derby', name: 'derbyshared', version: '10.15.1.3'
  2. Add the following entries to your module-info.java file:
    requires org.apache.derby.engine;
    requires org.apache.derby.commons;
    requires java.sql;
    
  3. In your Java class, you can create a connection like the following:
    final String DATABASE_NAME = "sample_table";
    String connectionURL = String.format("jdbc:derby:%s;create=true", DATABASE_NAME);
    connection = DriverManager.getConnection(connectionURL);
    
  4. Do your needed database operations with the connection (e.g. create table, execute a query, or call a stored procedure.).
  5. When your done using the connection, close the connection and shutdown the Derby engine like the following:
    connection.close();
    
    boolean gotSQLExc = false;
    try {
        //shutdown all databases and the Derby engine
        DriverManager.getConnection("jdbc:derby:;shutdown=true");
    } catch (SQLException se)  {
        if ( se.getSQLState().equals("XJ015") ) {
            gotSQLExc = true;
        }
    }
    if (!gotSQLExc) {
        System.out.println("Database did not shut down normally");
    }

    A clean shutdown always throws SQL exception XJ015, which can be ignored.

Publishing to Maven Central with Gradle

Pre-requisites

  1. Create an account to sonatype.
  2. Request for a group id from sonatype by using Jira to create an issue.
  3. Generate PGP keys to be used for signing the binaries (e.g. jar file). For windows, you can follow the procedure here.
  4. Wait for the completion of the issue you've created from item 2.

Gradle Configuration

  1. Update your gradle.properties to include the following properties:
    nexusUsername=<SONATYPE_USERNAME>
    nexusPassword=<SONATYPE_PASSWORD>
    
    signing.keyId=<PGP_PUBLIC_KEY_ID>
    signing.password=<PGP_PASS_PHRASE>
    signing.secretKeyRingFile=<PGP_EXPORTED_PRIVATE_KEY>
    
  2. In your build.gradle file,  add the Gradle Sonatype Nexus Plugin like the following:
    plugins {
       id "java"
       id "com.bmuschko.nexus" version "2.3.1" // Gradle Sonatype Nexus Plugin
    }
  3. Add the following plugin configurations:
    modifyPom {
        project {
            name '<PROJECT_NAME>'
            description '<PROJECT_DESCRIPTION>'
            url '<PROJECT_WEBSITE>'
            inceptionYear '<PROJECT_INCEPTION_YEAR>'
    
            scm {
                url '<PROJECT_SCM_ADDRESS>'
                connection '<PROJECT_SCM_ADDRESS>'
                developerConnection '<PROJECT_SCM_ADDRESS>'
            }
    
            licenses {
                license {
                    name '<PROJECT_LICENSE_NAME>'
                    url '<PROJECT_LICENSE_ADDRESS>'
                    distribution 'repo'
                }
            }
    
            developers {
                developer {
                    id '<DEVELOPER_ID>'
                    name '<DEVELOPER_NAME>'
                    email '<DEVELOPER_EMAIL>'
                }
            }
        }
    }
    
    extraArchive {
        sources = true
        tests = true
        javadoc = true
    }
    
    nexus {
        sign = true
        repositoryUrl = '<SONATYPE_RELEASE_REPOSITORY>'
        snapshotRepositoryUrl = '<SONATYPE_SNAPSHOT_REPOSITORY>'
    }
  4. Add the Gradle Nexus Staging plugin like the following:
    plugins {
        id 'java'
        id "com.bmuschko.nexus" version "2.3.1" // Gradle Sonatype Nexux Plugin
        id "io.codearte.nexus-staging" version "0.21.1" // Gradle Nexus Staging Plugin
    }
  5. After adding the plugin save the build.gradle file.
  6. Get the staging profile ID by running the following command:
    gradlew getStagingProfile
  7. Add the following plugin configuration:
    nexusStaging {
        stagingProfileId = "<STAGING_PROFILE_ID>"
    }
  8. Save the build.gradle file again.

Uploading to Sonatype Repository

Run the following command:

gradlew publishToSonatype

Publishing to Maven Central

Run the following command:

gradlew closeAndReleaseSonatypeStagingRepository

Errors in Publishing

If there are any errors after running the preceding command

  1. Sign in to the following address using your sonatype credentials: https://oss.sonatype.org/
  2. Click the Staging Repositories menu and investigate the error from there.
  3. Once the errors were identified and corrected locally, upload it again to sonatype repository before publishing it again.

Do this error correction process until all the errors were corrected.

Successful Publishing Validation

After around 10 minutes, navigate to your registered group id from sonatype, starting from the following address:

https://repo.maven.apache.org/maven2/

After around 2 hours, your artifact id may be searchable in maven central from the following address:

https://search.maven.org/

 

Configuring User Level Proxy for Gradle

  1. Create gradle.properties files in the following directory if not yet existing:
     <USER_HOME>/.gradle
  2. Add the following entries for http proxy configuration in the gradle.properties file:
    systemProp.http.proxyHost=<PROXY_HOST>
    systemProp.http.proxyPort=<PROXY_PORT>
    systemProp.http.proxyUser=<PROXY_USERNAME>
    systemProp.http.proxyPassword=<PROXY_PASSWORD>
    systemProp.http.nonProxyHosts=<NON_PROXY_HOSTS>

    Note: <NON_PROXY_HOSTS> is delimited by pipe symbol (e.g. localhost|*.test.net)

    Note: If https proxy configuration is needed use the same entries except that all http will must be replaced with https (e.g. systemProp.https.proxyHost).