[klib] Add an option to write out header klibs

The header klib is supposed to only contain the public abi of the module
similar to jvm-abi-gen. It is intended to be used as a dependency for other
klib compilations instead of the full klib for compilation avoidance.

^KT-60807
This commit is contained in:
Johan Bay
2023-05-09 14:29:21 +02:00
committed by Space Team
parent 829e0675f4
commit 0a612e4268
90 changed files with 1374 additions and 76 deletions
@@ -7,6 +7,7 @@ import org.jetbrains.kotlin.backend.common.serialization.metadata.serializeKlibH
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.driver.phases.Fir2IrOutput
import org.jetbrains.kotlin.backend.konan.driver.phases.FirOutput
import org.jetbrains.kotlin.backend.konan.driver.phases.FirSerializerInput
import org.jetbrains.kotlin.backend.konan.driver.phases.SerializerOutput
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer
import org.jetbrains.kotlin.config.CommonConfigurationKeys
@@ -34,11 +35,13 @@ internal fun PhaseContext.firSerializer(input: FirOutput): SerializerOutput? = w
else -> firSerializerBase(input.firResult, null)
}
internal fun PhaseContext.fir2IrSerializer(input: Fir2IrOutput) = firSerializerBase(input.firResult, input)
internal fun PhaseContext.fir2IrSerializer(input: FirSerializerInput) =
firSerializerBase(input.firToIrOutput.firResult, input.firToIrOutput, produceHeaderKlib = input.produceHeaderKlib)
internal fun PhaseContext.firSerializerBase(
firResult: FirResult,
fir2IrInput: Fir2IrOutput?,
produceHeaderKlib: Boolean = false,
): SerializerOutput {
val configuration = config.configuration
val sourceFiles = mutableListOf<KtSourceFile>()
@@ -71,6 +74,8 @@ internal fun PhaseContext.firSerializerBase(
moduleName = fir2IrInput?.irModuleFragment?.descriptor?.name?.asString()
?: firResult.outputs.last().session.moduleData.name.asString(),
firFilesAndSessionsBySourceFile,
bodiesOnlyForInlines = produceHeaderKlib,
skipPrivateApi = produceHeaderKlib,
) { firFile, session, scopeSession ->
serializeSingleFirFile(
firFile,
@@ -87,6 +92,7 @@ internal fun PhaseContext.firSerializerBase(
additionalAnnotationsProvider = fir2IrInput?.components?.annotationsFromPluginRegistrar?.createMetadataAnnotationsProvider()
),
configuration.languageVersionSettings,
produceHeaderKlib,
)
}
}
@@ -102,14 +108,16 @@ class KotlinFileSerializedData(
}
internal fun PhaseContext.serializeNativeModule(
configuration: CompilerConfiguration,
messageLogger: IrMessageLogger,
files: List<KtSourceFile>,
dependencies: List<KonanLibrary>?,
moduleFragment: IrModuleFragment?,
moduleName: String,
firFilesAndSessionsBySourceFile: Map<KtSourceFile, Triple<FirFile, FirSession, ScopeSession>>,
serializeSingleFile: (FirFile, FirSession, ScopeSession) -> ProtoBuf.PackageFragment
configuration: CompilerConfiguration,
messageLogger: IrMessageLogger,
files: List<KtSourceFile>,
dependencies: List<KonanLibrary>?,
moduleFragment: IrModuleFragment?,
moduleName: String,
firFilesAndSessionsBySourceFile: Map<KtSourceFile, Triple<FirFile, FirSession, ScopeSession>>,
bodiesOnlyForInlines: Boolean = false,
skipPrivateApi: Boolean = false,
serializeSingleFile: (FirFile, FirSession, ScopeSession) -> ProtoBuf.PackageFragment
): SerializerOutput {
if (moduleFragment != null) {
assert(files.size == moduleFragment.files.size)
@@ -126,6 +134,8 @@ internal fun PhaseContext.serializeNativeModule(
normalizeAbsolutePaths = absolutePathNormalization,
sourceBaseDirs = sourceBaseDirs,
languageVersionSettings = configuration.languageVersionSettings,
bodiesOnlyForInlines = bodiesOnlyForInlines,
skipPrivateApi = skipPrivateApi
).serializedIrModule(moduleFragment)
}
@@ -225,6 +225,8 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
internal val metadataKlib get() = configuration.get(KonanConfigKeys.METADATA_KLIB)!!
internal val headerKlibPath get() = configuration.get(KonanConfigKeys.HEADER_KLIB)
internal val produceStaticFramework get() = configuration.getBoolean(KonanConfigKeys.STATIC_FRAMEWORK)
internal val purgeUserLibs: Boolean
@@ -75,6 +75,8 @@ class KonanConfigKeys {
= CompilerConfigurationKey.create("provide manifest addend file")
val METADATA_KLIB: CompilerConfigurationKey<Boolean>
= CompilerConfigurationKey.create("metadata klib")
val HEADER_KLIB: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("path to file where header klib should be produced")
val MODULE_NAME: CompilerConfigurationKey<String?>
= CompilerConfigurationKey.create("module name")
val NATIVE_LIBRARY_FILES: CompilerConfigurationKey<List<String>>
@@ -56,6 +56,7 @@ fun CompilerConfiguration.setupFromArguments(arguments: K2NativeCompilerArgument
(arguments.produce ?: "program").uppercase())
put(PRODUCE, outputKind)
put(METADATA_KLIB, arguments.metadataKlib)
putIfNotNull(HEADER_KLIB, arguments.headerKlibPath)
arguments.libraryVersion?.let { put(LIBRARY_VERSION, it) }
@@ -108,8 +108,15 @@ internal class DynamicCompilerDriver : CompilerDriver() {
engine.runFirSerializer(frontendOutput)
} else {
val fir2IrOutput = engine.runFir2Ir(frontendOutput)
val headerKlibPath = environment.configuration.get(KonanConfigKeys.HEADER_KLIB)
if (!headerKlibPath.isNullOrEmpty()) {
val headerKlib = engine.runFir2IrSerializer(FirSerializerInput(fir2IrOutput, produceHeaderKlib = true))
engine.writeKlib(headerKlib, headerKlibPath)
}
engine.runK2SpecialBackendChecks(fir2IrOutput)
engine.runFir2IrSerializer(fir2IrOutput)
engine.runFir2IrSerializer(FirSerializerInput(fir2IrOutput))
}
}
@@ -124,6 +131,10 @@ internal class DynamicCompilerDriver : CompilerDriver() {
} else {
engine.runPsiToIr(frontendOutput, isProducingLibrary = true) as PsiToIrOutput.ForKlib
}
if (!config.headerKlibPath.isNullOrEmpty()) {
val headerKlib = engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput, produceHeaderKlib = true)
engine.writeKlib(headerKlib, config.headerKlibPath)
}
return engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput)
}
@@ -10,6 +10,12 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.firSerializer
import org.jetbrains.kotlin.backend.konan.fir2IrSerializer
internal data class FirSerializerInput(
val firToIrOutput: Fir2IrOutput,
val produceHeaderKlib: Boolean = false,
)
internal val FirSerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, FirOutput, SerializerOutput?>(
"FirSerializer", "Fir serializer",
outputIfNotEnabled = { _, _, _, _ -> SerializerOutput(null, null, null, listOf()) }
@@ -17,10 +23,10 @@ internal val FirSerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, F
context.firSerializer(input)
}
internal val Fir2IrSerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, Fir2IrOutput, SerializerOutput>(
internal val Fir2IrSerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, FirSerializerInput, SerializerOutput>(
"Fir2IrSerializer", "Fir2Ir serializer",
outputIfNotEnabled = { _, _, _, _ -> SerializerOutput(null, null, null, listOf()) }
) { context: PhaseContext, input: Fir2IrOutput ->
) { context: PhaseContext, input: FirSerializerInput ->
context.fir2IrSerializer(input)
}
@@ -31,7 +37,7 @@ internal fun <T : PhaseContext> PhaseEngine<T>.runFirSerializer(
}
internal fun <T : PhaseContext> PhaseEngine<T>.runFir2IrSerializer(
fir2irOutput: Fir2IrOutput
firSerializerInput: FirSerializerInput
): SerializerOutput {
return this.runPhase(Fir2IrSerializerPhase, fir2irOutput)
return this.runPhase(Fir2IrSerializerPhase, firSerializerInput)
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.library.SerializedMetadata
internal data class SerializerInput(
val moduleDescriptor: ModuleDescriptor,
val psiToIrOutput: PsiToIrOutput.ForKlib?,
val produceHeaderKlib: Boolean,
)
data class SerializerOutput(
@@ -48,24 +49,27 @@ internal val SerializerPhase = createSimpleNamedCompilerPhase<PhaseContext, Seri
normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = relativePathBase,
languageVersionSettings = config.languageVersionSettings,
bodiesOnlyForInlines = input.produceHeaderKlib,
skipPrivateApi = input.produceHeaderKlib,
).serializedIrModule(ir)
}
val serializer = KlibMetadataMonolithicSerializer(
config.configuration.languageVersionSettings,
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)!!,
config.project,
exportKDoc = context.shouldExportKDoc(),
!expectActualLinker, includeOnlyModuleContent = true)
config.configuration.languageVersionSettings,
config.configuration.get(CommonConfigurationKeys.METADATA_VERSION)!!,
config.project,
exportKDoc = context.shouldExportKDoc(),
!expectActualLinker, includeOnlyModuleContent = true, produceHeaderKlib = input.produceHeaderKlib)
val serializedMetadata = serializer.serializeModule(input.moduleDescriptor)
val neededLibraries = config.librariesWithDependencies()
SerializerOutput(serializedMetadata, serializedIr, null, neededLibraries)
}
internal fun <T : PhaseContext> PhaseEngine<T>.runSerializer(
moduleDescriptor: ModuleDescriptor,
psiToIrResult: PsiToIrOutput.ForKlib?,
moduleDescriptor: ModuleDescriptor,
psiToIrResult: PsiToIrOutput.ForKlib?,
produceHeaderKlib: Boolean = false,
): SerializerOutput {
val input = SerializerInput(moduleDescriptor, psiToIrResult)
val input = SerializerInput(moduleDescriptor, psiToIrResult, produceHeaderKlib)
return this.runPhase(SerializerPhase, input)
}
@@ -14,13 +14,19 @@ import org.jetbrains.kotlin.konan.library.impl.buildLibrary
import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibraryVersioning
import org.jetbrains.kotlin.library.metadata.KlibMetadataVersion
import org.jetbrains.kotlin.util.removeSuffixIfPresent
internal val WriteKlibPhase = createSimpleNamedCompilerPhase<PhaseContext, SerializerOutput>(
internal data class KlibWriterInput(
val serializerOutput: SerializerOutput,
val customOutputPath: String?
)
internal val WriteKlibPhase = createSimpleNamedCompilerPhase<PhaseContext, KlibWriterInput>(
"WriteKlib", "Write klib output",
) { context, input ->
val config = context.config
val configuration = config.configuration
val outputFiles = OutputFiles(config.outputPath, config.target, config.produce)
val outputFiles = OutputFiles(input.customOutputPath?.removeSuffixIfPresent(".klib")
?: config.outputPath, config.target, config.produce)
val nopack = configuration.getBoolean(KonanConfigKeys.NOPACK)
val output = outputFiles.klibOutputFileName(!nopack)
val libraryName = config.moduleId
@@ -51,14 +57,14 @@ internal val WriteKlibPhase = createSimpleNamedCompilerPhase<PhaseContext, Seria
(e.g. commonized cinterops, host vs client environment differences).
*/
val linkDependencies = if (context.config.metadataKlib) emptyList()
else input.neededLibraries
else input.serializerOutput.neededLibraries
buildLibrary(
natives = config.nativeLibraries,
included = config.includeBinaries,
linkDependencies = linkDependencies,
metadata = input.serializedMetadata!!,
ir = input.serializedIr,
metadata = input.serializerOutput.serializedMetadata!!,
ir = input.serializerOutput.serializedIr,
versions = versions,
target = target,
output = output,
@@ -66,12 +72,13 @@ internal val WriteKlibPhase = createSimpleNamedCompilerPhase<PhaseContext, Seria
nopack = nopack,
shortName = shortLibraryName,
manifestProperties = manifestProperties,
dataFlowGraph = input.dataFlowGraph
dataFlowGraph = input.serializerOutput.dataFlowGraph
)
}
internal fun <T : PhaseContext> PhaseEngine<T>.writeKlib(
serializationOutput: SerializerOutput,
customOutputPath: String? = null,
) {
this.runPhase(WriteKlibPhase, serializationOutput)
this.runPhase(WriteKlibPhase, KlibWriterInput(serializationOutput, customOutputPath))
}
@@ -12,21 +12,23 @@ import org.jetbrains.kotlin.ir.util.IrMessageLogger
import org.jetbrains.kotlin.ir.util.hasAnnotation
class KonanIrFileSerializer(
messageLogger: IrMessageLogger,
declarationTable: DeclarationTable,
languageVersionSettings: LanguageVersionSettings,
bodiesOnlyForInlines: Boolean = false,
compatibilityMode: CompatibilityMode,
normalizeAbsolutePaths: Boolean,
sourceBaseDirs: Collection<String>
messageLogger: IrMessageLogger,
declarationTable: DeclarationTable,
languageVersionSettings: LanguageVersionSettings,
bodiesOnlyForInlines: Boolean = false,
compatibilityMode: CompatibilityMode,
normalizeAbsolutePaths: Boolean,
sourceBaseDirs: Collection<String>,
skipPrivateApi: Boolean = false,
) : IrFileSerializer(
messageLogger = messageLogger,
declarationTable = declarationTable,
compatibilityMode = compatibilityMode,
languageVersionSettings = languageVersionSettings,
bodiesOnlyForInlines = bodiesOnlyForInlines,
normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = sourceBaseDirs
messageLogger,
declarationTable,
compatibilityMode,
languageVersionSettings,
skipPrivateApi = skipPrivateApi,
bodiesOnlyForInlines = bodiesOnlyForInlines,
normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = sourceBaseDirs
) {
override fun backendSpecificExplicitRoot(node: IrAnnotationContainer): Boolean {
@@ -15,6 +15,8 @@ class KonanIrModuleSerializer(
normalizeAbsolutePaths: Boolean,
sourceBaseDirs: Collection<String>,
private val languageVersionSettings: LanguageVersionSettings,
private val bodiesOnlyForInlines: Boolean = false,
private val skipPrivateApi: Boolean = false,
) : IrModuleSerializer<KonanIrFileSerializer>(messageLogger, compatibilityMode, normalizeAbsolutePaths, sourceBaseDirs) {
private val globalDeclarationTable = KonanGlobalDeclarationTable(irBuiltIns)
@@ -33,5 +35,7 @@ class KonanIrModuleSerializer(
compatibilityMode = compatibilityMode,
normalizeAbsolutePaths = normalizeAbsolutePaths,
sourceBaseDirs = sourceBaseDirs,
languageVersionSettings = languageVersionSettings)
languageVersionSettings = languageVersionSettings,
bodiesOnlyForInlines = bodiesOnlyForInlines,
skipPrivateApi = skipPrivateApi)
}