summaryrefslogtreecommitdiff
path: root/plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt')
-rw-r--r--plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt777
1 files changed, 0 insertions, 777 deletions
diff --git a/plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt b/plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
deleted file mode 100644
index 6b3f118edbc4..000000000000
--- a/plugins/kotlin/jps/jps-plugin/src/org/jetbrains/kotlin/jps/build/KotlinBuilder.kt
+++ /dev/null
@@ -1,777 +0,0 @@
-// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
-
-package org.jetbrains.kotlin.jps.build
-
-import com.intellij.openapi.diagnostic.Logger
-import com.intellij.openapi.util.NlsSafe
-import org.jetbrains.jps.ModuleChunk
-import org.jetbrains.jps.builders.DirtyFilesHolder
-import org.jetbrains.jps.builders.FileProcessor
-import org.jetbrains.jps.builders.impl.DirtyFilesHolderBase
-import org.jetbrains.jps.builders.java.JavaBuilderUtil
-import org.jetbrains.jps.builders.java.JavaSourceRootDescriptor
-import org.jetbrains.jps.builders.storage.BuildDataCorruptedException
-import org.jetbrains.jps.incremental.*
-import org.jetbrains.jps.incremental.BuildOperations.deleteRecursively
-import org.jetbrains.jps.incremental.ModuleLevelBuilder.ExitCode.*
-import org.jetbrains.jps.incremental.java.JavaBuilder
-import org.jetbrains.jps.model.JpsProject
-import org.jetbrains.kotlin.build.GeneratedFile
-import org.jetbrains.kotlin.build.GeneratedJvmClass
-import org.jetbrains.kotlin.cli.common.ExitCode
-import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
-import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.*
-import org.jetbrains.kotlin.cli.common.messages.MessageCollectorUtil
-import org.jetbrains.kotlin.compilerRunner.*
-import org.jetbrains.kotlin.config.IncrementalCompilation
-import org.jetbrains.kotlin.config.KotlinModuleKind
-import org.jetbrains.kotlin.config.Services
-import org.jetbrains.kotlin.daemon.common.isDaemonEnabled
-import org.jetbrains.kotlin.incremental.*
-import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
-import org.jetbrains.kotlin.incremental.components.LookupTracker
-import org.jetbrains.kotlin.build.report.ICReporterBase
-import org.jetbrains.kotlin.incremental.components.InlineConstTracker
-import org.jetbrains.kotlin.jps.KotlinJpsBundle
-import org.jetbrains.kotlin.jps.incremental.JpsIncrementalCache
-import org.jetbrains.kotlin.jps.incremental.JpsLookupStorageManager
-import org.jetbrains.kotlin.jps.model.kotlinKind
-import org.jetbrains.kotlin.jps.targets.KotlinJvmModuleBuildTarget
-import org.jetbrains.kotlin.jps.targets.KotlinModuleBuildTarget
-import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
-import org.jetbrains.kotlin.name.FqName
-import org.jetbrains.kotlin.preloading.ClassCondition
-import java.io.File
-import java.util.*
-import kotlin.collections.HashSet
-import kotlin.system.measureTimeMillis
-
-class KotlinBuilder : ModuleLevelBuilder(BuilderCategory.SOURCE_PROCESSOR) {
- companion object {
- @NlsSafe
- const val KOTLIN_BUILDER_NAME: String = "Kotlin Builder"
-
- val LOG = Logger.getInstance("#org.jetbrains.kotlin.jps.build.KotlinBuilder")
- const val SKIP_CACHE_VERSION_CHECK_PROPERTY = "kotlin.jps.skip.cache.version.check"
- const val JPS_KOTLIN_HOME_PROPERTY = "jps.kotlin.home"
-
- private val classesToLoadByParentFromRegistry =
- System.getProperty("kotlin.jps.classesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList()
- private val classPrefixesToLoadByParentFromRegistry =
- System.getProperty("kotlin.jps.classPrefixesToLoadByParent")?.split(',')?.map { it.trim() } ?: emptyList()
-
- val classesToLoadByParent: ClassCondition
- get() = ClassCondition { className ->
- val prefixes = listOf(
- "org.apache.log4j.", // For logging from compiler
- "org.jetbrains.kotlin.incremental.components.",
- "org.jetbrains.kotlin.incremental.js",
- "org.jetbrains.kotlin.load.kotlin.incremental.components."
- ) + classPrefixesToLoadByParentFromRegistry
-
- val classes = listOf(
- "org.jetbrains.kotlin.config.Services",
- "org.jetbrains.kotlin.progress.CompilationCanceledStatus",
- "org.jetbrains.kotlin.progress.CompilationCanceledException",
- "org.jetbrains.kotlin.modules.TargetId",
- "org.jetbrains.kotlin.cli.common.ExitCode"
- ) + classesToLoadByParentFromRegistry
-
- prefixes.forEach { if (className.startsWith(it)) return@ClassCondition true }
- classes.forEach { if (className == it) return@ClassCondition true }
-
- return@ClassCondition false
- }
- }
-
- private val statisticsLogger = TeamcityStatisticsLogger()
-
- override fun getPresentableName() = KOTLIN_BUILDER_NAME
-
- override fun getCompilableFileExtensions() = arrayListOf("kt", "kts")
-
- override fun buildStarted(context: CompileContext) {
- logSettings(context)
- }
-
- private fun logSettings(context: CompileContext) {
- LOG.debug("==========================================")
- LOG.info("is Kotlin incremental compilation enabled for JVM: ${IncrementalCompilation.isEnabledForJvm()}")
- LOG.info("is Kotlin incremental compilation enabled for JS: ${IncrementalCompilation.isEnabledForJs()}")
- LOG.info("is Kotlin compiler daemon enabled: ${isDaemonEnabled()}")
-
- val historyLabel = context.getBuilderParameter("history label")
- if (historyLabel != null) {
- LOG.info("Label in local history: $historyLabel")
- }
- }
-
- /**
- * Ensure Kotlin Context initialized.
- * Kotlin Context should be initialized only when required (before first kotlin chunk build).
- */
- private fun ensureKotlinContextInitialized(context: CompileContext): KotlinCompileContext {
- val kotlinCompileContext = context.getUserData(kotlinCompileContextKey)
- if (kotlinCompileContext != null) return kotlinCompileContext
-
- // don't synchronize on context, since it is chunk local only
- synchronized(kotlinCompileContextKey) {
- val actualKotlinCompileContext = context.getUserData(kotlinCompileContextKey)
- if (actualKotlinCompileContext != null) return actualKotlinCompileContext
-
- try {
- return initializeKotlinContext(context)
- } catch (t: Throwable) {
- jpsReportInternalBuilderError(context, Error("Cannot initialize Kotlin context: ${t.message}", t))
- throw t
- }
- }
- }
-
- private fun initializeKotlinContext(context: CompileContext): KotlinCompileContext {
- lateinit var kotlinContext: KotlinCompileContext
-
- val time = measureTimeMillis {
- kotlinContext = KotlinCompileContext(context)
-
- context.putUserData(kotlinCompileContextKey, kotlinContext)
- context.testingContext?.kotlinCompileContext = kotlinContext
-
- if (kotlinContext.shouldCheckCacheVersions && kotlinContext.hasKotlin()) {
- kotlinContext.checkCacheVersions()
- }
-
- kotlinContext.cleanupCaches()
- kotlinContext.reportUnsupportedTargets()
- }
-
- LOG.info("Total Kotlin global compile context initialization time: $time ms")
-
- return kotlinContext
- }
-
- override fun buildFinished(context: CompileContext) {
- ensureKotlinContextDisposed(context)
- }
-
- private fun ensureKotlinContextDisposed(context: CompileContext) {
- if (context.getUserData(kotlinCompileContextKey) != null) {
- // don't synchronize on context, since it chunk local only
- synchronized(kotlinCompileContextKey) {
- val kotlinCompileContext = context.getUserData(kotlinCompileContextKey)
- if (kotlinCompileContext != null) {
- kotlinCompileContext.dispose()
- context.putUserData(kotlinCompileContextKey, null)
-
- statisticsLogger.reportTotal()
- }
- }
- }
- }
-
- override fun chunkBuildStarted(context: CompileContext, chunk: ModuleChunk) {
- super.chunkBuildStarted(context, chunk)
-
- if (chunk.isDummy(context)) return
-
- val kotlinContext = ensureKotlinContextInitialized(context)
-
- val buildLogger = context.testingContext?.buildLogger
- buildLogger?.chunkBuildStarted(context, chunk)
-
- if (JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)) return
-
- val targets = chunk.targets
- if (targets.none { kotlinContext.hasKotlinMarker[it] == true }) return
-
- val kotlinChunk = kotlinContext.getChunk(chunk) ?: return
- kotlinContext.checkChunkCacheVersion(kotlinChunk)
-
- if (!kotlinContext.rebuildingAllKotlin && kotlinChunk.isEnabled) {
- markAdditionalFilesForInitialRound(kotlinChunk, chunk, kotlinContext)
- }
-
- buildLogger?.afterChunkBuildStarted(context, chunk)
- }
-
- /**
- * Invalidate usages of removed classes.
- * See KT-13677 for more details.
- *
- * todo(1.2.80): move to KotlinChunk
- * todo(1.2.80): got rid of jpsGlobalContext usages (replace with KotlinCompileContext)
- */
- private fun markAdditionalFilesForInitialRound(
- kotlinChunk: KotlinChunk,
- chunk: ModuleChunk,
- kotlinContext: KotlinCompileContext
- ) {
- val context = kotlinContext.jpsContext
- val dirtyFilesHolder = KotlinDirtySourceFilesHolder(
- chunk,
- context,
- object : DirtyFilesHolderBase<JavaSourceRootDescriptor, ModuleBuildTarget>(context) {
- override fun processDirtyFiles(processor: FileProcessor<JavaSourceRootDescriptor, ModuleBuildTarget>) {
- FSOperations.processFilesToRecompile(context, chunk, processor)
- }
- }
- )
- val fsOperations = FSOperationsHelper(context, chunk, dirtyFilesHolder, LOG)
-
- val representativeTarget = kotlinContext.targetsBinding[chunk.representativeTarget()] ?: return
-
- // dependent caches are not required, since we are not going to update caches
- val incrementalCaches = kotlinChunk.loadCaches(loadDependent = false)
-
- val messageCollector = MessageCollectorAdapter(context, representativeTarget)
- val environment = createCompileEnvironment(
- kotlinContext.jpsContext,
- representativeTarget,
- incrementalCaches,
- LookupTracker.DO_NOTHING,
- ExpectActualTracker.DoNothing,
- InlineConstTracker.DoNothing,
- chunk,
- messageCollector
- )
-
- val removedClasses = HashSet<String>()
- for (target in kotlinChunk.targets) {
- val cache = incrementalCaches[target] ?: continue
- val dirtyFiles = dirtyFilesHolder.getDirtyFiles(target.jpsModuleBuildTarget).keys
- val removedFiles = dirtyFilesHolder.getRemovedFiles(target.jpsModuleBuildTarget)
-
- val existingClasses = JpsKotlinCompilerRunner().classesFqNamesByFiles(environment, dirtyFiles)
- val previousClasses = cache.classesFqNamesBySources(dirtyFiles + removedFiles)
- for (jvmClassName in previousClasses) {
- val fqName = jvmClassName.asString()
- if (fqName !in existingClasses) {
- removedClasses.add(fqName)
- }
- }
- }
-
- val changesCollector = ChangesCollector()
- removedClasses.forEach { changesCollector.collectSignature(FqName(it), areSubclassesAffected = true) }
- val affectedByRemovedClasses = changesCollector.getDirtyFiles(incrementalCaches.values, kotlinContext.lookupStorageManager)
-
- fsOperations.markFilesForCurrentRound(affectedByRemovedClasses.dirtyFiles + affectedByRemovedClasses.forceRecompileTogether)
- }
-
- override fun chunkBuildFinished(context: CompileContext, chunk: ModuleChunk) {
- super.chunkBuildFinished(context, chunk)
-
- if (chunk.isDummy(context)) return
-
- // Temporary workaround for KT-33808
- val kotlinContext = ensureKotlinContextInitialized(context)
- for (target in chunk.targets) {
- if (kotlinContext.hasKotlinMarker[target] != true) continue
-
- val outputRoots = target.getOutputRoots(context)
- if (outputRoots.size > 1) {
- outputRoots.forEach { it.mkdirs() }
- }
- }
-
- LOG.debug("------------------------------------------")
- }
-
- override fun build(
- context: CompileContext,
- chunk: ModuleChunk,
- dirtyFilesHolder: DirtyFilesHolder<JavaSourceRootDescriptor, ModuleBuildTarget>,
- outputConsumer: OutputConsumer
- ): ExitCode {
- if (chunk.isDummy(context))
- return NOTHING_DONE
-
- val kotlinTarget = context.kotlin.targetsBinding[chunk.representativeTarget()] ?: return OK
- val messageCollector = MessageCollectorAdapter(context, kotlinTarget)
-
- // New mpp project model: modules which is imported from sources sets of the compilations shouldn't be compiled for now.
- // It should be compiled only as one of source root of target compilation, which is added in [KotlinSourceRootProvider].
- if (chunk.modules.any { it.kotlinKind == KotlinModuleKind.SOURCE_SET_HOLDER }) {
- if (chunk.modules.size > 1) {
- messageCollector.report(
- ERROR,
- KotlinJpsBundle.message("error.text.cyclically.dependent.modules.are.not.supported.in.multiplatform.projects")
- )
- return ABORT
- }
-
- return NOTHING_DONE
- }
-
- val kotlinDirtyFilesHolder = KotlinDirtySourceFilesHolder(chunk, context, dirtyFilesHolder)
- val fsOperations = FSOperationsHelper(context, chunk, kotlinDirtyFilesHolder, LOG)
-
- try {
- val proposedExitCode =
- doBuild(chunk, kotlinTarget, context, kotlinDirtyFilesHolder, messageCollector, outputConsumer, fsOperations)
-
- val actualExitCode = if (proposedExitCode == OK && fsOperations.hasMarkedDirty) ADDITIONAL_PASS_REQUIRED else proposedExitCode
-
- LOG.debug("Build result: $actualExitCode")
-
- context.testingContext?.buildLogger?.buildFinished(actualExitCode)
-
- return actualExitCode
- } catch (e: StopBuildException) {
- LOG.info("Caught exception: $e")
- throw e
- } catch (e: BuildDataCorruptedException) {
- LOG.info("Caught exception: $e")
- throw e
- } catch (e: Throwable) {
- LOG.info("Caught exception: $e")
- MessageCollectorUtil.reportException(messageCollector, e)
- return ABORT
- }
- }
-
- private fun doBuild(
- chunk: ModuleChunk,
- representativeTarget: KotlinModuleBuildTarget<*>,
- context: CompileContext,
- kotlinDirtyFilesHolder: KotlinDirtySourceFilesHolder,
- messageCollector: MessageCollectorAdapter,
- outputConsumer: OutputConsumer,
- fsOperations: FSOperationsHelper
- ): ExitCode {
- // Workaround for Android Studio
- if (representativeTarget is KotlinJvmModuleBuildTarget && !JavaBuilder.IS_ENABLED[context, true]) {
- messageCollector.report(INFO, KotlinJpsBundle.message("info.text.kotlin.jps.plugin.is.disabled"))
- return NOTHING_DONE
- }
-
- val kotlinContext = context.kotlin
- val kotlinChunk = chunk.toKotlinChunk(context)!!
-
- if (!kotlinChunk.haveSameCompiler) {
- messageCollector.report(
- ERROR,
- KotlinJpsBundle.message("error.text.cyclically.dependent.modules.0.should.have.same.compiler", kotlinChunk.presentableModulesToCompilersList)
- )
- return ABORT
- }
-
- if (!kotlinChunk.isEnabled) {
- return NOTHING_DONE
- }
-
- val projectDescriptor = context.projectDescriptor
- val targets = chunk.targets
-
- val isChunkRebuilding = JavaBuilderUtil.isForcedRecompilationAllJavaModules(context)
- || targets.any { kotlinContext.rebuildAfterCacheVersionChanged[it] == true }
-
- if (!kotlinDirtyFilesHolder.hasDirtyOrRemovedFiles) {
- if (isChunkRebuilding) {
- targets.forEach {
- kotlinContext.hasKotlinMarker[it] = false
- }
- }
-
- targets.forEach { kotlinContext.rebuildAfterCacheVersionChanged.clean(it) }
- return NOTHING_DONE
- }
-
- // Request CHUNK_REBUILD when IC is off and there are dirty Kotlin files
- // Otherwise unexpected compile error might happen, when there are Groovy files,
- // but they are not dirty, so Groovy builder does not generate source stubs,
- // and Kotlin builder is filtering out output directory from classpath
- // (because it may contain outdated Java classes).
- if (!isChunkRebuilding && !representativeTarget.isIncrementalCompilationEnabled) {
- targets.forEach { kotlinContext.rebuildAfterCacheVersionChanged[it] = true }
- return CHUNK_REBUILD_REQUIRED
- }
-
- val targetsWithoutOutputDir = targets.filter { it.outputDir == null }
- if (targetsWithoutOutputDir.isNotEmpty()) {
- messageCollector.report(ERROR,
- KotlinJpsBundle.message("error.text.output.directory.not.specified.for.0", targetsWithoutOutputDir.joinToString())
- )
- return ABORT
- }
-
- val project = projectDescriptor.project
- val lookupTracker = getLookupTracker(project, representativeTarget)
- val exceptActualTracer = ExpectActualTrackerImpl()
- val incrementalCaches = kotlinChunk.loadCaches()
- val inlineConstTracker = InlineConstTrackerImpl()
- val environment = createCompileEnvironment(
- context,
- representativeTarget,
- incrementalCaches,
- lookupTracker,
- exceptActualTracer,
- inlineConstTracker,
- chunk,
- messageCollector
- )
-
- context.testingContext?.buildLogger?.compilingFiles(
- kotlinDirtyFilesHolder.allDirtyFiles,
- kotlinDirtyFilesHolder.allRemovedFilesFiles
- )
-
- cleanJsOutputs(context, kotlinChunk, incrementalCaches, kotlinDirtyFilesHolder)
-
- if (LOG.isDebugEnabled) {
- LOG.debug("Compiling files: ${kotlinDirtyFilesHolder.allDirtyFiles}")
- }
-
- val start = System.nanoTime()
- val outputItemCollector = doCompileModuleChunk(
- kotlinChunk,
- representativeTarget,
- kotlinChunk.compilerArguments,
- context,
- kotlinDirtyFilesHolder,
- fsOperations,
- environment,
- incrementalCaches
- )
-
- statisticsLogger.registerStatistic(chunk, System.nanoTime() - start)
-
- if (outputItemCollector == null) {
- return NOTHING_DONE
- }
-
- val compilationErrors = Utils.ERRORS_DETECTED_KEY[context, false]
- if (compilationErrors) {
- LOG.info("Compiled with errors")
- return ABORT
- } else {
- LOG.info("Compiled successfully")
- }
-
- val generatedFiles = getGeneratedFiles(context, chunk, environment.outputItemsCollector)
-
- markDirtyComplementaryMultifileClasses(generatedFiles, kotlinContext, incrementalCaches, fsOperations)
-
- val kotlinTargets = kotlinContext.targetsBinding
- for ((target, outputItems) in generatedFiles) {
- val kotlinTarget = kotlinTargets[target] ?: error("Could not find Kotlin target for JPS target $target")
- kotlinTarget.registerOutputItems(outputConsumer, outputItems)
- }
- kotlinChunk.saveVersions()
-
- if (targets.any { kotlinContext.hasKotlinMarker[it] == null }) {
- fsOperations.markChunk(recursively = false, kotlinOnly = true, excludeFiles = kotlinDirtyFilesHolder.allDirtyFiles)
- }
-
- for (target in targets) {
- kotlinContext.hasKotlinMarker[target] = true
- kotlinContext.rebuildAfterCacheVersionChanged.clean(target)
- }
-
- kotlinChunk.targets.forEach {
- it.doAfterBuild()
- }
-
- representativeTarget.updateChunkMappings(
- context,
- chunk,
- kotlinDirtyFilesHolder,
- generatedFiles,
- incrementalCaches,
- environment
- )
-
- if (!representativeTarget.isIncrementalCompilationEnabled) {
- return OK
- }
-
- context.checkCanceled()
-
- environment.withProgressReporter { progress ->
- progress.progress("performing incremental compilation analysis")
-
- val changesCollector = ChangesCollector()
-
- for ((target, files) in generatedFiles) {
- val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target]!!
- kotlinModuleBuilderTarget.updateCaches(
- kotlinDirtyFilesHolder,
- incrementalCaches[kotlinModuleBuilderTarget]!!,
- files,
- changesCollector,
- environment
- )
- }
-
- updateLookupStorage(lookupTracker, kotlinContext.lookupStorageManager, kotlinDirtyFilesHolder)
-
- if (!isChunkRebuilding) {
- changesCollector.processChangesUsingLookups(
- kotlinDirtyFilesHolder.allDirtyFiles,
- kotlinContext.lookupStorageManager,
- fsOperations,
- incrementalCaches.values
- )
- }
- }
-
- return OK
- }
-
- private fun cleanJsOutputs(
- context: CompileContext,
- kotlinChunk: KotlinChunk,
- incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
- kotlinDirtyFilesHolder: KotlinDirtySourceFilesHolder
- ) {
- for (target in kotlinChunk.targets) {
- val cache = incrementalCaches[target] ?: continue
-
- if (cache is IncrementalJsCache) {
- val filesToDelete = mutableListOf<File>()
- val dirtyFiles = kotlinDirtyFilesHolder.getDirtyFiles(target.jpsModuleBuildTarget).keys
- val removedFiles = kotlinDirtyFilesHolder.getRemovedFiles(target.jpsModuleBuildTarget)
-
- for (file: File in dirtyFiles + removedFiles) {
- filesToDelete.addAll(cache.getOutputsBySource(file).filter { it !in filesToDelete })
- }
-
- if (filesToDelete.isNotEmpty()) {
- val deletedForThisSource = mutableSetOf<String>()
- val parentDirs = mutableSetOf<File>()
-
- for (kjsmFile in filesToDelete) {
- deleteRecursively(kjsmFile.path, deletedForThisSource, parentDirs)
- }
-
- FSOperations.pruneEmptyDirs(context, parentDirs)
-
- val logger = context.loggingManager.projectBuilderLogger
- if (logger.isEnabled && deletedForThisSource.isNotEmpty()) {
- logger.logDeletedFiles(deletedForThisSource)
- }
- }
- }
- }
- }
-
- // todo(1.2.80): got rid of ModuleChunk (replace with KotlinChunk)
- // todo(1.2.80): introduce KotlinRoundCompileContext, move dirtyFilesHolder, fsOperations, environment to it
- private fun doCompileModuleChunk(
- kotlinChunk: KotlinChunk,
- representativeTarget: KotlinModuleBuildTarget<*>,
- commonArguments: CommonCompilerArguments,
- context: CompileContext,
- dirtyFilesHolder: KotlinDirtySourceFilesHolder,
- fsOperations: FSOperationsHelper,
- environment: JpsCompilerEnvironment,
- incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>
- ): OutputItemsCollector? {
- loadPlugins(representativeTarget, commonArguments, context)
-
- kotlinChunk.targets.forEach {
- it.nextRound(context)
- }
-
- if (representativeTarget.isIncrementalCompilationEnabled) {
- for (target in kotlinChunk.targets) {
- val cache = incrementalCaches[target]
- val jpsTarget = target.jpsModuleBuildTarget
-
- val targetDirtyFiles = dirtyFilesHolder.byTarget[jpsTarget]
- if (cache != null && targetDirtyFiles != null) {
- val complementaryFiles = cache.getComplementaryFilesRecursive(targetDirtyFiles.dirty.keys + targetDirtyFiles.removed)
-
- fsOperations.markFilesForCurrentRound(jpsTarget, complementaryFiles)
-
- cache.markDirty(targetDirtyFiles.dirty.keys + targetDirtyFiles.removed)
- }
- }
- }
-
- val isDoneSomething = representativeTarget.compileModuleChunk(commonArguments, dirtyFilesHolder, environment)
-
- return if (isDoneSomething) environment.outputItemsCollector else null
- }
-
- private fun loadPlugins(
- representativeTarget: KotlinModuleBuildTarget<*>,
- commonArguments: CommonCompilerArguments,
- context: CompileContext
- ) {
- fun concatenate(strings: Array<String>?, cp: List<String>) = arrayOf(*strings.orEmpty(), *cp.toTypedArray())
-
- for (argumentProvider in ServiceLoader.load(KotlinJpsCompilerArgumentsProvider::class.java)) {
- val jpsModuleBuildTarget = representativeTarget.jpsModuleBuildTarget
- // appending to pluginOptions
- commonArguments.pluginOptions = concatenate(
- commonArguments.pluginOptions,
- argumentProvider.getExtraArguments(jpsModuleBuildTarget, context)
- )
- // appending to classpath
- commonArguments.pluginClasspaths = concatenate(
- commonArguments.pluginClasspaths,
- argumentProvider.getClasspath(jpsModuleBuildTarget, context)
- )
-
- LOG.debug("Plugin loaded: ${argumentProvider::class.java.simpleName}")
- }
- }
-
- private fun createCompileEnvironment(
- context: CompileContext,
- kotlinModuleBuilderTarget: KotlinModuleBuildTarget<*>,
- incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
- lookupTracker: LookupTracker,
- exceptActualTracer: ExpectActualTracker,
- inlineConstTracker: InlineConstTracker,
- chunk: ModuleChunk,
- messageCollector: MessageCollectorAdapter
- ): JpsCompilerEnvironment {
- val compilerServices = with(Services.Builder()) {
- kotlinModuleBuilderTarget.makeServices(this, incrementalCaches, lookupTracker, exceptActualTracer, inlineConstTracker)
- build()
- }
-
- return JpsCompilerEnvironment(
- compilerServices,
- classesToLoadByParent,
- messageCollector,
- OutputItemsCollectorImpl(),
- ProgressReporterImpl(context, chunk)
- )
- }
-
- private fun getGeneratedFiles(
- context: CompileContext,
- chunk: ModuleChunk,
- outputItemCollector: OutputItemsCollectorImpl
- ): Map<ModuleBuildTarget, List<GeneratedFile>> {
- // If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below
- val sourceToTarget = HashMap<File, ModuleBuildTarget>()
- if (chunk.targets.size > 1) {
- for (target in chunk.targets) {
- context.kotlin.targetsBinding[target]?.sourceFiles?.forEach {
- sourceToTarget[it] = target
- }
- }
- }
-
- val representativeTarget = chunk.representativeTarget()
- fun SimpleOutputItem.target() =
- sourceFiles.firstOrNull()?.let { sourceToTarget[it] }
- ?: chunk.targets.singleOrNull { target ->
- target.outputDir?.let { outputDir ->
- outputFile.startsWith(outputDir)
- } ?: false
- }
- ?: representativeTarget
-
- return outputItemCollector.outputs
- .sortedBy { it.outputFile }
- .groupBy(SimpleOutputItem::target, SimpleOutputItem::toGeneratedFile)
- }
-
- private fun updateLookupStorage(
- lookupTracker: LookupTracker,
- lookupStorageManager: JpsLookupStorageManager,
- dirtyFilesHolder: KotlinDirtySourceFilesHolder
- ) {
- if (lookupTracker !is LookupTrackerImpl)
- throw AssertionError("Lookup tracker is expected to be LookupTrackerImpl, got ${lookupTracker::class.java}")
-
- lookupStorageManager.withLookupStorage { lookupStorage ->
- lookupStorage.removeLookupsFrom(dirtyFilesHolder.allDirtyFiles.asSequence() + dirtyFilesHolder.allRemovedFilesFiles.asSequence())
- lookupStorage.addAll(lookupTracker.lookups, lookupTracker.pathInterner.values)
- }
- }
-
- private fun markDirtyComplementaryMultifileClasses(
- generatedFiles: Map<ModuleBuildTarget, List<GeneratedFile>>,
- kotlinContext: KotlinCompileContext,
- incrementalCaches: Map<KotlinModuleBuildTarget<*>, JpsIncrementalCache>,
- fsOperations: FSOperationsHelper
- ) {
- for ((target, files) in generatedFiles) {
- val kotlinModuleBuilderTarget = kotlinContext.targetsBinding[target] ?: continue
- val cache = incrementalCaches[kotlinModuleBuilderTarget] as? IncrementalJvmCache ?: continue
- val generated = files.filterIsInstance<GeneratedJvmClass>()
- val multifileClasses = generated.filter { it.outputClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS }
- val expectedAllParts = multifileClasses.flatMap { cache.getAllPartsOfMultifileFacade(it.outputClass.className).orEmpty() }
- if (multifileClasses.isEmpty()) continue
- val actualParts = generated.filter { it.outputClass.classHeader.kind == KotlinClassHeader.Kind.MULTIFILE_CLASS_PART }
- .map { it.outputClass.className.toString() }
- if (!actualParts.containsAll(expectedAllParts)) {
- fsOperations.markFiles(expectedAllParts.flatMap { cache.sourcesByInternalName(it) }
- + multifileClasses.flatMap { it.sourceFiles })
- }
- }
- }
-}
-
-private class JpsICReporter : ICReporterBase() {
- override fun reportCompileIteration(incremental: Boolean, sourceFiles: Collection<File>, exitCode: ExitCode) {
- }
-
- override fun report(message: () -> String) {
- if (KotlinBuilder.LOG.isDebugEnabled) {
- KotlinBuilder.LOG.debug(message())
- }
- }
-
- override fun reportVerbose(message: () -> String) {
- report(message)
- }
-}
-
-private fun ChangesCollector.processChangesUsingLookups(
- compiledFiles: Set<File>,
- lookupStorageManager: JpsLookupStorageManager,
- fsOperations: FSOperationsHelper,
- caches: Iterable<JpsIncrementalCache>
-) {
- val allCaches = caches.flatMap { it.thisWithDependentCaches }
- val reporter = JpsICReporter()
-
- reporter.reportVerbose { "Start processing changes" }
-
- val dirtyFiles = getDirtyFiles(allCaches, lookupStorageManager)
- // if list of inheritors of sealed class has changed it should be recompiled with all the inheritors
- // Here we have a small optimization. Do not recompile the bunch if ALL these files were recompiled during the previous round.
- val excludeFiles = if (compiledFiles.containsAll(dirtyFiles.forceRecompileTogether))
- compiledFiles
- else
- compiledFiles.minus(dirtyFiles.forceRecompileTogether)
- fsOperations.markInChunkOrDependents(
- (dirtyFiles.dirtyFiles + dirtyFiles.forceRecompileTogether).asIterable(),
- excludeFiles = excludeFiles
- )
-
- reporter.reportVerbose { "End of processing changes" }
-}
-
-data class FilesToRecompile(val dirtyFiles: Set<File>, val forceRecompileTogether: Set<File>)
-
-private fun ChangesCollector.getDirtyFiles(
- caches: Iterable<IncrementalCacheCommon>,
- lookupStorageManager: JpsLookupStorageManager
-): FilesToRecompile {
- val reporter = JpsICReporter()
- val (dirtyLookupSymbols, dirtyClassFqNames, forceRecompile) = getDirtyData(caches, reporter)
- val dirtyFilesFromLookups = lookupStorageManager.withLookupStorage {
- mapLookupSymbolsToFiles(it, dirtyLookupSymbols, reporter)
- }
- return FilesToRecompile(
- dirtyFilesFromLookups + mapClassesFqNamesToFiles(caches, dirtyClassFqNames, reporter),
- mapClassesFqNamesToFiles(caches, forceRecompile, reporter)
- )
-
-}
-
-private fun getLookupTracker(project: JpsProject, representativeTarget: KotlinModuleBuildTarget<*>): LookupTracker {
- val testLookupTracker = project.testingContext?.lookupTracker ?: LookupTracker.DO_NOTHING
-
- if (representativeTarget.isIncrementalCompilationEnabled) return LookupTrackerImpl(testLookupTracker)
-
- return testLookupTracker
-}