diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt index d513f185d2a..6f57743f8a1 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/BuildReportsIT.kt @@ -17,6 +17,7 @@ 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 @@ -263,17 +264,27 @@ class BuildReportsIT : KGPBaseTest() { } } - private val kotlinErrorPath = ".gradle/kotlin/errors" - @DisplayName("Error file is created") @GradleTestVersions( additionalVersions = [TestVersions.Gradle.G_7_6, TestVersions.Gradle.G_8_0], ) @GradleTest - fun testErrorsFileSmokeTest(gradleVersion: GradleVersion) { - project("simpleProject", gradleVersion) { + 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 + .resolve("projects-1") + .findInPath("errors") + ?: throw IllegalStateException("Could not find global Kotlin errors directory!") + buildGradle.appendText( """ tasks.named("compileKotlin") { @@ -283,16 +294,18 @@ class BuildReportsIT : KGPBaseTest() { } """.trimIndent() ) - build("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) { - assertTrue { projectPath.resolve(kotlinErrorPath).listDirectoryEntries().isEmpty() } + + build("compileKotlin") { + 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 file = projectPath.getSingleFileInDir(kotlinErrorPath) - file.bufferedReader().use { reader -> + val files = kotlinErrorPath().listDirectoryEntries() + assertTrue { files.first().exists() } + files.first().bufferedReader().use { reader -> val kotlinVersion = reader.readLine() assertTrue("kotlin version should be in the error file") { kotlinVersion != null && kotlinVersion.trim().equals("kotlin version: ${buildOptions.kotlinVersion}") @@ -312,17 +325,29 @@ class BuildReportsIT : KGPBaseTest() { additionalVersions = [TestVersions.Gradle.G_7_6, TestVersions.Gradle.G_8_0], ) @GradleTest - fun testErrorsFileWithCompilationError(gradleVersion: GradleVersion) { - project("simpleProject", gradleVersion) { + fun testErrorsFileWithCompilationError( + gradleVersion: GradleVersion, + @TempDir tempDir: Path, + ) { + project( + projectName = "simpleProject", + gradleVersion = gradleVersion, + buildOptions = defaultBuildOptions.copy(kotlinUserHome = tempDir) + ) { + fun kotlinErrorPath() = tempDir + .resolve("projects-1") + .findInPath("errors") + ?: throw IllegalStateException("Could not find global Kotlin errors directory!") + build("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) { - assertTrue { projectPath.resolve(kotlinErrorPath).listDirectoryEntries().isEmpty() } assertOutputDoesNotContain("errors were stored into file") + assertTrue { kotlinErrorPath().listDirectoryEntries().isEmpty() } } val kotlinFile = kotlinSourcesDir().resolve("helloWorld.kt") kotlinFile.modify { it.replace("ArrayList", "skjfghsjk") } buildAndFail("compileKotlin", buildOptions = defaultBuildOptions.copy(logLevel = LogLevel.DEBUG)) { - assertTrue { projectPath.resolve(kotlinErrorPath).listDirectoryEntries().isEmpty() } assertOutputDoesNotContain("errors were stored into file") + assertTrue { kotlinErrorPath().listDirectoryEntries().isEmpty() } } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt index 6873543b5b2..cc6c3d5a8b6 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerRunner.kt @@ -25,7 +25,6 @@ import org.jetbrains.kotlin.daemon.common.configureDaemonJVMOptions import org.jetbrains.kotlin.daemon.common.filterExtractProps import org.jetbrains.kotlin.gradle.dsl.KotlinJsProjectExtension import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinVersion import org.jetbrains.kotlin.gradle.dsl.multiplatformExtensionOrNull import org.jetbrains.kotlin.gradle.internal.ClassLoadersCachingBuildService import org.jetbrains.kotlin.gradle.logging.kotlinDebug @@ -47,6 +46,9 @@ import org.jetbrains.kotlin.statistics.metrics.BooleanMetrics import org.jetbrains.kotlin.statistics.metrics.StringMetrics import java.io.File import java.lang.ref.WeakReference +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write const val CREATED_CLIENT_FILE_PREFIX = "Created client-is-alive flag file: " @@ -105,7 +107,6 @@ internal open class GradleCompilerRunner( internal val loggerProvider = taskProvider.logger.get() internal val buildDirProvider = taskProvider.buildDir.get().asFile internal val projectDirProvider = taskProvider.projectDir.get() - internal val projectCacheDirProvider = taskProvider.projectCacheDir.get() internal val sessionDirProvider = taskProvider.sessionsDir.get() internal val projectNameProvider = taskProvider.projectName.get() internal val incrementalModuleInfoProvider = taskProvider.buildModulesInfo @@ -216,7 +217,6 @@ internal open class GradleCompilerRunner( projectDirProvider, buildDirProvider, projectNameProvider, - projectCacheDirProvider, sessionDirProvider ), compilerFullClasspath = environment.compilerFullClasspath(jdkToolsJar), @@ -456,25 +456,44 @@ internal open class GradleCompilerRunner( } // session is created per build - @Volatile private var sessionFlagFile: File? = null + private val sessionFileLock = ReentrantReadWriteLock(true) + // session files are deleted at org.jetbrains.kotlin.gradle.plugin.KotlinGradleBuildServices.buildFinished - @Synchronized - internal fun getOrCreateSessionFlagFile(log: Logger, sessionsDir: File, projectCacheDirProvider: File): File { - if (sessionFlagFile == null || !sessionFlagFile!!.exists()) { - val sessionFilesDir = sessionsDir.apply { mkdirs() } - sessionFlagFile = newTmpFile(prefix = "kotlin-compiler-", suffix = ".salive", directory = sessionFilesDir) - log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrAbsolute(projectCacheDirProvider) } - } else { - log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + sessionFlagFile!!.relativeOrAbsolute(projectCacheDirProvider) } + + internal fun getOrCreateSessionFlagFile( + log: Logger, + sessionsDir: File, + ): File { + sessionFileLock.read { + val sessionFlagRead = sessionFlagFile + if (sessionFlagRead != null && sessionFlagRead.exists()) { + return sessionFlagRead.sessionFileFlagExists(log) + } } - return sessionFlagFile!! + sessionFileLock.write { + val sessionFlagWrite = sessionFlagFile + if (sessionFlagWrite != null && sessionFlagWrite.exists()) { + return sessionFlagWrite.sessionFileFlagExists(log) + } + + return newTmpFile( + prefix = "kotlin-compiler-", + suffix = ".salive", + directory = sessionsDir.apply { mkdirs() }) + .also { + sessionFlagFile = it + log.kotlinDebug { CREATED_SESSION_FILE_PREFIX + it.absolutePath } + } + } } - internal fun sessionsDir(projectCacheDir: File): File = - File(File(projectCacheDir, "kotlin"), "sessions") + private fun File.sessionFileFlagExists(log: Logger): File { + log.kotlinDebug { EXISTING_SESSION_FILE_PREFIX + absolutePath } + return this + } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt index a6c2b3a0005..6e4158e3362 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/compilerRunner/GradleKotlinCompilerWork.kt @@ -42,10 +42,16 @@ internal class ProjectFilesForCompilation( val sessionFlagFile: File, val buildDir: File ) : Serializable { - constructor(logger: Logger, projectDir:File, buildDir: File, projectName: String, projectCacheDirProvider: File, sessionDir: File) : this( + constructor( + logger: Logger, + projectDir: File, + buildDir: File, + projectName: String, + sessionDir: File + ) : this( projectRootFile = projectDir, clientIsAliveFlagFile = GradleCompilerRunner.getOrCreateClientFlagFile(logger, projectName), - sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir, projectCacheDirProvider), + sessionFlagFile = GradleCompilerRunner.getOrCreateSessionFlagFile(logger, sessionDir), buildDir = buildDir ) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt index 12d71e2c9e9..fbb9cd95ad4 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleBuildServices.kt @@ -8,17 +8,18 @@ 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 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.projectCacheDir +import org.jetbrains.kotlin.gradle.utils.kotlinSessionsDir import java.io.File internal abstract class KotlinGradleBuildServices : BuildService, AutoCloseable { interface Parameters : BuildServiceParameters { - var projectCacheDir: File + val sessionsDir: Property } private val log = Logging.getLogger(this.javaClass) @@ -61,7 +62,7 @@ internal abstract class KotlinGradleBuildServices : BuildService - service.parameters.projectCacheDir = gradle.projectCacheDir + service.parameters.sessionsDir.set(gradle.rootProject.kotlinSessionsDir) } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt index 64b6273e0b1..d26f85c1282 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/KotlinGradleFinishBuildHandler.kt @@ -32,24 +32,23 @@ internal class KotlinGradleFinishBuildHandler { startMemory = getUsedMemoryKb() } - fun buildFinished(projectCacheDir: File) { + fun buildFinished(sessionsDir: File) { TaskLoggers.clear() TaskExecutionResults.clear() GradleCompilerRunner.clearBuildModulesInfo() - val sessionsDir = GradleCompilerRunner.sessionsDir(projectCacheDir) if (sessionsDir.exists()) { val sessionFiles = sessionsDir.listFiles() // it is expected that only one session file per build exists // afaik is is not possible to run multiple gradle builds in one project since gradle locks some dirs if (sessionFiles.size > 1) { - log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeOrAbsolute(projectCacheDir)}") + log.warn("w: Detected multiple Kotlin daemon sessions at ${sessionsDir.relativeOrAbsolute(sessionsDir)}") } for (file in sessionFiles) { file.delete() - log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.relativeOrAbsolute(projectCacheDir) } + log.kotlinDebug { DELETED_SESSION_FILE_PREFIX + file.absolutePath } } } diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt index 5e17d211e80..8eee1e28e96 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/PropertiesProvider.kt @@ -543,10 +543,9 @@ internal class PropertiesProvider private constructor(private val project: Proje * This data is not cleaned by gradle clean task. This dir can be used to store data * for IDE import i.e. `./gradlew clean` will not break IDE import state. * - * If the property is not set, the default value `$ROOT_DIR/.kotlin` is used. + * If the property is not set, the plugin will use `/.kotlin` as default. */ - val kotlinPersistentGradleDataDir: String - get() = get(PropertyNames.KOTLIN_PERSISTENT_GRADLE_DATA_DIR) ?: ".kotlin" + val kotlinPersistentGradleDataDir: String? = get(PropertyNames.KOTLIN_PERSISTENT_GRADLE_DATA_DIR) /** * Retrieves a comma-separated list of browsers to use when running karma tests for [target] diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataLibraryDirectories.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataLibraryDirectories.kt index b812d97e4c0..9a46ce1e85c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataLibraryDirectories.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/plugin/mpp/KotlinMetadataLibraryDirectories.kt @@ -18,9 +18,12 @@ private const val kotlinTransformedCInteropMetadataLibraries = "kotlinTransforme internal fun ProjectLayout.kotlinTransformedMetadataLibraryDirectoryForBuild(sourceSetName: String): File = buildDirectory.get().asFile.resolve(kotlinTransformedMetadataLibraries).resolve(sourceSetName) +internal val Project.kotlinGradleDataDirCompat + get() = kotlinPropertiesProvider.kotlinPersistentGradleDataDir ?: ".kotlin" + internal val Project.kotlinTransformedMetadataLibraryDirectoryForIde: File get() = compositeBuildRootProject.rootDir - .resolve(project.kotlinPropertiesProvider.kotlinPersistentGradleDataDir) + .resolve(project.kotlinGradleDataDirCompat) .resolve(kotlinTransformedMetadataLibraries) internal fun ProjectLayout.kotlinTransformedCInteropMetadataLibraryDirectoryForBuild(sourceSetName: String): File = @@ -28,10 +31,10 @@ internal fun ProjectLayout.kotlinTransformedCInteropMetadataLibraryDirectoryForB internal val Project.kotlinCInteropLibraryDirectoryForIde: File get() = compositeBuildRootProject.rootDir - .resolve(project.kotlinPropertiesProvider.kotlinPersistentGradleDataDir) + .resolve(project.kotlinGradleDataDirCompat) .resolve(kotlinCInteropLibraries) internal val Project.kotlinTransformedCInteropMetadataLibraryDirectoryForIde: File get() = compositeBuildRootProject.rootDir - .resolve(project.kotlinPropertiesProvider.kotlinPersistentGradleDataDir) + .resolve(project.kotlinGradleDataDirCompat) .resolve(kotlinTransformedCInteropMetadataLibraries) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt index 9ccfb9e629e..c0ea050e0a5 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/internal/CopyCInteropCommonizerTaskOutputForIdeTask.kt @@ -16,7 +16,7 @@ import org.jetbrains.kotlin.build.report.metrics.GradleBuildTime import org.jetbrains.kotlin.compilerRunner.addBuildMetricsForTaskAction import org.jetbrains.kotlin.gradle.report.GradleBuildMetricsReporter import org.jetbrains.kotlin.gradle.utils.property -import org.jetbrains.kotlin.gradle.plugin.PropertiesProvider.Companion.kotlinPropertiesProvider +import org.jetbrains.kotlin.gradle.plugin.mpp.kotlinGradleDataDirCompat import java.io.File import javax.inject.Inject @@ -34,7 +34,7 @@ internal abstract class CopyCommonizeCInteropForIdeTask @Inject constructor( @get:OutputDirectory override val outputDirectory: File = project.rootDir - .resolve(project.kotlinPropertiesProvider.kotlinPersistentGradleDataDir) + .resolve(project.kotlinGradleDataDirCompat) .resolve("commonizer") .resolve(project.path.removePrefix(":").replace(":", "/")) diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt index e5f06e76914..35cfe8a804d 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/tasks/GradleCompileTaskProvider.kt @@ -14,11 +14,10 @@ import org.gradle.api.logging.Logger import org.gradle.api.model.ObjectFactory import org.gradle.api.provider.Provider import org.gradle.api.tasks.Internal -import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner import org.jetbrains.kotlin.compilerRunner.GradleCompilerRunner.Companion.normalizeForFlagFile import org.jetbrains.kotlin.gradle.incremental.IncrementalModuleInfoProvider -import org.jetbrains.kotlin.gradle.utils.projectCacheDir import org.jetbrains.kotlin.gradle.utils.property +import org.jetbrains.kotlin.gradle.utils.kotlinSessionsDir import java.io.File import javax.inject.Inject @@ -44,13 +43,9 @@ abstract class GradleCompileTaskProvider @Inject constructor( val projectDir: Provider = objectFactory .property(project.rootProject.projectDir) - @get:Internal - val projectCacheDir: Provider = objectFactory - .property(gradle.projectCacheDir) - @get:Internal val sessionsDir: Provider = objectFactory - .property(GradleCompilerRunner.sessionsDir(gradle.projectCacheDir)) + .property(project.kotlinSessionsDir) @get:Internal val projectName: Provider = objectFactory diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleUtils.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleUtils.kt index e48db71dd03..47ec2375d0c 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleUtils.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/gradleUtils.kt @@ -18,9 +18,6 @@ import org.gradle.api.services.BuildServiceSpec import org.gradle.util.GradleVersion import kotlin.reflect.KClass -val Gradle.projectCacheDir - get() = startParameter.projectCacheDir ?: this.rootProject.projectDir.resolve(".gradle") - internal val Project.compositeBuildRootGradle: Gradle get() = generateSequence(project.gradle) { it.parent }.last() internal val Project.compositeBuildRootProject: Project get() = compositeBuildRootGradle.rootProject diff --git a/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/persistentCaches.kt b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/persistentCaches.kt new file mode 100644 index 00000000000..0c920e8a0c4 --- /dev/null +++ b/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/utils/persistentCaches.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2023 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.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" + +internal val Project.basePersistentDir + get() = kotlinPropertiesProvider.kotlinPersistentGradleDataDir?.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) + +private val md5Digest by lazy { MessageDigest.getInstance("MD5") } + +private fun File.absolutePathMd5Hash(): String = md5Digest.digest(absolutePath.toByteArray()).toHexString()