Compile and run different jdk within one jenkins job

Viewed 1275

I would like to receive jenkins job that first compiles tests with java 8 and then run them with java 11. Gradle is used in the project, which has lots of subprojects. Is it even possible?
I tried with approach (which was actually the best I came up with)

tasks.withType(AbstractCompile) {
    options.with {
        fork = true
        forkOptions.javaHome = [java-8-home]
    }
}

placed in main build.gradle but I am not sure if this will affect all compile type tasks in subprojects/tasks in other directories. Then I invoked gradle task with -Dorg.gradle.java.home=[java-11-home]

Let me know what do you think, thanks!

1 Answers

What you're trying to achieve is perfectly possible. Let's check step by step how this can be done. First of all, we instruct Gradle to compile all Java sources with Java 8 source and target compatibility and execute compilation with a Java 8 executable taken from environment variable JAVA8_HOME:

plugins {
    id 'java'
}

java {
    sourceCompatibility = JavaVersion.VERSION_1_8
    targetCompatibility = JavaVersion.VERSION_1_8
}

tasks.withType(JavaCompile) {
    options.with {
        fork = true
        forkOptions.javaHome = System.env.'JAVA8_HOME' as File
    }
}

Running the build with the --info switch shows that the Java 8 executable is indeed taken from the environment variable JAVA8_HOME and applies to all Java compilation tasks.

> Task :compileJava
Compiling with Java command line compiler 'C:\dev\java\jdk-8\bin\javac.exe'.

> Task :compileTestJava
Compiling with Java command line compiler 'C:\dev\java\jdk-8\bin\javac.exe'.

Executing tests with Java 11 taken from environment variable JAVA11_HOME works in a similar manner.

test {
   executable = "${System.env.'JAVA11_HOME'}/bin/java"
}

This produces again the following output:

> Task :test
Command: C:\dev\java\jdk-11/bin/java -Dorg.gradle.native=false ...

Subprojects

Applying this configuration to all subprojects in a multi-module project is straight forward. The following shows a root project's build.gradle that applies all rules only to those sub-projects having the Java plugin applied.

plugins {
    id 'base'
}

allprojects {
    plugins.withId('java') {
        repositories {
            mavenCentral()
        }

        java {
            sourceCompatibility = JavaVersion.VERSION_1_8
            targetCompatibility = JavaVersion.VERSION_1_8
        }

        tasks.withType(JavaCompile) {
            options.with {
                fork = true
                forkOptions.javaHome = System.env.'JAVA8_HOME' as File
            }
        }

        test {
            useJUnitPlatform()

            executable = "${System.env.'JAVA11_HOME'}/bin/java"
        }

        dependencies {
            testImplementation("org.junit.jupiter:junit-jupiter-api:5.6.0")
            testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.6.0")
        }
    }
}

Since you now depend on having environment variables set up, it makes sense to check them before actually starting the build. This can be done by checking the task graph once ready:

gradle.taskGraph.whenReady { taskGraph ->
    if (taskGraph.getAllTasks().find { it instanceof JavaCompile } != null
            && (System.env.'JAVA8_HOME' == null || System.env.'JAVA11_HOME' == null)) {
        throw new GradleException("Please export 'JAVA8_HOME' and 'JAVA11_HOME'")
    }
}

Jenkins

In Jenkins, all you need to do is to make sure to install the required JDKs and export the environment variables properly. A Jenkinsfile may look like this (take it with a grain of salt, I didn't test it yet):

pipeline {
    tools {
        jdk 'jdk-8'
        jdk 'jdk-11'
    }

    environment {
        JAVA8_HOME = "${tool 'jdk-8'}"
        JAVA11_HOME = "${tool 'jdk-11'}"
    }

    stages {
        stage('Build') {
            steps {
                checkout scm

                sh "./gradlew --no-daemon --info --stacktrace clean build"
            }
        }
    }
}
Related