From d3d854ec7df65176e6dda455873bc14f69dae15c Mon Sep 17 00:00:00 2001 From: Ilya Chernikov Date: Fri, 29 Jan 2016 10:17:17 +0100 Subject: [PATCH] Preparing build lib for migrating gradle plugin on it: moving ArgumentUtils from jps plugin, adding buildUtils with incremental compilation functions extracted from kotlinBuilder, minor tweak to lookup storage dump KT-8487 --- .../kotlin/compilerRunner/ArgumentUtils.java | 2 +- .../kotlin/incremental/LookupStorage.kt | 4 +- .../jetbrains/kotlin/incremental/buildUtil.kt | 203 ++++++++++++++++++ 3 files changed, 206 insertions(+), 3 deletions(-) rename {jps-plugin => build-common}/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java (98%) create mode 100644 build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt diff --git a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java b/build-common/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java similarity index 98% rename from jps-plugin/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java rename to build-common/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java index f2329867d68..2feb0cf2683 100644 --- a/jps-plugin/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java +++ b/build-common/src/org/jetbrains/kotlin/compilerRunner/ArgumentUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2015 JetBrains s.r.o. + * Copyright 2010-2016 JetBrains s.r.o. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/build-common/src/org/jetbrains/kotlin/incremental/LookupStorage.kt b/build-common/src/org/jetbrains/kotlin/incremental/LookupStorage.kt index d927c746f34..af8837847f7 100644 --- a/build-common/src/org/jetbrains/kotlin/incremental/LookupStorage.kt +++ b/build-common/src/org/jetbrains/kotlin/incremental/LookupStorage.kt @@ -171,7 +171,7 @@ open class LookupStorage(private val targetDataDir: File) : BasicMapsOwner() { flush(false) } - @TestOnly fun dump(lookupSymbols: Set): String { + @TestOnly fun dump(lookupSymbols: Set, basePath: File? = null): String { flush(false) val sb = StringBuilder() @@ -188,7 +188,7 @@ open class LookupStorage(private val targetDataDir: File) : BasicMapsOwner() { lookup.toString() } - val value = fileIds.map { idToFile[it]?.absolutePath ?: it.toString() }.sorted().joinToString(", ") + val value = fileIds.map { idToFile[it]?.let { if (basePath == null) it.absolutePath else it.toRelativeString(basePath) } ?: it.toString() }.sorted().joinToString(", ") p.println("$key -> $value") } diff --git a/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt b/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt new file mode 100644 index 00000000000..3acefade596 --- /dev/null +++ b/build-common/src/org/jetbrains/kotlin/incremental/buildUtil.kt @@ -0,0 +1,203 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// these functions are used in the kotlin gradle plugin +@file:Suppress("unused") + +package org.jetbrains.kotlin.incremental + +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.openapi.util.io.FileUtil +import com.intellij.openapi.util.text.StringUtil +import org.jetbrains.kotlin.build.GeneratedFile +import org.jetbrains.kotlin.build.GeneratedJvmClass +import org.jetbrains.kotlin.build.JvmSourceRoot +import org.jetbrains.kotlin.build.isModuleMappingFile +import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl +import org.jetbrains.kotlin.config.IncrementalCompilation +import org.jetbrains.kotlin.config.Services +import org.jetbrains.kotlin.incremental.components.LookupTracker +import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCache +import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents +import org.jetbrains.kotlin.modules.KotlinModuleXmlBuilder +import org.jetbrains.kotlin.modules.TargetId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.progress.CompilationCanceledStatus +import org.jetbrains.kotlin.utils.keysToMap +import java.io.File +import java.util.* + + +fun Iterable.javaSourceRoots(roots: Iterable): Iterable = + filter { it.isJavaFile() } + .map { findSrcDirRoot(it, roots) } + .filterNotNull() + +fun makeCompileServices( + incrementalCaches: Map, + lookupTracker: LookupTracker, + compilationCanceledStatus: CompilationCanceledStatus? +): Services = + with(Services.Builder()) { + register(IncrementalCompilationComponents::class.java, IncrementalCompilationComponentsImpl(incrementalCaches, lookupTracker)) + compilationCanceledStatus?.let { + register(CompilationCanceledStatus::class.java, it) + } + build() + } + +fun makeLookupTracker(parentLookupTracker: LookupTracker = LookupTracker.DO_NOTHING): LookupTracker = + if (IncrementalCompilation.isExperimental()) LookupTrackerImpl(parentLookupTracker) + else parentLookupTracker + +fun makeIncrementalCachesMap( + targets: Iterable, + getDependencies: (Target) -> Iterable, + getCache: (Target) -> IncrementalCacheImpl, + getTargetId: Target.() -> TargetId +): Map> +{ + val dependents = targets.keysToMap { hashSetOf() } + val targetsWithDependents = targets.toHashSet() + + for (target in targets) { + for (dependency in getDependencies(target)) { + if (dependency !in targets) continue + + dependents[dependency]!!.add(target) + targetsWithDependents.add(target) + } + } + + val caches = targetsWithDependents.keysToMap { getCache(it) } + + for ((target, cache) in caches) { + dependents[target]?.forEach { + cache.addDependentCache(caches[it]!!) + } + } + + return caches.mapKeys { it.key.getTargetId() } +} + +fun updateIncrementalCaches( + targets: Iterable, + generatedFiles: List>, + compiledWithErrors: Boolean, + getIncrementalCache: (Target) -> IncrementalCacheImpl +): CompilationResult { + + var changesInfo = CompilationResult.NO_CHANGES + for (generatedFile in generatedFiles) { + val ic = getIncrementalCache(generatedFile.target) + when { + generatedFile is GeneratedJvmClass -> changesInfo += ic.saveFileToCache(generatedFile) + generatedFile.outputFile.isModuleMappingFile() -> changesInfo += ic.saveModuleMappingToCache(generatedFile.sourceFiles, generatedFile.outputFile) + } + } + + if (!compiledWithErrors) { + targets.forEach { + val newChangesInfo = getIncrementalCache(it).clearCacheForRemovedClasses() + changesInfo += newChangesInfo + } + } + + return changesInfo +} + +fun LookupStorage.update( + lookupTracker: LookupTracker, + filesToCompile: Iterable, + removedFiles: Iterable +) { + if (lookupTracker !is LookupTrackerImpl) throw AssertionError("Lookup tracker is expected to be LookupTrackerImpl, got ${lookupTracker.javaClass}") + + removeLookupsFrom(filesToCompile.asSequence() + removedFiles.asSequence()) + + addAll(lookupTracker.lookups.entrySet(), lookupTracker.pathInterner.values) +} + +fun OutputItemsCollectorImpl.generatedFiles( + targets: Collection, + representativeTarget: Target, + getSources: (Target) -> Iterable, + getOutputDir: (Target) -> File? +): List> { + // If there's only one target, this map is empty: get() always returns null, and the representativeTarget will be used below + val sourceToTarget = + if (targets.size >1) targets.flatMap { target -> getSources(target).map { Pair(it, target) } }.toMap() + else mapOf() + + return outputs.map { outputItem -> + val target = + outputItem.sourceFiles.firstOrNull()?.let { sourceToTarget[it] } ?: + targets.filter { getOutputDir(it)?.let { outputItem.outputFile.startsWith(it) } ?: false }.singleOrNull() ?: + representativeTarget + if (outputItem.outputFile.name.endsWith(".class")) + GeneratedJvmClass(target, outputItem.sourceFiles, outputItem.outputFile) + else + GeneratedFile(target, outputItem.sourceFiles, outputItem.outputFile) + } +} + +fun CompilationResult.dirtyLookups( + caches: Sequence> +): Iterable = + changes.asIterable().flatMap { change -> + when (change) { + is ChangeInfo.SignatureChanged -> { + val fqNames = if (!change.areSubclassesAffected) listOf(change.fqName) else withSubtypes(change.fqName, caches) + fqNames.map { + val scope = it.parent().asString() + val name = it.shortName().identifier + LookupSymbol(name, scope) + } + } + is ChangeInfo.MembersChanged -> { + val scopes = withSubtypes(change.fqName, caches).map { it.asString() } + change.names.flatMap { name -> scopes.map { scope -> LookupSymbol(name, scope) } } + } + else -> listOf() + } + } + + +private fun File.isJavaFile() = extension.equals(JavaFileType.INSTANCE.defaultExtension, ignoreCase = true) + +private fun findSrcDirRoot(file: File, roots: Iterable): File? = + roots.firstOrNull { FileUtil.isAncestor(it, file, false) } + +private fun withSubtypes( + typeFqName: FqName, + caches: Sequence> +): Set { + val types = LinkedList(listOf(typeFqName)) + val subtypes = hashSetOf() + + while (types.isNotEmpty()) { + val unprocessedType = types.pollFirst() + + caches.flatMap { it.getSubtypesOf(unprocessedType) } + .filter { it !in subtypes } + .forEach { types.addLast(it) } + + subtypes.add(unprocessedType) + } + + return subtypes +} +