Skip to content

Commit 0a79555

Browse files
authored
Require JDK 9 for compilation (elastic#28071)
This commit modifies the build to require JDK 9 for compilation. Henceforth, we will compile with a JDK 9 compiler targeting JDK 8 as the class file format. Optionally, RUNTIME_JAVA_HOME can be set as the runtime JDK used for running tests. To enable this change, we separate the meaning of the compiler Java home versus the runtime Java home. If the runtime Java home is not set (via RUNTIME_JAVA_HOME) then we fallback to using JAVA_HOME as the runtime Java home. This enables: - developers only have to set one Java home (JAVA_HOME) - developers can set an optional Java home (RUNTIME_JAVA_HOME) to test on the minimum supported runtime - we can test compiling with JDK 9 running on JDK 8 and compiling with JDK 9 running on JDK 9 in CI
1 parent 8a58df4 commit 0a79555

File tree

16 files changed

+105
-107
lines changed

16 files changed

+105
-107
lines changed

CONTRIBUTING.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ Contributing to the Elasticsearch codebase
9292

9393
**Repository:** [https://github.com/elastic/elasticsearch](https://github.com/elastic/elasticsearch)
9494

95+
JDK 9 is required to build Elasticsearch. You must have a JDK 9 installation
96+
with the environment variable `JAVA_HOME` referencing the path to Java home for
97+
your JDK 9 installation. By default, tests use the same runtime as `JAVA_HOME`.
98+
However, since Elasticsearch, supports JDK 8 the build supports compiling with
99+
JDK 9 and testing on a JDK 8 runtime; to do this, set `RUNTIME_JAVA_HOME`
100+
pointing to the Java home of a JDK 8 installation. Note that this mechanism can
101+
be used to test against other JDKs as well, this is not only limited to JDK 8.
102+
95103
Elasticsearch uses the Gradle wrapper for its build. You can execute Gradle
96104
using the wrapper via the `gradlew` script in the root of the repository.
97105

buildSrc/src/main/groovy/org/elasticsearch/gradle/BuildPlugin.groovy

Lines changed: 53 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ import java.time.ZonedDateTime
5656
*/
5757
class BuildPlugin implements Plugin<Project> {
5858

59-
static final JavaVersion minimumJava = JavaVersion.VERSION_1_8
59+
static final JavaVersion minimumRuntimeVersion = JavaVersion.VERSION_1_8
60+
static final JavaVersion minimumCompilerVersion = JavaVersion.VERSION_1_9
6061

6162
@Override
6263
void apply(Project project) {
@@ -93,20 +94,26 @@ class BuildPlugin implements Plugin<Project> {
9394
/** Performs checks on the build environment and prints information about the build environment. */
9495
static void globalBuildInfo(Project project) {
9596
if (project.rootProject.ext.has('buildChecksDone') == false) {
96-
String javaHome = findJavaHome()
97+
String compilerJavaHome = findCompilerJavaHome()
98+
String runtimeJavaHome = findRuntimeJavaHome(compilerJavaHome)
9799
File gradleJavaHome = Jvm.current().javaHome
98100
String javaVendor = System.getProperty('java.vendor')
99101
String javaVersion = System.getProperty('java.version')
100102
String gradleJavaVersionDetails = "${javaVendor} ${javaVersion}" +
101103
" [${System.getProperty('java.vm.name')} ${System.getProperty('java.vm.version')}]"
102104

103-
String javaVersionDetails = gradleJavaVersionDetails
104-
JavaVersion javaVersionEnum = JavaVersion.current()
105-
if (new File(javaHome).canonicalPath != gradleJavaHome.canonicalPath) {
106-
javaVersionDetails = findJavaVersionDetails(project, javaHome)
107-
javaVersionEnum = JavaVersion.toVersion(findJavaSpecificationVersion(project, javaHome))
108-
javaVendor = findJavaVendor(project, javaHome)
109-
javaVersion = findJavaVersion(project, javaHome)
105+
String compilerJavaVersionDetails = gradleJavaVersionDetails
106+
JavaVersion compilerJavaVersionEnum = JavaVersion.current()
107+
if (new File(compilerJavaHome).canonicalPath != gradleJavaHome.canonicalPath) {
108+
compilerJavaVersionDetails = findJavaVersionDetails(project, compilerJavaHome)
109+
compilerJavaVersionEnum = JavaVersion.toVersion(findJavaSpecificationVersion(project, compilerJavaHome))
110+
}
111+
112+
String runtimeJavaVersionDetails = gradleJavaVersionDetails
113+
JavaVersion runtimeJavaVersionEnum = JavaVersion.current()
114+
if (new File(runtimeJavaHome).canonicalPath != gradleJavaHome.canonicalPath) {
115+
runtimeJavaVersionDetails = findJavaVersionDetails(project, runtimeJavaHome)
116+
runtimeJavaVersionEnum = JavaVersion.toVersion(findJavaSpecificationVersion(project, runtimeJavaHome))
110117
}
111118

112119
// Build debugging info
@@ -115,11 +122,13 @@ class BuildPlugin implements Plugin<Project> {
115122
println '======================================='
116123
println " Gradle Version : ${project.gradle.gradleVersion}"
117124
println " OS Info : ${System.getProperty('os.name')} ${System.getProperty('os.version')} (${System.getProperty('os.arch')})"
118-
if (gradleJavaVersionDetails != javaVersionDetails) {
125+
if (gradleJavaVersionDetails != compilerJavaVersionDetails || gradleJavaVersionDetails != runtimeJavaVersionDetails) {
119126
println " JDK Version (gradle) : ${gradleJavaVersionDetails}"
120127
println " JAVA_HOME (gradle) : ${gradleJavaHome}"
121-
println " JDK Version (compile) : ${javaVersionDetails}"
122-
println " JAVA_HOME (compile) : ${javaHome}"
128+
println " JDK Version (compile) : ${compilerJavaVersionDetails}"
129+
println " JAVA_HOME (compile) : ${compilerJavaHome}"
130+
println " JDK Version (runtime) : ${runtimeJavaVersionDetails}"
131+
println " JAVA_HOME (runtime) : ${runtimeJavaHome}"
123132
} else {
124133
println " JDK Version : ${gradleJavaVersionDetails}"
125134
println " JAVA_HOME : ${gradleJavaHome}"
@@ -135,54 +144,47 @@ class BuildPlugin implements Plugin<Project> {
135144
}
136145

137146
// enforce Java version
138-
if (javaVersionEnum < minimumJava) {
139-
throw new GradleException("Java ${minimumJava} or above is required to build Elasticsearch")
147+
if (compilerJavaVersionEnum < minimumCompilerVersion) {
148+
throw new GradleException("Java ${minimumCompilerVersion} or above is required to build Elasticsearch")
140149
}
141150

142-
// this block of code detecting buggy JDK 8 compiler versions can be removed when minimum Java version is incremented
143-
assert minimumJava == JavaVersion.VERSION_1_8 : "Remove JDK compiler bug detection only applicable to JDK 8"
144-
if (javaVersionEnum == JavaVersion.VERSION_1_8) {
145-
if (Objects.equals("Oracle Corporation", javaVendor)) {
146-
def matcher = javaVersion =~ /1\.8\.0(?:_(\d+))?/
147-
if (matcher.matches()) {
148-
int update;
149-
if (matcher.group(1) == null) {
150-
update = 0
151-
} else {
152-
update = matcher.group(1).toInteger()
153-
}
154-
if (update < 40) {
155-
throw new GradleException("JDK ${javaVendor} ${javaVersion} has compiler bug JDK-8052388, update your JDK to at least 8u40")
156-
}
157-
}
158-
}
151+
if (runtimeJavaVersionEnum < minimumRuntimeVersion) {
152+
throw new GradleException("Java ${minimumRuntimeVersion} or above is required to run Elasticsearch")
159153
}
160154

161-
project.rootProject.ext.javaHome = javaHome
162-
project.rootProject.ext.javaVersion = javaVersionEnum
155+
project.rootProject.ext.compilerJavaHome = compilerJavaHome
156+
project.rootProject.ext.runtimeJavaHome = runtimeJavaHome
157+
project.rootProject.ext.compilerJavaVersion = compilerJavaVersionEnum
158+
project.rootProject.ext.runtimeJavaVersion = runtimeJavaVersionEnum
163159
project.rootProject.ext.buildChecksDone = true
164160
}
165-
project.targetCompatibility = minimumJava
166-
project.sourceCompatibility = minimumJava
161+
project.targetCompatibility = minimumRuntimeVersion
162+
project.sourceCompatibility = minimumRuntimeVersion
167163
// set java home for each project, so they dont have to find it in the root project
168-
project.ext.javaHome = project.rootProject.ext.javaHome
169-
project.ext.javaVersion = project.rootProject.ext.javaVersion
164+
project.ext.compilerJavaHome = project.rootProject.ext.compilerJavaHome
165+
project.ext.runtimeJavaHome = project.rootProject.ext.runtimeJavaHome
166+
project.ext.compilerJavaVersion = project.rootProject.ext.compilerJavaVersion
167+
project.ext.runtimeJavaVersion = project.rootProject.ext.runtimeJavaVersion
170168
}
171169

172-
/** Finds and enforces JAVA_HOME is set */
173-
private static String findJavaHome() {
174-
String javaHome = System.getenv('JAVA_HOME')
170+
private static String findCompilerJavaHome() {
171+
final String javaHome = System.getenv('JAVA_HOME')
175172
if (javaHome == null) {
176173
if (System.getProperty("idea.active") != null || System.getProperty("eclipse.launcher") != null) {
177-
// intellij doesn't set JAVA_HOME, so we use the jdk gradle was run with
178-
javaHome = Jvm.current().javaHome
174+
// IntelliJ does not set JAVA_HOME, so we use the JDK that Gradle was run with
175+
return Jvm.current().javaHome
179176
} else {
180-
throw new GradleException('JAVA_HOME must be set to build Elasticsearch')
177+
throw new GradleException("JAVA_HOME must be set to build Elasticsearch")
181178
}
182179
}
183180
return javaHome
184181
}
185182

183+
private static String findRuntimeJavaHome(final String compilerJavaHome) {
184+
assert compilerJavaHome != null
185+
return System.getenv('RUNTIME_JAVA_HOME') ?: compilerJavaHome
186+
}
187+
186188
/** Finds printable java version of the given JAVA_HOME */
187189
private static String findJavaVersionDetails(Project project, String javaHome) {
188190
String versionInfoScript = 'print(' +
@@ -412,7 +414,7 @@ class BuildPlugin implements Plugin<Project> {
412414

413415
/** Adds compiler settings to the project */
414416
static void configureCompile(Project project) {
415-
if (project.javaVersion < JavaVersion.VERSION_1_10) {
417+
if (project.compilerJavaVersion < JavaVersion.VERSION_1_10) {
416418
project.ext.compactProfile = 'compact3'
417419
} else {
418420
project.ext.compactProfile = 'full'
@@ -422,7 +424,7 @@ class BuildPlugin implements Plugin<Project> {
422424
File gradleJavaHome = Jvm.current().javaHome
423425
// we fork because compiling lots of different classes in a shared jvm can eventually trigger GC overhead limitations
424426
options.fork = true
425-
options.forkOptions.executable = new File(project.javaHome, 'bin/javac')
427+
options.forkOptions.javaHome = new File(project.compilerJavaHome)
426428
options.forkOptions.memoryMaximumSize = "1g"
427429
if (project.targetCompatibility >= JavaVersion.VERSION_1_8) {
428430
// compile with compact 3 profile by default
@@ -447,22 +449,18 @@ class BuildPlugin implements Plugin<Project> {
447449

448450
options.encoding = 'UTF-8'
449451
options.incremental = true
450-
451-
if (project.javaVersion == JavaVersion.VERSION_1_9) {
452-
// hack until gradle supports java 9's new "--release" arg
453-
assert minimumJava == JavaVersion.VERSION_1_8
454-
options.compilerArgs << '--release' << '8'
455-
}
452+
// TODO: use native Gradle support for --release when available (cf. https://github.com/gradle/gradle/issues/2510)
453+
options.compilerArgs << '--release' << project.targetCompatibility.majorVersion
456454
}
457455
}
458456
}
459457

460458
static void configureJavadoc(Project project) {
461459
project.tasks.withType(Javadoc) {
462-
executable = new File(project.javaHome, 'bin/javadoc')
460+
executable = new File(project.compilerJavaHome, 'bin/javadoc')
463461
}
464462
configureJavadocJar(project)
465-
if (project.javaVersion == JavaVersion.VERSION_1_10) {
463+
if (project.compilerJavaVersion == JavaVersion.VERSION_1_10) {
466464
project.tasks.withType(Javadoc) { it.enabled = false }
467465
project.tasks.getByName('javadocJar').each { it.enabled = false }
468466
}
@@ -508,7 +506,7 @@ class BuildPlugin implements Plugin<Project> {
508506
'X-Compile-Lucene-Version': VersionProperties.lucene,
509507
'X-Compile-Elasticsearch-Snapshot': isSnapshot,
510508
'Build-Date': ZonedDateTime.now(ZoneOffset.UTC),
511-
'Build-Java-Version': project.javaVersion)
509+
'Build-Java-Version': project.compilerJavaVersion)
512510
if (jarTask.manifest.attributes.containsKey('Change') == false) {
513511
logger.warn('Building without git revision id.')
514512
jarTask.manifest.attributes('Change': 'Unknown')
@@ -545,7 +543,7 @@ class BuildPlugin implements Plugin<Project> {
545543
/** Returns a closure of common configuration shared by unit and integration tests. */
546544
static Closure commonTestConfig(Project project) {
547545
return {
548-
jvm "${project.javaHome}/bin/java"
546+
jvm "${project.runtimeJavaHome}/bin/java"
549547
parallelism System.getProperty('tests.jvms', 'auto')
550548
ifNoTests 'fail'
551549
onNonEmptyWorkDirectory 'wipe'

buildSrc/src/main/groovy/org/elasticsearch/gradle/plugin/PluginBuildPlugin.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ public class PluginBuildPlugin extends BuildPlugin {
169169
Files.copy(jarFile.resolveSibling(sourcesFileName), jarFile.resolveSibling(clientSourcesFileName),
170170
StandardCopyOption.REPLACE_EXISTING)
171171

172-
if (project.javaVersion < JavaVersion.VERSION_1_10) {
172+
if (project.compilerJavaVersion < JavaVersion.VERSION_1_10) {
173173
String javadocFileName = jarFile.fileName.toString().replace('.jar', '-javadoc.jar')
174174
String clientJavadocFileName = clientFileName.replace('.jar', '-javadoc.jar')
175175
Files.copy(jarFile.resolveSibling(javadocFileName), jarFile.resolveSibling(clientJavadocFileName),

buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/JarHellTask.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public class JarHellTask extends LoggedExec {
4242
inputs.files(classpath)
4343
dependsOn(classpath)
4444
description = "Runs CheckJarHell on ${classpath}"
45-
executable = new File(project.javaHome, 'bin/java')
45+
executable = new File(project.runtimeJavaHome, 'bin/java')
4646
doFirst({
4747
/* JarHell doesn't like getting directories that don't exist but
4848
gradle isn't especially careful about that. So we have to do it

buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/LoggerUsageTask.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public class LoggerUsageTask extends LoggedExec {
4444
project.afterEvaluate {
4545
dependsOn(classpath)
4646
description = "Runs LoggerUsageCheck on ${classDirectories}"
47-
executable = new File(project.javaHome, 'bin/java')
47+
executable = new File(project.runtimeJavaHome, 'bin/java')
4848
if (classDirectories == null) {
4949
// Default to main and test class files
5050
List files = []

buildSrc/src/main/groovy/org/elasticsearch/gradle/precommit/NamingConventionsTask.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public class NamingConventionsTask extends LoggedExec {
8080
FileCollection classpath = project.sourceSets.test.runtimeClasspath
8181
inputs.files(classpath)
8282
description = "Tests that test classes aren't misnamed or misplaced"
83-
executable = new File(project.javaHome, 'bin/java')
83+
executable = new File(project.runtimeJavaHome, 'bin/java')
8484
if (false == checkForTestsInMain) {
8585
/* This task is created by default for all subprojects with this
8686
* setting and there is no point in running it if the files don't

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/ClusterFormationTasks.groovy

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ class ClusterFormationTasks {
655655
String pid = node.pidFile.getText('UTF-8')
656656
ByteArrayOutputStream output = new ByteArrayOutputStream()
657657
project.exec {
658-
commandLine = ["${project.javaHome}/bin/jstack", pid]
658+
commandLine = ["${project.runtimeJavaHome}/bin/jstack", pid]
659659
standardOutput = output
660660
}
661661
output.toString('UTF-8').eachLine { line -> logger.error("| ${line}") }
@@ -699,7 +699,7 @@ class ClusterFormationTasks {
699699
}
700700

701701
private static File getJpsExecutableByName(Project project, String jpsExecutableName) {
702-
return Paths.get(project.javaHome.toString(), "bin/" + jpsExecutableName).toFile()
702+
return Paths.get(project.runtimeJavaHome.toString(), "bin/" + jpsExecutableName).toFile()
703703
}
704704

705705
/** Adds a task to kill an elasticsearch node with the given pidfile */

buildSrc/src/main/groovy/org/elasticsearch/gradle/test/NodeInfo.groovy

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ class NodeInfo {
162162
args.add("${esScript}")
163163
}
164164

165-
env = ['JAVA_HOME': project.javaHome]
165+
env = ['JAVA_HOME': project.runtimeJavaHome]
166166
args.addAll("-E", "node.portsfile=true")
167167
String collectedSystemProperties = config.systemProperties.collect { key, value -> "-D${key}=${value}" }.join(" ")
168168
String esJavaOpts = config.jvmArgs.isEmpty() ? collectedSystemProperties : collectedSystemProperties + " " + config.jvmArgs

distribution/bwc/build.gradle

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.apache.tools.ant.taskdefs.condition.Os
2121
import org.elasticsearch.gradle.LoggedExec
2222
import org.elasticsearch.gradle.Version
23+
2324
import java.util.regex.Matcher
2425

2526
/**
@@ -118,29 +119,31 @@ if (project.hasProperty('bwcVersion')) {
118119
task buildBwcVersion(type: Exec) {
119120
dependsOn checkoutBwcBranch, writeBuildMetadata
120121
workingDir = checkoutDir
122+
if (project.rootProject.ext.runtimeJavaVersion == JavaVersion.VERSION_1_8 && ["5.6", "6.0", "6.1"].contains(bwcBranch)) {
123+
/*
124+
* If runtime Java home is set to JDK 8 and we are building branches that are officially built with JDK 8, push this to JAVA_HOME for
125+
* these builds.
126+
*/
127+
environment('JAVA_HOME', System.getenv('RUNTIME_JAVA_HOME'))
128+
}
121129
if (Os.isFamily(Os.FAMILY_WINDOWS)) {
122130
executable 'cmd'
123131
args '/C', 'call', new File(checkoutDir, 'gradlew').toString()
124132
} else {
125-
executable = new File(checkoutDir, 'gradlew').toString()
133+
executable new File(checkoutDir, 'gradlew').toString()
126134
}
127-
final ArrayList<String> commandLineArgs = [
128-
":distribution:deb:assemble",
129-
":distribution:rpm:assemble",
130-
":distribution:zip:assemble",
131-
"-Dbuild.snapshot=${System.getProperty('build.snapshot') ?: 'true'}"]
135+
args ":distribution:deb:assemble", ":distribution:rpm:assemble", ":distribution:zip:assemble", "-Dbuild.snapshot=${System.getProperty('build.snapshot') ?: 'true'}"
132136
final LogLevel logLevel = gradle.startParameter.logLevel
133137
if ([LogLevel.QUIET, LogLevel.WARN, LogLevel.INFO, LogLevel.DEBUG].contains(logLevel)) {
134-
commandLineArgs << "--${logLevel.name().toLowerCase(Locale.ENGLISH)}"
138+
args "--${logLevel.name().toLowerCase(Locale.ENGLISH)}"
135139
}
136140
final String showStacktraceName = gradle.startParameter.showStacktrace.name()
137141
assert ["INTERNAL_EXCEPTIONS", "ALWAYS", "ALWAYS_FULL"].contains(showStacktraceName)
138142
if (showStacktraceName.equals("ALWAYS")) {
139-
commandLineArgs << "--stacktrace"
143+
args "--stacktrace"
140144
} else if (showStacktraceName.equals("ALWAYS_FULL")) {
141-
commandLineArgs << "--full-stacktrace"
145+
args "--full-stacktrace"
142146
}
143-
args commandLineArgs
144147
doLast {
145148
List missing = [bwcDeb, bwcRpm, bwcZip].grep { file ->
146149
false == file.exists()

plugins/discovery-azure-classic/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ task createKey(type: LoggedExec) {
6767
project.delete(keystore.parentFile)
6868
keystore.parentFile.mkdirs()
6969
}
70-
executable = new File(project.javaHome, 'bin/keytool')
70+
executable = new File(project.runtimeJavaHome, 'bin/keytool')
7171
standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8'))
7272
args '-genkey',
7373
'-alias', 'test-node',

plugins/discovery-ec2/build.gradle

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,13 +75,11 @@ thirdPartyAudit.excludes = [
7575
'software.amazon.ion.system.IonSystemBuilder',
7676
'software.amazon.ion.system.IonTextWriterBuilder',
7777
'software.amazon.ion.system.IonWriterBuilder',
78+
'javax.xml.bind.DatatypeConverter',
79+
'javax.xml.bind.JAXBContext',
7880
'javax.servlet.ServletContextEvent',
7981
'javax.servlet.ServletContextListener',
8082
'org.apache.avalon.framework.logger.Logger',
8183
'org.apache.log.Hierarchy',
8284
'org.apache.log.Logger',
8385
]
84-
85-
if (JavaVersion.current() > JavaVersion.VERSION_1_8) {
86-
thirdPartyAudit.excludes += ['javax.xml.bind.DatatypeConverter']
87-
}

plugins/discovery-gce/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ task createKey(type: LoggedExec) {
3535
project.delete(keystore.parentFile)
3636
keystore.parentFile.mkdirs()
3737
}
38-
executable = new File(project.javaHome, 'bin/keytool')
38+
executable = new File(project.runtimeJavaHome, 'bin/keytool')
3939
standardInput = new ByteArrayInputStream('FirstName LastName\nUnit\nOrganization\nCity\nState\nNL\nyes\n\n'.getBytes('UTF-8'))
4040
args '-genkey',
4141
'-alias', 'test-node',

0 commit comments

Comments
 (0)