From 8f4b4007fec145e0c763a3132c38a971f6ce575d Mon Sep 17 00:00:00 2001 From: Georgy Bronnikov Date: Tue, 26 Nov 2019 17:53:00 +0300 Subject: [PATCH] JVM_IR: Add test for compiling against cross-platform Klib --- .../Generate_Compiler_Tests.xml | 1 + build.gradle.kts | 3 +- compiler/build.gradle.kts | 1 + .../arguments/K2JVMCompilerArguments.kt | 7 ++ .../jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt | 1 + .../jetbrains/kotlin/cli/jvm/jvmArguments.kt | 9 +- .../kotlin/config/JVMConfigurationKeys.java | 3 + compiler/testData/codegen/boxKlib/simple.kt | 16 ++++ compiler/tests-against-klib/build.gradle.kts | 30 ++++++ .../AbstractIrCompileKotlinAgainstKlibTest.kt | 95 +++++++++++++++++++ ...CompileKotlinAgainstKlibTestGenerated.java | 36 +++++++ .../tests/GenerateCompilerTestsAgainstKlib.kt | 20 ++++ .../codegen/AbstractBlackBoxCodegenTest.java | 16 +++- .../generators/tests/GenerateCompilerTests.kt | 1 - .../plugin/impl/compilationContext.kt | 2 + settings.gradle | 1 + 16 files changed, 235 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/codegen/boxKlib/simple.kt create mode 100644 compiler/tests-against-klib/build.gradle.kts create mode 100644 compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCompileKotlinAgainstKlibTest.kt create mode 100644 compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/CompileKotlinAgainstKlibTestGenerated.java create mode 100644 compiler/tests-against-klib/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTestsAgainstKlib.kt diff --git a/.idea/runConfigurations/Generate_Compiler_Tests.xml b/.idea/runConfigurations/Generate_Compiler_Tests.xml index 89bc789a6b0..6c72d3e9721 100644 --- a/.idea/runConfigurations/Generate_Compiler_Tests.xml +++ b/.idea/runConfigurations/Generate_Compiler_Tests.xml @@ -12,6 +12,7 @@ diff --git a/build.gradle.kts b/build.gradle.kts index a2256426939..a761683d1a7 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -513,7 +513,8 @@ tasks { ":compiler:test", ":compiler:container:test", ":compiler:tests-java8:test", - ":compiler:tests-spec:remoteRunTests" + ":compiler:tests-spec:remoteRunTests", + ":compiler:tests-against-klib:test" ) dependsOn(":plugins:jvm-abi-gen:test") } diff --git a/compiler/build.gradle.kts b/compiler/build.gradle.kts index 5359aa8943e..03e0e837691 100644 --- a/compiler/build.gradle.kts +++ b/compiler/build.gradle.kts @@ -102,6 +102,7 @@ sourceSets { projectTest(parallel = true) { dependsOn(":dist") + workingDir = rootDir systemProperty("kotlin.test.script.classpath", testSourceSet.output.classesDirs.joinToString(File.pathSeparator)) doFirst { diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index 8917c27d985..223edfca474 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -302,6 +302,13 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { ) var emitJvmTypeAnnotations: Boolean by FreezableVar(false) + @Argument( + value = "-Xklib", + valueDescription = "", + description = "Paths to cross-platform libraries in .klib format" + ) + val klibLibraries: String? by NullableStringFreezableVar(null) + override fun configureAnalysisFlags(collector: MessageCollector): MutableMap, Any> { val result = super.configureAnalysisFlags(collector) result[JvmAnalysisFlags.strictMetadataVersionSemantics] = strictMetadataVersionSemantics diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt index e6e28fe294a..8df28673458 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/K2JVMCompiler.kt @@ -75,6 +75,7 @@ class K2JVMCompiler : CLICompiler() { configuration.configureExplicitContentRoots(arguments) configuration.configureStandardLibs(paths, arguments) configuration.configureAdvancedJvmOptions(arguments) + configuration.configureKlibPaths(arguments) if (arguments.buildFile == null && !arguments.version && !arguments.allowNoSourceFiles && (arguments.script || arguments.expression != null || arguments.freeArgs.isEmpty())) { diff --git a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt index 0fccea89a9a..523f0742c42 100644 --- a/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt +++ b/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/jvmArguments.kt @@ -138,7 +138,6 @@ fun KotlinCoreEnvironment.registerJavacIfNeeded( return true } - fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerArguments) { put(JVMConfigurationKeys.PARAMETERS_METADATA, arguments.javaParameters) @@ -199,3 +198,11 @@ fun CompilerConfiguration.configureAdvancedJvmOptions(arguments: K2JVMCompilerAr arguments.declarationsOutputPath?.let { put(JVMConfigurationKeys.DECLARATIONS_JSON_PATH, it) } } + +fun CompilerConfiguration.configureKlibPaths(arguments: K2JVMCompilerArguments) { + assert(arguments.useIR || arguments.klibLibraries == null) { "Klib libraries can only be used with IR backend" } + arguments.klibLibraries?.split(File.pathSeparator.toRegex()) + ?.toTypedArray() + ?.filterNot { it.isEmpty() } + ?.let { put(JVMConfigurationKeys.KLIB_PATHS, it) } +} \ No newline at end of file diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java index 593cb7d85f9..8e7a4a6671f 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/config/JVMConfigurationKeys.java @@ -113,4 +113,7 @@ public class JVMConfigurationKeys { public static final CompilerConfigurationKey EMIT_JVM_TYPE_ANNOTATIONS = CompilerConfigurationKey.create("Emit JVM type annotations in bytecode"); + + public static final CompilerConfigurationKey> KLIB_PATHS = + CompilerConfigurationKey.create("Paths to .klib libraries"); } diff --git a/compiler/testData/codegen/boxKlib/simple.kt b/compiler/testData/codegen/boxKlib/simple.kt new file mode 100644 index 00000000000..189b568d338 --- /dev/null +++ b/compiler/testData/codegen/boxKlib/simple.kt @@ -0,0 +1,16 @@ +// FILE: klib.kt +package fromKlib + +class C { + val x = "OK" +} +fun foo(): String { + return C().x +} + +// FILE: test.kt +import fromKlib.foo + +fun box(): String { + return foo() +} \ No newline at end of file diff --git a/compiler/tests-against-klib/build.gradle.kts b/compiler/tests-against-klib/build.gradle.kts new file mode 100644 index 00000000000..d7c5b15cbd9 --- /dev/null +++ b/compiler/tests-against-klib/build.gradle.kts @@ -0,0 +1,30 @@ +import org.jetbrains.kotlin.gradle.dsl.KotlinCompile +import java.io.File + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +dependencies { + compile(kotlinStdlib()) + testCompile(projectTests(":compiler:visualizer")) + testCompile(projectTests(":generators:test-generator")) +} + +sourceSets { + "main" { } + "test" { projectDefault() } +} + +testsJar {} + +projectTest(parallel = true) { + dependsOn(":dist") + dependsOn(":kotlin-stdlib-js-ir:generateFullRuntimeKLib") + + workingDir = rootDir + systemProperty("kotlin.test.script.classpath", testSourceSet.output.classesDirs.joinToString(File.pathSeparator)) +} + +val generateTests by generator("org.jetbrains.kotlin.generators.tests.GenerateCompilerTestsAgainstKlibKt") diff --git a/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCompileKotlinAgainstKlibTest.kt b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCompileKotlinAgainstKlibTest.kt new file mode 100644 index 00000000000..ff2a5dea9ab --- /dev/null +++ b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/AbstractIrCompileKotlinAgainstKlibTest.kt @@ -0,0 +1,95 @@ +/* + * Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.ir + +import org.jetbrains.kotlin.cli.AbstractCliTest +import org.jetbrains.kotlin.cli.common.ExitCode +import org.jetbrains.kotlin.cli.js.K2JSCompiler +import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment.Companion.createForTests +import org.jetbrains.kotlin.codegen.AbstractBlackBoxCodegenTest +import org.jetbrains.kotlin.codegen.CodegenTestCase +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.jetbrains.kotlin.test.TargetBackend +import org.jetbrains.kotlin.utils.rethrow +import java.io.File +import java.nio.file.Paths +import java.util.* +import java.util.concurrent.TimeUnit + +abstract class AbstractCompileKotlinAgainstKlibTest : AbstractBlackBoxCodegenTest() { + lateinit var klibName: String + lateinit var outputDir: File + + override fun getBackend() = TargetBackend.JVM_IR + + override fun doMultiFileTest(wholeFile: File, files: List) { + outputDir = javaSourcesOutputDirectory + klibName = Paths.get(outputDir.toString(), wholeFile.name.toString().removeSuffix(".kt")).toString() + + val classpath: MutableList = ArrayList() + classpath.add(KotlinTestUtils.getAnnotationsJar()) + val configuration = createConfiguration( + configurationKind, CodegenTestCase.getJdkKind(files), + classpath, + listOf(outputDir), + files + ) + myEnvironment = createForTests( + testRootDisposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES + ) + setupEnvironment(myEnvironment) + + + // All files but last are Klib's sources. + try { + compileToKlib(files.dropLast(1)) + } catch (t: Throwable) { + if (!isIgnoredTarget(wholeFile)) { + throw t + } + } + super.doMultiFileTest(wholeFile, listOf(files.last())) + } + + override fun updateConfiguration(configuration: CompilerConfiguration) { + configuration.put(JVMConfigurationKeys.KLIB_PATHS, listOf(klibName + ".klib")) + } + + // We need real (as opposed to virtual) files in order to produce a Klib. + private fun loadMultiFilesReal(files: List): List { + val dir = outputDir + return files.map { testFile -> + assert(testFile.name.endsWith(".kt")) + val ktFile = File(Paths.get(dir.toString(), testFile.name).toString()) + ktFile.writeText(testFile.content) + ktFile.toString() + } + } + + // For now, while there is no common backend, we generate Klib using + // the JS_IR compiler. + private fun compileToKlib(files: List) { + val sourceFiles = loadMultiFilesReal(files) + val (output, exitCode) = AbstractCliTest.executeCompilerGrabOutput( + K2JSCompiler(), + listOf( + "-output", klibName, + "-Xir-produce-klib-file", + "-libraries", "libraries/stdlib/js-ir/build/fullRuntime/klib/" + ) + sourceFiles + ) + if (exitCode != ExitCode.OK) { + throw Exception(output) + } + } + + companion object { + const val TIMEOUT_SECONDS = 10L + } +} diff --git a/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/CompileKotlinAgainstKlibTestGenerated.java b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/CompileKotlinAgainstKlibTestGenerated.java new file mode 100644 index 00000000000..5b1573a3e95 --- /dev/null +++ b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/codegen/ir/CompileKotlinAgainstKlibTestGenerated.java @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.ir; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("compiler/testData/codegen/boxKlib") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class CompileKotlinAgainstKlibTestGenerated extends AbstractCompileKotlinAgainstKlibTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM_IR, testDataFilePath); + } + + public void testAllFilesPresentInBoxKlib() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/boxKlib"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/boxKlib/simple.kt"); + } +} diff --git a/compiler/tests-against-klib/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTestsAgainstKlib.kt b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTestsAgainstKlib.kt new file mode 100644 index 00000000000..cd58a264a43 --- /dev/null +++ b/compiler/tests-against-klib/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTestsAgainstKlib.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.generators.tests + +import org.jetbrains.kotlin.codegen.ir.* +import org.jetbrains.kotlin.generators.tests.generator.testGroup +import org.jetbrains.kotlin.test.TargetBackend + +fun main(args: Array) { + System.setProperty("java.awt.headless", "true") + + testGroup("compiler/tests-against-klib/tests", "compiler/testData") { + testClass { + model("codegen/boxKlib", targetBackend = TargetBackend.JVM_IR) + } + } +} diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java index edfb6d117f4..d07960878d9 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/AbstractBlackBoxCodegenTest.java @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.codegen.ir.AbstractFirBlackBoxCodegenTest; import org.jetbrains.kotlin.fileClasses.JvmFileClassUtil; import org.jetbrains.kotlin.psi.KtFile; import org.jetbrains.kotlin.test.InTextDirectivesUtils; -import org.jetbrains.kotlin.test.TargetBackend; import org.jetbrains.kotlin.utils.ExceptionUtilsKt; import java.io.File; @@ -33,9 +32,7 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { @NotNull List files, boolean unexpectedBehaviour ) throws Exception { - boolean isIgnored = InTextDirectivesUtils.isIgnoredTarget(getBackend(), wholeFile) || - (this instanceof AbstractFirBlackBoxCodegenTest && - InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "IGNORE_BACKEND_FIR: JVM_IR")); + boolean isIgnored = isIgnoredTarget(wholeFile); compile(files, !isIgnored, false); @@ -142,4 +139,15 @@ public abstract class AbstractBlackBoxCodegenTest extends CodegenTestCase { ? null : JvmFileClassUtil.getFileClassInfoNoResolve(file).getFacadeClassFqName().asString(); } + + protected boolean isIgnoredTarget(@NotNull File wholeFile) { + try { + return InTextDirectivesUtils.isIgnoredTarget(getBackend(), wholeFile) || + (this instanceof AbstractFirBlackBoxCodegenTest && + InTextDirectivesUtils.isDirectiveDefined(FileUtil.loadFile(wholeFile), "IGNORE_BACKEND_FIR: JVM_IR")); + } + catch (Exception e) { + throw ExceptionUtilsKt.rethrow(e); + } + } } diff --git a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt index 167a0c645fb..ed8a9bb04c9 100644 --- a/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt +++ b/compiler/tests/org/jetbrains/kotlin/generators/tests/GenerateCompilerTests.kt @@ -513,7 +513,6 @@ fun main(args: Array) { } } - testGroup("compiler/fir/psi2fir/tests", "compiler/fir/psi2fir/testData") { testClass { model("rawBuilder", testMethod = "doRawFirTest") diff --git a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt index 4f4e9c7d914..2ec8f74b79e 100644 --- a/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt +++ b/plugins/scripting/scripting-compiler/src/org/jetbrains/kotlin/scripting/compiler/plugin/impl/compilationContext.kt @@ -181,6 +181,8 @@ private fun CompilerConfiguration.updateWithCompilerOptions( setupJvmSpecificArguments(compilerArguments) configureAdvancedJvmOptions(compilerArguments) + + configureKlibPaths(compilerArguments) } private fun ScriptCompilationConfiguration.withUpdatesFromCompilerConfiguration(kotlinCompilerConfiguration: CompilerConfiguration) = diff --git a/settings.gradle b/settings.gradle index cd355b17b61..fa98f33392a 100644 --- a/settings.gradle +++ b/settings.gradle @@ -114,6 +114,7 @@ include ":kotlin-build-common", ":compiler:android-tests", ":compiler:tests-common", ":compiler:tests-common-jvm6", + ":compiler:tests-against-klib", ":dukat", ":js:js.ast", ":js:js.serializer",