[JKLIB] add support for -XheaderModeType compilation
diff --git a/compiler/arguments/resources/kotlin-compiler-arguments.json b/compiler/arguments/resources/kotlin-compiler-arguments.json index d2ad003..3b34425 100644 --- a/compiler/arguments/resources/kotlin-compiler-arguments.json +++ b/compiler/arguments/resources/kotlin-compiler-arguments.json
@@ -3869,7 +3869,8 @@ "current": false, "valueInVersions": [] } - } + }, + "deprecatedMessage": null }, { "name": "Xexpect-actual-classes",
diff --git a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibCliPipeline.kt b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibCliPipeline.kt index d50327d..1c936f9 100644 --- a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibCliPipeline.kt +++ b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibCliPipeline.kt
@@ -6,11 +6,17 @@ package org.jetbrains.kotlin.cli.jklib.pipeline import org.jetbrains.kotlin.backend.common.phaser.then +import org.jetbrains.kotlin.backend.common.phaser.thenIf import org.jetbrains.kotlin.cli.common.arguments.K2JKlibCompilerArguments import org.jetbrains.kotlin.cli.pipeline.AbstractCliPipeline import org.jetbrains.kotlin.cli.pipeline.ArgumentsPipelineArtifact import org.jetbrains.kotlin.cli.pipeline.PipelineContext +import org.jetbrains.kotlin.config.AnalysisFlags +import org.jetbrains.kotlin.config.HeaderMode +import org.jetbrains.kotlin.config.languageVersionSettings import org.jetbrains.kotlin.config.phaser.CompilerPhase +import org.jetbrains.kotlin.fir.declarations.* +import org.jetbrains.kotlin.fir.pipeline.AllModulesFrontendOutput import org.jetbrains.kotlin.util.PerformanceManager class JKlibCliPipeline( @@ -21,9 +27,14 @@ ): CompilerPhase<PipelineContext, ArgumentsPipelineArtifact<K2JKlibCompilerArguments>, *> { val phases = JKlibConfigurationPhase then - JKlibFrontendPipelinePhase then - JKlibFir2IrPipelinePhase then - JKlibKlibSerializationPhase + // For header klibs compiled with headerModeType="compilation", skip IR + // generation and serialize metadata only, unless the module contains + // declarations that require IR (inline functions/accessors, value classes). + JKlibFrontendPipelinePhase.thenIf( + condition = ::skipIrGeneration, + onTrue = JKlibMetadataSerializationPhase, + onFalse = JKlibFir2IrPipelinePhase then JKlibKlibSerializationPhase, + ) return if (arguments.compileIr) { phases then JKlibIrCompilationPhase @@ -31,4 +42,41 @@ phases } } + + /** + * Skips IR generation when running header mode with -Xheader-mode and + * -Xheader-mode-type=compilation, unless the module contains declarations that require IR + * (inline functions, inline property accessors, or value classes). + */ + private fun skipIrGeneration(artifact: JKlibFrontendPipelineArtifact): Boolean { + val configuration = artifact.configuration + return configuration.languageVersionSettings.getFlag(AnalysisFlags.headerMode) && + configuration.languageVersionSettings.getFlag(AnalysisFlags.headerModeType) == HeaderMode.COMPILATION && + !requireIrForHeaderCompilationMode(artifact.frontendOutput) + } + + @OptIn(DirectDeclarationsAccess::class) + private fun requireIrForHeaderCompilationMode(frontendOutput: AllModulesFrontendOutput): Boolean { + for (output in frontendOutput.outputs) { + for (file in output.fir) { + if (requireIrForHeaderCompilationMode(file.declarations)) return true + } + } + return false + } + + @OptIn(DirectDeclarationsAccess::class) + private fun requireIrForHeaderCompilationMode(declarations: List<FirDeclaration>): Boolean { + for (declaration in declarations) { + if (declaration is FirFunction && declaration.status.isInline) return true + if (declaration is FirProperty) { + if (declaration.getter?.status?.isInline == true || declaration.setter?.status?.isInline == true) return true + } + if (declaration is FirClass) { + if (declaration.status.isValue) return true + if (requireIrForHeaderCompilationMode(declaration.declarations)) return true + } + } + return false + } }
diff --git a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibIrCompilationPhase.kt b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibIrCompilationPhase.kt index a4288a3..d78da06 100644 --- a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibIrCompilationPhase.kt +++ b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibIrCompilationPhase.kt
@@ -85,6 +85,11 @@ ) { override fun executePhase(input: JKlibSerializationArtifact): JKlibIrCompilationArtifact { + // A metadata-only header klib (headerModeType="compilation") contains no IR and cannot be + // compiled further. Reaching this phase with such a klib indicates a misconfigured pipeline. + if (!input.hasIr) { + error("JKlibIrCompilationPhase cannot run on a metadata-only header klib (hasIr = false): ${input.outputKlibPath}") + } val configuration = input.configuration val klib = Path(input.outputKlibPath)
diff --git a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelineArtifacts.kt b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelineArtifacts.kt index 866d21e..4bfdecd 100644 --- a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelineArtifacts.kt +++ b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelineArtifacts.kt
@@ -53,6 +53,9 @@ override val configuration: CompilerConfiguration, val projectEnvironment: VfsBasedProjectEnvironment, val rootDisposable: Disposable, + // `hasIr` is false for metadata-only header klibs produced with + // headerModeType="compilation". The optional IR compilation phase rejects such klibs. + val hasIr: Boolean = true, override val exitCode: ExitCode = ExitCode.OK, ) : PipelineArtifactWithExitCode() { @CliPipelineInternals(OPT_IN_MESSAGE)
diff --git a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelinePhases.kt b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelinePhases.kt index c822499..9a0cdeb 100644 --- a/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelinePhases.kt +++ b/compiler/cli/cli-jklib/src/org/jetbrains/kotlin/cli/jklib/pipeline/JKlibPipelinePhases.kt
@@ -380,3 +380,69 @@ } } +// Metadata-only serialization phase used for header klibs compiled with +// headerModeType="compilation". It mirrors JKlibKlibSerializationPhase but skips IR +// generation entirely: it serializes only the FIR metadata (produceHeaderKlib = true) +// and writes a klib that contains no IR. The resulting JKlibSerializationArtifact is +// marked with hasIr = false so the optional IR compilation phase can reject it. +object JKlibMetadataSerializationPhase : PipelinePhase<JKlibFrontendPipelineArtifact, JKlibSerializationArtifact>( + name = "JKlibMetadataSerializationPhase", + postActions = setOf(CheckCompilationErrors.CheckDiagnosticCollector) +) { + override fun executePhase(input: JKlibFrontendPipelineArtifact): JKlibSerializationArtifact { + val configuration = input.configuration + val diagnosticsReporter = configuration.diagnosticsCollector + val destination = Path(configuration.jklibOutputDestination ?: "result.klib").absolute() + + val rawModuleName = configuration.moduleName ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME + val moduleName = Name.special("<$rawModuleName>").asString() + + val serializerOutput = serializeModuleIntoKlib( + moduleName = moduleName, + irModuleFragment = null, + configuration = configuration, + diagnosticReporter = KtDiagnosticReporterWithImplicitIrBasedContext( + diagnosticsReporter.deduplicating(), + configuration.languageVersionSettings + ), + cleanFiles = emptyList(), + dependencies = emptyList(), + createModuleSerializer = { error("createModuleSerializer should not be called for metadata-only serialization") }, + metadataSerializer = Fir2KlibMetadataSerializer( + configuration, + input.frontendOutput.outputs, + fir2IrActualizedResult = null, + exportKDoc = false, + produceHeaderKlib = true, + ), + ) + + val versions = KotlinLibraryVersioning( + abiVersion = KotlinAbiVersion.CURRENT, + compilerVersion = KotlinCompilerVersion.getVersion(), + metadataVersion = configuration.metadataVersion(), + ) + + KlibWriter { + format(KlibFormat.ZipArchive) + manifest { + moduleName(rawModuleName) + versions(versions) + platformAndTargets(BuiltInsPlatform.JKLIB, emptyList()) + metadataFlags(configuration.languageVersionSettings) + } + includeMetadata(serializerOutput.serializedMetadata ?: error("expected serialized metadata")) + // serializedIr is null for metadata-only serialization; includeIr(null) is a no-op. + includeIr(serializerOutput.serializedIr) + }.writeTo(destination) + + return JKlibSerializationArtifact( + destination.pathString, + configuration, + input.projectEnvironment, + input.rootDisposable, + hasIr = false, + ) + } +} +