使用Gradle构建Kotlin + Java 9项目

22

我对Gradle(和Java 9,老实说)还比较陌生,我试图使用Gradle构建一个简单的库项目,其中既包含Java 9元素,又包含Kotlin元素。具体来说,Java中有一个接口,在Kotlin中有相应的实现;我本可以完全用Kotlin实现,但是因为modules-info.java必须使用Java,所以我选择了这种方式。

我正在使用IntelliJ Idea进行构建,使用1.2.0 kotlin插件和4.3.1 gradle在外部定义。

文件系统结构如下:

+ src
  + main
    + java
      + some.package
        - Roundabout.java [an interface]
      - module-info.java
    + kotlin
      + some.package.impl
        - RoundaboutImpl.kt [implementing the interface]

module-info.java 是:

module some.package {
  requires kotlin.stdlib;
  exports some.package;
}

build.gradle 是:

buildscript {
    ext.kotlin_version = '1.2.0'

    repositories {
        mavenCentral()
    }
    dependencies {
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
    }
}

group 'some.package'
version '1.0-PRE_ALPHA'

apply plugin: 'java-library'
apply plugin: 'kotlin'

tasks.withType(JavaCompile) {
    options.encoding = 'UTF-8'
}

sourceCompatibility = 9

compileJava {
    dependsOn(':compileKotlin')
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
        ]
        classpath = files()
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib', version: "$kotlin_version"
    testCompile group: 'junit', name: 'junit', version: '4.12'
}

compileKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

compileTestKotlin {
    kotlinOptions.jvmTarget = "1.8"
}

注意,我必须在Java编译任务中指定模块路径,否则编译会失败并出现以下错误:

error: module not found: kotlin.stdlib requires kotlin.stdlib;

无论如何,现在此构建失败并出现以下错误,我无法弄清楚如何解决:

error: package some.package.impl does not exist

import some.package.impl.RoundaboutImpl;

error: cannot find symbol

return new RoundaboutImpl<>(queueSize, parallelism, worker, threadPool);

我认为编译的Kotlin部分没有问题,然后Java部分失败,因为它没有“看到”Kotlin的那一面,可以这么说。

我认为我应该以某种方式告诉它在类路径中加载已编译的Kotlin类;但是,(首先)我如何在Gradle中实现这一点? (第二)甚至可能吗?我认为您不能在Java 9中混合使用模块路径和类路径。

我该如何解决这个问题?我认为这是一个非常常见的情况,因为每个Java9风格的模块都将是混合语言模块(因为module-info.java),所以我认为我在这里缺少了一些非常基本的东西。

提前致谢!


严谨地说,module-info.java并不是真正的Java,而是一种特殊的DSL,但是Kotlin的开发人员已经表示他们认为重新发明它没有价值,所以它将保持不变。 - Salem
2
如果您将接口和实现都保留在Java或Kotlin中,会发生什么呢?那样会编译吗?另外,为什么要使用JVM 1.8编译Kotlin? - Naman
感谢您的评论。全Java解决方案可以编译,而全Kotlin解决方案始终会是混合项目,因为如上所述,modules-info.java由javac管理,因此结果相同。 另外,我不认为我在使用JVM 1.8进行compileKotlin,只是告诉编译器使用1.8目标(这是最新可用的);我错了吗? - Germano Rizzo
1
从 Kotlin 版本 1.3.30 开始,您现在可以指定 jvmTarget 9-12。 - StephanS
讨论在这里:https://github.com/gradle/gradle/issues/17271 - tkruse
显示剩余2条评论
5个回答

16

搞定了!只需将 Kotlin 编译目录设置为与 Java 相同的目录即可:

compileKotlin.destinationDir = compileJava.destinationDir

现在它可以同时使用相同树或不同树中的源文件;但有一个小问题: jar 任务会生成一个包含所有条目重复的 jar 文件。我接下来会解决这个问题。

感谢大家!


3
完成了,只需一个简单的jar { duplicatesStrategy = "exclude" }。这条命令的意思是排除重复的jar包。 - Germano Rizzo

2
我正在使用以下Gradle脚本,将module-info.java放在src/module下。它会自动包含在jar文件中(不会重复):

