Skip to content

Commit

Permalink
Make PropertyResolver public + productionize API a bit (#715)
Browse files Browse the repository at this point in the history
This allows use for it from other plugins and buildscripts

<!--
  ⬆ Put your description above this! ⬆

  Please be descriptive and detailed.
  
Please read our [Contributing
Guidelines](https://github.com/tinyspeck/slack-gradle-plugin/blob/main/.github/CONTRIBUTING.md)
and [Code of Conduct](https://slackhq.github.io/code-of-conduct).

Don't worry about deleting this, it's not visible in the PR!
-->
  • Loading branch information
ZacSweers authored Jan 11, 2024
1 parent 9438999 commit 3ca7efc
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 98 deletions.
20 changes: 10 additions & 10 deletions slack-plugin/src/main/kotlin/slack/gradle/SlackProperties.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import slack.gradle.util.sneakyNull
/**
* (Mostly Gradle) properties for configuration of SlackPlugin.
*
* Order attempted as described by [PropertyResolver.safeProperty].
* Order attempted as described by [PropertyResolver.providerFor].
*/
public class SlackProperties
internal constructor(
Expand All @@ -46,10 +46,10 @@ internal constructor(
private fun fileProperty(key: String): File? = optionalStringProperty(key)?.let(project::file)

private fun intProperty(key: String, defaultValue: Int = -1): Int =
resolver.intProperty(key, defaultValue = defaultValue)
resolver.intValue(key, defaultValue = defaultValue)

private fun booleanProperty(key: String, defaultValue: Boolean = false): Boolean =
resolver.booleanProperty(key, defaultValue = defaultValue)
resolver.booleanValue(key, defaultValue = defaultValue)

private fun stringProperty(key: String): String =
optionalStringProperty(key)
Expand All @@ -63,7 +63,7 @@ internal constructor(
defaultValue: String? = null,
blankIsNull: Boolean = false
): String? =
resolver.optionalStringProperty(key, defaultValue = defaultValue)?.takeUnless {
resolver.optionalStringValue(key, defaultValue = defaultValue)?.takeUnless {
blankIsNull && it.isBlank()
}

Expand Down Expand Up @@ -249,7 +249,7 @@ internal constructor(
* alternative to running gradle with `--info` or `--debug`.
*/
public val verboseLogging: Boolean
get() = resolver.booleanProperty("sgp.logging.verbose")
get() = resolver.booleanValue("sgp.logging.verbose")

/** Flag to enable verbose logging in unit tests. */
public val testVerboseLogging: Boolean
Expand Down Expand Up @@ -562,26 +562,26 @@ internal constructor(

/** Global boolean that controls whether mod score is enabled on this project. */
public val modScoreGlobalEnabled: Boolean
get() = resolver.booleanProperty("slack.gradle.config.modscore.enabled")
get() = resolver.booleanValue("slack.gradle.config.modscore.enabled")

/**
* Per-project boolean that allows for excluding this project from mod score.
*
* Note this should only be applied to projects that cannot be depended on.
*/
public val modScoreIgnore: Boolean
get() = resolver.booleanProperty("slack.gradle.config.modscore.ignore")
get() = resolver.booleanValue("slack.gradle.config.modscore.ignore")

/** Experimental flag to enable logging thermal throttling on macOS devices. */
public val logThermals: Boolean
get() = resolver.booleanProperty("slack.log-thermals", defaultValue = false)
get() = resolver.booleanValue("slack.log-thermals", defaultValue = false)

/**
* Enables applying common build tags. We are likely to remove these in favor of Gradle's
* first-party plugin.
*/
public val applyCommonBuildTags: Boolean
get() = resolver.booleanProperty("sgp.ge.apply-common-build-tags", defaultValue = true)
get() = resolver.booleanValue("sgp.ge.apply-common-build-tags", defaultValue = true)

/**
* Enables eager configuration of [SgpArtifact] publishing in subprojects. This is behind a flag
Expand All @@ -590,7 +590,7 @@ internal constructor(
* @see StandardProjectConfigurations.setUpSubprojectArtifactPublishing
*/
public val eagerlyConfigureArtifactPublishing: Boolean
get() = resolver.booleanProperty("sgp.artifacts.configure-eagerly", defaultValue = false)
get() = resolver.booleanValue("sgp.artifacts.configure-eagerly", defaultValue = false)

/** Defines a required vendor for JDK toolchains. */
public val jvmVendor: Provider<String>
Expand Down
118 changes: 118 additions & 0 deletions slack-plugin/src/main/kotlin/slack/gradle/util/PropertyResolver.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2024 Slack Technologies, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package slack.gradle.util

import org.gradle.api.Project
import org.gradle.api.provider.Provider

/**
* A property resolver that handles multiple property sources.
*
* @property project The project to resolve properties for.
* @property startParameterProperty A provider of a property _only_ contained in the project's start
* parameters.
* @property globalLocalProperty A provider of a property _only_ contained in the root project's
* `local.properties`.
*/
public class PropertyResolver(
private val project: Project,
private val startParameterProperty: (String) -> Provider<String> = project.emptyStringProvider(),
private val globalLocalProperty: (String) -> Provider<String> = project.emptyStringProvider(),
) {

private val providers = project.providers

/**
* Returns a "safe" property [Provider] mechanism that handles multiple property sources in a
* hierarchical fashion.
*
* This checks in the following order of priority
* - project-local `local.properties`
* - project-local `gradle.properties`
* - root-project `local.properties`
* - root-project/global `gradle.properties`
*/
public fun providerFor(
key: String,
): Provider<String> =
startParameterProperty(key) // start parameters
.orElse(project.localProperty(key)) // project-local `local.properties`
.orElse(project.localGradleProperty(key)) // project-local `gradle.properties`
.orElse(globalLocalProperty(key)) // root-project `local.properties`
.orElse(project.providers.gradleProperty(key)) // root-project/global `gradle.properties`

public fun booleanValue(key: String, defaultValue: Boolean = false): Boolean {
return booleanProvider(key, defaultValue).get()
}

public fun booleanValue(key: String, defaultValue: Provider<Boolean>): Boolean {
return booleanProvider(key, defaultValue).get()
}

public fun booleanProvider(key: String, defaultValue: Boolean = false): Provider<Boolean> {
return booleanProvider(key, providers.provider { defaultValue })
}

public fun booleanProvider(key: String, defaultValue: Provider<Boolean>): Provider<Boolean> {
return booleanProvider(key).orElse(defaultValue)
}

public fun booleanProvider(
key: String,
): Provider<Boolean> {
return providerFor(key).mapToBoolean()
}

public fun intValue(key: String, defaultValue: Int = -1): Int {
return intProvider(key, defaultValue).get()
}

public fun intValue(key: String, defaultValue: Provider<Int>): Int {
return intProvider(key, defaultValue).get()
}

public fun intProvider(key: String, defaultValue: Int = -1): Provider<Int> {
return intProvider(key, providers.provider { defaultValue })
}

public fun intProvider(key: String, defaultValue: Provider<Int>): Provider<Int> {
return providerFor(key).mapToInt().orElse(defaultValue)
}

public fun stringValue(key: String): String {
return optionalStringValue(key)
?: error("No property for '$key' found and no default value was provided.")
}

public fun stringValue(key: String, defaultValue: String): String {
return optionalStringValue(key, defaultValue)
?: error("No property for '$key' found and no default value was provided.")
}

public fun optionalStringValue(key: String, defaultValue: String? = null): String? {
return providerFor(key).orNull ?: defaultValue
}

public fun optionalStringProvider(key: String): Provider<String> {
return optionalStringProvider(key, null)
}

public fun optionalStringProvider(key: String, defaultValue: String? = null): Provider<String> {
return providerFor(key).let { defaultValue?.let { providers.provider { defaultValue } } ?: it }
}
}

private fun Project.emptyStringProvider(): (String) -> Provider<String> = { provider { null } }
88 changes: 0 additions & 88 deletions slack-plugin/src/main/kotlin/slack/gradle/util/PropertyUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -125,94 +125,6 @@ internal fun Project.localGradleProperty(key: String): Provider<String> {
)
}

internal class PropertyResolver(
private val project: Project,
private val startParameterProperty: (String) -> Provider<String>,
private val globalLocalProperty: (String) -> Provider<String>,
) {

private val providers = project.providers

/**
* A "safe" property access mechanism that handles multiple property sources.
*
* This checks in the following order of priority
* - project-local `local.properties`
* - project-local `gradle.properties`
* - root-project `local.properties`
* - root-project/global `gradle.properties`
*/
fun safeProperty(
key: String,
): Provider<String> =
startParameterProperty(key) // start parameters
.orElse(project.localProperty(key)) // project-local `local.properties`
.orElse(project.localGradleProperty(key)) // project-local `gradle.properties`
.orElse(globalLocalProperty(key)) // root-project `local.properties`
.orElse(project.providers.gradleProperty(key)) // root-project/global `gradle.properties`

// TODO rename these scalar types to <type>Value
internal fun booleanProperty(key: String, defaultValue: Boolean = false): Boolean {
return booleanProvider(key, defaultValue).get()
}

internal fun booleanProperty(key: String, defaultValue: Provider<Boolean>): Boolean {
return booleanProvider(key, defaultValue).get()
}

internal fun booleanProvider(key: String, defaultValue: Boolean = false): Provider<Boolean> {
return booleanProvider(key, providers.provider { defaultValue })
}

internal fun booleanProvider(key: String, defaultValue: Provider<Boolean>): Provider<Boolean> {
return booleanProvider(key).orElse(defaultValue)
}

internal fun booleanProvider(
key: String,
): Provider<Boolean> {
return safeProperty(key).mapToBoolean()
}

internal fun intProperty(key: String, defaultValue: Int = -1): Int {
return intProvider(key, defaultValue).get()
}

internal fun intProperty(key: String, defaultValue: Provider<Int>): Int {
return intProvider(key, defaultValue).get()
}

internal fun intProvider(key: String, defaultValue: Int = -1): Provider<Int> {
return intProvider(key, providers.provider { defaultValue })
}

internal fun intProvider(key: String, defaultValue: Provider<Int>): Provider<Int> {
return safeProperty(key).mapToInt().orElse(defaultValue)
}

internal fun stringProperty(key: String): String {
return optionalStringProperty(key)
?: error("No property for $key found and no default value was provided.")
}

internal fun stringProperty(key: String, defaultValue: String): String {
return optionalStringProperty(key, defaultValue)
?: error("No property for $key found and no default value was provided.")
}

internal fun optionalStringProperty(key: String, defaultValue: String? = null): String? {
return safeProperty(key).orNull ?: defaultValue
}

internal fun optionalStringProvider(key: String): Provider<String> {
return optionalStringProvider(key, null)
}

internal fun optionalStringProvider(key: String, defaultValue: String? = null): Provider<String> {
return safeProperty(key).let { defaultValue?.let { providers.provider { defaultValue } } ?: it }
}
}

internal fun Provider<String>.mapToBoolean(): Provider<Boolean> {
return map(String::toBoolean)
}
Expand Down

0 comments on commit 3ca7efc

Please sign in to comment.