【问题标题】:Building a Kotlin + Java 9 project with Gradle使用 Gradle 构建 Kotlin + Java 9 项目
【发布时间】:2017-12-05 15:59:06
【问题描述】:

我对 Gradle(老实说,还有 Java 9)还很陌生,我正在尝试使用 Gradle 构建一个简单的库项目,它混合了 Java 9 和 Kotlin。更详细地说,Java 中有一个接口,Kotlin 中有一个实现;我会在 Kotlin 中做所有事情,但 modules-info.java 无论如何都是 java,所以我决定这样做。

我正在 IntelliJ Idea 上构建,并在外部定义了 1.2.0 kotlin 插件和 gradle 4.3.1。

文件系统架构是:

+ 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 compile 任务上指定模块路径,否则编译失败:

错误:找不到模块:kotlin.stdlib 需要 kotlin.stdlib;

无论如何,现在这个构建失败并出现这个错误,我不知道如何解决它:

错误:包 some.package.impl 不存在

导入 some.package.impl.RoundaboutImpl;

错误:找不到符号

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

认为编译的 Kotlin 部分运行正常,然后 java 部分失败,因为它没有“看到”kotlin 方面,可以这么说。

我想我应该告诉它在类路径中加载已经编译的 kotlin 类;但是(首先)我如何在 gradle 中做到这一点?和(第二个)有可能吗?我认为你不能在 Java 9 中混合模块路径和类路径。

我该如何解决这个问题?我认为这是一种很常见的情况,因为每个 java9 样式的模块都是混合语言模块(因为 module-info.java),所以我认为我在这里遗漏了一些非常基本的东西。

提前致谢!

【问题讨论】:

  • 学究起来,module-info.java 不是真的 Java,它是一种特殊的 DSL,但是 Kotlin 开发人员说他们 see no value in reinventing it 所以它会保持原样。
  • 是的,感谢您的澄清!
  • 如果你在 Java 或 Kotlin 中同时保留接口和 impl 会怎样。那它会编译吗?另外,为什么要使用 JVM 1.8 编译 Kotlin?
  • 感谢您的评论。一个全java解决方案编译,而一个全kotlin解决方案总是一个混合项目,因为正如所说的modules-info.java是由javac管理的,所以结果是一样的。另外,我不认为我使用 JVM 1.8 来编译 Kotlin,只是告诉编译器使用 1.8 目标(它是最新可用的);我错了吗?
  • 从 Kotlin 版本 1.3.30 开始,您现在可以指定 jvmTarget 9-12。

标签: java gradle kotlin java-9


【解决方案1】:

解决了!将 kotlin 编译目录设置为与 Java 相同的目录就足够了:

compileKotlin.destinationDir = compileJava.destinationDir

它现在可以使用同一棵树或不同树中的源;但有一个怪癖:jar 任务会生成一个所有条目都重复的 jar。接下来我会努力解决这个问题。

谢谢大家!

【讨论】:

  • 完成,简单的jar { duplicatesStrategy = "exclude" }
【解决方案2】:

接受的答案对我不起作用(至少不是它的呈现方式),但这是有效的:

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

compileKotlin {
    doFirst {
        destinationDir = compileJava.destinationDir
    }
}

jar {
    duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

按照接受的答案建议的方式进行操作导致我收到此错误:

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


Gradle 版本:5.6

【讨论】:

    【解决方案3】:

    我正在使用以下 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 查看当前使用的版本。

    【讨论】:

      【解决方案4】:

      我遇到了同样的问题,现有的答案只为我解决了部分问题,所以我搜索了整个互联网并最终找到了一个可行的解决方案。我不知道为什么会这样,但我决定在这里分享我的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
      

      模块信息.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"
          }
      }
      
      

      【讨论】:

        【解决方案5】:

        在使用 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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-30
          • 1970-01-01
          • 2020-03-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多