if (JavaVersion.current() >= JavaVersion.VERSION_1_9) {
    subprojects {
        def srcModule = "src/module"
        def moduleInfo = file("${project.projectDir}/$srcModule/module-info.java")
        if (moduleInfo.exists()) {

            sourceSets {
                module {
                    java {
                        srcDirs = [srcModule]
                        compileClasspath = main.compileClasspath
                        sourceCompatibility = '9'
                        targetCompatibility = '9'
                    }
                }
                main {
                    kotlin { srcDirs += [srcModule] }
                }
            }

            compileModuleJava.configure {
                dependsOn compileKotlin
                destinationDir = compileKotlin.destinationDir
                doFirst {
                    options.compilerArgs = ['--module-path', classpath.asPath,]
                    classpath = files()
                }
            }
            jar.dependsOn compileModuleJava
        }
    }
}

我不再更新它了,你可以查看https://github.com/robstoll/atrium/blob/master/build.gradle来查看目前正在使用的版本。


2

对于我来说,被接受的答案并没有起作用(至少不是以它呈现的方式),但这是有效的:

plugins {
    id "org.jetbrains.kotlin.jvm" version "1.3.50"
}

compileKotlin {
    doFirst {
        destinationDir = compileJava.destinationDir
    }
}

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

按照被接受的答案所建议的方法进行操作,导致我遇到了以下错误:

属性 'compileKotlinOutputClasses' 指定的目录 '/path/to/project/build/classes/kotlin/main' 不存在。


Gradle 版本: 5.6


1
我遇到了同样的问题,现有的答案只能解决其中一部分问题,所以我在整个互联网上搜索,最终找到了一个可行的解决方案。我不知道为什么这个方法有效,但我决定在这里分享我的build.gradle.kts文件,以帮助其他人找到他们的方法。这个文件是我从互联网上找到的许多片段的组合。
我正在使用Java 16、Kotlin 1.5.31和Gradle 7.1。 文件树如下:
+ project
  - build.gradle.kts
  + src
    + main
      + java
        - module-info.java
        + my
          + package
            - SomeClasses.java
      + kotlin
        + my
          + package
            - MoreClasses.kt

module-info.java

module name.of.your.javamodule {
    requires kotlin.stdlib;
    requires kotlinx.coroutines.core.jvm;
    requires org.jetbrains.annotations;

    exports my.pacakge;
}

build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

plugins {
    application
    kotlin("jvm") version "1.5.31"
    id("org.jetbrains.kotlin.plugin.serialization") version "1.5.31"
}
val kotlinVersion = "1.5.31"

group = "your.group.id"
version = "0.0.1-SNAPSHOT"
application {
    mainClass.set("full.name.of.your.MainClass")
    mainModule.set("name.of.your.javamodule") // Same defined in module-info.java
    executableDir = "run"
}

repositories {
    mavenCentral()
}

dependencies {
    implementation(kotlin("stdlib-jdk8", kotlinVersion))
    implementation("com.michael-bull.kotlin-inline-logger:kotlin-inline-logger:1.0.3")
    implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2-native-mt")
    implementation("org.jetbrains:annotations:22.0.0")
    testImplementation(kotlin("test", kotlinVersion))
}

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

tasks {
    run.configure {
        dependsOn(jar)
        doFirst {
            jvmArgs = listOf(
                "--module-path", classpath.asPath
            )
            classpath = files()
        }
    }

    compileJava {
        dependsOn(compileKotlin)
        doFirst {
            options.compilerArgs = listOf(
                "--module-path", classpath.asPath
            )
        }
    }

    compileKotlin {
        destinationDirectory.set(compileJava.get().destinationDirectory)
    }

    jar {
        duplicatesStrategy = DuplicatesStrategy.EXCLUDE
    }
}

tasks.withType<KotlinCompile>().configureEach {
    kotlinOptions {
        jvmTarget = "16"
    }
}


0

在使用 Kotlin DSL 的 Gradle 7.4 中,我需要:

  • 将 module-info.java 移动到 src/main/java
  • 在每个包下创建任何 Java 文件以在 src/main/java 下导出,至少是空的 package-info.java

在 build.gradle.kts 中:

val compileKotlin: KotlinCompile by tasks
val compileJava: JavaCompile by tasks
compileKotlin.destinationDirectory.set(compileJava.destinationDirectory)

此外还讨论了这个问题: https://github.com/gradle/gradle/issues/17271


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接