All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.gradle.plugin.PropertiesBuildService.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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.plugin

import org.gradle.api.Project
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.jetbrains.kotlin.gradle.plugin.internal.ConfigurationTimePropertiesAccessor
import org.jetbrains.kotlin.gradle.plugin.internal.configurationTimePropertiesAccessor
import org.jetbrains.kotlin.gradle.plugin.internal.usedAtConfigurationTime
import org.jetbrains.kotlin.gradle.utils.localProperties
import org.jetbrains.kotlin.gradle.utils.registerClassLoaderScopedBuildService
import java.util.concurrent.Callable
import java.util.concurrent.ConcurrentHashMap

/**
 * [BuildService] that looks up properties in the following precedence order:
 *   1. Project's extra properties ([org.gradle.api.plugins.ExtensionContainer.getExtraProperties])
 *   2. Project's Gradle properties ([org.gradle.api.provider.ProviderFactory.gradleProperty])
 *   3. Root project's `local.properties` file ([Project.localProperties])
 *
 *  Note that extra and Gradle properties may differ across projects, whereas `local.properties` is shared across all projects.
 */
internal abstract class PropertiesBuildService : BuildService {

    interface Params : BuildServiceParameters {
        val localProperties: MapProperty
    }

    private val propertiesManager = ConcurrentHashMap()

    /** Returns a [Provider] of the value of the property with the given [propertyName] in the given [project]. */
    fun property(propertyName: String, project: Project): Provider {
        return propertiesManager.computeIfAbsent(project) { PropertiesManager(project, parameters.localProperties.get()) }
            .property(propertyName)
    }

    /** Returns the value of the property with the given [propertyName] in the given [project]. */
    fun get(propertyName: String, project: Project): String? {
        return property(propertyName, project).orNull
    }

    companion object {

        fun registerIfAbsent(project: Project): Provider =
            project.gradle.registerClassLoaderScopedBuildService(PropertiesBuildService::class) {
                it.parameters.localProperties.set(project.localProperties)
            }
    }
}

private class PropertiesManager(private val project: Project, private val localProperties: Map) {

    private val configurationTimePropertiesAccessor: ConfigurationTimePropertiesAccessor by lazy {
        project.configurationTimePropertiesAccessor
    }

    private val properties = ConcurrentHashMap>()

    fun property(propertyName: String): Provider {
        // Note: The same property may be read many times (KT-62496). Therefore,
        //   - Use a map to create only one Provider per property.
        //   - Use MemoizedCallable to resolve the Provider only once.
        return properties.computeIfAbsent(propertyName) {
            // We need to create the MemoizedCallable instance up front so that each time the Provider is resolved, it will reuse the same
            // MemoizedCallable.
            val valueFromGradleAndLocalProperties = MemoizedCallable {
                project.providers.gradleProperty(propertyName).usedAtConfigurationTime(configurationTimePropertiesAccessor).orNull
                    ?: localProperties[propertyName]
            }
            project.provider {
                // FIXME(KT-62684): We currently don't memoize extraProperties as they may still change, we'll fix this later.
                project.extraProperties.getOrNull(propertyName) as String?
                    ?: valueFromGradleAndLocalProperties.call()
            }
        }
    }
}

private class MemoizedCallable(valueResolver: Callable) : Callable {
    private val value: T? by lazy { valueResolver.call() }
    override fun call(): T? = value
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy