[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,
+        )
+    }
+}
+