Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions compiler/arguments/resources/kotlin-compiler-arguments.json
Original file line number Diff line number Diff line change
Expand Up @@ -3842,6 +3842,37 @@
"deprecatedVersion": null,
"removedVersion": null
}
},
{
"name": "Xheader-mode",
"shortName": null,
"deprecatedName": null,
"description": {
"current": "Enable header compilation mode.\nIn this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be\ncompiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation\nbuild systems where header libraries can be used to replace downstream dependencies for which we only need to\nsee the type names and method signatures required to compile a given translation unit. Inline functions are still kept\nwith bodies.",
"valueInVersions": []
},
"delimiter": null,
"valueType": {
"type": "org.jetbrains.kotlin.arguments.dsl.types.BooleanType",
"isNullable": {
"current": false,
"valueInVersions": []
},
"defaultValue": {
"current": false,
"valueInVersions": []
}
},
"valueDescription": {
"current": null,
"valueInVersions": []
},
"releaseVersionsMetadata": {
"introducedVersion": "2.3.0",
"stabilizedVersion": null,
"deprecatedVersion": null,
"removedVersion": null
}
}
],
"nestedLevels": [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1183,4 +1183,21 @@ Warning: this flag is not intended for production use. If you want to configure
introducedVersion = KotlinReleaseVersion.v1_0_0
)
}

compilerArgument {
name = "Xheader-mode"
description = """
Enable header compilation mode.
In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be
compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation
build systems where header libraries can be used to replace downstream dependencies for which we only need to
see the type names and method signatures required to compile a given translation unit. Inline functions are still kept
with bodies.
""".trimIndent().asReleaseDependent()
valueType = BooleanType.defaultFalse

lifecycle(
introducedVersion = KotlinReleaseVersion.v2_3_0
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -737,5 +737,20 @@ public interface CommonCompilerArguments : CommonToolArguments {
@ExperimentalCompilerArgument
public val X_NAME_BASED_DESTRUCTURING: CommonCompilerArgument<String?> =
CommonCompilerArgument("X_NAME_BASED_DESTRUCTURING", KotlinKotlinVersion(2, 3, 0))

/**
* Enable header compilation mode.
* In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be
* compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation
* build systems where header libraries can be used to replace downstream dependencies for which we only need to
* see the type names and method signatures required to compile a given translation unit. Inline functions are still kept
* with bodies.
*
* WARNING: this option is EXPERIMENTAL and it may be changed in the future without notice or may be removed entirely.
*/
@JvmField
@ExperimentalCompilerArgument
public val X_HEADER_MODE: CommonCompilerArgument<Boolean> =
CommonCompilerArgument("X_HEADER_MODE", KotlinKotlinVersion(2, 3, 0))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgume
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_FRAGMENT_FRIEND_DEPENDENCY
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_FRAGMENT_REFINES
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_FRAGMENT_SOURCES
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_HEADER_MODE
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_IGNORE_CONST_OPTIMIZATION_ERRORS
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_INLINE_CLASSES
import org.jetbrains.kotlin.buildtools.`internal`.arguments.CommonCompilerArgumentsImpl.Companion.X_INTELLIJ_PLUGIN_ROOT
Expand Down Expand Up @@ -243,6 +244,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(),
if (X_ALLOW_HOLDSIN_CONTRACT in this) { arguments.allowHoldsinContract = get(X_ALLOW_HOLDSIN_CONTRACT)}
if (X_NAME_BASED_DESTRUCTURING in this) { arguments.nameBasedDestructuring = get(X_NAME_BASED_DESTRUCTURING)}
if (XX_LANGUAGE in this) { arguments.manuallyConfiguredFeatures = get(XX_LANGUAGE)}
if (X_HEADER_MODE in this) { arguments.headerMode = get(X_HEADER_MODE)}
return arguments
}

Expand Down Expand Up @@ -340,6 +342,7 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(),
try { this[X_ALLOW_HOLDSIN_CONTRACT] = arguments.allowHoldsinContract } catch (_: NoSuchMethodError) { }
try { this[X_NAME_BASED_DESTRUCTURING] = arguments.nameBasedDestructuring } catch (_: NoSuchMethodError) { }
try { this[XX_LANGUAGE] = arguments.manuallyConfiguredFeatures } catch (_: NoSuchMethodError) { }
try { this[X_HEADER_MODE] = arguments.headerMode } catch (_: NoSuchMethodError) { }
internalArguments.addAll(arguments.internalArguments.map { it.stringRepresentation })
}

Expand Down Expand Up @@ -615,5 +618,8 @@ internal abstract class CommonCompilerArgumentsImpl : CommonToolArgumentsImpl(),

public val XX_LANGUAGE: CommonCompilerArgument<Array<String>?> =
CommonCompilerArgument("XX_LANGUAGE")

public val X_HEADER_MODE: CommonCompilerArgument<Boolean> =
CommonCompilerArgument("X_HEADER_MODE")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1045,6 +1045,21 @@ Warning: this flag is not intended for production use. If you want to configure
field = value
}

