[IC] Relocatable IC caches for projects with custom buildDir

IC caches often contain file paths. To make them relocatable, we need
to convert these file paths into relative paths, relative to a base
directory.
  - If the file paths are source files, we can use the root project
    directory as base.
  - If the file paths are class files, we should use the classes
    directory as base (before this commit, we used the root project
    directory in both cases, that's why we hit KT-58547).

The key changes in this commit include:
  - RelocatableFileToPathConverter: converts paths to relative paths
  - IncrementalCompilationContext: contains 2 different path converters,
    one for source files and one for class files
  - SourceToOutputFilesMap: maps source files to class files using the
    above path converters
  - IncrementalCompilerRunner: creates the path converters based on file
    locations

Test: RelocatableFileToPathConverterTest unit test
      SourceToOutputFilesMapTest unit test
      BuildCacheRelocationIT.testCustomBuildDirectory integration test
^KT-58547 Fixed
This commit is contained in:
Hung Nguyen
2023-08-10 11:05:55 +01:00
committed by Space Team
parent 432c9fe592
commit 894ba9ab80
30 changed files with 329 additions and 293 deletions
+1
View File
@@ -4,6 +4,7 @@
<w>externalizers</w>
<w>granularities</w>
<w>multifile</w>
<w>relocatability</w>
<w>shrinker</w>
<w>snapshotter</w>
<w>snapshotter's</w>
@@ -7,14 +7,13 @@ package org.jetbrains.kotlin.incremental
import org.jetbrains.kotlin.build.report.DoNothingICReporter
import org.jetbrains.kotlin.build.report.ICReporter
import org.jetbrains.kotlin.incremental.storage.FileToAbsolutePathConverter
import org.jetbrains.kotlin.incremental.storage.FileToPathConverter
import org.jetbrains.kotlin.incremental.storage.IncrementalFileToPathConverter
import java.io.File
private fun createDefaultPathConverter(rootProjectDir: File?) = IncrementalFileToPathConverter(rootProjectDir)
class IncrementalCompilationContext(
val pathConverter: FileToPathConverter,
// The root directories of source files and class files are different, so we may need different `FileToPathConverter`s
val pathConverterForSourceFiles: FileToPathConverter = FileToAbsolutePathConverter,
val pathConverterForOutputFiles: FileToPathConverter = FileToAbsolutePathConverter,
val storeFullFqNamesInLookupCache: Boolean = false,
val transaction: CompilationTransaction = NonRecoverableCompilationTransaction(),
val reporter: ICReporter = DoNothingICReporter,
@@ -29,19 +28,7 @@ class IncrementalCompilationContext(
*/
val keepIncrementalCompilationCachesInMemory: Boolean = false,
) {
constructor(
rootProjectDir: File?,
storeFullFqNamesInLookupCache: Boolean = false,
transaction: CompilationTransaction = NonRecoverableCompilationTransaction(),
reporter: ICReporter = DoNothingICReporter,
trackChangesInLookupCache: Boolean = false,
keepIncrementalCompilationCachesInMemory: Boolean = false,
) : this(
createDefaultPathConverter(rootProjectDir),
storeFullFqNamesInLookupCache,
transaction,
reporter,
trackChangesInLookupCache,
keepIncrementalCompilationCachesInMemory
)
// FIXME: Remove `pathConverter` and require its users to decide whether to use `pathConverterForSourceFiles` or
// `pathConverterForClassFiles`
val pathConverter = pathConverterForSourceFiles
}
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.incremental.storage
import java.io.File
/** Converts a [File] to a path of type [String] to store in IC caches, and vice versa. */
interface FileToPathConverter {
fun toPath(file: File): String
fun toFile(path: String): File
@@ -1,34 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental.storage
import java.io.File
open class IncrementalFileToPathConverter(val rootProjectDir: File?) : FileToPathConverter {
//project root dir
private val projectDirPath = rootProjectDir?.normalize()?.absolutePath
override fun toPath(file: File): String {
val path = file.normalize().absolutePath
return when {
projectDirPath == null || !path.startsWith(projectDirPath) -> path
else -> PROJECT_DIR_PLACEHOLDER + path.substring(projectDirPath.length)
}
}
override fun toFile(path: String): File =
when {
rootProjectDir != null && path.startsWith(PROJECT_DIR_PLACEHOLDER) -> rootProjectDir.resolve(path.substring(PATH_PREFIX.length))
else -> File(path)
}
private companion object {
private const val PROJECT_DIR_PLACEHOLDER = "${'$'}PROJECT_DIR$"
//use only for prefix length because it OS dependent
private const val PATH_PREFIX = "$PROJECT_DIR_PLACEHOLDER/"
}
}
@@ -16,7 +16,9 @@
package org.jetbrains.kotlin.incremental.storage
interface LazyStorage<K, V> {
import java.io.Closeable
interface LazyStorage<K, V> : Closeable {
val keys: Collection<K>
operator fun contains(key: K): Boolean
operator fun get(key: K): V?
@@ -24,7 +26,7 @@ interface LazyStorage<K, V> {
fun remove(key: K)
fun clean()
fun flush(memoryCachesOnly: Boolean)
fun close()
override fun close()
}
interface AppendableLazyStorage<K, V> : LazyStorage<K, V> {
@@ -0,0 +1,54 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental.storage
import java.io.File
/**
* [FileToPathConverter] which converts a [File] to a Unix-style relative path, which is relative to a base directory ([baseDir]), and vice
* versa.
*
* This is to support build cache relocatability
* (https://docs.gradle.org/current/userguide/build_cache_concepts.html#relocatability).
*/
class RelocatableFileToPathConverter(private val baseDir: File) : FileToPathConverter {
private val unixStyleBaseDirPathPrefix = "${baseDir.invariantSeparatorsPath}/"
override fun toPath(file: File): String {
check(file.invariantSeparatorsPath.startsWith(unixStyleBaseDirPathPrefix)) {
"The given file '${file.path}' is located outside the base directory '${baseDir.path}'"
}
return file.relativeTo(baseDir).invariantSeparatorsPath
}
override fun toFile(path: String): File {
// Note: The given path is Unix-style but baseDir could be Windows-style; call normalize() so that the style of the returned file's
// path is consistent with baseDir.
return baseDir.resolve(path).normalize()
}
}
/**
* File locations to support build cache relocatability (see [RelocatableFileToPathConverter]).
*
* These are the root directories of
* - Source files
* - Output files, which include .class files and possibly additional output files such as `.java` stub files for KaptGenerateStubs tasks.
*
* Ideally, they should be the most specific root directories (e.g., `/path/to/MyApplication/app/src/main/kotlin` for source files and
* `/path/to/MyApplication/app/build/tmp/kotlin-classes/debug` for output .class files). However, for simplicity we are using the root
* project directory ([rootProjectDir]) for source files and ([buildDir]) for output files.
*/
class FileLocations(
val rootProjectDir: File,
val buildDir: File,
) {
fun getRelocatablePathConverterForSourceFiles() = RelocatableFileToPathConverter(rootProjectDir)
fun getRelocatablePathConverterForOutputFiles() = RelocatableFileToPathConverter(buildDir)
}
@@ -125,7 +125,7 @@ class InMemoryStorageWrapperTest {
val storageFile = storageRoot.resolve("lookup").toFile()
val fileToPathConverter = RelativeFileToPathConverter(storageFile)
val icContext = IncrementalCompilationContext(
pathConverter = fileToPathConverter,
pathConverterForSourceFiles = fileToPathConverter,
keepIncrementalCompilationCachesInMemory = useInMemoryWrapper
)
icContext.transaction.runWithin { transaction ->
@@ -1,62 +0,0 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental.storage
import org.jetbrains.kotlin.TestWithWorkingDir
import org.junit.Test
import java.io.File
internal class IncrementalFileToPathConverterTest : TestWithWorkingDir() {
val separator: String = File.separator
@Test
fun testPathTransform() {
val relativeFilePath = "testFile.txt"
val transformedPath = testPathTransformation(workingDir.resolve("testDir"), relativeFilePath)
assertEquals("${'$'}PROJECT_DIR${'$'}$separator$relativeFilePath", transformedPath)
}
@Test
fun testComplicatedProjectRootPath() {
val relativeFilePath = "testFile.txt"
val transformedPath = testPathTransformation(workingDir.resolve("first$separator..${separator}testDir"), relativeFilePath)
assertEquals("${'$'}PROJECT_DIR${'$'}$separator$relativeFilePath", transformedPath)
}
@Test
fun testInccorectProjectRootPath() {
val relativeFilePath = "testFile.txt"
val transformedPath = testPathTransformation(workingDir.resolve("testDir$separator"), relativeFilePath)
assertEquals("${'$'}PROJECT_DIR${'$'}$separator$relativeFilePath", transformedPath)
}
@Test
fun testFileOutOfProject() {
val relativeFilePath = "..${separator}testFile.txt"
val transformedPath = testPathTransformation(workingDir.resolve("testDir"), relativeFilePath)
assertEquals("${workingDir.absolutePath}${separator}testFile.txt", transformedPath)
}
@Test
fun testFileWithExtraSlash() {
val relativeFilePath = "testFile.txt$separator"
val transformedPath = testPathTransformation(workingDir.resolve("testDir"), relativeFilePath)
assertEquals("${'$'}PROJECT_DIR${'$'}${separator}testFile.txt", transformedPath)
}
private fun testPathTransformation(projectRoot: File, relativeFilePath: String): String {
val pathConverter = IncrementalFileToPathConverter(projectRoot)
val testFile = projectRoot.resolve(relativeFilePath)
val transformedPath = pathConverter.toPath(testFile)
assertEquals(testFile.normalize().absolutePath, pathConverter.toFile(transformedPath).normalize().absolutePath)
return transformedPath
}
}
@@ -7,8 +7,6 @@ package org.jetbrains.kotlin.incremental.storage
import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.TestWithWorkingDir
import org.jetbrains.kotlin.cli.common.CompilerSystemProperties
import org.jetbrains.kotlin.cli.common.toBooleanLenient
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
import org.jetbrains.kotlin.incremental.LookupStorage
import org.jetbrains.kotlin.incremental.LookupSymbol
@@ -52,7 +50,7 @@ class RelocatableCachesTest : TestWithWorkingDir() {
val storageRoot = projectRoot.storageRoot
val fileToPathConverter = RelativeFileToPathConverter(projectRoot)
val icContext = IncrementalCompilationContext(
pathConverter = fileToPathConverter,
pathConverterForSourceFiles = fileToPathConverter,
storeFullFqNamesInLookupCache = storeFullFqNames,
)
val lookupStorage = LookupStorage(storageRoot, icContext)
@@ -0,0 +1,38 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.incremental.storage
import org.jetbrains.kotlin.TestWithWorkingDir
import org.junit.Test
import kotlin.test.assertFailsWith
class RelocatableFileToPathConverterTest : TestWithWorkingDir() {
private lateinit var pathConverter: RelocatableFileToPathConverter
override fun setUp() {
super.setUp()
pathConverter = RelocatableFileToPathConverter(workingDir)
}
@Test
fun testToPath() {
assertEquals("com/example/Foo.kt", pathConverter.toPath(workingDir.resolve("com/example/Foo.kt")))
}
@Test
fun testToPathFails() {
assertFailsWith(IllegalStateException::class) {
pathConverter.toPath(workingDir.resolve("../outsideWorkingDir/com/example/Foo.kt").normalize())
}
}
@Test
fun testToFile() {
assertEquals(workingDir.resolve("com/example/Foo.kt"), pathConverter.toFile("com/example/Foo.kt"))
}
}
@@ -113,10 +113,11 @@ public abstract interface class org/jetbrains/kotlin/buildtools/api/jvm/Classpat
public abstract fun forceNonIncrementalMode (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun getAssuredNoClasspathSnapshotsChanges ()Z
public abstract fun keepIncrementalCompilationCachesInMemory (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun setBuildDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun setRootProjectDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun useOutputDirs (Ljava/util/Collection;)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun usePreciseCompilationResultsBackup (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun usePreciseJavaTracking (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
public abstract fun useProjectDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration;
}
public abstract interface class org/jetbrains/kotlin/buildtools/api/jvm/InaccessibleClassSnapshot : org/jetbrains/kotlin/buildtools/api/jvm/ClassSnapshot {
@@ -128,17 +129,19 @@ public abstract interface class org/jetbrains/kotlin/buildtools/api/jvm/Incremen
public abstract interface class org/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration {
public abstract fun forceNonIncrementalMode (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public static synthetic fun forceNonIncrementalMode$default (Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;ZILjava/lang/Object;)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun getBuildDir ()Ljava/io/File;
public abstract fun getForcedNonIncrementalMode ()Z
public abstract fun getIncrementalCompilationCachesKeptInMemory ()Z
public abstract fun getOutputDirs ()Ljava/util/Set;
public abstract fun getPreciseCompilationResultsBackupEnabled ()Z
public abstract fun getPreciseJavaTrackingEnabled ()Z
public abstract fun getProjectDir ()Ljava/io/File;
public abstract fun getRootProjectDir ()Ljava/io/File;
public abstract fun keepIncrementalCompilationCachesInMemory (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun setBuildDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun setRootProjectDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun useOutputDirs (Ljava/util/Collection;)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun usePreciseCompilationResultsBackup (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun usePreciseJavaTracking (Z)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
public abstract fun useProjectDir (Ljava/io/File;)Lorg/jetbrains/kotlin/buildtools/api/jvm/IncrementalJvmCompilationConfiguration;
}
public abstract interface class org/jetbrains/kotlin/buildtools/api/jvm/JvmCompilationConfiguration {
@@ -78,20 +78,36 @@ public interface JvmCompilationConfiguration {
*/
@ExperimentalBuildToolsApi
public interface IncrementalJvmCompilationConfiguration<P : IncrementalCompilationApproachParameters> {
/**
* A directory used as a base path for computing relative paths in the incremental compilation caches.
*
* If is not specified, incremental compilation caches will be non-relocatable
*
* Managed by [useProjectDir]
* Default values is `null`
*/
public val projectDir: File?
/**
* @see [IncrementalJvmCompilationConfiguration.projectDir]
* The root project directory, used for computing relative paths in the incremental compilation caches.
*
* If it is not specified, incremental compilation caches will be non-relocatable.
*
* Managed by [setRootProjectDir]
* Default value is `null`
*/
public fun useProjectDir(projectDir: File): IncrementalJvmCompilationConfiguration<P>
public val rootProjectDir: File?
/**
* @see [IncrementalJvmCompilationConfiguration.rootProjectDir]
*/
public fun setRootProjectDir(rootProjectDir: File): IncrementalJvmCompilationConfiguration<P>
/**
* The build directory, used for computing relative paths in the incremental compilation caches.
*
* If it is not specified, incremental compilation caches will be non-relocatable.
*
* Managed by [setBuildDir]
* Default value is `null`
*/
public val buildDir: File?
/**
* @see [IncrementalJvmCompilationConfiguration.buildDir]
*/
public fun setBuildDir(buildDir: File): IncrementalJvmCompilationConfiguration<P>
/**
* An indicator whether incremental compilation will analyze Java files precisely for better changes detection
@@ -186,7 +202,9 @@ public interface ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration :
*/
public fun assureNoClasspathSnapshotsChanges(value: Boolean = true): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration
override fun useProjectDir(projectDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration
override fun setRootProjectDir(rootProjectDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration
override fun setBuildDir(buildDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration
override fun usePreciseJavaTracking(value: Boolean): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration
@@ -50,12 +50,18 @@ internal abstract class JvmIncrementalCompilationConfigurationImpl<P : Increment
override var preciseJavaTrackingEnabled: Boolean = true,
override var preciseCompilationResultsBackupEnabled: Boolean = false,
override var incrementalCompilationCachesKeptInMemory: Boolean = false,
override var projectDir: File? = null,
override var rootProjectDir: File? = null,
override var buildDir: File? = null,
override var forcedNonIncrementalMode: Boolean = false,
override var outputDirs: Set<File> = emptySet(),
) : IncrementalJvmCompilationConfiguration<P> {
override fun useProjectDir(projectDir: File): IncrementalJvmCompilationConfiguration<P> {
this.projectDir = projectDir
override fun setRootProjectDir(rootProjectDir: File): IncrementalJvmCompilationConfiguration<P> {
this.rootProjectDir = rootProjectDir
return this
}
override fun setBuildDir(buildDir: File): IncrementalJvmCompilationConfiguration<P> {
this.buildDir = buildDir
return this
}
@@ -90,8 +96,13 @@ internal class ClasspathSnapshotBasedIncrementalJvmCompilationConfigurationImpl(
) :
JvmIncrementalCompilationConfigurationImpl<ClasspathSnapshotBasedIncrementalCompilationApproachParameters>(),
ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration {
override fun useProjectDir(projectDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration {
super.useProjectDir(projectDir)
override fun setRootProjectDir(rootProjectDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration {
super.setRootProjectDir(rootProjectDir)
return this
}
override fun setBuildDir(buildDir: File): ClasspathSnapshotBasedIncrementalJvmCompilationConfiguration {
super.setBuildDir(buildDir)
return this
}
@@ -56,7 +56,8 @@ internal val JvmCompilationConfigurationImpl.asDaemonCompilationOptions: Compila
outputFiles = options.outputDirs,
multiModuleICSettings = null, // required only for the build history approach
modulesInfo = null, // required only for the build history approach
rootProjectDir = options.projectDir,
rootProjectDir = options.rootProjectDir,
buildDir = options.buildDir,
kotlinScriptExtensions = ktsExtensionsAsArray,
withAbiSnapshot = false,
preciseCompilationResultsBackup = options.preciseCompilationResultsBackupEnabled,
@@ -70,10 +70,11 @@ class IncrementalCompilationOptions(
val outputFiles: Collection<File>? = null,
val multiModuleICSettings: MultiModuleICSettings? = null,
val modulesInfo: IncrementalModuleInfo? = null,
/**
* Root project directory, used to resolve relative paths
*/
// rootProjectDir and buildDir are used to resolve relative paths
val rootProjectDir: File?,
val buildDir: File?,
kotlinScriptExtensions: Array<String>? = null,
val withAbiSnapshot: Boolean = false,
val preciseCompilationResultsBackup: Boolean = false,
@@ -60,6 +60,7 @@ import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryAndroid
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryJs
import org.jetbrains.kotlin.incremental.multiproject.ModulesApiHistoryJvm
import org.jetbrains.kotlin.incremental.parsing.classesFqNames
import org.jetbrains.kotlin.incremental.storage.FileLocations
import org.jetbrains.kotlin.load.kotlin.incremental.components.IncrementalCompilationComponents
import org.jetbrains.kotlin.progress.CompilationCanceledStatus
import java.io.File
@@ -612,20 +613,21 @@ abstract class CompileServiceImplBase(
val workingDir = incrementalCompilationOptions.workingDir
val projectRoot = incrementalCompilationOptions.rootProjectDir
val rootProjectDir = incrementalCompilationOptions.rootProjectDir
val buildDir = incrementalCompilationOptions.buildDir
val modulesApiHistory = incrementalCompilationOptions.multiModuleICSettings?.run {
reporter.info { "Use module detection: $useModuleDetection" }
val modulesInfo = incrementalCompilationOptions.modulesInfo
?: error("The build is configured to use the history-file based IC approach, but doesn't provide the modulesInfo")
check(projectRoot != null) {
check(rootProjectDir != null) {
"rootProjectDir is expected to be non null when the history-file based IC approach is used"
}
if (!useModuleDetection) {
ModulesApiHistoryJvm(projectRoot, modulesInfo)
ModulesApiHistoryJvm(rootProjectDir, modulesInfo)
} else {
ModulesApiHistoryAndroid(projectRoot, modulesInfo)
ModulesApiHistoryAndroid(rootProjectDir, modulesInfo)
}
} ?: EmptyModulesApiHistory
@@ -648,7 +650,12 @@ abstract class CompileServiceImplBase(
keepIncrementalCompilationCachesInMemory = incrementalCompilationOptions.keepIncrementalCompilationCachesInMemory,
)
return try {
compiler.compile(allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles, projectRoot)
compiler.compile(
allKotlinFiles, k2jvmArgs, compilerMessageCollector, changedFiles,
fileLocations = if (rootProjectDir != null && buildDir != null) {
FileLocations(rootProjectDir, buildDir)
} else null
)
} finally {
reporter.endMeasureGc()
reporter.flush()
@@ -21,8 +21,11 @@ import org.jetbrains.kotlin.build.GeneratedFile
import org.jetbrains.kotlin.build.report.BuildReporter
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.info
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute
import org.jetbrains.kotlin.build.report.metrics.BuildAttribute.*
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
import org.jetbrains.kotlin.build.report.metrics.measure
import org.jetbrains.kotlin.build.report.warn
import org.jetbrains.kotlin.cli.common.*
import org.jetbrains.kotlin.cli.common.arguments.CommonCompilerArguments
@@ -35,6 +38,8 @@ import org.jetbrains.kotlin.config.Services
import org.jetbrains.kotlin.incremental.components.ExpectActualTracker
import org.jetbrains.kotlin.incremental.components.LookupTracker
import org.jetbrains.kotlin.incremental.parsing.classesFqNames
import org.jetbrains.kotlin.incremental.storage.FileLocations
import org.jetbrains.kotlin.incremental.storage.FileToAbsolutePathConverter
import org.jetbrains.kotlin.incremental.util.BufferingMessageCollector
import org.jetbrains.kotlin.incremental.util.ExceptionLocation
import org.jetbrains.kotlin.incremental.util.reportException
@@ -45,7 +50,6 @@ import org.jetbrains.kotlin.util.removeSuffixIfPresent
import org.jetbrains.kotlin.utils.toMetadataVersion
import java.io.File
import java.nio.file.Files
import java.util.*
abstract class IncrementalCompilerRunner<
Args : CommonCompilerArguments,
@@ -83,11 +87,12 @@ abstract class IncrementalCompilerRunner<
* Creates an instance of [IncrementalCompilationContext] that holds common incremental compilation context mostly required for [CacheManager]
*/
private fun createIncrementalCompilationContext(
projectDir: File?,
transaction: CompilationTransaction
fileLocations: FileLocations?,
transaction: CompilationTransaction,
) = IncrementalCompilationContext(
pathConverterForSourceFiles = fileLocations?.let { it.getRelocatablePathConverterForSourceFiles() } ?: FileToAbsolutePathConverter,
pathConverterForOutputFiles = fileLocations?.let { it.getRelocatablePathConverterForOutputFiles() } ?: FileToAbsolutePathConverter,
transaction = transaction,
rootProjectDir = projectDir,
reporter = reporter,
trackChangesInLookupCache = shouldTrackChangesInLookupCache,
storeFullFqNamesInLookupCache = shouldStoreFullFqNamesInLookupCache,
@@ -105,12 +110,10 @@ abstract class IncrementalCompilerRunner<
allSourceFiles: List<File>,
args: Args,
messageCollector: MessageCollector,
// when `changedFiles` is not null, changes are provided by external system (e.g. Gradle)
// otherwise we track source files changes ourselves.
changedFiles: ChangedFiles?,
projectDir: File? = null
changedFiles: ChangedFiles?, // When not-null, changes are provided by the build system; otherwise, the IC will need to track them
fileLocations: FileLocations? = null, // Must be not-null if the build system needs to support build cache relocatability
): ExitCode = reporter.measure(GradleBuildTime.INCREMENTAL_COMPILATION_DAEMON) {
return when (val result = tryCompileIncrementally(allSourceFiles, changedFiles, args, projectDir, messageCollector)) {
return when (val result = tryCompileIncrementally(allSourceFiles, changedFiles, args, fileLocations, messageCollector)) {
is ICResult.Completed -> {
reporter.debug { "Incremental compilation completed" }
result.exitCode
@@ -120,7 +123,7 @@ abstract class IncrementalCompilerRunner<
reporter.addAttribute(result.reason)
compileNonIncrementally(
result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector
result.reason, allSourceFiles, args, fileLocations, trackChangedFiles = changedFiles == null, messageCollector
)
}
is ICResult.Failed -> {
@@ -139,7 +142,7 @@ abstract class IncrementalCompilerRunner<
reporter.addAttribute(result.reason)
compileNonIncrementally(
result.reason, allSourceFiles, args, projectDir, trackChangedFiles = changedFiles == null, messageCollector
result.reason, allSourceFiles, args, fileLocations, trackChangedFiles = changedFiles == null, messageCollector
)
}
}
@@ -173,8 +176,8 @@ abstract class IncrementalCompilerRunner<
allSourceFiles: List<File>,
changedFiles: ChangedFiles?,
args: Args,
projectDir: File?,
messageCollector: MessageCollector
fileLocations: FileLocations?,
messageCollector: MessageCollector,
): ICResult {
if (changedFiles is ChangedFiles.Unknown) {
return ICResult.RequiresRebuild(UNKNOWN_CHANGES_IN_GRADLE_INPUTS)
@@ -182,7 +185,7 @@ abstract class IncrementalCompilerRunner<
changedFiles as ChangedFiles.Known?
return createTransaction().runWithin(::incrementalCompilationExceptionTransformer) { transaction ->
val icContext = createIncrementalCompilationContext(projectDir, transaction)
val icContext = createIncrementalCompilationContext(fileLocations, transaction)
val caches = createCacheManager(icContext, args).also {
// this way we make the transaction to be responsible for closing the caches manager
transaction.cachesManager = it
@@ -253,7 +256,7 @@ abstract class IncrementalCompilerRunner<
rebuildReason: BuildAttribute,
allSourceFiles: List<File>,
args: Args,
projectDir: File?,
fileLocations: FileLocations?,
trackChangedFiles: Boolean, // Whether we need to track changes to the source files or the build system already handles it
messageCollector: MessageCollector,
): ExitCode {
@@ -266,7 +269,7 @@ abstract class IncrementalCompilerRunner<
reporter.debug { "Cleaning ${outputDirsToClean.size} output directories" }
cleanOrCreateDirectories(outputDirsToClean)
}
val icContext = createIncrementalCompilationContext(projectDir, NonRecoverableCompilationTransaction())
val icContext = createIncrementalCompilationContext(fileLocations, NonRecoverableCompilationTransaction())
return createCacheManager(icContext, args).use { caches ->
if (trackChangedFiles) {
caches.inputsCache.sourceSnapshotMap.compareAndUpdate(allSourceFiles)
@@ -38,7 +38,7 @@ class InputsCache(
fun removeOutputForSourceFiles(sources: Iterable<File>) {
for (sourceFile in sources) {
sourceToOutputMap.remove(sourceFile).forEach {
sourceToOutputMap.getAndRemove(sourceFile)?.forEach {
icContext.reporter.debug { "Deleting $it on clearing cache for $sourceFile" }
icContext.transaction.deleteFile(it.toPath())
}
@@ -46,7 +46,7 @@ class InputsCache(
}
fun getOutputForSourceFiles(sources: Iterable<File>): List<File> = sources.flatMap {
sourceToOutputMap[it]
sourceToOutputMap[it].orEmpty()
}
// generatedFiles can contain multiple entries with the same source file
@@ -8,12 +8,14 @@ package org.jetbrains.kotlin.incremental.classpathDiff
import com.intellij.openapi.util.io.FileUtil
import org.jetbrains.kotlin.build.report.DoNothingICReporter
import org.jetbrains.kotlin.build.report.debug
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.metrics.BuildMetricsReporter
import org.jetbrains.kotlin.build.report.metrics.GradleBuildPerformanceMetric
import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime
import org.jetbrains.kotlin.build.report.metrics.measure
import org.jetbrains.kotlin.incremental.*
import org.jetbrains.kotlin.incremental.classpathDiff.BreadthFirstSearch.findReachableNodes
import org.jetbrains.kotlin.incremental.classpathDiff.ClasspathSnapshotShrinker.shrinkClasspath
import org.jetbrains.kotlin.incremental.classpathDiff.ImpactedSymbolsComputer.computeImpactedSymbols
import org.jetbrains.kotlin.incremental.storage.FileToAbsolutePathConverter
import org.jetbrains.kotlin.incremental.storage.ListExternalizer
import org.jetbrains.kotlin.incremental.storage.loadFromFile
import org.jetbrains.kotlin.name.ClassId
@@ -202,7 +204,7 @@ object ClasspathChangesComputer {
): ProgramSymbolSet {
val workingDir =
FileUtil.createTempDirectory(this::class.java.simpleName, "_WorkingDir_${UUID.randomUUID()}", /* deleteOnExit */ true)
val icContext = IncrementalCompilationContext(pathConverter = FileToAbsolutePathConverter)
val icContext = IncrementalCompilationContext()
val incrementalJvmCache = IncrementalJvmCache(workingDir, icContext, null)
// Step 1:
@@ -14,15 +14,15 @@ class SourceToOutputFilesMap(
icContext: IncrementalCompilationContext,
) : BasicStringMap<Collection<String>>(storageFile, PathStringDescriptor, StringCollectionExternalizer, icContext) {
operator fun set(sourceFile: File, outputFiles: Collection<File>) {
storage[pathConverter.toPath(sourceFile)] = outputFiles.map(pathConverter::toPath)
storage[icContext.pathConverterForSourceFiles.toPath(sourceFile)] = outputFiles.map(icContext.pathConverterForOutputFiles::toPath)
}
operator fun get(sourceFile: File): Collection<File> =
storage[pathConverter.toPath(sourceFile)].orEmpty().map(pathConverter::toFile)
operator fun get(sourceFile: File): Collection<File>? =
storage[icContext.pathConverterForSourceFiles.toPath(sourceFile)]?.map(icContext.pathConverterForOutputFiles::toFile)
override fun dumpValue(value: Collection<String>) =
value.dumpCollection()
fun remove(file: File): Collection<File> =
get(file).also { storage.remove(pathConverter.toPath(file)) }
fun getAndRemove(file: File): Collection<File>? =
get(file).also { storage.remove(icContext.pathConverterForSourceFiles.toPath(file)) }
}
@@ -28,7 +28,7 @@ import java.util.*
class BuildDiffsStorageTest {
lateinit var storageFile: File
private val random = Random(System.currentTimeMillis())
private val icContext = IncrementalCompilationContext(null)
private val icContext = IncrementalCompilationContext()
@Before
fun setUp() {
@@ -7,8 +7,7 @@ package org.jetbrains.kotlin.incremental.snapshots
import org.jetbrains.kotlin.TestWithWorkingDir
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
import org.jetbrains.kotlin.incremental.storage.FileToPathConverter
import org.jetbrains.kotlin.incremental.storage.IncrementalFileToPathConverter
import org.jetbrains.kotlin.incremental.storage.RelocatableFileToPathConverter
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Before
@@ -24,9 +23,9 @@ class FileSnapshotMapTest : TestWithWorkingDir() {
super.setUp()
val caches = File(workingDir, "caches").apply { mkdirs() }
val snapshotMapFile = File(caches, "snapshots.tab")
val pathConverter = IncrementalFileToPathConverter((workingDir.canonicalFile))
val pathConverter = RelocatableFileToPathConverter((workingDir.canonicalFile))
val icContext = IncrementalCompilationContext(
pathConverter = pathConverter
pathConverterForSourceFiles = pathConverter
)
snapshotMap = FileSnapshotMap(snapshotMapFile, icContext)
}
@@ -5,131 +5,105 @@
package org.jetbrains.kotlin.incremental.storage
import org.jetbrains.kotlin.TestWithWorkingDir
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNull
import org.jetbrains.kotlin.incremental.IncrementalCompilationContext
import org.junit.After
import org.junit.Assert.assertArrayEquals
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import java.io.File
import kotlin.properties.Delegates
import kotlin.test.assertFailsWith
class SourceToOutputFilesMapTest : TestWithWorkingDir() {
private var stofMap: SourceToOutputFilesMap by Delegates.notNull()
private var pathConverter: FileToPathConverter by Delegates.notNull()
class SourceToOutputFilesMapTest {
@get:Rule
val tmpDir = TemporaryFolder()
private lateinit var srcDir: File
private lateinit var classesDir: File
private lateinit var stofMap: SourceToOutputFilesMap
private lateinit var fooDotKt: File
private lateinit var fooDotClass: File
@Before
override fun setUp() {
super.setUp()
val caches = File(workingDir, "caches").apply { mkdirs() }
val stofMapFile = File(caches, "stof.tab")
pathConverter = IncrementalFileToPathConverter((workingDir.canonicalFile))
val icContext = IncrementalCompilationContext(
pathConverter = pathConverter
fun setUp() {
val workingDir = tmpDir.root
srcDir = workingDir.resolve("src")
classesDir = workingDir.resolve("classes")
stofMap = SourceToOutputFilesMap(
storageFile = workingDir.resolve("stof.tab"),
icContext = IncrementalCompilationContext(
pathConverterForSourceFiles = RelocatableFileToPathConverter(srcDir),
pathConverterForOutputFiles = RelocatableFileToPathConverter(classesDir),
)
)
stofMap = SourceToOutputFilesMap(stofMapFile, icContext)
fooDotKt = srcDir.resolve("Foo.kt")
fooDotClass = classesDir.resolve("Foo.class")
}
@After
override fun tearDown() {
fun tearDown() {
stofMap.flush(false)
stofMap.closeForTest()
super.tearDown()
}
@Test
fun testEmptyGetReturnsEmpty() {
assertTrue(stofMap.get(File("")).isEmpty())
fun testNoSetGetReturnsNull() {
assertNull(stofMap[fooDotKt])
}
@Test
fun testSetGetOneReturnsOne() {
stofMap.set(
File(""),
listOf(File("one").canonicalFile))
assertEquals(
listOf(File("one").canonicalFile),
stofMap.get(File("")))
fun testSetOneGetReturnsOne() {
stofMap[fooDotKt] = listOf(fooDotClass)
assertEquals(listOf(fooDotClass), stofMap[fooDotKt])
}
@Test
fun testSetDupeReturnsUnique() {
stofMap.set(
File(""),
listOf(File("one").canonicalFile, File("one").canonicalFile, File("one").canonicalFile))
fun testSetDupeGetReturnsUnique() {
stofMap[fooDotKt] = listOf(fooDotClass, fooDotClass)
assertEquals(
listOf(File("one").canonicalFile),
stofMap.get(File("")))
assertEquals(listOf(fooDotClass), stofMap[fooDotKt])
}
@Test
fun testSetOverwriteReturnsNew() {
stofMap.set(
File(""),
listOf(File("old").canonicalFile, File("old").canonicalFile, File("old").canonicalFile))
stofMap.set(
File(""),
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile))
fun testSetOverwriteGetReturnsNew() {
val fooKtDotClass = classesDir.resolve("FooKt.class")
stofMap[fooDotKt] = listOf(fooDotClass)
stofMap[fooDotKt] = listOf(fooKtDotClass)
assertArrayEquals(
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile).toSortedPaths(),
stofMap.get(File("")).toSortedPaths())
assertEquals(listOf(fooKtDotClass), stofMap[fooDotKt])
}
@Test
fun testRelativeInReturnsAbsolute() {
stofMap.set(
File(""),
listOf(File("one"), File("two"), File("three")))
assertArrayEquals(
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile).toSortedPaths(),
stofMap.get(File("")).toSortedPaths()
)
}
@Test
fun testSetRelativeGetAbsolute() {
stofMap.set(
File("blah"),
listOf(File("one"), File("two"), File("three")))
assertArrayEquals(
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile).toSortedPaths(),
stofMap.get(File("blah").canonicalFile).toSortedPaths()
)
}
@Test
fun testSetRemove() {
stofMap.set(
File("blah"),
listOf(File("one"), File("two"), File("three")))
assertArrayEquals(
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile).toSortedPaths(),
stofMap.remove(File("blah")).toSortedPaths()
)
assertTrue(stofMap.get(File("blah")).isEmpty())
}
@Test
fun testSetRemoveLoop() {
repeat(5) {
stofMap.set(
File("blah"),
listOf(File("one"), File("two"), File("three"))
)
assertArrayEquals(
listOf(File("one").canonicalFile, File("two").canonicalFile, File("three").canonicalFile).toSortedPaths(),
stofMap.remove(File("blah")).toSortedPaths()
)
assertTrue(stofMap.get(File("blah")).isEmpty())
fun testSetRelativeFails() {
assertFailsWith<IllegalStateException> {
stofMap[fooDotKt] = listOf(File("relativePath"))
}
}
private fun Iterable<File>.toSortedPaths(): Array<String> =
map { it.canonicalPath }.sorted().toTypedArray()
@Test
fun testGetRelativeFails() {
stofMap[fooDotKt] = listOf(fooDotClass)
assertFailsWith<IllegalStateException> {
stofMap[fooDotKt.relativeTo(srcDir)]
}
}
@Test
fun testGetAndRemove() {
stofMap[fooDotKt] = listOf(fooDotClass)
assertEquals(listOf(fooDotClass), stofMap.getAndRemove(fooDotKt))
assertNull(stofMap[fooDotKt])
}
}
@@ -82,7 +82,10 @@ class KotlinCompileContext(val jpsContext: CompileContext) {
val fileToPathConverter: FileToPathConverter =
JpsFileToPathConverter(jpsContext.projectDescriptor.project)
val icContext = IncrementalCompilationContext(pathConverter = fileToPathConverter)
val icContext = IncrementalCompilationContext(
pathConverterForSourceFiles = fileToPathConverter,
pathConverterForOutputFiles = fileToPathConverter
)
val lookupStorageManager = JpsLookupStorageManager(dataManager, icContext)
@@ -17,13 +17,15 @@
package org.jetbrains.kotlin.gradle
import org.gradle.api.logging.LogLevel
import org.gradle.api.logging.configuration.WarningMode
import org.gradle.testkit.runner.BuildResult
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.DisplayName
import java.io.File
import kotlin.io.path.createDirectory
import kotlin.io.path.isRegularFile
import kotlin.io.path.readText
import kotlin.io.path.walk
@DisplayName("Build cache relocation")
class BuildCacheRelocationIT : KGPBaseTest() {
@@ -400,4 +402,29 @@ class BuildCacheRelocationIT : KGPBaseTest() {
assertTasksFromCache(":app:kaptGenerateStubsKotlin", ":app:kaptKotlin")
}
}
@JvmGradlePluginTests
@DisplayName("test relocatability for projects using custom build directory") // Regression test for KT-58547
@GradleTest
fun testCustomBuildDirectory(gradleVersion: GradleVersion) {
val (firstProject, secondProject) = prepareTestProjects("buildCacheSimple", gradleVersion)
firstProject.buildGradle.append("buildDir = \"../BUILD_DIR_1\"")
secondProject.buildGradle.append("buildDir = \"../BUILD_DIR_2\"")
firstProject.build(":compileKotlin")
val outputFilesContainingNonRelocatablePaths =
firstProject.projectPath.resolve("../BUILD_DIR_1/kotlin/compileKotlin").walk()
.filter {
// Use readText() even for binary files as we don't have a better way for now
it.isRegularFile() && it.readText().contains("BUILD_DIR_1")
}.toList()
assert(outputFilesContainingNonRelocatablePaths.isEmpty()) {
"The following output files contain non-relocatable paths:\n" + outputFilesContainingNonRelocatablePaths.joinToString("\n")
}
secondProject.build(":compileKotlin") {
assertTasksFromCache(":compileKotlin")
}
}
}
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.logging.Logger
import org.jetbrains.kotlin.build.report.metrics.*
import org.jetbrains.kotlin.build.report.statistics.StatTag
import org.jetbrains.kotlin.buildtools.api.KotlinLogger
import org.jetbrains.kotlin.cli.common.ExitCode
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.config.Services
@@ -15,13 +17,8 @@ import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.logging.*
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskExecutionResults
import org.jetbrains.kotlin.gradle.plugin.internal.state.TaskLoggers
import org.jetbrains.kotlin.build.report.statistics.StatTag
import org.jetbrains.kotlin.buildtools.api.KotlinLogger
import org.jetbrains.kotlin.gradle.report.*
import org.jetbrains.kotlin.gradle.tasks.*
import org.jetbrains.kotlin.gradle.tasks.OOMErrorException
import org.jetbrains.kotlin.gradle.tasks.cleanOutputsAndLocalState
import org.jetbrains.kotlin.gradle.tasks.kotlinDaemonOOMHelperMessage
import org.jetbrains.kotlin.gradle.utils.stackTraceAsString
import org.jetbrains.kotlin.incremental.ChangedFiles
import org.jetbrains.kotlin.incremental.ClasspathChanges
@@ -38,7 +35,6 @@ import java.util.*
import java.util.concurrent.Callable
import java.util.concurrent.Executors
import javax.inject.Inject
import kotlin.collections.HashSet
internal class ProjectFilesForCompilation(
val projectRootFile: File,
@@ -331,6 +327,7 @@ internal class GradleKotlinCompilerWork @Inject constructor(
multiModuleICSettings = icEnv.multiModuleICSettings,
modulesInfo = incrementalModuleInfo!!,
rootProjectDir = icEnv.rootProjectDir,
buildDir = icEnv.buildDir,
kotlinScriptExtensions = kotlinScriptExtensions,
withAbiSnapshot = icEnv.withAbiSnapshot,
preciseCompilationResultsBackup = icEnv.preciseCompilationResultsBackup,
@@ -16,6 +16,7 @@ internal class IncrementalCompilationEnvironment(
val classpathChanges: ClasspathChanges,
val workingDir: File,
val rootProjectDir: File,
val buildDir: File,
val usePreciseJavaTracking: Boolean = false,
val disableMultiModuleIC: Boolean = false,
val multiModuleICSettings: MultiModuleICSettings,
@@ -96,7 +96,8 @@ internal abstract class BuildToolsApiCompilationWork : WorkAction<BuildToolsApiC
val classpathChanges = icEnv?.classpathChanges
if (classpathChanges is ClasspathChanges.ClasspathSnapshotEnabled) {
val classpathSnapshotsConfig = jvmCompilationConfig.makeClasspathSnapshotBasedIncrementalCompilationConfiguration()
.useProjectDir(icEnv.rootProjectDir)
.setRootProjectDir(icEnv.rootProjectDir)
.setBuildDir(icEnv.buildDir)
.usePreciseJavaTracking(icEnv.usePreciseJavaTracking)
.usePreciseCompilationResultsBackup(icEnv.preciseCompilationResultsBackup)
.keepIncrementalCompilationCachesInMemory(icEnv.keepIncrementalCompilationCachesInMemory)
@@ -328,7 +328,8 @@ abstract class Kotlin2JsCompile @Inject constructor(
getChangedFiles(inputChanges, incrementalProps),
ClasspathChanges.NotAvailableForJSCompiler,
taskBuildCacheableOutputDirectory.get().asFile,
projectRootDir,
rootProjectDir = rootProjectDir,
buildDir = buildDir,
multiModuleICSettings = multiModuleICSettings,
preciseCompilationResultsBackup = preciseCompilationResultsBackup.get(),
keepIncrementalCompilationCachesInMemory = keepIncrementalCompilationCachesInMemory.get(),
@@ -351,16 +352,17 @@ abstract class Kotlin2JsCompile @Inject constructor(
}
private val projectRootDir = project.rootDir
private val rootProjectDir = project.rootDir
private val buildDir = project.buildDir
private fun validateOutputDirectory() {
val outputFile = outputFileProperty.get()
val outputDir = outputFile.parentFile
if (outputDir.isParentOf(projectRootDir)) {
if (outputDir.isParentOf(rootProjectDir)) {
throw InvalidUserDataException(
"The output directory '$outputDir' (defined by outputFile of ':$name') contains or " +
"matches the project root directory '${projectRootDir}'.\n" +
"matches the project root directory '${rootProjectDir}'.\n" +
"Gradle will not be able to build the project because of the root directory lock.\n" +
"To fix this, consider using the default outputFile location instead of providing it explicitly."
)
@@ -28,9 +28,7 @@ import org.jetbrains.kotlin.compilerRunner.IncrementalCompilationEnvironment
import org.jetbrains.kotlin.compilerRunner.OutputItemsCollectorImpl
import org.jetbrains.kotlin.config.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.dsl.KotlinJvmCompilerOptionsHelper
import org.jetbrains.kotlin.gradle.dsl.jvm.JvmTargetValidationMode
import org.jetbrains.kotlin.gradle.dsl.usesK2
import org.jetbrains.kotlin.gradle.internal.tasks.allOutputFiles
import org.jetbrains.kotlin.gradle.logging.GradleErrorMessageCollector
import org.jetbrains.kotlin.gradle.logging.GradlePrintingMessageCollector
@@ -44,10 +42,11 @@ import org.jetbrains.kotlin.gradle.report.BuildReportMode
import org.jetbrains.kotlin.gradle.tasks.internal.KotlinJvmOptionsCompat
import org.jetbrains.kotlin.gradle.utils.*
import org.jetbrains.kotlin.incremental.ClasspathChanges
import org.jetbrains.kotlin.incremental.ClasspathChanges.*
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.*
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotDisabled
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.NoChanges
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.IncrementalRun.ToBeComputedByIncrementalCompiler
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.NotAvailableDueToMissingClasspathSnapshot
import org.jetbrains.kotlin.incremental.ClasspathChanges.ClasspathSnapshotEnabled.NotAvailableForNonIncrementalRun
import org.jetbrains.kotlin.incremental.ClasspathSnapshotFiles
import org.jetbrains.kotlin.incremental.classpathAsList
import org.jetbrains.kotlin.incremental.destinationAsFile
@@ -302,6 +301,7 @@ abstract class KotlinCompile @Inject constructor(
}
private val projectRootDir = project.rootDir
private val buildDir = project.buildDir
override fun callCompilerAsync(
args: K2JVMCompilerArguments,
@@ -325,6 +325,7 @@ abstract class KotlinCompile @Inject constructor(
classpathChanges = getClasspathChanges(inputChanges),
workingDir = taskBuildCacheableOutputDirectory.get().asFile,
rootProjectDir = projectRootDir,
buildDir = buildDir,
usePreciseJavaTracking = usePreciseJavaTracking,
disableMultiModuleIC = disableMultiModuleIC,
multiModuleICSettings = multiModuleICSettings,