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",
|
value = "-Xrelease",
|
||||||
valueDescription = "Supported versions depend on used JDK",
|
valueDescription = "Supported versions depend on used JDK",
|
||||||
description = """
|
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)
|
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 javaFileManager = ServiceManager.getService(project, CoreJavaFileManager::class.java) as KotlinCliJavaFileManagerImpl
|
||||||
|
|
||||||
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
val jdkHome = configuration.get(JVMConfigurationKeys.JDK_HOME)
|
||||||
val jrtFileSystem = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JRT_PROTOCOL)
|
|
||||||
val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0)
|
val releaseTarget = configuration.get(JVMConfigurationKeys.RELEASE, 0)
|
||||||
val javaModuleFinder = CliJavaModuleFinder(
|
val javaModuleFinder = CliJavaModuleFinder(
|
||||||
jdkHome?.path?.let { path ->
|
jdkHome,
|
||||||
VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.FILE_PROTOCOL).findFileByPath(path)
|
messageCollector,
|
||||||
},
|
|
||||||
jdkHome?.path?.let { path ->
|
|
||||||
jrtFileSystem?.findFileByPath(path + URLUtil.JAR_SEPARATOR)
|
|
||||||
},
|
|
||||||
javaFileManager,
|
javaFileManager,
|
||||||
project,
|
project,
|
||||||
releaseTarget
|
releaseTarget
|
||||||
|
|||||||
@@ -73,17 +73,26 @@ fun CompilerConfiguration.setupJvmSpecificArguments(arguments: K2JVMCompilerArgu
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val release = arguments.release
|
val releaseTarget = arguments.release
|
||||||
if (release != null) {
|
if (releaseTarget != null) {
|
||||||
val value = release.toIntOrNull()
|
val value = releaseTarget.toIntOrNull()
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
messageCollector.report(
|
messageCollector.report(
|
||||||
ERROR,
|
ERROR,
|
||||||
"Can't parse value passed for `-Xrelease`: $release."
|
"Can't parse value passed for `-Xrelease`: $releaseTarget."
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
// TODO: check
|
if (value < 6) {
|
||||||
put(JVMConfigurationKeys.RELEASE, value)
|
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
|
private val CompilerConfiguration.messageCollector: MessageCollector
|
||||||
get() = getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY)
|
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.PsiJavaModule
|
||||||
import com.intellij.psi.search.GlobalSearchScope
|
import com.intellij.psi.search.GlobalSearchScope
|
||||||
import com.intellij.util.io.URLUtil
|
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.KotlinCliJavaFileManager
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModule
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleFinder
|
||||||
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
import org.jetbrains.kotlin.resolve.jvm.modules.JavaModuleInfo
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class CliJavaModuleFinder(
|
class CliJavaModuleFinder(
|
||||||
jdkRootFile: VirtualFile?,
|
private val jdkHome: File?,
|
||||||
jrtFileSystemRoot: VirtualFile?,
|
private val messageCollector: MessageCollector?,
|
||||||
private val javaFileManager: KotlinCliJavaFileManager,
|
private val javaFileManager: KotlinCliJavaFileManager,
|
||||||
project: Project,
|
project: Project,
|
||||||
private val releaseTarget: Int
|
private val releaseTarget: Int
|
||||||
) : JavaModuleFinder {
|
) : 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 modulesRoot = jrtFileSystemRoot?.findChild("modules")
|
||||||
private val ctSymFile = jdkRootFile?.findChild("lib")?.findChild("ct.sym")
|
|
||||||
private val userModules = linkedMapOf<String, JavaModule>()
|
private val userModules = linkedMapOf<String, JavaModule>()
|
||||||
|
|
||||||
private val allScope = GlobalSearchScope.allScope(project)
|
private val allScope = GlobalSearchScope.allScope(project)
|
||||||
|
|
||||||
public val compilationJdkVersion by lazy {
|
private val ctSymFile: VirtualFile? by lazy {
|
||||||
//TODO: add test with -jdk-home
|
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,
|
// Observe all JDK codes from folder name chars in ct.sym file,
|
||||||
// there should be maximal one corresponding to used compilation JDK
|
// there should be maximal one corresponding to used compilation JDK
|
||||||
ctSymRootFolder()?.children?.maxOf {
|
ctSymRootFolder?.children?.maxOf {
|
||||||
if (it.name == "META-INF") -1
|
if (it.name == "META-INF") -1
|
||||||
else it.name.substringBeforeLast("-modules").maxOf { char ->
|
else it.name.substringBeforeLast("-modules").maxOf { char ->
|
||||||
char.toString().toIntOrNull(36) ?: -1
|
char.toString().toIntOrNull(36) ?: -1
|
||||||
@@ -53,7 +85,11 @@ class CliJavaModuleFinder(
|
|||||||
} ?: -1
|
} ?: -1
|
||||||
}
|
}
|
||||||
|
|
||||||
private val isJDK12OrLater
|
val ctSymModules by lazy {
|
||||||
|
collectModuleRoots()
|
||||||
|
}
|
||||||
|
|
||||||
|
private val isCompilationJDK12OrLater
|
||||||
get() = compilationJdkVersion >= 12
|
get() = compilationJdkVersion >= 12
|
||||||
|
|
||||||
private val useLastJdkApi: Boolean
|
private val useLastJdkApi: Boolean
|
||||||
@@ -66,10 +102,6 @@ class CliJavaModuleFinder(
|
|||||||
val allObservableModules: Sequence<JavaModule>
|
val allObservableModules: Sequence<JavaModule>
|
||||||
get() = systemModules + userModules.values
|
get() = systemModules + userModules.values
|
||||||
|
|
||||||
val ctSymModules by lazy {
|
|
||||||
collectModuleRoots(releaseTarget)
|
|
||||||
}
|
|
||||||
|
|
||||||
val systemModules: Sequence<JavaModule.Explicit>
|
val systemModules: Sequence<JavaModule.Explicit>
|
||||||
get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else
|
get() = if (useLastJdkApi) modulesRoot?.children.orEmpty().asSequence().mapNotNull(this::findSystemModule) else
|
||||||
ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) }
|
ctSymModules.values.asSequence().mapNotNull { findSystemModule(it, true) }
|
||||||
@@ -101,44 +133,41 @@ class CliJavaModuleFinder(
|
|||||||
private fun matchesRelease(fileName: String, release: Int) =
|
private fun matchesRelease(fileName: String, release: Int) =
|
||||||
!fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules`
|
!fileName.contains("-") && fileName.contains(codeFor(release)) // skip `*-modules`
|
||||||
|
|
||||||
private fun hasCtSymFile() = ctSymFile != null && ctSymFile.isValid
|
|
||||||
|
|
||||||
fun listFoldersForRelease(): List<VirtualFile> {
|
fun listFoldersForRelease(): List<VirtualFile> {
|
||||||
if (!hasCtSymFile()) return emptyList()
|
if (ctSymRootFolder == null) return emptyList()
|
||||||
val root = ctSymRootFolder() ?: return emptyList()
|
return ctSymRootFolder!!.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
|
||||||
return root.children.filter { matchesRelease(it.name, releaseTarget) }.flatMap {
|
if (isCompilationJDK12OrLater)
|
||||||
if (isJDK12OrLater)
|
|
||||||
it.children.toList()
|
it.children.toList()
|
||||||
else {
|
else {
|
||||||
listOf(it)
|
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> {
|
private fun collectModuleRoots(): Map<String, VirtualFile> {
|
||||||
if (!hasCtSymFile()) return emptyMap()
|
|
||||||
val result = mutableMapOf<String, VirtualFile>()
|
val result = mutableMapOf<String, VirtualFile>()
|
||||||
val root = ctSymRootFolder() ?: return emptyMap()
|
if (isCompilationJDK12OrLater) {
|
||||||
|
|
||||||
|
|
||||||
if (isJDK12OrLater) {
|
|
||||||
listFoldersForRelease().forEach { modulesRoot ->
|
listFoldersForRelease().forEach { modulesRoot ->
|
||||||
modulesRoot.findChild("module-info.sig")?.let {
|
modulesRoot.findChild("module-info.sig")?.let {
|
||||||
result[modulesRoot.name] = modulesRoot
|
result[modulesRoot.name] = modulesRoot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (releaseTarget > 8) {
|
if (this.releaseTarget > 8 && ctSymRootFolder != null) {
|
||||||
val moduleSigs = root.findChild(codeFor(release) + if (!isJDK12OrLater) "-modules" else "")
|
ctSymRootFolder!!.findChild(codeFor(releaseTarget) + if (!isCompilationJDK12OrLater) "-modules" else "")?.apply {
|
||||||
?: error("Can't find modules signatures in `ct.sym` file for `-release $release` in ${ctSymFile?.path}")
|
children.forEach {
|
||||||
moduleSigs.children.forEach {
|
result[it.name] = it
|
||||||
result[it.name] = it
|
}
|
||||||
}
|
} ?: reportError("Can't find modules signatures in `ct.sym` file for `-Xrelease=$releaseTarget` in ${ctSymFile!!.path}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ctSymRootFolder() = VirtualFileManager.getInstance().getFileSystem(StandardFileSystems.JAR_PROTOCOL)
|
private fun reportError(message: String): VirtualFile? {
|
||||||
?.findFileByPath(ctSymFile!!.path + URLUtil.JAR_SEPARATOR)
|
messageCollector?.report(CompilerMessageSeverity.ERROR, message)
|
||||||
|
return null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ class CliJavaModuleResolver(
|
|||||||
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
private val sourceModule: JavaModule? = userModules.firstOrNull(JavaModule::isSourceModule)
|
||||||
|
|
||||||
private fun findJavaModule(file: VirtualFile): JavaModule? {
|
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 }
|
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 com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.cli.AbstractCliTest
|
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.codegen.forTestCompile.ForTestCompileRuntime
|
||||||
import org.jetbrains.kotlin.test.KotlinTestUtils
|
import org.jetbrains.kotlin.test.KotlinTestUtils
|
||||||
import org.jetbrains.kotlin.test.util.KtTestUtil
|
import org.jetbrains.kotlin.test.util.KtTestUtil
|
||||||
@@ -59,7 +61,10 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkKotlinOutput(moduleName: String): (String) -> Unit = { actual ->
|
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)
|
private data class ModuleRunResult(val stdout: String, val stderr: String)
|
||||||
@@ -144,6 +149,21 @@ class Java11ModulesIntegrationTest : AbstractKotlinCompilerIntegrationTest() {
|
|||||||
module("moduleB", listOf(module("moduleA")), addModules = emptyList())
|
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() {
|
fun testNamedReadsTransitive() {
|
||||||
val a = module("moduleA")
|
val a = module("moduleA")
|
||||||
val b = module("moduleB", listOf(a))
|
val b = module("moduleB", listOf(a))
|
||||||
|
|||||||
Reference in New Issue
Block a user