[Gradle] Split global and project persistent caches

Now for project persistent caches plugin will use '<rootDir>/.kotlin'
directory. Global persistent cache has stayed in the
'<user.home>/.kotlin' directory.

^KT-58223 Fixed
This commit is contained in:
Yahor Berdnikau
2023-10-24 15:33:47 +02:00
committed by Space Team
parent 5413973891
commit 070de9cc85
11 changed files with 55 additions and 83 deletions
@@ -17,7 +17,6 @@ import kotlin.io.path.*
import kotlin.test.assertTrue
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion
import org.jetbrains.kotlin.gradle.testbase.TestVersions.ThirdPartyDependencies.GRADLE_ENTERPRISE_PLUGIN_VERSION
import org.junit.jupiter.api.io.TempDir
@DisplayName("Build reports")
@JvmGradlePluginTests
@@ -271,16 +270,14 @@ class BuildReportsIT : KGPBaseTest() {
@GradleTest
fun testErrorsFileSmokeTest(
gradleVersion: GradleVersion,
@TempDir tempDir: Path,
) {
project(
projectName = "simpleProject",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(kotlinUserHome = tempDir)
) {
val lookupsTab = projectPath.resolve("build/kotlin/compileKotlin/cacheable/caches-jvm/lookups/lookups.tab")
fun kotlinErrorPath() = tempDir.inProjectsPersistentCache("errors")
val kotlinErrorPath = projectPersistentCache.resolve("errors")
buildGradle.appendText(
"""
@@ -293,14 +290,14 @@ class BuildReportsIT : KGPBaseTest() {
)
build("compileKotlin") {
assertTrue { kotlinErrorPath().listDirectoryEntries().isEmpty() }
assertTrue { kotlinErrorPath.listDirectoryEntries().isEmpty() }
assertOutputDoesNotContain("errors were stored into file")
}
val kotlinFile = kotlinSourcesDir().resolve("helloWorld.kt")
kotlinFile.modify { it.replace("ArrayList", "skjfghsjk") }
buildAndFail("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
assertOutputContains("errors were stored into file")
val files = kotlinErrorPath().listDirectoryEntries()
val files = kotlinErrorPath.listDirectoryEntries()
assertTrue { files.first().exists() }
files.first().bufferedReader().use { reader ->
val kotlinVersion = reader.readLine()
@@ -324,24 +321,22 @@ class BuildReportsIT : KGPBaseTest() {
@GradleTest
fun testErrorsFileWithCompilationError(
gradleVersion: GradleVersion,
@TempDir tempDir: Path,
) {
project(
projectName = "simpleProject",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(kotlinUserHome = tempDir)
) {
fun kotlinErrorPath() = tempDir.inProjectsPersistentCache("errors")
val kotlinErrorPath = projectPersistentCache.resolve("errors")
build("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
assertOutputDoesNotContain("errors were stored into file")
assertTrue { kotlinErrorPath().listDirectoryEntries().isEmpty() }
assertTrue { kotlinErrorPath.listDirectoryEntries().isEmpty() }
}
val kotlinFile = kotlinSourcesDir().resolve("helloWorld.kt")
kotlinFile.modify { it.replace("ArrayList", "skjfghsjk") }
buildAndFail("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) {
assertOutputDoesNotContain("errors were stored into file")
assertTrue { kotlinErrorPath().listDirectoryEntries().isEmpty() }
assertTrue { kotlinErrorPath.listDirectoryEntries().isEmpty() }
}
}
}
@@ -170,28 +170,26 @@ open class CommonizerIT : KGPBaseTest() {
@GradleTest
fun testCommonizeCurlInteropcopyCommonizeCInteropForIde(
gradleVersion: GradleVersion,
@TempDir tempDir: Path,
) {
nativeProject("commonizeCurlInterop", gradleVersion, buildOptions = defaultBuildOptions.copy(kotlinUserHome = null)) {
nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
fun expectedOutputDirectoryForIde(): Path = tempDir.inProjectsPersistentCache("commonizer")
val commonizerIdeOutput: Path = projectPersistentCache.resolve("metadata").resolve("commonizer")
val expectedOutputDirectoryForBuild = projectPath.resolve("build/classes/kotlin/commonizer")
build(":copyCommonizeCInteropForIde", "-Pkotlin.user.home=${tempDir.absolutePathString()}") {
build(":copyCommonizeCInteropForIde") {
assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop")
val commonizerIdeOutput = expectedOutputDirectoryForIde()
assertDirectoryExists(commonizerIdeOutput, "Missing output directory for IDE")
assertDirectoryExists(expectedOutputDirectoryForBuild, "Missing output directory for build")
assertEqualDirectories(expectedOutputDirectoryForBuild.toFile(), commonizerIdeOutput.toFile(), false)
}
build(":clean", "-Pkotlin.user.home=${tempDir.absolutePathString()}") {
assertDirectoryExists(expectedOutputDirectoryForIde(), "Expected ide output directory to survive cleaning")
build(":clean") {
assertDirectoryExists(commonizerIdeOutput, "Expected ide output directory to survive cleaning")
assertFileNotExists(expectedOutputDirectoryForBuild, "Expected output directory for build to be cleaned")
}
}
@@ -542,24 +540,21 @@ open class CommonizerIT : KGPBaseTest() {
@GradleTest
fun testCommonizedLibrariesForIDECanBeStoredInDifferentDir(
gradleVersion: GradleVersion,
@TempDir tempDir: Path,
) {
nativeProject("commonizeCurlInterop", gradleVersion, buildOptions = defaultBuildOptions.copy(kotlinUserHome = null)) {
gradleProperties.append("kotlin.user.home=${tempDir.absolutePathString()}")
nativeProject("commonizeCurlInterop", gradleVersion) {
configureCommonizerTargets()
fun expectedOutputDirectoryForIde(): Path = tempDir.inProjectsPersistentCache("commonizer")
val commonizerIdeOutput: Path = projectPersistentCache.resolve("metadata").resolve("commonizer")
build(":copyCommonizeCInteropForIde") {
assertTasksExecuted(":cinteropCurlTargetB")
assertTasksExecuted(":commonizeCInterop")
assertDirectoryExists(expectedOutputDirectoryForIde(), "Missing output directory for IDE")
assertDirectoryExists(commonizerIdeOutput, "Missing output directory for IDE")
}
build(":clean") {
assertDirectoryExists(expectedOutputDirectoryForIde(), "Expected ide output directory to survive cleaning")
assertDirectoryExists(commonizerIdeOutput, "Expected ide output directory to survive cleaning")
}
}
}
@@ -191,12 +191,10 @@ class MppIdeDependencyResolutionIT : KGPBaseTest() {
@GradleTest
fun `test cinterops - are stored in root gradle folder`(
gradleVersion: GradleVersion,
@TempDir tempDir: Path,
) {
project(
projectName = "cinteropImport",
gradleVersion = gradleVersion,
buildOptions = defaultBuildOptions.copy(kotlinUserHome = tempDir)
) {
resolveIdeDependencies("dep-with-cinterop") { dependencies ->
@@ -205,7 +203,7 @@ class MppIdeDependencyResolutionIT : KGPBaseTest() {
.filter { !it.isNativeDistribution && it.klibExtra?.isInterop == true }
.ifEmpty { fail("Expected at least one cinterop on linuxX64Main") }
val persistentCInteropsCache = tempDir.inProjectsPersistentCache("kotlinCInteropLibraries")
val persistentCInteropsCache = projectPersistentCache.resolve("metadata").resolve("kotlinCInteropLibraries")
cinterops.forEach { cinterop ->
if (cinterop.classpath.isEmpty()) fail("Missing classpath for $cinterop")
cinterop.classpath.forEach { cinteropFile ->
@@ -5,6 +5,7 @@ import org.gradle.api.logging.configuration.WarningMode
import org.gradle.util.GradleVersion
import org.jetbrains.kotlin.gradle.tasks.KotlinCompilerExecutionStrategy
import org.jetbrains.kotlin.gradle.testbase.*
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.io.TempDir
import java.nio.file.Path
@@ -293,6 +294,7 @@ class SimpleKotlinGradleIT : KGPBaseTest() {
}
}
@Disabled("KT-58223: Currently is not used and we should start using it after working on followup issues")
@DisplayName("Possible to override kotlin.user.home location")
@GradleTest
fun overrideKotlinUserHome(
@@ -307,19 +309,15 @@ class SimpleKotlinGradleIT : KGPBaseTest() {
gradleProperties.appendText(
"""
|
|kotlin.user.home=${tempDir.absolutePathString().normalizePath()}
|kotlin.user.home=${tempDir.resolve("kotlin-cache").absolutePathString().normalizePath()}
""".trimMargin()
)
build("compileKotlin") {
assertTasksExecuted(":compileKotlin")
val baseProjectsDir = tempDir.resolve("projects-1")
val baseProjectsDir = tempDir.resolve("kotlin-cache")
assertDirectoryExists(baseProjectsDir)
val projectsDirs = baseProjectsDir.listDirectoryEntries()
assertEquals(1, projectsDirs.size, message = "More then 1 child! Actual content ${projectsDirs.joinToString { it.pathString }}")
assertDirectoryExists(projectsDirs.first().resolve("sessions"))
assertDirectoryExists(projectsDirs.first().resolve("errors"))
}
}
}
@@ -121,20 +121,9 @@ fun Path.getSingleFileInDir(relativePath: String): Path {
}
/**
* Get a path for per-project persistent cache directory from this base persistent cache directory.
*/
fun Path.projectsPersistentCache() = resolve("projects-1")
/**
* Searches for the specified directory in the projects persistent cache.
* Get Gradle project Kotlin persistent cache.
*
* @param directoryName The name of the directory to search for.
* @return The path of the directory if found.
*
* @throws IllegalStateException if the directory does not exist in the projects cache. Usually they are populated after some build.
* **Note**: if a test project is using composite build - [GradleProject] should point to the root project in this composite build.
*/
fun Path.inProjectsPersistentCache(
directoryName: String
) = projectsPersistentCache().findInPath(directoryName)
?: throw IllegalStateException("$directoryName does not exist in the projects cache under ${this.absolutePathString()}")
val GradleProject.projectPersistentCache: Path
get() = projectPath.resolve(".kotlin")
@@ -6,7 +6,6 @@
package org.jetbrains.kotlin.gradle.plugin
import org.gradle.api.Project
import org.gradle.api.invocation.Gradle
import org.gradle.api.logging.Logging
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
@@ -14,6 +13,7 @@ import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.kotlin.gradle.logging.kotlinDebug
import org.jetbrains.kotlin.gradle.utils.kotlinSessionsDir
import org.jetbrains.kotlin.gradle.utils.registerClassLoaderScopedBuildService
import java.io.File
internal abstract class KotlinGradleBuildServices : BuildService<KotlinGradleBuildServices.Parameters>, AutoCloseable {
@@ -71,14 +71,10 @@ internal abstract class KotlinGradleBuildServices : BuildService<KotlinGradleBui
private val INIT_MESSAGE = "Initialized $CLASS_NAME"
private val DISPOSE_MESSAGE = "Disposed $CLASS_NAME"
fun registerIfAbsent(gradle: Gradle): Provider<KotlinGradleBuildServices> =
gradle.sharedServices.registerIfAbsent(
"kotlin-build-service-${KotlinGradleBuildServices::class.java.canonicalName}_${KotlinGradleBuildServices::class.java.classLoader.hashCode()}",
KotlinGradleBuildServices::class.java
) { service ->
service.parameters.sessionsDir.set(gradle.rootProject.kotlinSessionsDir)
fun registerIfAbsent(project: Project): Provider<KotlinGradleBuildServices> =
project.gradle.registerClassLoaderScopedBuildService(KotlinGradleBuildServices::class) {
it.parameters.sessionsDir.set(project.kotlinSessionsDir)
}
}
}
@@ -82,7 +82,7 @@ abstract class DefaultKotlinBasePlugin : KotlinBasePlugin {
addGradlePluginMetadataAttributes(project)
}
val kotlinGradleBuildServices = KotlinGradleBuildServices.registerIfAbsent(project.gradle).get()
val kotlinGradleBuildServices = KotlinGradleBuildServices.registerIfAbsent(project).get()
if (!project.isProjectIsolationEnabled) {
kotlinGradleBuildServices.detectKotlinPluginLoadedInMultipleProjects(project, pluginVersion)
}
@@ -543,7 +543,16 @@ internal class PropertiesProvider private constructor(private val project: Proje
*
* If the property is not set, the plugin will use `<user_home>/.kotlin` as default.
*/
val kotlinUserHomeDir: String? = get(PropertyNames.KOTLIN_USER_HOME_DIR)
val kotlinUserHomeDir: String?
get() = get(PropertyNames.KOTLIN_USER_HOME_DIR)
/**
* The directory where Kotlin stores project-specific persistent caches.
*
* If the property is not set, the plugin will use `<project_dir>/.kotlin` as default.
*/
val kotlinProjectPersistentDir: String?
get() = get(PropertyNames.KOTLIN_PROJECT_PERSISTENT_DIR)
/**
* Retrieves a comma-separated list of browsers to use when running karma tests for [target]
@@ -636,6 +645,7 @@ internal class PropertiesProvider private constructor(private val project: Proje
val KOTLIN_SUPPRESS_BUILD_TOOLS_API_VERSION_CONSISTENCY_CHECKS =
property("kotlin.internal.suppress.buildToolsApiVersionConsistencyChecks")
val KOTLIN_USER_HOME_DIR = property("kotlin.user.home")
val KOTLIN_PROJECT_PERSISTENT_DIR = property("kotlin.project.persistent.dir")
/**
* Internal properties: builds get big non-suppressible warning when such properties are used
@@ -19,19 +19,16 @@ internal fun ProjectLayout.kotlinTransformedMetadataLibraryDirectoryForBuild(sou
buildDirectory.get().asFile.resolve(kotlinTransformedMetadataLibraries).resolve(sourceSetName)
internal val Project.kotlinTransformedMetadataLibraryDirectoryForIde: File
get() = compositeBuildRootProject
.kotlinMetadataDir
get() = kotlinMetadataDir(compositeBuildRootProject)
.resolve(kotlinTransformedMetadataLibraries)
internal fun ProjectLayout.kotlinTransformedCInteropMetadataLibraryDirectoryForBuild(sourceSetName: String): File =
buildDirectory.get().asFile.resolve(kotlinTransformedCInteropMetadataLibraries).resolve(sourceSetName)
internal val Project.kotlinCInteropLibraryDirectoryForIde: File
get() = compositeBuildRootProject
.kotlinMetadataDir
get() = kotlinMetadataDir(compositeBuildRootProject)
.resolve(kotlinCInteropLibraries)
internal val Project.kotlinTransformedCInteropMetadataLibraryDirectoryForIde: File
get() = compositeBuildRootProject
.kotlinMetadataDir
get() = kotlinMetadataDir(compositeBuildRootProject)
.resolve(kotlinTransformedCInteropMetadataLibraries)
@@ -33,7 +33,7 @@ internal abstract class CopyCommonizeCInteropForIdeTask @Inject constructor(
commonizeCInteropTask.map { it.allOutputDirectories }
@get:OutputDirectory
override val outputDirectory: File = project.kotlinMetadataDir
override val outputDirectory: File = project.kotlinMetadataDir()
.resolve("commonizer")
.resolve(project.path.removePrefix(":").replace(":", "/"))
@@ -8,31 +8,25 @@ package org.jetbrains.kotlin.gradle.utils
import org.gradle.api.Project
import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider
import java.io.File
import java.security.MessageDigest
private const val PROJECTS_CACHE_NAME = "projects"
private const val PROJECTS_CACHE_VERSION = 1
private const val PROJECTS_CACHE_NAME_FULL = "$PROJECTS_CACHE_NAME-$PROJECTS_CACHE_VERSION"
private const val SESSIONS_DIR_NAME = "sessions"
private const val METADATA_DIR_NAME = "metadata"
private const val ERRORS_DIR_NAME = "errors"
internal val Project.basePersistentDir
@Suppress("unused") // will be used in the followup KT-58223 issues
internal val Project.userKotlinPersistentDir
get() = kotlinPropertiesProvider.kotlinUserHomeDir?.let { File(it) }
?: File(System.getProperty("user.home")).resolve(".kotlin")
internal val Project.kotlinSessionsDir
get() = basePersistentDir.resolve(PROJECTS_CACHE_NAME_FULL).resolve(rootDir.absolutePathMd5Hash()).resolve(SESSIONS_DIR_NAME)
internal fun Project.projectKotlinPersistentDir(compositeRootProject: Project? = null) =
kotlinPropertiesProvider.kotlinProjectPersistentDir?.let { File(it) }
?: (compositeRootProject ?: this).rootDir.resolve(".kotlin")
internal val Project.kotlinMetadataDir
get() = basePersistentDir.projectSpecificCache(rootDir).resolve(METADATA_DIR_NAME)
internal val Project.kotlinSessionsDir
get() = projectKotlinPersistentDir().resolve(SESSIONS_DIR_NAME)
internal fun Project.kotlinMetadataDir(compositeBuildRootProject: Project? = null) =
projectKotlinPersistentDir(compositeBuildRootProject).resolve(METADATA_DIR_NAME)
internal val Project.kotlinErrorsDir
get() = basePersistentDir.projectSpecificCache(rootDir).resolve(ERRORS_DIR_NAME)
private val md5Digest by lazy { MessageDigest.getInstance("MD5") }
private fun File.absolutePathMd5Hash(): String = md5Digest.digest(absolutePath.toByteArray()).toHexString()
private fun File.projectSpecificCache(projectRootDir: File) = resolve(PROJECTS_CACHE_NAME_FULL).resolve(projectRootDir.absolutePathMd5Hash())
get() = projectKotlinPersistentDir().resolve(ERRORS_DIR_NAME)