Skip to content

Commit

Permalink
Update DGP plugin mode flags
Browse files Browse the repository at this point in the history
Use `org.jetbrains.dokka.experimental.gradle.pluginMode=[V1Enabled|V2EnabledWithHelpers|V2Enabled]` to control the behaviour.

Use `org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn=[true|false]` to enable/disable the warning logs.

KT-71236
  • Loading branch information
adam-enko committed Sep 23, 2024
1 parent 7733051 commit 82e7ca3
Show file tree
Hide file tree
Showing 11 changed files with 377 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import org.gradle.api.provider.Provider
import org.gradle.api.services.BuildService
import org.gradle.api.services.BuildServiceParameters
import org.gradle.kotlin.dsl.extra
import org.gradle.kotlin.dsl.provideDelegate
import org.jetbrains.dokka.gradle.internal.PluginFeaturesService.PluginMode.*
import java.io.File
import java.util.*

Expand All @@ -25,62 +27,102 @@ import java.util.*
internal abstract class PluginFeaturesService : BuildService<PluginFeaturesService.Params> {

interface Params : BuildServiceParameters {
/** @see PluginFeaturesService.v2PluginEnabled */
val v2PluginEnabled: Property<Boolean>

/** @see [PluginFeaturesService.v2PluginNoWarn] */
val v2PluginNoWarn: Property<Boolean>

/** @see [PluginFeaturesService.v2PluginMigrationHelpersEnabled] */
val v2PluginMigrationHelpersEnabled: Property<Boolean>

/** @see [PluginFeaturesService.primaryService] */
val primaryService: Property<Boolean>

/** @see PluginFeaturesService.pluginMode */
val pluginMode: Property<String>

/** If `true`, suppress [pluginMode] messages. */
val pluginModeNoWarn: Property<Boolean>

/** If `true`, enable K2 analysis. */
val k2AnalysisEnabled: Property<Boolean>

/** If `true`, suppress the K2 analysis message. */
/** If `true`, suppress [k2AnalysisEnabled] messages. */
val k2AnalysisNoWarn: Property<Boolean>

/** [Project.getDisplayName] - only used for log messages. */
val projectDisplayName: Property<String>

/** [Project.getProjectDir] - only used for log messages. */
val projectDirectory: Property<String>
}

/**
* Designate this [BuildService] as 'primary', meaning it should log messages to users.
* Non-primary services should not log messages.
*
* Why? Because Gradle is buggy. Sometimes registering a BuildService fails.
* See https://github.com/gradle/gradle/issues/17559.
* If service registration fails then re-register the service, but with a distinct name
* (so it doesn't clash with the existing but inaccessible BuildService), and don't mark it as 'primary'.
* Why? Two reasons:
*
* 1. Sometimes registering a BuildService fails.
* See https://github.com/gradle/gradle/issues/17559.
* If service registration fails then re-register the service, but with a distinct name
* (so it doesn't clash with the existing but inaccessible BuildService),
* and don't mark it as 'primary' to avoid duplicate logging.
* 2. The method Gradle uses to generates accessors for pre-compiled script plugins
* (see [PluginFeaturesService.Companion.configureParamsDuringAccessorsGeneration])
* runs in a temporary project that is evaluated twice.
*
* @see org.jetbrains.dokka.gradle.internal.registerIfAbsent
*/
private val primaryService: Boolean get() = parameters.primaryService.getOrElse(false)
private val primaryService: Boolean
get() = parameters.primaryService.getOrElse(false)

/**
* Whether DGP should use V2 [org.jetbrains.dokka.gradle.DokkaBasePlugin].
*
* Otherwise, fallback to V1 [org.jetbrains.dokka.gradle.DokkaClassicPlugin].
* @see pluginMode
*/
internal val v2PluginEnabled: Boolean by lazy {
val v2PluginEnabled = parameters.v2PluginEnabled.getOrElse(false)
when (pluginMode) {
V1Enabled,
-> false

if (v2PluginEnabled) {
logV2PluginMessage()
} else {
logV1PluginMessage()
V2EnabledWithHelpers,
V2Enabled,
-> true
}
}

/**
* Determines the behaviour of DGP.
*
* If no valid value is detected then use [PluginMode.Default].
*/
private val pluginMode: PluginMode by lazy {
val parameterValue = parameters.pluginMode.getOrElse(PluginMode.Default.name)

val value = PluginMode.findOrNull(parameterValue)
?: run {
logger.warn("Invalid value for $PLUGIN_MODE_FLAG. Got '$parameterValue' but expected one of: ${PluginMode.values.map { it.name }}")
PluginMode.Default
}

logger.info { "Detected PluginMode: $parameterValue/$value in ${parameters.projectDisplayName.orNull} ${parameters.projectDirectory.orNull}" }

logPluginMessage(value)

v2PluginEnabled
value
}

/** If `true`, suppress any messages regarding V2 mode. */
private val v2PluginNoWarn: Boolean
get() = parameters.v2PluginNoWarn.getOrElse(false)
private val pluginModeNoWarn: Boolean by lazy {
parameters.pluginModeNoWarn.getOrElse(false)
}

private fun logPluginMessage(mode: PluginMode) {
when (mode) {
V1Enabled,
-> logV1PluginMessage()

V2Enabled,
V2EnabledWithHelpers,
-> logV2PluginMessage()
}
}

private fun logV1PluginMessage() {
if (primaryService) {
if (primaryService && !pluginModeNoWarn) {
logger.warn(
"""
|⚠ Warning: Dokka Gradle Plugin V1 mode is enabled
Expand All @@ -92,7 +134,7 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi
| https://kotl.in/dokka-gradle-migration
|
| Once you have prepared your project, enable V2 by adding
| $V2_PLUGIN_ENABLED_FLAG=true
| $PLUGIN_MODE_FLAG=${V2EnabledWithHelpers}
| to your project's `gradle.properties`
|
| Please report any feedback or problems to Dokka GitHub Issues
Expand All @@ -103,20 +145,20 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi
}

private fun logV2PluginMessage() {
if (primaryService && !v2PluginNoWarn) {
if (primaryService && !pluginModeNoWarn) {
logger.lifecycle(
"""
|Dokka Gradle Plugin V2 is enabled ♡
|
| We would appreciate your feedback!
| Please report any feedback or problems to Dokka GitHub Issues
| https://github.com/Kotlin/dokka/issues/
|
|
| If you need help or advice, check out the migration guide
| https://kotl.in/dokka-gradle-migration
|
|
| You can suppress this message by adding
| $V2_PLUGIN_NO_WARN_FLAG=true
| $PLUGIN_MODE_NO_WARN_FLAG_PRETTY=true
| to your project's `gradle.properties`
""".trimMargin().surroundWithBorder()
)
Expand Down Expand Up @@ -162,26 +204,41 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi
* @see addV2MigrationHelpers
*/
internal val v2PluginMigrationHelpersEnabled: Boolean by lazy {
parameters.v2PluginMigrationHelpersEnabled.getOrElse(true)
pluginMode == V2EnabledWithHelpers
}

/** Values for [pluginMode]. */
private enum class PluginMode {
V1Enabled,
V2EnabledWithHelpers,
V2Enabled,
;

companion object {
/** The default value, if [Params.pluginMode] is not set. */
val Default: PluginMode = V1Enabled

val values: Set<PluginMode> = values().toSet()

fun findOrNull(value: String): PluginMode? =
values.find { it.name == value }
}
}

companion object {
private val logger = Logging.getLogger(PluginFeaturesService::class.java)

/** @see [PluginFeaturesService.v2PluginEnabled] */
internal const val V2_PLUGIN_ENABLED_FLAG =
"org.jetbrains.dokka.experimental.gradlePlugin.enableV2"

/** @see [PluginFeaturesService.v2PluginNoWarn] */
internal const val V2_PLUGIN_NO_WARN_FLAG =
"$V2_PLUGIN_ENABLED_FLAG.nowarn"
/** @see [PluginFeaturesService.pluginMode] */
private const val PLUGIN_MODE_FLAG =
"org.jetbrains.dokka.experimental.gradle.pluginMode"

/** The same as [V2_PLUGIN_NO_WARN_FLAG], but it doesn't trigger spell-checks. */
private const val V2_PLUGIN_NO_WARN_FLAG_PRETTY =
"$V2_PLUGIN_ENABLED_FLAG.noWarn"
/** @see [PluginFeaturesService.pluginModeNoWarn] */
private const val PLUGIN_MODE_NO_WARN_FLAG =
"$PLUGIN_MODE_FLAG.nowarn"

private const val V2_PLUGIN_MIGRATION_HELPERS_FLAG =
"org.jetbrains.dokka.experimental.gradlePlugin.enableV2MigrationHelpers"
/** The same as [PLUGIN_MODE_NO_WARN_FLAG], but it doesn't trigger spell-checks. */
private const val PLUGIN_MODE_NO_WARN_FLAG_PRETTY =
"$PLUGIN_MODE_FLAG.noWarn"

private const val K2_ANALYSIS_ENABLED_FLAG =
"org.jetbrains.dokka.experimental.tryK2"
Expand Down Expand Up @@ -234,7 +291,7 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi
): Action<Params> {

/** Find a flag for [PluginFeaturesService]. */
fun getFlag(flag: String): Provider<Boolean> =
fun getFlag(flag: String): Provider<String> =
project.providers
.gradleProperty(flag)
.forUseAtConfigurationTimeCompat()
Expand All @@ -246,17 +303,23 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi
.provider { project.extra.properties[flag]?.toString() }
.forUseAtConfigurationTimeCompat()
)
.map(String::toBoolean)


return Action {
v2PluginEnabled.set(getFlag(V2_PLUGIN_ENABLED_FLAG))
v2PluginNoWarn.set(getFlag(V2_PLUGIN_NO_WARN_FLAG_PRETTY).orElse(getFlag(V2_PLUGIN_NO_WARN_FLAG)))
v2PluginMigrationHelpersEnabled.set(getFlag(V2_PLUGIN_MIGRATION_HELPERS_FLAG))
k2AnalysisEnabled.set(getFlag(K2_ANALYSIS_ENABLED_FLAG))
projectDirectory.set(project.projectDir.invariantSeparatorsPath)
projectDisplayName.set(project.displayName)

pluginMode.set(getFlag(PLUGIN_MODE_FLAG))
pluginModeNoWarn.set(
getFlag(PLUGIN_MODE_NO_WARN_FLAG)
.orElse(getFlag(PLUGIN_MODE_NO_WARN_FLAG_PRETTY))
.toBoolean()
)

k2AnalysisEnabled.set(getFlag(K2_ANALYSIS_ENABLED_FLAG).toBoolean())
k2AnalysisNoWarn.set(
getFlag(K2_ANALYSIS_NO_WARN_FLAG_PRETTY)
.orElse(getFlag(K2_ANALYSIS_NO_WARN_FLAG))
.toBoolean()
)

configureParamsDuringAccessorsGeneration(project)
Expand Down Expand Up @@ -288,7 +351,8 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi

// Disable all warnings, regardless of the discovered flag values.
// Log messages will be printed too soon and aren't useful for users.
v2PluginNoWarn.set(true)
primaryService.set(true)
pluginModeNoWarn.set(true)

// Because Gradle is generating accessors, it won't give us access to Gradle properties
// defined for the main project. So, we must discover `gradle.properties` ourselves.
Expand All @@ -302,11 +366,8 @@ internal abstract class PluginFeaturesService : BuildService<PluginFeaturesServi

// These are the only flags that are important when Gradle is generating accessors,
// because they control what accessors DGP registers.
properties[V2_PLUGIN_ENABLED_FLAG]?.toString()?.toBoolean()?.let {
v2PluginEnabled.set(it)
}
properties[V2_PLUGIN_MIGRATION_HELPERS_FLAG]?.toString()?.toBoolean()?.let {
v2PluginMigrationHelpersEnabled.set(it)
properties[PLUGIN_MODE_FLAG]?.toString()?.let {
pluginMode.set(it)
}
}
} catch (t: Throwable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,11 @@ internal fun <T> Provider<T>.forUseAtConfigurationTimeCompat(): Provider<T> =
} else {
this
}

/**
* Convert a String Provider to a Boolean Provider.
*
* @see String.toBoolean
*/
internal fun Provider<String>.toBoolean(): Provider<Boolean> =
map(String::toBoolean)
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,8 @@ import org.gradle.api.logging.Logger
internal fun Logger.warn(msg: () -> String) {
if (isWarnEnabled) warn(msg())
}

/** Only evaluate and log [msg] when [Logger.isInfoEnabled] is `true`. */
internal fun Logger.info(msg: () -> String) {
if (isInfoEnabled) warn(msg())
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,11 @@
package org.jetbrains.dokka.gradle.utils

import org.gradle.api.Project
import org.jetbrains.dokka.gradle.internal.PluginFeaturesService.Companion.V2_PLUGIN_ENABLED_FLAG
import org.jetbrains.dokka.gradle.internal.PluginFeaturesService.Companion.V2_PLUGIN_NO_WARN_FLAG

fun Project.enableV2Plugin(
suppressV2Message: Boolean = true
): Project {
extensions.extraProperties.set(V2_PLUGIN_ENABLED_FLAG, true)
extensions.extraProperties.set(V2_PLUGIN_NO_WARN_FLAG, suppressV2Message)
extensions.extraProperties.set("org.jetbrains.dokka.experimental.gradle.pluginMode", "V2Enabled")
extensions.extraProperties.set("org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn", suppressV2Message)
return this
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,9 @@ class GradleProjectTest(

/** Dokka specific options. */
data class DokkaArgs(
var v2Plugin: Boolean? = true,
var v2PluginNoWarn: Boolean? = v2Plugin,
var v2MigrationHelpers: Boolean? = null,
/** @see org.jetbrains.dokka.gradle.internal.PluginFeaturesService.PluginMode */
var pluginMode: String? = "V2Enabled",
var pluginModeNoWarn: Boolean? = true,
var k2Analysis: Boolean? = null,
var k2AnalysisNoWarn: Boolean? = null,
var enableLogHtmlPublicationLink: Boolean? = false,
Expand Down Expand Up @@ -122,9 +122,8 @@ class GradleProjectTest(
}

with(dokka) {
putNotNull("org.jetbrains.dokka.experimental.gradlePlugin.enableV2", v2Plugin)
putNotNull("org.jetbrains.dokka.experimental.gradlePlugin.enableV2.noWarn", v2PluginNoWarn)
putNotNull("org.jetbrains.dokka.experimental.gradlePlugin.enableV2MigrationHelpers", v2MigrationHelpers)
putNotNull("org.jetbrains.dokka.experimental.gradle.pluginMode", pluginMode)
putNotNull("org.jetbrains.dokka.experimental.gradle.pluginMode.noWarn", pluginModeNoWarn)
putNotNull("org.jetbrains.dokka.experimental.tryK2", k2Analysis)
putNotNull("org.jetbrains.dokka.experimental.tryK2.noWarn", k2AnalysisNoWarn)
putNotNull("org.jetbrains.dokka.gradle.enableLogHtmlPublicationLink", enableLogHtmlPublicationLink)
Expand Down Expand Up @@ -163,7 +162,7 @@ class GradleProjectTest(
val runner: GradleRunner
get() = GradleRunner.create()
.withProjectDir(projectDir.toFile())
.updateGradleProperties(gradleProperties)
.writeGradleProperties(gradleProperties)

companion object {
val dokkaVersionOverride: String? by optionalSystemProperty()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package org.jetbrains.dokka.gradle.utils
import org.gradle.testkit.runner.BuildResult
import org.gradle.testkit.runner.BuildTask
import org.gradle.testkit.runner.GradleRunner
import java.util.*
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
Expand Down Expand Up @@ -35,7 +34,10 @@ inline fun GradleRunner.buildAndFail(
): Unit = buildAndFail().let(handleResult)


fun GradleRunner.updateGradleProperties(
/**
* Create, or overwrite, the `gradle.properties` file in [GradleRunner.getProjectDir].
*/
fun GradleRunner.writeGradleProperties(
arguments: GradleProjectTest.GradleProperties,
): GradleRunner {
val gradlePropertiesFile = projectDir.resolve("gradle.properties").apply {
Expand All @@ -45,13 +47,10 @@ fun GradleRunner.updateGradleProperties(
}
}

val gradleProperties = Properties().apply {
load(gradlePropertiesFile.inputStream())
}.entries.associate { it.key.toString() to it.value.toString() }.toMutableMap()
val gradleProperties = arguments.toGradleProperties()

arguments.toGradleProperties().forEach { (k, v) ->
gradleProperties[k] = v
}
// Avoid using java.util.Properties because it produces non-deterministic output (e.g. a timestamp),
// which negatively impacts caching.

gradlePropertiesFile.writeText(
gradleProperties
Expand Down
Loading

0 comments on commit 82e7ca3

Please sign in to comment.