@@ -5,10 +5,7 @@ package org.jetbrains.dokka.gradle.adapters
55
66import com.android.build.api.variant.AndroidComponentsExtension
77import com.android.build.api.variant.Variant
8- import org.gradle.api.Named
9- import org.gradle.api.NamedDomainObjectContainer
10- import org.gradle.api.Plugin
11- import org.gradle.api.Project
8+ import org.gradle.api.*
129import org.gradle.api.file.ConfigurableFileCollection
1310import org.gradle.api.file.FileCollection
1411import org.gradle.api.logging.Logger
@@ -22,6 +19,7 @@ import org.gradle.kotlin.dsl.*
2219import org.jetbrains.dokka.gradle.DokkaBasePlugin
2320import org.jetbrains.dokka.gradle.DokkaExtension
2421import org.jetbrains.dokka.gradle.adapters.KotlinAdapter.Companion.currentKotlinToolingVersion
22+ import org.jetbrains.dokka.gradle.adapters.KotlinAdapter.Companion.logKgpClassNotFoundWarning
2523import org.jetbrains.dokka.gradle.engine.parameters.DokkaSourceSetSpec
2624import org.jetbrains.dokka.gradle.engine.parameters.KotlinPlatform
2725import org.jetbrains.dokka.gradle.engine.parameters.SourceSetIdSpec
@@ -33,12 +31,9 @@ import org.jetbrains.kotlin.commonizer.stdlib
3331import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
3432import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension
3533import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension
36- import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation
34+ import org.jetbrains.kotlin.gradle.plugin.*
3735import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation.Companion.MAIN_COMPILATION_NAME
38- import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType
3936import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType.androidJvm
40- import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet
41- import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion
4237import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeCompilation
4338import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation
4439import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation
@@ -62,17 +57,6 @@ abstract class KotlinAdapter @Inject constructor(
6257 override fun apply (project : Project ) {
6358 logger.info(" Applying $dkaName to ${project.path} " )
6459
65- project.plugins.withType<DokkaBasePlugin >().configureEach {
66- project.pluginManager.apply {
67- withPlugin(PluginId .KotlinAndroid ) { exec(project) }
68- withPlugin(PluginId .KotlinJs ) { exec(project) }
69- withPlugin(PluginId .KotlinJvm ) { exec(project) }
70- withPlugin(PluginId .KotlinMultiplatform ) { exec(project) }
71- }
72- }
73- }
74-
75- private fun exec (project : Project ) {
7660 val kotlinExtension = project.findKotlinExtension()
7761 if (kotlinExtension == null ) {
7862 logger.info(" Skipping applying $dkaName in ${project.path} - could not find KotlinProjectExtension" )
@@ -210,6 +194,109 @@ abstract class KotlinAdapter @Inject constructor(
210194 val kgpVersion = getKotlinPluginVersion(logger)
211195 KotlinToolingVersion (kgpVersion)
212196 }
197+
198+ /* *
199+ * Applies [KotlinAdapter] to the current project when any plugin of type [KotlinBasePlugin]
200+ * is applied.
201+ *
202+ * [KotlinBasePlugin] is the parent type for the Kotlin/JVM, Kotlin/Multiplatform, Kotlin/JS plugins,
203+ * as well as AGP's kotlin-built-in plugin.
204+ */
205+ internal fun applyTo (project : Project ) {
206+ findKotlinBasePlugins(project)?.all {
207+ project.pluginManager.apply (KotlinAdapter ::class )
208+ }
209+ }
210+
211+ /* *
212+ * Tries fetching all plugins with type [KotlinBasePlugin],
213+ * returning `null` if the class is not available in the current classloader.
214+ *
215+ * (The class might not be available if the current project is a Java or Android project,
216+ * or the buildscripts have an inconsistent classpath https://github.com/gradle/gradle/issues/27218)
217+ */
218+ private fun findKotlinBasePlugins (project : Project ): DomainObjectCollection <KotlinBasePlugin >? {
219+ return try {
220+ project.plugins.withType<KotlinBasePlugin >()
221+ } catch (ex: Throwable ) {
222+ when (ex) {
223+ is ClassNotFoundException ,
224+ is NoClassDefFoundError -> {
225+ logKgpClassNotFoundWarning(
226+ project,
227+ kotlinBasePluginNotFoundException = ex,
228+ )
229+ null
230+ }
231+
232+ else -> throw ex
233+ }
234+ }
235+ }
236+
237+ /* *
238+ * Check all plugins to see if they are a subtype of [KotlinBasePlugin].
239+ * If any are, log a warning.
240+ *
241+ * Also, log an info message with the stacktrace of [kotlinBasePluginNotFoundException].
242+ *
243+ * ##### Motivation
244+ *
245+ * If the buildscript classpath is inconsistent, it might not be possible for DGP
246+ * to react to KGP because the [KotlinBasePlugin] class can't be loaded.
247+ * If so, DGP will be lenient and not cause errors,
248+ * but it must display a prominent warning to help users find the problem.
249+ *
250+ * @param[kotlinBasePluginNotFoundException] The exception thrown when [KotlinBasePlugin] is not available.
251+ */
252+ private fun logKgpClassNotFoundWarning (
253+ project : Project ,
254+ kotlinBasePluginNotFoundException : Throwable ,
255+ ) {
256+ // hide the stacktrace at `--info` log level, to avoid flooding the log
257+ logger.info(
258+ " Dokka Gradle Plugin could not load KotlinBasePlugin in ${project.displayName} " ,
259+ kotlinBasePluginNotFoundException,
260+ )
261+
262+ /* *
263+ * Keep track of which projects have been warned by [logKgpClassNotFoundWarning],
264+ * otherwise it'll log the same warning multiple times for the same project, which is annoying.
265+ *
266+ * The warning can be logged multiple times if a project has both
267+ * `org.jetbrains.dokka` and `org.jetbrains.dokka-javadoc` applied.
268+ */
269+ fun checkIfAlreadyWarned (): Boolean {
270+ val key = " DOKKA INTERNAL - projectsWithKgpClassNotFoundWarningApplied"
271+ if (project.extra.has(key)) {
272+ return true
273+ } else {
274+ project.extra.set(key, true )
275+ return false
276+ }
277+ }
278+
279+ PluginIds .kotlin.forEach { pluginId ->
280+ project.pluginManager.withPlugin(pluginId) {
281+ if (checkIfAlreadyWarned()) return @withPlugin
282+ logger.warn(
283+ """
284+ |warning: Dokka could not load KotlinBasePlugin in ${project.displayName} , even though plugin $pluginId is applied.
285+ |The most common cause is a Gradle limitation: the plugins applied to subprojects should be consistent.
286+ |Please try the following:
287+ |1. Apply the Dokka and Kotlin plugins to the root project using the `plugins {}` DSL.
288+ | (If the root project does not need the plugins, use 'apply false')
289+ |2. Remove the Dokka and Kotlin plugins versions in the subprojects.
290+ |For more information see:
291+ | - https://docs.gradle.org/current/userguide/plugins_intermediate.html#sec:plugins_apply
292+ | - https://github.com/gradle/gradle/issues/25616
293+ | - https://github.com/gradle/gradle/issues/35117
294+ |Please report any feedback or problems https://kotl.in/dokka-issues
295+ |""" .trimMargin()
296+ )
297+ }
298+ }
299+ }
213300 }
214301}
215302
@@ -298,10 +385,10 @@ private class KotlinCompilationDetailsBuilder(
298385 ): Provider <Set <AndroidVariantInfo >> {
299386 val androidVariants = objects.setProperty(AndroidVariantInfo ::class )
300387
301- project.pluginManager. apply {
302- withPlugin(PluginId . AndroidBase ) { collectAndroidVariants(project, androidVariants) }
303- withPlugin( PluginId . AndroidApplication ) { collectAndroidVariants(project, androidVariants) }
304- withPlugin( PluginId . AndroidLibrary ) { collectAndroidVariants(project, androidVariants) }
388+ PluginIds .android.forEach { pluginId ->
389+ project.pluginManager. withPlugin(pluginId ) {
390+ collectAndroidVariants(project, androidVariants)
391+ }
305392 }
306393
307394 return androidVariants
0 commit comments