Add plugin for JVM ABI classes generation

#KT-25128 Fixed

ABI class generation is implemented as a compiler plugin.
Command-line usage:
1. Add a path to 'jvm-abi-gen.jar' to the plguin classpath argument
(`-Xplugin`). By default the jar is located at
'kotlinc/lib/jvm-abi-gen.jar' in the kotlinc distribution archive.
2. Specify an output directory for ABI classes via
`-Pplugin:org.jetbrains.kotlin.jvm.abi:outputDir=<DIR>`.
This commit is contained in:
Alexey Tsvetkov
2018-11-29 19:33:04 +03:00
parent 7fcd29843e
commit 33faa00184
9 changed files with 210 additions and 0 deletions
+1
View File
@@ -480,6 +480,7 @@ tasks {
":compiler:container:test",
":compiler:tests-java8:test",
":compiler:tests-spec:remoteRunTests")
dependsOn(":plugins:jvm-abi-gen:test")
}
create("jsCompilerTest") {
+39
View File
@@ -0,0 +1,39 @@
description = "ABI generation for Kotlin/JVM"
plugins {
kotlin("jvm")
id("jps-compatible")
}
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":compiler:cli"))
compileOnly(project(":compiler:backend"))
compileOnly(project(":compiler:frontend"))
compileOnly(project(":compiler:frontend.java"))
compileOnly(project(":compiler:plugin-api"))
compileOnly(project(":kotlin-build-common"))
runtimeOnly(project(":kotlin-compiler"))
testCompile(commonDep("junit:junit"))
testCompile(projectTests(":compiler:incremental-compilation-impl"))
}
sourceSets {
"main" { projectDefault() }
"test" { projectDefault() }
}
projectTest {
workingDir = rootDir
dependsOn(":dist")
}
sourcesJar()
javadocJar()
dist()
publish()
testsJar()
@@ -0,0 +1,6 @@
#
# Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
# that can be found in the license/LICENSE.txt file.
#
org.jetbrains.kotlin.jvm.abi.JvmAbiCommandLineProcessor
@@ -0,0 +1,6 @@
#
# Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
# that can be found in the license/LICENSE.txt file.
#
org.jetbrains.kotlin.jvm.abi.JvmAbiComponentRegistrar
@@ -0,0 +1,82 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.jvm.abi
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.analyzer.AnalysisResult
import org.jetbrains.kotlin.backend.common.output.OutputFile
import org.jetbrains.kotlin.cli.common.output.writeAll
import org.jetbrains.kotlin.codegen.*
import org.jetbrains.kotlin.codegen.state.GenerationState
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.diagnostics.Severity
import org.jetbrains.kotlin.modules.TargetId
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import org.jetbrains.org.objectweb.asm.*
import java.io.File
class JvmAbiAnalysisHandlerExtension(
private val compilerConfiguration: CompilerConfiguration
) : AnalysisHandlerExtension {
override fun analysisCompleted(
project: Project,
module: ModuleDescriptor,
bindingTrace: BindingTrace,
files: Collection<KtFile>
): AnalysisResult? {
val bindingContext = bindingTrace.bindingContext
if (bindingContext.diagnostics.any { it.severity == Severity.ERROR }) return null
val targetId = TargetId(
name = compilerConfiguration[CommonConfigurationKeys.MODULE_NAME] ?: module.name.asString(),
type = "java-production"
)
val generationState = GenerationState.Builder(
project,
AbiBinaries,
module,
bindingContext,
files.toList(),
compilerConfiguration
).targetId(targetId).build()
KotlinCodegenFacade.compileCorrectFiles(generationState, CompilationErrorHandler.THROW_EXCEPTION)
val outputDir = compilerConfiguration.get(JVMConfigurationKeys.OUTPUT_DIRECTORY)!!
generationState.factory.writeAll(
outputDir,
fun(file: OutputFile, sources: List<File>, output: File) {
// todo report
}
)
return null
}
private object AbiBinaries : ClassBuilderFactory {
override fun getClassBuilderMode(): ClassBuilderMode =
ClassBuilderMode.FULL
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder =
AbstractClassBuilder.Concrete(ClassWriter(0))
override fun asText(builder: ClassBuilder): String =
throw UnsupportedOperationException("AbiBinaries generator asked for text")
override fun asBytes(builder: ClassBuilder): ByteArray {
val visitor = builder.visitor as ClassWriter
return visitor.toByteArray()
}
override fun close() {}
}
}
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.jvm.abi
import org.jetbrains.kotlin.compiler.plugin.AbstractCliOption
import org.jetbrains.kotlin.compiler.plugin.CliOption
import org.jetbrains.kotlin.compiler.plugin.CliOptionProcessingException
import org.jetbrains.kotlin.compiler.plugin.CommandLineProcessor
import org.jetbrains.kotlin.config.CompilerConfiguration
class JvmAbiCommandLineProcessor : CommandLineProcessor {
companion object {
const val COMPILER_PLUGIN_ID: String = "org.jetbrains.kotlin.jvm.abi"
val OUTPUT_DIR_OPTION: CliOption =
CliOption("outputDir", "<path>", "Output path for the generated files", required = true)
}
override val pluginId: String
get() = COMPILER_PLUGIN_ID
override val pluginOptions: Collection<CliOption>
get() = listOf(OUTPUT_DIR_OPTION)
override fun processOption(option: AbstractCliOption, value: String, configuration: CompilerConfiguration) {
when (option) {
OUTPUT_DIR_OPTION -> configuration.put(JvmAbiConfigurationKeys.OUTPUT_DIR, value)
else -> throw CliOptionProcessingException("Unknown option: ${option.optionName}")
}
}
}
@@ -0,0 +1,26 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.jvm.abi
import com.intellij.mock.MockProject
import org.jetbrains.kotlin.compiler.plugin.ComponentRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.JVMConfigurationKeys
import org.jetbrains.kotlin.resolve.jvm.extensions.AnalysisHandlerExtension
import java.io.File
class JvmAbiComponentRegistrar : ComponentRegistrar {
override fun registerProjectComponents(project: MockProject, configuration: CompilerConfiguration) {
@Suppress("NAME_SHADOWING")
val configuration = configuration.copy()
configuration.get(JvmAbiConfigurationKeys.OUTPUT_DIR)?.let {
val dir = File(it)
configuration.put(JVMConfigurationKeys.OUTPUT_DIRECTORY, dir)
}
val extension = JvmAbiAnalysisHandlerExtension(configuration)
AnalysisHandlerExtension.registerExtension(project, extension)
}
}
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.jvm.abi
import org.jetbrains.kotlin.config.CompilerConfigurationKey
object JvmAbiConfigurationKeys {
val OUTPUT_DIR: CompilerConfigurationKey<String> =
CompilerConfigurationKey.create<String>(JvmAbiCommandLineProcessor.OUTPUT_DIR_OPTION.description)
}
+2
View File
@@ -198,6 +198,7 @@ include ":kotlin-build-common",
":libraries:kotlin-prepush-hook",
":libraries:tools:mutability-annotations-compat",
":include:kotlin-compiler",
":plugins:jvm-abi-gen",
// plugin markers:
':kotlin-gradle-plugin:plugin-marker',
@@ -335,6 +336,7 @@ project(':examples:scripting-jvm-embeddable-host').projectDir = "$rootDir/librar
project(':pill:generate-all-tests').projectDir = "$rootDir/plugins/pill/generate-all-tests" as File
project(':kotlin-imports-dumper-compiler-plugin').projectDir = "$rootDir/plugins/imports-dumper" as File
project(':libraries:kotlin-prepush-hook').projectDir = "$rootDir/libraries/tools/kotlin-prepush-hook" as File
project(':plugins:jvm-abi-gen').projectDir = "$rootDir/plugins/jvm-abi-gen" as File
// plugin markers:
project(':kotlin-gradle-plugin:plugin-marker').projectDir = file("$rootDir/libraries/tools/kotlin-gradle-plugin/plugin-marker")