@Argument(
value = "-Xheader-mode",
description = """Enable header compilation mode.
In this mode, the compiler produces class files that only contain the 'skeleton' of the classes to be
compiled but the method bodies of all the implementations are empty. This is used to speed up parallel compilation
build systems where header libraries can be used to replace downstream dependencies for which we only need to
see the type names and method signatures required to compile a given translation unit. Inline functions are still kept
with bodies.""",
)
var headerMode: Boolean = false
set(value) {
checkFrozen()
field = value
}

@get:Transient
abstract val configurator: CommonCompilerArgumentsConfigurator

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ fun copyCommonCompilerArguments(from: CommonCompilerArguments, to: CommonCompile
to.fragmentRefines = from.fragmentRefines?.copyOf()
to.fragmentSources = from.fragmentSources?.copyOf()
to.fragments = from.fragments?.copyOf()
to.headerMode = from.headerMode
to.ignoreConstOptimizationErrors = from.ignoreConstOptimizationErrors
to.incrementalCompilation = from.incrementalCompilation
to.inlineClasses = from.inlineClasses
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.cli.pipeline.ConfigurationPipelineArtifact
import org.jetbrains.kotlin.cli.pipeline.PerformanceNotifications
import org.jetbrains.kotlin.cli.pipeline.PipelinePhase
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.headerCompilation
import org.jetbrains.kotlin.config.lookupTracker
import org.jetbrains.kotlin.config.messageCollector
import org.jetbrains.kotlin.config.perfManager
Expand Down Expand Up @@ -101,6 +102,7 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
incrementalDataProvider = configuration.incrementalDataProvider,
lookupTracker = lookupTracker,
useWasmPlatform = isWasm,
configuration.headerCompilation,
).also {
kotlinPackageUsageIsFine = it.output.all { checkKotlinPackageUsageForLightTree(configuration, it.fir) }
}
Expand All @@ -127,6 +129,7 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
incrementalDataProvider = configuration.incrementalDataProvider,
lookupTracker = lookupTracker,
useWasmPlatform = isWasm,
headerCompilationMode = configuration.headerCompilation
)
}

Expand All @@ -150,6 +153,7 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
incrementalDataProvider: IncrementalDataProvider?,
lookupTracker: LookupTracker?,
useWasmPlatform: Boolean,
headerCompilationMode: Boolean
): AnalyzedFirWithPsiOutput {
for (ktFile in ktFiles) {
AnalyzerWithCompilerReport.reportSyntaxErrors(ktFile, diagnosticsReporter)
Expand All @@ -164,11 +168,11 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
isCommonSource = isCommonSourceForPsi,
fileBelongsToModule = fileBelongsToModuleForPsi,
buildResolveAndCheckFir = { session, files ->
buildResolveAndCheckFirFromKtFiles(session, files, diagnosticsReporter)
buildResolveAndCheckFirFromKtFiles(session, files, diagnosticsReporter, headerCompilationMode)
},
useWasmPlatform = useWasmPlatform,
)
output.runPlatformCheckers(diagnosticsReporter)
output.runPlatformCheckers(diagnosticsReporter, headerCompilationMode)
return AnalyzedFirWithPsiOutput(output, ktFiles)
}

