Gradle: compile tests incrementally when main is changed
#KT-17674 fixed
This commit is contained in:
+3
-1
@@ -48,7 +48,9 @@ class IncrementalCompilationOptions(
|
|||||||
/** @See [ReportSeverity] */
|
/** @See [ReportSeverity] */
|
||||||
reportSeverity: Int,
|
reportSeverity: Int,
|
||||||
/** @See [CompilationResultCategory]] */
|
/** @See [CompilationResultCategory]] */
|
||||||
requestedCompilationResults: Array<Int>
|
requestedCompilationResults: Array<Int>,
|
||||||
|
val resultDifferenceFile: File? = null,
|
||||||
|
val friendDifferenceFile: File? = null
|
||||||
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
) : CompilationOptions(compilerMode, targetPlatform, reportCategories, reportSeverity, requestedCompilationResults) {
|
||||||
companion object {
|
companion object {
|
||||||
const val serialVersionUID: Long = 0
|
const val serialVersionUID: Long = 0
|
||||||
|
|||||||
@@ -507,9 +507,12 @@ class CompileServiceImpl(
|
|||||||
workingDir,
|
workingDir,
|
||||||
enabled = true)
|
enabled = true)
|
||||||
|
|
||||||
return IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions, reporter, annotationFileUpdater,
|
val compiler = IncrementalJvmCompilerRunner(workingDir, javaSourceRoots, versions,
|
||||||
artifactChanges, changesRegistry)
|
reporter, annotationFileUpdater,
|
||||||
.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
artifactChanges, changesRegistry,
|
||||||
|
buildHistoryFile = incrementalCompilationOptions.resultDifferenceFile,
|
||||||
|
friendBuildHistoryFile = incrementalCompilationOptions.friendDifferenceFile)
|
||||||
|
return compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, { changedFiles })
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun leaseReplSession(
|
override fun leaseReplSession(
|
||||||
|
|||||||
+132
@@ -0,0 +1,132 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.TestOnly
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.ObjectInputStream
|
||||||
|
import java.io.ObjectOutputStream
|
||||||
|
|
||||||
|
data class BuildDifference(val ts: Long, val isIncremental: Boolean, val dirtyData: DirtyData)
|
||||||
|
|
||||||
|
// todo: storage format can be optimized by compressing fq-names
|
||||||
|
data class BuildDiffsStorage(val buildDiffs: List<BuildDifference>) {
|
||||||
|
companion object {
|
||||||
|
fun readFromFile(file: File, reporter: ICReporter?): BuildDiffsStorage? {
|
||||||
|
fun reportFail(reason: String) {
|
||||||
|
reporter?.report { "Could not read diff from file $file: $reason" }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!file.exists()) return null
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectInputStream(file.inputStream().buffered()).use { input ->
|
||||||
|
val version = input.readInt()
|
||||||
|
if (version != CURRENT_VERSION) {
|
||||||
|
reportFail("incompatible version $version, actual version is $CURRENT_VERSION")
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val size = input.readInt()
|
||||||
|
val result = ArrayList<BuildDifference>(size)
|
||||||
|
repeat(size) {
|
||||||
|
result.add(input.readBuildDifference())
|
||||||
|
}
|
||||||
|
return BuildDiffsStorage(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IOException) {
|
||||||
|
reportFail(e.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun writeToFile(file: File, storage: BuildDiffsStorage, reporter: ICReporter?) {
|
||||||
|
file.parentFile.mkdirs()
|
||||||
|
|
||||||
|
try {
|
||||||
|
ObjectOutputStream(file.outputStream().buffered()).use { output ->
|
||||||
|
output.writeInt(CURRENT_VERSION)
|
||||||
|
|
||||||
|
val diffsToWrite = storage.buildDiffs.sortedBy { it.ts }.takeLast(MAX_DIFFS_ENTRIES)
|
||||||
|
output.writeInt(diffsToWrite.size)
|
||||||
|
for (diff in diffsToWrite) {
|
||||||
|
output.writeBuildDifference(diff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e: IOException) {
|
||||||
|
reporter?.report { "Could not write diff to file $file: $e" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectInputStream.readBuildDifference(): BuildDifference {
|
||||||
|
val ts = readLong()
|
||||||
|
val isIncremental = readBoolean()
|
||||||
|
val dirtyData = readDirtyData()
|
||||||
|
return BuildDifference(ts, isIncremental, dirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeBuildDifference(diff: BuildDifference) {
|
||||||
|
writeLong(diff.ts)
|
||||||
|
writeBoolean(diff.isIncremental)
|
||||||
|
writeDirtyData(diff.dirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectInputStream.readDirtyData(): DirtyData {
|
||||||
|
val lookupSymbolSize = readInt()
|
||||||
|
val lookupSymbols = ArrayList<LookupSymbol>(lookupSymbolSize)
|
||||||
|
repeat(lookupSymbolSize) {
|
||||||
|
val name = readUTF()
|
||||||
|
val scope = readUTF()
|
||||||
|
lookupSymbols.add(LookupSymbol(name = name, scope = scope))
|
||||||
|
}
|
||||||
|
|
||||||
|
val dirtyClassesSize = readInt()
|
||||||
|
val dirtyClassesFqNames = ArrayList<FqName>(dirtyClassesSize)
|
||||||
|
repeat(dirtyClassesSize) {
|
||||||
|
val fqNameString = readUTF()
|
||||||
|
dirtyClassesFqNames.add(FqName(fqNameString))
|
||||||
|
}
|
||||||
|
|
||||||
|
return DirtyData(lookupSymbols, dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ObjectOutputStream.writeDirtyData(dirtyData: DirtyData) {
|
||||||
|
val lookupSymbols = dirtyData.dirtyLookupSymbols
|
||||||
|
writeInt(lookupSymbols.size)
|
||||||
|
for ((name, scope) in lookupSymbols) {
|
||||||
|
writeUTF(name)
|
||||||
|
writeUTF(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
val dirtyClassesFqNames = dirtyData.dirtyClassesFqNames
|
||||||
|
writeInt(dirtyClassesFqNames.size)
|
||||||
|
for (fqName in dirtyClassesFqNames) {
|
||||||
|
writeUTF(fqName.asString())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val MAX_DIFFS_ENTRIES: Int = 10
|
||||||
|
|
||||||
|
@set:TestOnly
|
||||||
|
var CURRENT_VERSION: Int = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -18,7 +18,7 @@ package org.jetbrains.kotlin.incremental
|
|||||||
|
|
||||||
import java.io.*
|
import java.io.*
|
||||||
|
|
||||||
internal data class BuildInfo(val startTS: Long) : Serializable {
|
data class BuildInfo(val startTS: Long) : Serializable {
|
||||||
companion object {
|
companion object {
|
||||||
fun read(file: File): BuildInfo? =
|
fun read(file: File): BuildInfo? =
|
||||||
try {
|
try {
|
||||||
|
|||||||
+15
-10
@@ -255,16 +255,9 @@ abstract class IncrementalCompilerRunner<
|
|||||||
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
if (exitCode == ExitCode.OK && compilationMode is CompilationMode.Incremental) {
|
||||||
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
buildDirtyLookupSymbols.addAll(additionalDirtyLookupSymbols())
|
||||||
}
|
}
|
||||||
if (changesRegistry != null) {
|
|
||||||
if (compilationMode is CompilationMode.Incremental) {
|
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
||||||
val dirtyData = DirtyData(buildDirtyLookupSymbols, buildDirtyFqNames)
|
processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||||
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
|
||||||
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (exitCode == ExitCode.OK) {
|
if (exitCode == ExitCode.OK) {
|
||||||
cacheVersions.forEach { it.saveIfNeeded() }
|
cacheVersions.forEach { it.saveIfNeeded() }
|
||||||
@@ -273,6 +266,18 @@ abstract class IncrementalCompilerRunner<
|
|||||||
return exitCode
|
return exitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected open fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||||
|
if (changesRegistry == null) return
|
||||||
|
|
||||||
|
if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
changesRegistry.registerChanges(currentBuildInfo.startTS, dirtyData)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(compilationMode is CompilationMode.Rebuild) { "Unexpected compilation mode: ${compilationMode::class.java}" }
|
||||||
|
changesRegistry.unknownChanges(currentBuildInfo.startTS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
const val DIRTY_SOURCES_FILE_NAME = "dirty-sources.txt"
|
||||||
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
const val LAST_BUILD_INFO_FILE_NAME = "last-build.bin"
|
||||||
|
|||||||
+73
-23
@@ -16,10 +16,10 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.incremental
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
|
import org.jetbrains.kotlin.annotation.AnnotationFileUpdater
|
||||||
import org.jetbrains.kotlin.build.GeneratedFile
|
import org.jetbrains.kotlin.build.GeneratedFile
|
||||||
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
import org.jetbrains.kotlin.build.GeneratedJvmClass
|
||||||
import org.jetbrains.kotlin.build.isModuleMappingFile
|
|
||||||
import org.jetbrains.kotlin.build.JvmSourceRoot
|
import org.jetbrains.kotlin.build.JvmSourceRoot
|
||||||
import org.jetbrains.kotlin.cli.common.ExitCode
|
import org.jetbrains.kotlin.cli.common.ExitCode
|
||||||
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments
|
||||||
@@ -38,6 +38,7 @@ import org.jetbrains.kotlin.name.FqName
|
|||||||
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
import org.jetbrains.kotlin.resolve.jvm.JvmClassName
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
import kotlin.collections.HashSet
|
||||||
|
|
||||||
fun makeIncrementally(
|
fun makeIncrementally(
|
||||||
cachesDir: File,
|
cachesDir: File,
|
||||||
@@ -89,7 +90,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
reporter: ICReporter,
|
reporter: ICReporter,
|
||||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||||
artifactChangesProvider: ArtifactChangesProvider? = null,
|
artifactChangesProvider: ArtifactChangesProvider? = null,
|
||||||
changesRegistry: ChangesRegistry? = null
|
changesRegistry: ChangesRegistry? = null,
|
||||||
|
private val buildHistoryFile: File? = null,
|
||||||
|
private val friendBuildHistoryFile: File? = null
|
||||||
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
) : IncrementalCompilerRunner<K2JVMCompilerArguments, IncrementalJvmCachesManager>(
|
||||||
workingDir,
|
workingDir,
|
||||||
"caches-jvm",
|
"caches-jvm",
|
||||||
@@ -110,16 +113,58 @@ class IncrementalJvmCompilerRunner(
|
|||||||
private var javaFilesProcessor = ChangedJavaFilesProcessor(reporter)
|
private var javaFilesProcessor = ChangedJavaFilesProcessor(reporter)
|
||||||
|
|
||||||
override fun calculateSourcesToCompile(caches: IncrementalJvmCachesManager, changedFiles: ChangedFiles.Known, args: K2JVMCompilerArguments): CompilationMode {
|
override fun calculateSourcesToCompile(caches: IncrementalJvmCachesManager, changedFiles: ChangedFiles.Known, args: K2JVMCompilerArguments): CompilationMode {
|
||||||
val removedClassFiles = changedFiles.removed.filter(File::isClassFile)
|
val dirtyFiles = getDirtyFiles(changedFiles)
|
||||||
if (removedClassFiles.any()) return CompilationMode.Rebuild { "Removed class files: ${reporter.pathsAsString(removedClassFiles)}" }
|
|
||||||
|
|
||||||
val modifiedClassFiles = changedFiles.modified.filter(File::isClassFile)
|
fun markDirtyBy(lookupSymbols: Collection<LookupSymbol>) {
|
||||||
if (modifiedClassFiles.any()) return CompilationMode.Rebuild { "Modified class files: ${reporter.pathsAsString(modifiedClassFiles)}" }
|
if (lookupSymbols.isEmpty()) return
|
||||||
|
|
||||||
|
val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter)
|
||||||
|
dirtyFiles.addAll(dirtyFilesFromLookups)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun markDirtyBy(dirtyClassesFqNames: Collection<FqName>) {
|
||||||
|
if (dirtyClassesFqNames.isEmpty()) return
|
||||||
|
|
||||||
|
val fqNamesWithSubtypes = dirtyClassesFqNames.flatMap { withSubtypes(it, listOf(caches.platformCache)) }
|
||||||
|
val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.platformCache), fqNamesWithSubtypes, reporter)
|
||||||
|
dirtyFiles.addAll(dirtyFilesFromFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
||||||
|
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
||||||
|
|
||||||
|
val changesFromFriend by lazy {
|
||||||
|
val myLastTS = lastBuildInfo?.startTS ?: return@lazy ChangesEither.Unknown()
|
||||||
|
val storage = friendBuildHistoryFile?.let { BuildDiffsStorage.readFromFile(it, reporter) } ?: return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
val (prevDiffs, newDiffs) = storage.buildDiffs.partition { it.ts < myLastTS }
|
||||||
|
if (prevDiffs.isEmpty()) return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
val dirtyLookupSymbols = HashSet<LookupSymbol>()
|
||||||
|
val dirtyClassesFqNames = HashSet<FqName>()
|
||||||
|
for ((_, isIncremental, dirtyData) in newDiffs) {
|
||||||
|
if (!isIncremental) return@lazy ChangesEither.Unknown()
|
||||||
|
|
||||||
|
dirtyLookupSymbols.addAll(dirtyData.dirtyLookupSymbols)
|
||||||
|
dirtyClassesFqNames.addAll(dirtyData.dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
markDirtyBy(dirtyLookupSymbols)
|
||||||
|
markDirtyBy(dirtyClassesFqNames)
|
||||||
|
ChangesEither.Known(dirtyLookupSymbols, dirtyClassesFqNames)
|
||||||
|
}
|
||||||
|
val friendDirs = args.friendPaths?.map { File(it) } ?: emptyList()
|
||||||
|
for (file in changedFiles.removed.asSequence() + changedFiles.modified.asSequence()) {
|
||||||
|
if (!file.isClassFile()) continue
|
||||||
|
|
||||||
|
val isFriendClassFile = friendDirs.any { FileUtil.isAncestor(it, file, false) }
|
||||||
|
if (isFriendClassFile && changesFromFriend is ChangesEither.Known) continue
|
||||||
|
|
||||||
|
return CompilationMode.Rebuild { "Cannot get changes from modified or removed class file: ${reporter.pathsAsString(file)}" }
|
||||||
|
}
|
||||||
|
|
||||||
val classpathSet = args.classpathAsList.toHashSet()
|
val classpathSet = args.classpathAsList.toHashSet()
|
||||||
val modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
val modifiedClasspathEntries = changedFiles.modified.filter { it in classpathSet }
|
||||||
val lastBuildInfo = BuildInfo.read(lastBuildInfoFile)
|
|
||||||
reporter.report { "Last Kotlin Build info -- $lastBuildInfo" }
|
|
||||||
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
val classpathChanges = getClasspathChanges(modifiedClasspathEntries, lastBuildInfo)
|
||||||
if (classpathChanges !is ChangesEither.Known) {
|
if (classpathChanges !is ChangesEither.Known) {
|
||||||
return CompilationMode.Rebuild { "could not get changes from modified classpath entries: ${reporter.pathsAsString(modifiedClasspathEntries)}" }
|
return CompilationMode.Rebuild { "could not get changes from modified classpath entries: ${reporter.pathsAsString(modifiedClasspathEntries)}" }
|
||||||
@@ -131,21 +176,9 @@ class IncrementalJvmCompilerRunner(
|
|||||||
is ChangesEither.Unknown -> return CompilationMode.Rebuild { "Could not get changes for java files" }
|
is ChangesEither.Unknown -> return CompilationMode.Rebuild { "Could not get changes for java files" }
|
||||||
}
|
}
|
||||||
|
|
||||||
val dirtyFiles = getDirtyFiles(changedFiles)
|
markDirtyBy(affectedJavaSymbols)
|
||||||
val lookupSymbols = HashSet<LookupSymbol>()
|
markDirtyBy(classpathChanges.lookupSymbols)
|
||||||
lookupSymbols.addAll(affectedJavaSymbols)
|
markDirtyBy(classpathChanges.fqNames)
|
||||||
lookupSymbols.addAll(classpathChanges.lookupSymbols)
|
|
||||||
|
|
||||||
if (lookupSymbols.any()) {
|
|
||||||
val dirtyFilesFromLookups = mapLookupSymbolsToFiles(caches.lookupCache, lookupSymbols, reporter)
|
|
||||||
dirtyFiles.addAll(dirtyFilesFromLookups)
|
|
||||||
}
|
|
||||||
|
|
||||||
val dirtyClassesFqNames = classpathChanges.fqNames.flatMap { withSubtypes(it, listOf(caches.platformCache)) }
|
|
||||||
if (dirtyClassesFqNames.any()) {
|
|
||||||
val dirtyFilesFromFqNames = mapClassesFqNamesToFiles(listOf(caches.platformCache), dirtyClassesFqNames, reporter)
|
|
||||||
dirtyFiles.addAll(dirtyFilesFromFqNames)
|
|
||||||
}
|
|
||||||
|
|
||||||
return CompilationMode.Incremental(dirtyFiles)
|
return CompilationMode.Incremental(dirtyFiles)
|
||||||
}
|
}
|
||||||
@@ -263,6 +296,23 @@ class IncrementalJvmCompilerRunner(
|
|||||||
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
override fun additionalDirtyLookupSymbols(): Iterable<LookupSymbol> =
|
||||||
javaFilesProcessor.allChangedSymbols
|
javaFilesProcessor.allChangedSymbols
|
||||||
|
|
||||||
|
override fun processChangesAfterBuild(compilationMode: CompilationMode, currentBuildInfo: BuildInfo, dirtyData: DirtyData) {
|
||||||
|
super.processChangesAfterBuild(compilationMode, currentBuildInfo, dirtyData)
|
||||||
|
|
||||||
|
if (buildHistoryFile == null) return
|
||||||
|
|
||||||
|
val prevDiffs = BuildDiffsStorage.readFromFile(buildHistoryFile, reporter)?.buildDiffs ?: emptyList()
|
||||||
|
val newDiff = if (compilationMode is CompilationMode.Incremental) {
|
||||||
|
BuildDifference(currentBuildInfo.startTS, true, dirtyData)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val emptyDirtyData = DirtyData()
|
||||||
|
BuildDifference(currentBuildInfo.startTS, false, emptyDirtyData)
|
||||||
|
}
|
||||||
|
|
||||||
|
BuildDiffsStorage.writeToFile(buildHistoryFile, BuildDiffsStorage(prevDiffs + newDiff), reporter)
|
||||||
|
}
|
||||||
|
|
||||||
override fun makeServices(
|
override fun makeServices(
|
||||||
args: K2JVMCompilerArguments,
|
args: K2JVMCompilerArguments,
|
||||||
lookupTracker: LookupTracker,
|
lookupTracker: LookupTracker,
|
||||||
|
|||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2017 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.incremental
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Assert
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
class BuildDiffsStorageTest {
|
||||||
|
lateinit var storageFile: File
|
||||||
|
private val random = Random(System.currentTimeMillis())
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
storageFile = File.createTempFile("BuildDiffsStorageTest", "storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
storageFile.delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testToString() {
|
||||||
|
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||||
|
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||||
|
val diff = BuildDifference(100, true, DirtyData(lookupSymbols, fqNames))
|
||||||
|
val diffs = BuildDiffsStorage(listOf(diff))
|
||||||
|
Assert.assertEquals("BuildDiffsStorage(buildDiffs=[BuildDifference(ts=100, isIncremental=true, dirtyData=DirtyData(dirtyLookupSymbols=[LookupSymbol(name=foo, scope=bar)], dirtyClassesFqNames=[fizz.Buzz]))])",
|
||||||
|
diffs.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun writeReadSimple() {
|
||||||
|
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(diffs.toString(), diffsDeserialized.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun writeReadMany() {
|
||||||
|
val generated = Array(20) { getRandomDiff() }.toList()
|
||||||
|
val diffs = BuildDiffsStorage(generated)
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
val expected = generated.sortedBy { it.ts }.takeLast(BuildDiffsStorage.MAX_DIFFS_ENTRIES).toTypedArray()
|
||||||
|
Assert.assertArrayEquals(expected, diffsDeserialized?.buildDiffs?.toTypedArray())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun readFileNotExist() {
|
||||||
|
storageFile.delete()
|
||||||
|
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(null, diffsDeserialized)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun versionChanged() {
|
||||||
|
val diffs = BuildDiffsStorage(listOf(getRandomDiff()))
|
||||||
|
BuildDiffsStorage.writeToFile(storageFile, diffs, reporter = null)
|
||||||
|
|
||||||
|
val versionBackup = BuildDiffsStorage.CURRENT_VERSION
|
||||||
|
try {
|
||||||
|
BuildDiffsStorage.CURRENT_VERSION++
|
||||||
|
val diffsDeserialized = BuildDiffsStorage.readFromFile(storageFile, reporter = null)
|
||||||
|
Assert.assertEquals(null, diffsDeserialized)
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
BuildDiffsStorage.CURRENT_VERSION = versionBackup
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getRandomDiff(): BuildDifference {
|
||||||
|
val ts = random.nextLong()
|
||||||
|
val lookupSymbols = listOf(LookupSymbol("foo", "bar"))
|
||||||
|
val fqNames = listOf(FqName("fizz.Buzz"))
|
||||||
|
return BuildDifference(ts, true, DirtyData(lookupSymbols, fqNames))
|
||||||
|
}
|
||||||
|
}
|
||||||
+22
-4
@@ -20,10 +20,7 @@ import org.gradle.api.logging.LogLevel
|
|||||||
import com.intellij.openapi.util.io.FileUtil
|
import com.intellij.openapi.util.io.FileUtil
|
||||||
import org.jetbrains.kotlin.gradle.plugin.CopyClassesToJavaOutputStatus
|
import org.jetbrains.kotlin.gradle.plugin.CopyClassesToJavaOutputStatus
|
||||||
import org.jetbrains.kotlin.gradle.tasks.USING_INCREMENTAL_COMPILATION_MESSAGE
|
import org.jetbrains.kotlin.gradle.tasks.USING_INCREMENTAL_COMPILATION_MESSAGE
|
||||||
import org.jetbrains.kotlin.gradle.util.checkBytecodeContains
|
import org.jetbrains.kotlin.gradle.util.*
|
||||||
import org.jetbrains.kotlin.gradle.util.getFileByName
|
|
||||||
import org.jetbrains.kotlin.gradle.util.getFilesByNames
|
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.zip.ZipFile
|
import java.util.zip.ZipFile
|
||||||
@@ -490,6 +487,27 @@ class KotlinGradleIT: BaseGradleIT() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testIncrementalTestCompile() {
|
||||||
|
val project = Project("kotlinProject", GRADLE_VERSION)
|
||||||
|
val options = defaultBuildOptions().copy(incremental = true)
|
||||||
|
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
}
|
||||||
|
|
||||||
|
val joinerKt = project.projectDir.getFileByName("KotlinGreetingJoiner.kt")
|
||||||
|
joinerKt.modify {
|
||||||
|
it.replace("class KotlinGreetingJoiner", "internal class KotlinGreetingJoiner")
|
||||||
|
}
|
||||||
|
|
||||||
|
project.build("build", options = options) {
|
||||||
|
assertSuccessful()
|
||||||
|
val testJoinerKt = project.projectDir.getFileByName("TestKotlinGreetingJoiner.kt")
|
||||||
|
assertCompiledKotlinSources(project.relativize(joinerKt, testJoinerKt))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLanguageVersionApiVersionExplicit() {
|
fun testLanguageVersionApiVersionExplicit() {
|
||||||
val project = Project("kotlinProject", "3.3")
|
val project = Project("kotlinProject", "3.3")
|
||||||
|
|||||||
+6
-21
@@ -1,15 +1,13 @@
|
|||||||
package org.jetbrains.kotlin.gradle
|
package org.jetbrains.kotlin.gradle
|
||||||
|
|
||||||
import org.jetbrains.kotlin.gradle.util.allKotlinFiles
|
|
||||||
import org.jetbrains.kotlin.gradle.util.getFileByName
|
import org.jetbrains.kotlin.gradle.util.getFileByName
|
||||||
import org.jetbrains.kotlin.gradle.util.modify
|
import org.jetbrains.kotlin.gradle.util.modify
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class TestRootAffectedIT : BaseGradleIT() {
|
class TestRootAffectedIT : BaseGradleIT() {
|
||||||
@Test
|
@Test
|
||||||
fun testSourceRootClassIsModifiedIC() {
|
fun testSourceRootClassIsModifiedIC() {
|
||||||
val project = Project("kotlinProject", "2.10")
|
val project = Project("kotlinProject", "4.1")
|
||||||
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
@@ -26,8 +24,8 @@ class TestRootAffectedIT : BaseGradleIT() {
|
|||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
val expectedToCompile = project.relativize(listOf(kotlinGreetingJoinerFile) + project.allTestKotlinFiles())
|
val testKotlinGreetingJoinerFile = project.projectDir.getFileByName("TestKotlinGreetingJoiner.kt")
|
||||||
assertCompiledKotlinSources(expectedToCompile)
|
assertCompiledKotlinSources(project.relativize(kotlinGreetingJoinerFile, testKotlinGreetingJoinerFile))
|
||||||
}
|
}
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
@@ -38,7 +36,8 @@ class TestRootAffectedIT : BaseGradleIT() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testSourceRootClassIsRemovedIC() {
|
fun testSourceRootClassIsRemovedIC() {
|
||||||
val project = Project("kotlinProject", "2.10")
|
// todo: update Gradle after https://github.com/gradle/gradle/issues/3051 is resolved
|
||||||
|
val project = Project("kotlinProject", "3.0")
|
||||||
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
@@ -48,12 +47,6 @@ class TestRootAffectedIT : BaseGradleIT() {
|
|||||||
val dummyFile = project.projectDir.getFileByName("Dummy.kt")
|
val dummyFile = project.projectDir.getFileByName("Dummy.kt")
|
||||||
dummyFile.delete()
|
dummyFile.delete()
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
|
||||||
assertSuccessful()
|
|
||||||
val expectedToCompile = project.relativize(project.allTestKotlinFiles())
|
|
||||||
assertCompiledKotlinSources(expectedToCompile)
|
|
||||||
}
|
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
assertCompiledKotlinSources(emptyList())
|
assertCompiledKotlinSources(emptyList())
|
||||||
@@ -62,7 +55,7 @@ class TestRootAffectedIT : BaseGradleIT() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testTestRootClassIsRemovedIC() {
|
fun testTestRootClassIsRemovedIC() {
|
||||||
val project = Project("kotlinProject", "2.10")
|
val project = Project("kotlinProject", "4.1")
|
||||||
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
val buildOptions = defaultBuildOptions().copy(incremental = true)
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
project.build("build", options = buildOptions) {
|
||||||
@@ -76,13 +69,5 @@ class TestRootAffectedIT : BaseGradleIT() {
|
|||||||
assertSuccessful()
|
assertSuccessful()
|
||||||
assertCompiledKotlinSources(emptyList())
|
assertCompiledKotlinSources(emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
project.build("build", options = buildOptions) {
|
|
||||||
assertSuccessful()
|
|
||||||
assertCompiledKotlinSources(emptyList())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Project.allTestKotlinFiles(): Iterable<File> =
|
|
||||||
File(projectDir, "src/test").allKotlinFiles()
|
|
||||||
}
|
}
|
||||||
+3
-1
@@ -36,5 +36,7 @@ internal class GradleIncrementalCompilerEnvironment(
|
|||||||
compilerArgs: CommonCompilerArguments,
|
compilerArgs: CommonCompilerArguments,
|
||||||
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
val kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null,
|
||||||
val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null,
|
val artifactDifferenceRegistryProvider: ArtifactDifferenceRegistryProvider? = null,
|
||||||
val artifactFile: File? = null
|
val artifactFile: File? = null,
|
||||||
|
val buildHistoryFile: File? = null,
|
||||||
|
val friendBuildHistoryFile: File? = null
|
||||||
) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs)
|
) : GradleCompilerEnvironment(compilerClasspath, messageCollector, outputItemsCollector, compilerArgs)
|
||||||
+3
-1
@@ -255,7 +255,9 @@ internal class GradleCompilerRunner(private val project: Project) : KotlinCompil
|
|||||||
reportSeverity = reportSeverity(verbose),
|
reportSeverity = reportSeverity(verbose),
|
||||||
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
|
requestedCompilationResults = arrayOf(CompilationResultCategory.IC_COMPILE_ITERATION.code),
|
||||||
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
|
compilerMode = CompilerMode.INCREMENTAL_COMPILER,
|
||||||
targetPlatform = targetPlatform
|
targetPlatform = targetPlatform,
|
||||||
|
resultDifferenceFile = environment.buildHistoryFile,
|
||||||
|
friendDifferenceFile = environment.friendBuildHistoryFile
|
||||||
)
|
)
|
||||||
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(project, environment)
|
val servicesFacade = GradleIncrementalCompilerServicesFacadeImpl(project, environment)
|
||||||
val argsArray = ArgumentUtils.convertArgumentsToStringList(environment.compilerArgs).toTypedArray()
|
val argsArray = ArgumentUtils.convertArgumentsToStringList(environment.compilerArgs).toTypedArray()
|
||||||
|
|||||||
+5
-1
@@ -243,6 +243,7 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
|||||||
internal open val sourceRootsContainer = FilteringSourceRootsContainer()
|
internal open val sourceRootsContainer = FilteringSourceRootsContainer()
|
||||||
|
|
||||||
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null
|
private var kaptAnnotationsFileUpdater: AnnotationFileUpdater? = null
|
||||||
|
val buildHistoryFile: File = File(taskBuildDirectory, "build-history.bin")
|
||||||
|
|
||||||
val kaptOptions = KaptOptions()
|
val kaptOptions = KaptOptions()
|
||||||
|
|
||||||
@@ -316,10 +317,13 @@ open class KotlinCompile : AbstractKotlinCompile<K2JVMCompilerArguments>(), Kotl
|
|||||||
!incremental -> GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
|
!incremental -> GradleCompilerEnvironment(computedCompilerClasspath, messageCollector, outputItemCollector, args)
|
||||||
else -> {
|
else -> {
|
||||||
logger.warn(USING_INCREMENTAL_COMPILATION_MESSAGE)
|
logger.warn(USING_INCREMENTAL_COMPILATION_MESSAGE)
|
||||||
|
val friendTask = friendTaskName?.let { project.tasks.findByName(it) as? KotlinCompile }
|
||||||
GradleIncrementalCompilerEnvironment(computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory,
|
GradleIncrementalCompilerEnvironment(computedCompilerClasspath, changedFiles, reporter, taskBuildDirectory,
|
||||||
messageCollector, outputItemCollector, args, kaptAnnotationsFileUpdater,
|
messageCollector, outputItemCollector, args, kaptAnnotationsFileUpdater,
|
||||||
artifactDifferenceRegistryProvider,
|
artifactDifferenceRegistryProvider,
|
||||||
artifactFile)
|
artifactFile = artifactFile,
|
||||||
|
buildHistoryFile = buildHistoryFile,
|
||||||
|
friendBuildHistoryFile = friendTask?.buildHistoryFile)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user