Migrate repo to use JVM toolchains Gradle feature.
^KT-46972 Fixed
This commit is contained in:
@@ -4,32 +4,36 @@
|
||||
*/
|
||||
|
||||
import groovy.lang.Closure
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.gradle.internal.jvm.inspection.JvmVersionDetector
|
||||
import org.gradle.jvm.toolchain.JavaLauncher
|
||||
import org.gradle.kotlin.dsl.property
|
||||
import proguard.ClassSpecification
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
@CacheableTask
|
||||
open class CacheableProguardTask @Inject constructor(
|
||||
private val jvmVersionDetector: JvmVersionDetector
|
||||
) : proguard.gradle.ProGuardTask() {
|
||||
open class CacheableProguardTask : proguard.gradle.ProGuardTask() {
|
||||
|
||||
@Internal
|
||||
var jdkHome: File? = null
|
||||
@get:Internal
|
||||
val javaLauncher: Property<JavaLauncher> = project.objects.property()
|
||||
|
||||
@get:Internal
|
||||
val jdkHomePath: Provider<File> = javaLauncher.map { it.metadata.installationPath.asFile }
|
||||
|
||||
@get:Optional
|
||||
@get:Input
|
||||
internal val jdkMajorVersion: String?
|
||||
get() = jdkHome?.let { jvmVersionDetector.getJavaVersion(Jvm.forHome(jdkHome)) }?.majorVersion
|
||||
internal val jdkMajorVersion: Provider<JavaVersion> = javaLauncher.map {
|
||||
JavaVersion.toVersion(it.metadata.languageVersion.toString())
|
||||
}
|
||||
|
||||
@CompileClasspath
|
||||
override fun getLibraryJarFileCollection(): FileCollection = super.getLibraryJarFileCollection().filter { libraryFile ->
|
||||
jdkHome?.let { !libraryFile.absoluteFile.startsWith(it.absoluteFile) } ?: true
|
||||
}
|
||||
override fun getLibraryJarFileCollection(): FileCollection = super.getLibraryJarFileCollection()
|
||||
.filter { libraryFile ->
|
||||
jdkHomePath.orNull?.let { !libraryFile.absoluteFile.startsWith(it.absoluteFile) } ?: true
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
@file:JvmName("JvmToolchain")
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.jvm.toolchain.*
|
||||
import org.gradle.kotlin.dsl.getByType
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.jetbrains.kotlin.gradle.dsl.KotlinTopLevelExtension
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
enum class JdkMajorVersion(
|
||||
val majorVersion: Int,
|
||||
private val mandatory: Boolean = true
|
||||
) {
|
||||
JDK_1_6(6),
|
||||
JDK_1_7(7),
|
||||
JDK_1_8(8),
|
||||
JDK_9(9),
|
||||
JDK_10(10, false),
|
||||
JDK_11(11, false),
|
||||
JDK_15(15, false),
|
||||
JDK_16(16, false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
}
|
||||
|
||||
fun Project.configureJvmDefaultToolchain() {
|
||||
configureJvmToolchain(JdkMajorVersion.JDK_1_8)
|
||||
}
|
||||
|
||||
fun Project.configureJvmToolchain(
|
||||
jdkVersion: JdkMajorVersion
|
||||
) {
|
||||
plugins.withId("org.jetbrains.kotlin.jvm") {
|
||||
val kotlinExtension = extensions.getByType<KotlinTopLevelExtension>()
|
||||
|
||||
kotlinExtension.jvmToolchain {
|
||||
(this as JavaToolchainSpec).languageVersion
|
||||
.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
|
||||
}
|
||||
|
||||
tasks
|
||||
.matching { it.name != "compileJava9Java" && it is JavaCompile }
|
||||
.configureEach {
|
||||
with(this as JavaCompile) {
|
||||
options.compilerArgs.add("-proc:none")
|
||||
options.encoding = "UTF-8"
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions.freeCompilerArgs += "-Xjvm-default=compatibility"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.updateJvmTarget(
|
||||
jvmTarget: String
|
||||
) {
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions.jvmTarget = jvmTarget
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = jvmTarget
|
||||
targetCompatibility = jvmTarget
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.getToolchainCompilerFor(
|
||||
jdkVersion: JdkMajorVersion
|
||||
): Provider<JavaCompiler> {
|
||||
val service = project.extensions.getByType<JavaToolchainService>()
|
||||
return service.compilerFor {
|
||||
this.languageVersion.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.getToolchainLauncherFor(
|
||||
jdkVersion: JdkMajorVersion
|
||||
): Provider<JavaLauncher> {
|
||||
val service = project.extensions.getByType<JavaToolchainService>()
|
||||
return service.launcherFor {
|
||||
this.languageVersion.set(JavaLanguageVersion.of(jdkVersion.majorVersion))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
@file:JvmName("LibrariesCommon")
|
||||
|
||||
import org.gradle.api.JavaVersion
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.extra
|
||||
import org.gradle.kotlin.dsl.get
|
||||
import org.gradle.kotlin.dsl.provideDelegate
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
import org.gradle.process.CommandLineArgumentProvider
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
@JvmOverloads
|
||||
fun Project.configureJava9Compilation(
|
||||
moduleName: String,
|
||||
moduleOutputs: Collection<FileCollection> = setOf(sourceSets["main"].output)
|
||||
) {
|
||||
configurations["java9CompileClasspath"].extendsFrom(configurations["compileClasspath"])
|
||||
|
||||
tasks.named("compileJava9Java", JavaCompile::class.java) {
|
||||
dependsOn(moduleOutputs)
|
||||
|
||||
javaCompiler.set(getToolchainCompilerFor(JdkMajorVersion.JDK_9))
|
||||
targetCompatibility = JavaVersion.VERSION_1_9.toString()
|
||||
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
|
||||
|
||||
// module-info.java should be in java9 source set by convention
|
||||
val java9SourceSet = sourceSets["java9"].java
|
||||
destinationDir = file("${java9SourceSet.outputDir}/META-INF/versions/9")
|
||||
options.sourcepath = files(java9SourceSet.srcDirs)
|
||||
val compileClasspath = configurations["java9CompileClasspath"]
|
||||
val moduleFiles = objects.fileCollection().from(moduleOutputs)
|
||||
val modulePath = compileClasspath.filter { it !in moduleFiles.files }
|
||||
classpath = objects.fileCollection().from()
|
||||
options.compilerArgumentProviders.add(
|
||||
Java9AdditionalArgumentsProvider(
|
||||
moduleName,
|
||||
moduleFiles,
|
||||
modulePath
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private class Java9AdditionalArgumentsProvider(
|
||||
private val moduleName: String,
|
||||
private val moduleFiles: FileCollection,
|
||||
private val modulePath: FileCollection
|
||||
) : CommandLineArgumentProvider {
|
||||
override fun asArguments(): Iterable<String> = listOf(
|
||||
"--module-path", modulePath.asPath,
|
||||
"--patch-module", "$moduleName=${moduleFiles.asPath}",
|
||||
"-Xlint:-requires-transitive-automatic" // suppress automatic module transitive dependencies in kotlin.test
|
||||
)
|
||||
}
|
||||
|
||||
fun Project.disableDeprecatedJvmTargetWarning() {
|
||||
if (!kotlinBuildProperties.disableWerror) {
|
||||
val tasksWithWarnings: List<String> by rootProject.extra
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
if (!tasksWithWarnings.contains(path)) {
|
||||
kotlinOptions {
|
||||
allWarningsAsErrors = true
|
||||
freeCompilerArgs += "-Xsuppress-deprecated-jvm-target-warning"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,6 +13,8 @@ import org.gradle.api.artifacts.*
|
||||
import org.gradle.api.artifacts.dsl.DependencyHandler
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.internal.jvm.Jvm
|
||||
import org.gradle.jvm.toolchain.JavaLanguageVersion
|
||||
import org.gradle.kotlin.dsl.accessors.runtime.addDependencyTo
|
||||
import org.gradle.kotlin.dsl.closureOf
|
||||
import org.gradle.kotlin.dsl.exclude
|
||||
@@ -287,14 +289,16 @@ fun Project.firstFromJavaHomeThatExists(vararg paths: String, jdkHome: File = Fi
|
||||
|
||||
fun Project.toolsJarApi(): Any =
|
||||
if (kotlinBuildProperties.isInJpsBuildIdeaSync)
|
||||
files(toolsJarFile() ?: error("tools.jar is not found!"))
|
||||
toolsJar()
|
||||
else
|
||||
dependencies.project(":dependencies:tools-jar-api")
|
||||
|
||||
fun Project.toolsJar(): FileCollection = files(toolsJarFile() ?: error("tools.jar is not found!"))
|
||||
|
||||
fun Project.toolsJarFile(jdkHome: File = File(this.property("JDK_18") as String)): File? =
|
||||
firstFromJavaHomeThatExists("lib/tools.jar", jdkHome = jdkHome)
|
||||
fun Project.toolsJar(): FileCollection = files(
|
||||
getToolchainLauncherFor(JdkMajorVersion.JDK_1_8)
|
||||
.map {
|
||||
Jvm.forHome(it.metadata.installationPath.asFile).toolsJar ?: throw GradleException("tools.jar not found!")
|
||||
}
|
||||
)
|
||||
|
||||
val compilerManifestClassPath
|
||||
get() = "annotations-13.0.jar kotlin-stdlib.jar kotlin-reflect.jar kotlin-script-runtime.jar trove4j.jar"
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
|
||||
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.artifacts.DependencySubstitution
|
||||
import org.gradle.api.artifacts.component.ProjectComponentSelector
|
||||
import org.gradle.api.file.DuplicatesStrategy
|
||||
import org.gradle.api.tasks.TaskProvider
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
@@ -80,6 +82,24 @@ private fun Project.compilerShadowJar(taskName: String, body: ShadowJar.() -> Un
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.configureShadowJarSubstitutionInCompileClasspath() {
|
||||
val substitutionMap = mapOf(":kotlin-reflect" to ":kotlin-reflect-api")
|
||||
|
||||
fun configureSubstitution(substitution: DependencySubstitution) {
|
||||
val requestedProject = (substitution.requested as? ProjectComponentSelector)?.projectPath ?: return
|
||||
val replacementProject = substitutionMap[requestedProject] ?: return
|
||||
substitution.useTarget(project(replacementProject), "Non-default shadow jars should not be used in compile classpath")
|
||||
}
|
||||
|
||||
sourceSets.all {
|
||||
for (configName in listOf(compileOnlyConfigurationName, compileClasspathConfigurationName)) {
|
||||
configurations.getByName(configName).resolutionStrategy.dependencySubstitution {
|
||||
all(::configureSubstitution)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.embeddableCompiler(taskName: String = "embeddable", body: ShadowJar.() -> Unit = {}): TaskProvider<out ShadowJar> =
|
||||
compilerShadowJar(taskName) {
|
||||
configureEmbeddableCompilerRelocation()
|
||||
|
||||
@@ -1,198 +0,0 @@
|
||||
@file:Suppress("unused") // usages in build scripts are not tracked properly
|
||||
|
||||
import net.rubygrapefruit.platform.Native
|
||||
import net.rubygrapefruit.platform.WindowsRegistry
|
||||
import org.gradle.api.GradleException
|
||||
import org.gradle.api.Project
|
||||
import java.nio.file.Paths
|
||||
import java.io.File
|
||||
import net.rubygrapefruit.platform.WindowsRegistry.Key.HKEY_LOCAL_MACHINE
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
enum class JdkMajorVersion(private val mandatory: Boolean = true) {
|
||||
JDK_16, JDK_17, JDK_18, JDK_9, JDK_10(false), JDK_11(false), /*15.0*/JDK_15(false);
|
||||
|
||||
fun isMandatory(): Boolean = mandatory
|
||||
}
|
||||
|
||||
val jdkAlternativeVarNames = mapOf(JdkMajorVersion.JDK_9 to listOf("JDK_19"), JdkMajorVersion.JDK_15 to listOf("JDK_15_0"))
|
||||
|
||||
data class JdkId(val explicit: Boolean, val majorVersion: JdkMajorVersion, var version: String, var homeDir: File)
|
||||
|
||||
fun Project.getConfiguredJdks(): List<JdkId> {
|
||||
val res = arrayListOf<JdkId>()
|
||||
for (jdkMajorVersion in JdkMajorVersion.values()) {
|
||||
val explicitJdkEnvVal = findProperty(jdkMajorVersion.name)?.toString()
|
||||
?: System.getenv(jdkMajorVersion.name)
|
||||
?: jdkAlternativeVarNames[jdkMajorVersion]?.mapNotNull { System.getenv(it) }?.firstOrNull()
|
||||
?: continue
|
||||
val explicitJdk = Paths.get(explicitJdkEnvVal).toRealPath().toFile()
|
||||
if (!explicitJdk.isDirectory) {
|
||||
throw GradleException("Invalid environment value $jdkMajorVersion: $explicitJdkEnvVal, expecting JDK home path")
|
||||
}
|
||||
res.add(JdkId(true, jdkMajorVersion, "X", explicitJdk))
|
||||
}
|
||||
if (res.size < JdkMajorVersion.values().size) {
|
||||
res.discoverJdks(this)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
// see JEP 223
|
||||
private val javaMajorVersionRegex = Regex("""(?:1\.)?(\d+).*""")
|
||||
private val javaVersionRegex = Regex("""(?:1\.)?(\d+)(\.\d+)?([+-_]\w+){0,3}""")
|
||||
|
||||
fun MutableCollection<JdkId>.addIfBetter(project: Project, version: String, id: String, homeDir: File): Boolean {
|
||||
val matchString = javaMajorVersionRegex.matchEntire(version)?.groupValues?.get(1)
|
||||
val majorJdkVersion = when (matchString) {
|
||||
"6" -> JdkMajorVersion.JDK_16
|
||||
"7" -> JdkMajorVersion.JDK_17
|
||||
"8" -> JdkMajorVersion.JDK_18
|
||||
"9" -> JdkMajorVersion.JDK_9
|
||||
else -> {
|
||||
project.logger.info("Cannot recognize version string '$version' (found version '$matchString')")
|
||||
return false
|
||||
}
|
||||
}
|
||||
val prev = find { it.majorVersion == majorJdkVersion }
|
||||
if (prev == null) {
|
||||
add(JdkId(false, majorJdkVersion, version, homeDir))
|
||||
return true
|
||||
}
|
||||
if (prev.explicit) return false
|
||||
val versionsComparisonRes = compareVersions(prev.version, version)
|
||||
if (versionsComparisonRes < 0 || (versionsComparisonRes == 0 && id.contains("64"))) { // prefer 64-bit
|
||||
prev.version = version
|
||||
prev.homeDir = homeDir
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun compareVersions(left: String, right: String): Int {
|
||||
if (left == right) return 0
|
||||
fun MatchResult.extractNumVer(): List<Int> =
|
||||
groups.drop(2).map {
|
||||
it?.value?.filter { it in '0'..'9' }?.toIntOrNull() ?: 0
|
||||
}
|
||||
val lmi = (javaVersionRegex.matchEntire(left)?.extractNumVer() ?: emptyList()).iterator()
|
||||
val rmi = (javaVersionRegex.matchEntire(right)?.extractNumVer() ?: emptyList()).iterator()
|
||||
while (lmi.hasNext() && rmi.hasNext()) {
|
||||
val l = lmi.next()
|
||||
val r = rmi.next()
|
||||
when {
|
||||
l < r -> return -1
|
||||
l > r -> return 1
|
||||
}
|
||||
}
|
||||
return when {
|
||||
rmi.hasNext() -> -1
|
||||
lmi.hasNext() -> 1
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdks(project: Project) {
|
||||
val os = OperatingSystem.current()
|
||||
when {
|
||||
os.isWindows -> discoverJdksOnWindows(project)
|
||||
os.isMacOsX -> discoverJdksOnMacOS(project)
|
||||
else -> discoverJdksOnUnix(project)
|
||||
}
|
||||
}
|
||||
|
||||
private val macOsJavaHomeOutRegexes =
|
||||
listOf(
|
||||
Regex("""\s+(\S+),\s+(\S+):\s+".*?"\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\):\s+(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.*?)\)\s+"[^"]*"\s+-\s+"[^"]*"\s(.+)"""),
|
||||
Regex("""\s+(\S+)\s+\((.+)\)\s+".+"\s+-\s+".+"\s+(.+)"""))
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnMacOS(project: Project) {
|
||||
val procBuilder = ProcessBuilder("/usr/libexec/java_home", "-V").redirectErrorStream(true)
|
||||
val process = procBuilder.start()
|
||||
val retCode = process.waitFor()
|
||||
if (retCode != 0) throw GradleException("Unable to run 'java_home', return code $retCode")
|
||||
process.inputStream.bufferedReader().forEachLine { line ->
|
||||
for (rex in macOsJavaHomeOutRegexes) {
|
||||
val matchResult = rex.matchEntire(line)
|
||||
if (matchResult != null) {
|
||||
val jdkHomeDir = File(matchResult.groupValues[3])
|
||||
// Filter out JRE installed at /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/
|
||||
// and shown by the java_home tool
|
||||
if (!jdkHomeDir.path.contains("JavaAppletPlugin.plugin")) {
|
||||
addIfBetter(project, matchResult.groupValues[1], matchResult.groupValues[0], jdkHomeDir)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val unixConventionalJdkLocations = listOf(
|
||||
"/usr/lib/jvm", // *deb, Arch
|
||||
"/opt", // *rpm, Gentoo, HP/UX
|
||||
"/usr/lib", // Slackware 32
|
||||
"/usr/lib64", // Slackware 64
|
||||
"/usr/local", // OpenBSD, FreeBSD
|
||||
"/usr/pkg/java", // NetBSD
|
||||
"/usr/jdk/instances") // Solaris
|
||||
|
||||
private val unixConventionalJdkDirRex = Regex("jdk|jre|java|zulu")
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnUnix(project: Project) {
|
||||
for (loc in unixConventionalJdkLocations) {
|
||||
val installedJdks = File(loc).listFiles { dir ->
|
||||
dir.isDirectory &&
|
||||
unixConventionalJdkDirRex.containsMatchIn(dir.name) &&
|
||||
fileFrom(dir, "bin", "java").isFile
|
||||
} ?: continue
|
||||
for (dir in installedJdks) {
|
||||
val versionMatch = javaVersionRegex.find(dir.name)
|
||||
if (versionMatch == null) {
|
||||
project.logger.info("Unable to extract version from possible JDK dir: $dir")
|
||||
}
|
||||
else {
|
||||
addIfBetter(project, versionMatch.value, dir.name, dir)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val windowsConventionalJdkRegistryPaths = listOf(
|
||||
"SOFTWARE\\JavaSoft\\Java Development Kit",
|
||||
"SOFTWARE\\Wow6432Node\\JavaSoft\\Java Development Kit",
|
||||
"SOFTWARE\\JavaSoft\\JDK",
|
||||
"SOFTWARE\\Wow6432Node\\JavaSoft\\JDK")
|
||||
|
||||
fun MutableCollection<JdkId>.discoverJdksOnWindows(project: Project) {
|
||||
val registry = Native.get(WindowsRegistry::class.java)
|
||||
for (regPath in windowsConventionalJdkRegistryPaths) {
|
||||
val jdkKeys = try {
|
||||
registry.getSubkeys(HKEY_LOCAL_MACHINE, regPath)
|
||||
} catch (e: RuntimeException) {
|
||||
// ignore missing nodes
|
||||
continue
|
||||
}
|
||||
for (jdkKey in jdkKeys) {
|
||||
try {
|
||||
val javaHome = registry.getStringValue(HKEY_LOCAL_MACHINE, regPath + "\\" + jdkKey, "JavaHome")
|
||||
val versionMatch = javaVersionRegex.find(jdkKey)
|
||||
if (versionMatch == null) {
|
||||
project.logger.info("Unable to extract version from possible JDK location: $javaHome ($jdkKey)")
|
||||
}
|
||||
else {
|
||||
javaHome.takeIf { it.isNotEmpty() }
|
||||
?.let { File(it) }
|
||||
?.takeIf { it.isDirectory && fileFrom(it, "bin", "java.exe").isFile }
|
||||
?.let {
|
||||
addIfBetter(project, versionMatch.value, jdkKey, it)
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e: RuntimeException) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user