[Gradle] Always declare system property reads for compile tasks

When Kotlin plugin is applied in buildSrc or included build that defines Gradle plugins it can cause Gradle to report undeclared system property reads at configuration time. Declaring all system properties reads through Gradle providers to cover all these cases.
#KT-45910 Fixed
This commit is contained in:
Alexander Likhachev
2021-04-10 18:20:31 +03:00
parent 47407c4445
commit 9e78e43c49
8 changed files with 108 additions and 35 deletions
@@ -16,10 +16,10 @@
package org.jetbrains.kotlin.cli.common
import com.intellij.util.LineSeparator
import java.util.*
enum class CompilerSystemProperties(val property: String) {
/**
* @param alwaysDirectAccess Gradle has a list of properties that can be read without declaring, see https://github.com/gradle/gradle/blob/f191a61cec61afe308f2b45184cb303d32706a6f/subprojects/configuration-cache/src/main/kotlin/org/gradle/configurationcache/SystemPropertyAccessListener.kt#L32
*/
enum class CompilerSystemProperties(val property: String, val alwaysDirectAccess: Boolean = false) {
COMPILE_DAEMON_ENABLED_PROPERTY("kotlin.daemon.enabled"),
COMPILE_DAEMON_JVM_OPTIONS_PROPERTY("kotlin.daemon.jvm.options"),
COMPILE_DAEMON_OPTIONS_PROPERTY("kotlin.daemon.options"),
@@ -36,24 +36,31 @@ enum class CompilerSystemProperties(val property: String) {
KOTLIN_COMPILER_ENVIRONMENT_KEEPALIVE_PROPERTY("kotlin.environment.keepalive"),
COMPILE_DAEMON_CUSTOM_RUN_FILES_PATH_FOR_TESTS("kotlin.daemon.custom.run.files.path.for.tests"),
KOTLIN_COLORS_ENABLED_PROPERTY("kotlin.colors.enabled"),
OS_NAME("os.name"),
OS_NAME("os.name", alwaysDirectAccess = true),
TMP_DIR("java.io.tmpdir"),
USER_HOME("user.home"),
JAVA_VERSION("java.specification.version"),
JAVA_HOME("java.home"),
JAVA_CLASS_PATH("java.class.path"),
USER_HOME("user.home", alwaysDirectAccess = true),
JAVA_VERSION("java.specification.version", alwaysDirectAccess = true),
JAVA_HOME("java.home", alwaysDirectAccess = true),
JAVA_CLASS_PATH("java.class.path", alwaysDirectAccess = true),
;
var value
get() = (systemPropertyGetter ?: System::getProperty)(property)
private fun <T> getProperFunction(custom: T?, default: T): T {
if (alwaysDirectAccess) return default
return custom ?: default
}
var value: String?
get() {
return getProperFunction(systemPropertyGetter, System::getProperty)(property)
}
set(value) {
(systemPropertySetter ?: System::setProperty)(property, value!!)
getProperFunction(systemPropertySetter, System::setProperty)(property, value!!)
}
val safeValue
get() = value ?: error("No value for $property system property")
fun clear(): String? = (systemPropertyCleaner ?: System::clearProperty)(property)
fun clear(): String? = getProperFunction(systemPropertyCleaner, System::clearProperty)(property)
companion object {
var systemPropertyGetter: ((String) -> String?)? = null
@@ -77,6 +77,17 @@ class ConfigurationCacheIT : AbstractConfigurationCacheIT() {
)
}
@Test
fun testInstantExecutionWithIncludedBuildPlugin() =
with(Project("instantExecutionWithIncludedBuildPlugin", gradleVersionRequirement = GradleVersionRequired.AtLeast("6.8"))) {
setupWorkingDir()
testConfigurationCacheOf(
"build", executedTaskNames = listOf(
":compileKotlin",
)
)
}
@Test
fun testInstantExecutionForJs() = with(Project("instantExecutionToJs")) {
testConfigurationCacheOf("assemble", executedTaskNames = asList(":compileKotlin2Js"))
@@ -0,0 +1,20 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id("foo-plugin")
}
apply plugin: 'kotlin'
repositories {
mavenLocal()
mavenCentral()
}
@@ -0,0 +1,27 @@
buildscript {
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
plugins {
id 'java-gradle-plugin'
}
apply plugin: 'kotlin'
repositories {
mavenLocal()
mavenCentral()
}
gradlePlugin {
plugins.register("foo-plugin") {
id = "foo-plugin"
implementationClass = "FooPlugin"
}
}
@@ -0,0 +1,7 @@
import org.gradle.api.Plugin
import org.gradle.api.Project
class FooPlugin : Plugin<Project> {
override fun apply(project: Project) {
}
}
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.compilerRunner
import org.gradle.api.Project
import org.gradle.api.invocation.Gradle
import org.gradle.api.provider.MapProperty
import org.gradle.api.provider.Provider
@@ -22,22 +21,25 @@ internal abstract class CompilerSystemPropertiesService : BuildService<CompilerS
private val properties by lazy { parameters.properties.get().mapValues { it.value.orNull }.toMutableMap() }
fun startIntercept() {
if (!parameters.properties.isPresent) return
if (parameters.properties.get().isEmpty()) return
CompilerSystemProperties.systemPropertyGetter = {
if (it in properties) properties[it] else System.getProperty(it)
}
CompilerSystemProperties.systemPropertySetter = setter@{ key, value ->
if (key !in properties) {
return@setter System.setProperty(key, value)
}
val oldValue = properties[key]
if (oldValue == value) return@setter oldValue
properties[key] = value
System.setProperty(key, value)
oldValue
}
CompilerSystemProperties.systemPropertyCleaner = {
CompilerSystemProperties.systemPropertyCleaner = cleaner@{
if (it !in properties) {
return@cleaner System.clearProperty(it)
}
val oldValue = properties[it]
properties.remove(it)
System.clearProperty(it)
oldValue
}
}
@@ -49,22 +51,19 @@ internal abstract class CompilerSystemPropertiesService : BuildService<CompilerS
}
companion object {
private val Project.isBuildSrc get() = name == ":buildSrc"
fun registerIfAbsent(gradle: Gradle): Provider<CompilerSystemPropertiesService> =
gradle.sharedServices.registerIfAbsent(
"${CompilerSystemPropertiesService::class.java.canonicalName}_${CompilerSystemPropertiesService::class.java.classLoader.hashCode()}",
CompilerSystemPropertiesService::class.java
) { service ->
val rootProject = gradle.rootProject
if (rootProject.isBuildSrc && isConfigurationCacheAvailable(gradle)) {
service.parameters.properties.set(
CompilerSystemProperties.values()
.associate {
it.property to rootProject.providers.systemProperty(it.property).forUseAtConfigurationTime()
}.toMap()
)
}
fun registerIfAbsent(gradle: Gradle): Provider<CompilerSystemPropertiesService> = gradle.sharedServices.registerIfAbsent(
"${CompilerSystemPropertiesService::class.java.canonicalName}_${CompilerSystemPropertiesService::class.java.classLoader.hashCode()}",
CompilerSystemPropertiesService::class.java
) { service ->
if (isConfigurationCacheAvailable(gradle)) {
service.parameters.properties.set(
CompilerSystemProperties.values()
.filterNot { it.alwaysDirectAccess }
.associate {
it.property to gradle.rootProject.providers.systemProperty(it.property).forUseAtConfigurationTime()
}.toMap()
)
}
}
}
}