Expand All @@ -183,6 +187,7 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
incrementalDataProvider: IncrementalDataProvider?,
lookupTracker: LookupTracker?,
useWasmPlatform: Boolean,
headerCompilationMode: Boolean,
): AnalyzedFirOutput {
val output = compileModuleToAnalyzedFir(
moduleStructure,
Expand All @@ -194,11 +199,11 @@ object WebFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, W
isCommonSource = { groupedSources.isCommonSourceForLt(it) },
fileBelongsToModule = { file, it -> groupedSources.fileBelongsToModuleForLt(file, it) },
buildResolveAndCheckFir = { session, files ->
buildResolveAndCheckFirViaLightTree(session, files, diagnosticsReporter, performanceManager?.let { it::addSourcesStats })
buildResolveAndCheckFirViaLightTree(session, files, diagnosticsReporter, headerCompilationMode, performanceManager?.let { it::addSourcesStats })
},
useWasmPlatform = useWasmPlatform,
)
output.runPlatformCheckers(diagnosticsReporter)
output.runPlatformCheckers(diagnosticsReporter, headerCompilationMode)
return AnalyzedFirOutput(output)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ fun CompilerConfiguration.setupCommonArguments(
put(CommonConfigurationKeys.INCREMENTAL_COMPILATION, incrementalCompilationIsEnabled(arguments))
put(CommonConfigurationKeys.ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS, arguments.allowAnyScriptsInSourceRoots)
put(CommonConfigurationKeys.IGNORE_CONST_OPTIMIZATION_ERRORS, arguments.ignoreConstOptimizationErrors)
put(CommonConfigurationKeys.HEADER_COMPILATION, arguments.headerMode)

val irVerificationMode = arguments.verifyIr?.let { verifyIrString ->
IrVerificationMode.resolveMode(verifyIrString).also {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.prepareJvmSessions
import org.jetbrains.kotlin.cli.jvm.compiler.VfsBasedProjectEnvironment
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.config.headerCompilation
import org.jetbrains.kotlin.config.perfManager
import org.jetbrains.kotlin.diagnostics.impl.BaseDiagnosticsCollector
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
Expand Down Expand Up @@ -96,9 +97,9 @@ private fun FrontendContext.compileModuleToAnalyzedFirViaLightTreeIncrementally(
val countFilesAndLines = if (performanceManager == null) null else performanceManager::addSourcesStats

val outputs = sessionsWithSources.map { (session, sources) ->
buildResolveAndCheckFirViaLightTree(session, sources, diagnosticsReporter, countFilesAndLines)
buildResolveAndCheckFirViaLightTree(session, sources, diagnosticsReporter, configuration.headerCompilation, countFilesAndLines)
}
outputs.runPlatformCheckers(diagnosticsReporter)
outputs.runPlatformCheckers(diagnosticsReporter, configuration.headerCompilation)
FirResult(outputs)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -263,12 +263,12 @@ object JvmFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifact, J
val countFilesAndLines = if (perfManager == null) null else perfManager::addSourcesStats
val outputs = sessionsWithSources.map { (session, sources) ->
val rawFirFiles = when (configuration.useLightTree) {
true -> session.buildFirViaLightTree(sources, diagnosticsCollector, countFilesAndLines)
true -> session.buildFirViaLightTree(sources, diagnosticsCollector, configuration.headerCompilation, countFilesAndLines)
else -> session.buildFirFromKtFiles(sources.asKtFilesList())
}
resolveAndCheckFir(session, rawFirFiles, diagnosticsCollector)
resolveAndCheckFir(session, rawFirFiles, diagnosticsCollector, configuration.headerCompilation)
}
outputs.runPlatformCheckers(diagnosticsCollector)
outputs.runPlatformCheckers(diagnosticsCollector, configuration.headerCompilation)

val kotlinPackageUsageIsFine = when (configuration.useLightTree) {
true -> outputs.all { checkKotlinPackageUsageForLightTree(configuration, it.fir) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.cli.pipeline.PerformanceNotifications
import org.jetbrains.kotlin.cli.pipeline.PipelinePhase
import org.jetbrains.kotlin.cli.pipeline.jvm.asKtFilesList
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.headerCompilation
import org.jetbrains.kotlin.config.messageCollector
import org.jetbrains.kotlin.config.moduleName
import org.jetbrains.kotlin.config.perfManager
Expand Down Expand Up @@ -110,10 +111,10 @@ object MetadataFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifa
}
)
sessionsWithSources.map { (session, files) ->
val firFiles = session.buildFirViaLightTree(files, diagnosticsReporter) { files, lines ->
val firFiles = session.buildFirViaLightTree(files, diagnosticsReporter, configuration.headerCompilation) { files, lines ->
perfManager?.addSourcesStats(files, lines)
}
resolveAndCheckFir(session, firFiles, diagnosticsReporter)
resolveAndCheckFir(session, firFiles, diagnosticsReporter, configuration.headerCompilation)
}
} else {
val projectEnvironment = VfsBasedProjectEnvironment(
Expand Down Expand Up @@ -149,11 +150,11 @@ object MetadataFrontendPipelinePhase : PipelinePhase<ConfigurationPipelineArtifa

sessionsWithSources.map { (session, files) ->
val firFiles = session.buildFirFromKtFiles(files)
resolveAndCheckFir(session, firFiles, diagnosticsReporter)
resolveAndCheckFir(session, firFiles, diagnosticsReporter, configuration.headerCompilation)
}
}

outputs.runPlatformCheckers(diagnosticsReporter)
outputs.runPlatformCheckers(diagnosticsReporter, configuration.headerCompilation)

when (configuration.useLightTree) {
true -> outputs.all { checkKotlinPackageUsageForLightTree(configuration, it.fir) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ object CommonConfigurationKeysContainer : KeysContainer("org.jetbrains.kotlin.co
val ALLOW_ANY_SCRIPTS_IN_SOURCE_ROOTS by key<Boolean>("Allow to compile any scripts along with regular Kotlin sources")
val IGNORE_CONST_OPTIMIZATION_ERRORS by key<Boolean>("Ignore errors from IrConstTransformer")
val EVALUATED_CONST_TRACKER by key<EvaluatedConstTracker>("Keeps track of all evaluated by IrInterpreter constants")
val HEADER_COMPILATION by key<Boolean>("Enable header compilation mode")

val MESSAGE_COLLECTOR_KEY by key<MessageCollector>(
"message collector",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ object CommonConfigurationKeys {
@JvmField
val EVALUATED_CONST_TRACKER = CompilerConfigurationKey.create<EvaluatedConstTracker>("Keeps track of all evaluated by IrInterpreter constants")

@JvmField
val HEADER_COMPILATION = CompilerConfigurationKey.create<Boolean>("Enable header compilation mode")

@JvmField
val MESSAGE_COLLECTOR_KEY = CompilerConfigurationKey.create<MessageCollector>("message collector")

Expand Down Expand Up @@ -230,6 +233,10 @@ var CompilerConfiguration.evaluatedConstTracker: EvaluatedConstTracker?
get() = get(CommonConfigurationKeys.EVALUATED_CONST_TRACKER)
set(value) { put(CommonConfigurationKeys.EVALUATED_CONST_TRACKER, requireNotNull(value) { "nullable values are not allowed" }) }

var CompilerConfiguration.headerCompilation: Boolean
get() = getBoolean(CommonConfigurationKeys.HEADER_COMPILATION)
set(value) { put(CommonConfigurationKeys.HEADER_COMPILATION, value) }

var CompilerConfiguration.messageCollector: MessageCollector
get() = get(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE)
set(value) { put(CommonConfigurationKeys.MESSAGE_COLLECTOR_KEY, value) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ abstract class AbstractFirOldFrontendLightClassesTest : BaseDiagnosticsTest() {
private fun mapKtFilesToFirFiles(session: FirSession, ktFiles: List<KtFile>, firFiles: MutableList<FirFile>, useLightTree: Boolean) {
val firProvider = (session.firProvider as FirProviderImpl)
if (useLightTree) {
val lightTreeBuilder = LightTree2Fir(session, firProvider.kotlinScopeProvider)
val lightTreeBuilder = LightTree2Fir(session, false, firProvider.kotlinScopeProvider)
ktFiles.mapTo(firFiles) {
val firFile =
lightTreeBuilder.buildFirFile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,8 @@ data class Fir2IrActualizedResult(
val symbolTable: SymbolTable,
)

fun List<ModuleCompilerAnalyzedOutput>.runPlatformCheckers(reporter: BaseDiagnosticsCollector) {
fun List<ModuleCompilerAnalyzedOutput>.runPlatformCheckers(reporter: BaseDiagnosticsCollector, headerCompilationMode: Boolean) {
if (headerCompilationMode) return
val platformModule = this.last()
val session = platformModule.session
val scopeSession = platformModule.scopeSession
Expand Down
Loading