Release flag. Add diagnostics
This commit is contained in:
+1
-1
@@ -392,7 +392,7 @@ default: `indy-with-constants` for JVM target 9 or greater, `inline` otherwise""
|
||||
value = "-Xrelease",
|
||||
valueDescription = "Supported versions depend on used JDK",
|
||||
description = """
|
||||
Compile against specified JDK API. Supported versions depend on used JDK
|
||||
Compile against specified JDK API. Supported versions depend on used JDK.
|
||||
"""
|
||||
)
|
||||
var release: String? by NullableStringFreezableVar(null)
|
||||
|
||||
@@ -239,15 +239,10 @@ class KotlinCoreEnvironment private constructor(
|
||||
val javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||
|
||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
||||
val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0)
|
||||
val javaModuleFinder = CliJavaModuleFinder(
|
||||
jdkHome?.path?.let { path ->
|
||||
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(path)
|
||||
},
|
||||
jdkHome?.path?.let { path ->
|
||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
},
|
||||
jdkHome,
|
||||
messageCollector,
|
||||
javaFileManager,
|
||||
project,
|
||||
releaseTarget
|
||||
|
||||
@@ -73,17 +73,26 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
|
||||
}
|
||||
}
|
||||
|
||||
val release = arguments.release
|
||||
if (release != null) {
|
||||
val value = release.toIntOrNull()
|
||||
val releaseTarget = arguments.release
|
||||
if (releaseTarget != null) {
|
||||
val value = releaseTarget.toIntOrNull()
|
||||
if (value == null) {
|
||||
messageCollector.report(
|
||||
ERROR,
|
||||
"Can't parse value passed for `-Xrelease`: $release."
|
||||
"Can't parse value passed for `-Xrelease`: $releaseTarget."
|
||||
)
|
||||
} else {
|
||||
// TODO: check
|
||||
put(JVMConfigurationKeys.RELEASE, value)
|
||||
if (value < 6) {
|
||||
messageCollector.report(
|
||||
ERROR,
|
||||
"`$value` is not valid value for `-Xrelease` flag."
|
||||
)
|
||||
} else {
|
||||
//don't use release flag if it equals to compilation JDK version
|
||||
if (value != getJavaVersion() || arguments.jdkHome != null) {
|
||||
put(JVMConfigurationKeys.RELEASE, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,3 +330,6 @@ fun CompilerConfiguration.configureKlibPaths(arguments: K2JVMCompilerArguments)
|
||||
|
||||
private val CompilerConfiguration.messageCollector: MessageCollector
|
||||
get() = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
||||
|
||||
private fun getJavaVersion(): Int =
|
||||
System.getProperty("java.specification.version")?.substringAfter('.')?.toIntOrNull() ?: 6
|
||||
|
||||
@@ -23,29 +23,61 @@ import com.intellij.openapi.vfs.VirtualFileManager
|
||||
import com.intellij.psi.PsiJavaModule
|
||||
import com.intellij.psi.search.GlobalSearchScope
|
||||
import com.intellij.util.io.URLUtil
|
||||
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
|
||||
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
|
||||
import org.jetbrains.kotlin.resolve.jvm.KotlinCliJavaFileManager
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||
import java.io.File
|
||||
|
||||
class CliJavaModuleFinder(
|
||||
jdkRootFile: VirtualFile?,
|
||||
jrtFileSystemRoot: VirtualFile?,
|
||||
private val jdkHome: File?,
|
||||
private val messageCollector: MessageCollector?,
|
||||
private val javaFileManager: KotlinCliJavaFileManager,
|
||||
project: Project,
|
||||
private val releaseTarget: Int
|
||||
) : JavaModuleFinder {
|
||||
|
||||
private val jrtFileSystemRoot = jdkHome?.path?.let { path ->
|
||||
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
||||
}
|
||||
|
||||
private val modulesRoot = jrtFileSystemRoot?.findChild("modules")
|
||||
private val ctSymFile = jdkRootFile?.findChild("lib")?.findChild("ct.sym")
|
||||
|
||||
private val userModules = linkedMapOf<String, JavaModule>()
|
||||
|
||||
private val allScope = GlobalSearchScope.allScope(project)
|
||||
|
||||
public val compilationJdkVersion by lazy {
|
||||
//TODO: add test with -jdk-home
|
||||
private val ctSymFile: VirtualFile? by lazy {
|
||||
if (jdkHome == null) return@lazy reportError("JDK_HOME path is not specified in compiler configuration")
|
||||
|
||||
val jdkRootFile = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(jdkHome.path)
|
||||
?: return@lazy reportError("Can't create virtual file for JDK root under ${jdkHome.path}")
|
||||
|
||||
val lib = jdkRootFile.findChild("lib") ?: return@lazy reportError("Can't find `lib` folder under JDK root: ${jdkHome.path}")
|
||||
|
||||
val ctSym = lib.findChild("ct.sym") ?: return@lazy reportError("Can't find `ct.sym` file in ${jdkHome.path}")
|
||||
|
||||
if (!ctSym.isValid) return@lazy reportError("`ct.sym` is invalid: ${ctSym.path}")
|
||||
|
||||
ctSym
|
||||
}
|
||||
|
||||
private val ctSymRootFolder: VirtualFile? by lazy {
|
||||
if (ctSymFile != null) {
|
||||
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
|
||||
?.findFileByPath(ctSymFile?.path + URLUtil.JAR_SEPARATOR)
|
||||
?: reportError("Can't open `ct.sym` as jar file, file path: ${ctSymFile?.path} ")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private val compilationJdkVersion by lazy {
|
||||
// Observe all JDK codes from folder name chars in ct.sym file,
|
||||
// there should be maximal one corresponding to used compilation JDK
|
||||
ctSymRootFolder()?.children?.maxOf {
|
||||
ctSymRootFolder?.children?.maxOf {
|
||||
if (it.name == "META-INF") -1
|
||||
else it.name.substringBeforeLast("-modules").maxOf { char ->
|
||||
char.toString().toIntOrNull(36) ?: -1
|
||||
@@ -53,7 +85,11 @@ class CliJavaModuleFinder(
|
||||
} ?: -1
|
||||
}
|
||||
|
||||
private val isJDK12OrLater
|
||||
val ctSymModules by lazy {
|
||||
collectModuleRoots()
|
||||
}
|
||||
|
||||
private val isCompilationJDK12OrLater
|
||||
get() = compilationJdkVersion >= 12
|
||||
|
||||
private val useLastJdkApi: Boolean
|
||||
@@ -66,10 +102,6 @@ class CliJavaModuleFinder(
|
||||
val allObservableModules: Sequence<JavaModule>
|
||||
get() = systemModules + userModules.values
|
||||
|
||||
val ctSymModules by lazy {
|
||||
collectModuleRoots(releaseTarget)
|
||||
}
|
||||
|
||||
val systemModules: Sequence<JavaModule.Explicit>
|
||||
get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else
|
||||
ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) }
|
||||
@@ -101,44 +133,41 @@ class CliJavaModuleFinder(
|
||||
private fun matchesRelease(fileName: String, release: Int) =
|
||||
!fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules`
|
||||
|
||||
private fun hasCtSymFile() = ctSymFile != null && ctSymFile.isValid
|
||||
|
||||
fun listFoldersForRelease(): List<VirtualFile> {
|
||||
if (!hasCtSymFile()) return emptyList()
|
||||
val root = ctSymRootFolder() ?: return emptyList()
|
||||
return root.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
|
||||
if (isJDK12OrLater)
|
||||
if (ctSymRootFolder == null) return emptyList()
|
||||
return ctSymRootFolder!!.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
|
||||
if (isCompilationJDK12OrLater)
|
||||
it.children.toList()
|
||||
else {
|
||||
listOf(it)
|
||||
}
|
||||
}.apply {
|
||||
if (isEmpty()) reportError("'-Xrelease=${releaseTarget}' option is not supported by used JDK: ${jdkHome?.path}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun collectModuleRoots(release: Int): Map<String, VirtualFile> {
|
||||
if (!hasCtSymFile()) return emptyMap()
|
||||
private fun collectModuleRoots(): Map<String, VirtualFile> {
|
||||
val result = mutableMapOf<String, VirtualFile>()
|
||||
val root = ctSymRootFolder() ?: return emptyMap()
|
||||
|
||||
|
||||
if (isJDK12OrLater) {
|
||||
if (isCompilationJDK12OrLater) {
|
||||
listFoldersForRelease().forEach { modulesRoot ->
|
||||
modulesRoot.findChild("module-info.sig")?.let {
|
||||
result[modulesRoot.name] = modulesRoot
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (releaseTarget > 8) {
|
||||
val moduleSigs = root.findChild(codeFor(release) + if (!isJDK12OrLater) "-modules" else "")
|
||||
?: error("Can't find modules signatures in `ct.sym` file for `-release $release` in ${ctSymFile?.path}")
|
||||
moduleSigs.children.forEach {
|
||||
result[it.name] = it
|
||||
}
|
||||
if (this.releaseTarget > 8 && ctSymRootFolder != null) {
|
||||
ctSymRootFolder!!.findChild(codeFor(releaseTarget) + if (!isCompilationJDK12OrLater) "-modules" else "")?.apply {
|
||||
children.forEach {
|
||||
result[it.name] = it
|
||||
}
|
||||
} ?: reportError("Can't find modules signatures in `ct.sym` file for `-Xrelease=$releaseTarget` in ${ctSymFile!!.path}")
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
private fun ctSymRootFolder() = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
|
||||
?.findFileByPath(ctSymFile!!.path + URLUtil.JAR_SEPARATOR)
|
||||
private fun reportError(message: String): VirtualFile? {
|
||||
messageCollector?.report(CompilerMessageSeverity.ERROR, message)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,8 @@ class CliJavaModuleResolver(
|
||||
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
||||
|
||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
||||
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL) {
|
||||
//TODO: proper root filtering is required
|
||||
if (file.fileSystem.protocol == StandardFileSystems.JRT_PROTOCOL /*|| file.extension == "sig"*/) {
|
||||
return systemModules.firstOrNull { module -> file in module }
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
OK
|
||||
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
val z: java.nio.ByteBuffer? = null
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module module {
|
||||
exports foo;
|
||||
|
||||
requires kotlin.stdlib;
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
OK
|
||||
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
val z: java.nio.ByteBuffer? = null
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
module module11 {
|
||||
exports foo;
|
||||
|
||||
requires kotlin.stdlib;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
compiler/testData/javaModules/releaseFlag/moduleSwing/foo/Foo.kt:5:24: error: symbol is declared in module 'javafx.media' which current module does not depend on
|
||||
val z: javax.swing.JFrame? = null
|
||||
^
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,6 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
//no requirement
|
||||
val z: javax.swing.JFrame? = null
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package foo;
|
||||
|
||||
public class Foo2 {}
|
||||
@@ -0,0 +1,5 @@
|
||||
module moduleSwing {
|
||||
exports foo;
|
||||
|
||||
requires kotlin.stdlib;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
error: can't find modules signatures in `ct.sym` file for `-Xrelease=12` in $JDK11/lib/ct.sym
|
||||
error: module java.base cannot be found in the module graph
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,6 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
val z: java.nio.ByteBuffer? = null
|
||||
}
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
error: `5` is not valid value for `-Xrelease` flag.
|
||||
COMPILATION_ERROR
|
||||
@@ -0,0 +1,5 @@
|
||||
package foo;
|
||||
|
||||
public class Foo {
|
||||
val z: java.nio.ByteBuffer? = null
|
||||
}
|
||||
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.jvm.compiler
|
||||
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import org.jetbrains.kotlin.cli.AbstractCliTest
|
||||
import org.jetbrains.kotlin.cli.AbstractCliTest.getNormalizedCompilerOutput
|
||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||
import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime
|
||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||
@@ -59,7 +61,10 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||
}
|
||||
|
||||
private fun checkKotlinOutput(moduleName: String): (String) -> Unit = { actual ->
|
||||
KotlinTestUtils.assertEqualsToFile(File(testDataDirectory, "$moduleName.txt"), actual)
|
||||
KotlinTestUtils.assertEqualsToFile(
|
||||
File(testDataDirectory, "$moduleName.txt"),
|
||||
getNormalizedCompilerOutput(actual, null, testDataPath).replace(System.getenv("JDK_11"), "\$JDK11")
|
||||
)
|
||||
}
|
||||
|
||||
private data class ModuleRunResult(val stdout: String, val stderr: String)
|
||||
@@ -144,6 +149,21 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
||||
module("moduleB", listOf(module("moduleA")), addModules = emptyList())
|
||||
}
|
||||
|
||||
fun testReleaseFlagWrongValue() {
|
||||
// Test that although we have moduleA in the module path, it's not in the module graph
|
||||
// because we did not provide -Xadd-modules=moduleA
|
||||
module("module5", additionalKotlinArguments = listOf("-Xrelease=5"))
|
||||
module("module12", additionalKotlinArguments = listOf("-Xrelease=12"))
|
||||
}
|
||||
|
||||
fun testReleaseFlag() {
|
||||
// Test that although we have moduleA in the module path, it's not in the module graph
|
||||
// because we did not provide -Xadd-modules=moduleA
|
||||
module("module")
|
||||
module("module11", additionalKotlinArguments = listOf("-Xrelease=11"))
|
||||
module("moduleSwing", additionalKotlinArguments = listOf("-Xrelease=9"))
|
||||
}
|
||||
|
||||
fun testNamedReadsTransitive() {
|
||||
val a = module("moduleA")
|
||||
val b = module("moduleB", listOf(a))
|
||||
|
||||
Reference in New Issue
Block a user