Gradle,如何使用 Kotlin 库发布 jdk8 变体
Gradle, how to publish jdk8 variant with a Kotlin library
长话短说:我想为我的一个仅使用 kotlin 的库发布一个 jdk8 追溯兼容性的变体。
这是我长期以来一直想要解决的功能,但一直没有成功。然而,在 Gradle Slack 上进行了多次尝试和帮助后,我认为我已经很接近了,但我仍然有一个错误,我似乎无法摆脱它。
想法是使用 jdk11 编译主版本(src/main
和 scr/jpms
,后者仅包含 module-info.class
),同时具有 jdk8
变体对于 src/main
当然只用 jdk8 编译。
这是我现在的 build.gradle.kts
:
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.5.10"
`java-library`
`maven-publish`
}
group = "kotlin.graphics"
version = "3.3.1"
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
testImplementation("io.kotest:kotest-runner-junit5:4.4.1")
testImplementation("io.kotest:kotest-assertions-core:4.4.1")
}
val jdk8 = sourceSets.create("jdk8") {
java.srcDir("src/main/java")
kotlin.srcDir("src/main/kotlin")
}
val jdk11 = sourceSets["main"].apply {
java.srcDir("src/jpms/java")
}
java.registerFeature("jdk8") {
usingSourceSet(jdk8)
capability("group", "name", "0.1")
}
configureCompileVersion(jdk8, 8)
configureCompileVersion(jdk11, 11)
val moduleName = "$group.$name"
fun configureCompileVersion(set: SourceSet, jdkVersion: Int) {
val compiler = project.javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(jdkVersion))
}.get()
val target = if (jdkVersion == 8) "1.8" else jdkVersion.toString()
tasks {
named<KotlinCompile>(set.compileKotlinTaskName) {
kotlinOptions {
jvmTarget = target
jdkHome = compiler.metadata.installationPath.asFile.absolutePath
}
source = sourceSets.main.get().kotlin
}
named<JavaCompile>(set.compileJavaTaskName) {
targetCompatibility = target
sourceCompatibility = target
modularity.inferModulePath.set(jdkVersion >= 9)
javaCompiler.set(compiler)
source = sourceSets.main.get().allJava + set.allJava
if (jdkVersion >= 9)
options.compilerArgs = listOf("--patch-module", "$moduleName=${set.output.asPath}")
}
}
}
val SourceSet.compileKotlinTaskName: String
get() = getCompileTaskName("kotlin")
val SourceSet.kotlin: SourceDirectorySet
get() = withConvention(KotlinSourceSet::class) { kotlin }
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "org.gradle.sample"
artifactId = "library"
version = "1.1"
from(components["java"])
}
}
repositories.maven {
name = "prova"
url = uri("repo")
}
}
如果我运行 :assemble
,生成的工件是用jdk11正确编译的。
直到一切都如预期的那样。
但是如果我尝试发布,我会得到:
Task :compileJdk8Kotlin FAILED
5 actionable tasks: 1 executed, 4 up-to-date
e: Module java.base cannot be found in the module graph
出于某些原因,Gradle 似乎尝试使用 jpms 编译 jdk8
变体,尽管它应该被自动禁用。我尝试手动将其设置为开启和关闭:
modularity.inferModulePath.set(jdkVersion >= 9)
但是也没用。
项目是here
Gradle7.1.1
我想我明白了
// these two are simple helpers
val SourceSet.compileKotlinTaskName: String
get() = getCompileTaskName("kotlin")
val SourceSet.kotlin: SourceDirectorySet
get() = project.extensions.getByType<KotlinJvmProjectExtension>().sourceSets.getByName(name).kotlin
// pick the `main` sourceSet and use it for jdk11
val jdk11 = sourceSets.main.get()
// now we clone `main` into `jdk8`, with the only difference being the exclusion
// of `module-info.class`. We need to call `::create` to avoid getting the
// reference to the same sourceSet.
val jdk8 = sourceSets.create("jdk8") {
// this is superfluous, adding not-existing folders is harmless, but it's
// rather confusing when you need to debug two sourceSet java/kotlin
java.setSrcDirs(emptySet<File>())
kotlin.setSrcDirs(emptySet<File>())
// assign the very same source directories
java.setSrcDirs(jdk11.java.srcDirs)
kotlin.setSrcDirs(jdk11.kotlin.srcDirs)
// exclude the file from both, since kotlin includes always the java sources
java.setExcludes(listOf("module-info.java"))
kotlin.setExcludes(listOf("module-info.java"))
}
// this will create the `jdk8` variant using the given sourceSet at the given
// capabilities
java.registerFeature("jdk8") {
usingSourceSet(jdk8)
// I experienced `version` to be `null` if it's declared in the
// build.gradle.kts, then I moved it into `settings.gradle.kts to fix this
capability(group.toString(), name, version.toString())
}
// set everything for each variant, jdk11 is the default/main one
configureCompileVersion(jdk8, 8)
configureCompileVersion(jdk11, 11)
val moduleName = "$group.$name"
fun configureCompileVersion(set: SourceSet, jdkVersion: Int) {
tasks {
val target = if (jdkVersion == 8) "1.8" else jdkVersion.toString()
// we do need the task name because we have compileKotlin and
// jdk8CompileKotlin and we want to set stuff accordingly
named<KotlinCompile>(set.compileKotlinTaskName) {
targetCompatibility = target
sourceCompatibility = target
kotlinOptions {
// jdkHome is deprecated in 1.5.30
jvmTarget = target
// this is outside the variant scope
freeCompilerArgs += listOf("-Xinline-classes", "-Xopt-in=kotlin.RequiresOptIn")
}
source = sourceSets.main.get().kotlin
}
named<JavaCompile>(set.compileJavaTaskName) {
targetCompatibility = target
sourceCompatibility = target
// this is supposed to be set automatically, well with a jdk8 variant
// you need to set it up explicitly
modularity.inferModulePath.set(jdkVersion >= 9)
javaCompiler.set(project.javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(jdkVersion))
}.get())
source = set.allJava
if (jdkVersion >= 9)
options.compilerArgs = listOf("--patch-module", "$moduleName=${set.output.asPath}")
}
withType<Test> { useJUnitPlatform() }
}
}
// We also want to automatically set all the jdk11 dependencies to jdk8 as well
configurations {
named("jdk8Implementation") {
extendsFrom(implementation.get())
}
}
长话短说:我想为我的一个仅使用 kotlin 的库发布一个 jdk8 追溯兼容性的变体。
这是我长期以来一直想要解决的功能,但一直没有成功。然而,在 Gradle Slack 上进行了多次尝试和帮助后,我认为我已经很接近了,但我仍然有一个错误,我似乎无法摆脱它。
想法是使用 jdk11 编译主版本(src/main
和 scr/jpms
,后者仅包含 module-info.class
),同时具有 jdk8
变体对于 src/main
当然只用 jdk8 编译。
这是我现在的 build.gradle.kts
:
import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.5.10"
`java-library`
`maven-publish`
}
group = "kotlin.graphics"
version = "3.3.1"
repositories {
mavenCentral()
}
dependencies {
implementation(kotlin("stdlib-jdk8"))
testImplementation("io.kotest:kotest-runner-junit5:4.4.1")
testImplementation("io.kotest:kotest-assertions-core:4.4.1")
}
val jdk8 = sourceSets.create("jdk8") {
java.srcDir("src/main/java")
kotlin.srcDir("src/main/kotlin")
}
val jdk11 = sourceSets["main"].apply {
java.srcDir("src/jpms/java")
}
java.registerFeature("jdk8") {
usingSourceSet(jdk8)
capability("group", "name", "0.1")
}
configureCompileVersion(jdk8, 8)
configureCompileVersion(jdk11, 11)
val moduleName = "$group.$name"
fun configureCompileVersion(set: SourceSet, jdkVersion: Int) {
val compiler = project.javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(jdkVersion))
}.get()
val target = if (jdkVersion == 8) "1.8" else jdkVersion.toString()
tasks {
named<KotlinCompile>(set.compileKotlinTaskName) {
kotlinOptions {
jvmTarget = target
jdkHome = compiler.metadata.installationPath.asFile.absolutePath
}
source = sourceSets.main.get().kotlin
}
named<JavaCompile>(set.compileJavaTaskName) {
targetCompatibility = target
sourceCompatibility = target
modularity.inferModulePath.set(jdkVersion >= 9)
javaCompiler.set(compiler)
source = sourceSets.main.get().allJava + set.allJava
if (jdkVersion >= 9)
options.compilerArgs = listOf("--patch-module", "$moduleName=${set.output.asPath}")
}
}
}
val SourceSet.compileKotlinTaskName: String
get() = getCompileTaskName("kotlin")
val SourceSet.kotlin: SourceDirectorySet
get() = withConvention(KotlinSourceSet::class) { kotlin }
publishing {
publications {
create<MavenPublication>("maven") {
groupId = "org.gradle.sample"
artifactId = "library"
version = "1.1"
from(components["java"])
}
}
repositories.maven {
name = "prova"
url = uri("repo")
}
}
如果我运行 :assemble
,生成的工件是用jdk11正确编译的。
直到一切都如预期的那样。
但是如果我尝试发布,我会得到:
Task :compileJdk8Kotlin FAILED 5 actionable tasks: 1 executed, 4 up-to-date e: Module java.base cannot be found in the module graph
出于某些原因,Gradle 似乎尝试使用 jpms 编译 jdk8
变体,尽管它应该被自动禁用。我尝试手动将其设置为开启和关闭:
modularity.inferModulePath.set(jdkVersion >= 9)
但是也没用。
项目是here
Gradle7.1.1
我想我明白了
// these two are simple helpers
val SourceSet.compileKotlinTaskName: String
get() = getCompileTaskName("kotlin")
val SourceSet.kotlin: SourceDirectorySet
get() = project.extensions.getByType<KotlinJvmProjectExtension>().sourceSets.getByName(name).kotlin
// pick the `main` sourceSet and use it for jdk11
val jdk11 = sourceSets.main.get()
// now we clone `main` into `jdk8`, with the only difference being the exclusion
// of `module-info.class`. We need to call `::create` to avoid getting the
// reference to the same sourceSet.
val jdk8 = sourceSets.create("jdk8") {
// this is superfluous, adding not-existing folders is harmless, but it's
// rather confusing when you need to debug two sourceSet java/kotlin
java.setSrcDirs(emptySet<File>())
kotlin.setSrcDirs(emptySet<File>())
// assign the very same source directories
java.setSrcDirs(jdk11.java.srcDirs)
kotlin.setSrcDirs(jdk11.kotlin.srcDirs)
// exclude the file from both, since kotlin includes always the java sources
java.setExcludes(listOf("module-info.java"))
kotlin.setExcludes(listOf("module-info.java"))
}
// this will create the `jdk8` variant using the given sourceSet at the given
// capabilities
java.registerFeature("jdk8") {
usingSourceSet(jdk8)
// I experienced `version` to be `null` if it's declared in the
// build.gradle.kts, then I moved it into `settings.gradle.kts to fix this
capability(group.toString(), name, version.toString())
}
// set everything for each variant, jdk11 is the default/main one
configureCompileVersion(jdk8, 8)
configureCompileVersion(jdk11, 11)
val moduleName = "$group.$name"
fun configureCompileVersion(set: SourceSet, jdkVersion: Int) {
tasks {
val target = if (jdkVersion == 8) "1.8" else jdkVersion.toString()
// we do need the task name because we have compileKotlin and
// jdk8CompileKotlin and we want to set stuff accordingly
named<KotlinCompile>(set.compileKotlinTaskName) {
targetCompatibility = target
sourceCompatibility = target
kotlinOptions {
// jdkHome is deprecated in 1.5.30
jvmTarget = target
// this is outside the variant scope
freeCompilerArgs += listOf("-Xinline-classes", "-Xopt-in=kotlin.RequiresOptIn")
}
source = sourceSets.main.get().kotlin
}
named<JavaCompile>(set.compileJavaTaskName) {
targetCompatibility = target
sourceCompatibility = target
// this is supposed to be set automatically, well with a jdk8 variant
// you need to set it up explicitly
modularity.inferModulePath.set(jdkVersion >= 9)
javaCompiler.set(project.javaToolchains.compilerFor {
languageVersion.set(JavaLanguageVersion.of(jdkVersion))
}.get())
source = set.allJava
if (jdkVersion >= 9)
options.compilerArgs = listOf("--patch-module", "$moduleName=${set.output.asPath}")
}
withType<Test> { useJUnitPlatform() }
}
}
// We also want to automatically set all the jdk11 dependencies to jdk8 as well
configurations {
named("jdk8Implementation") {
extendsFrom(implementation.get())
}
}