Kotlin / dokka

API documentation engine for Kotlin
https://kotl.in/dokka
Apache License 2.0
3.45k stars 412 forks source link

[Bug] Cannot Configure Custom Dokka Plugin #3869

Open solonovamax opened 1 month ago

solonovamax commented 1 month ago

Describe the Bug

It is currently impossible to configure custom dokka plugins.

Expected Behaviour

It should be possible to, and documented how to, configure custom dokka plugins.

Actual Behaviour

When attempting to configure custom dokka plugins with the (entirely undocumented) DokkaPluginParametersBuilder class and pluginParameters function, eg.

dokka {
    pluginsConfiguration {
        pluginParameters("ca.solostudios.dokkascript.plugin.DokkaScriptsPlugin") {
            files("scripts") {
                from(fileTree(dokkaScripts))
            }
        }
    }
}

then the following error is produced:

Cannot create a DokkaPluginParametersBuilder because this type is not known to this container. Known types are: DokkaHtmlPluginParameters, DokkaVersioningPluginParameters

If DokkaPluginParametersBuilder then has a factory registered using

dokka {
    pluginsConfiguration {
        registerFactory(DokkaPluginParametersBuilder::class.java) { name ->
            objects.newInstance<DokkaPluginParametersBuilder>(name, name)
        }
    }
}

then, gradle will properly load, however gradle will not allow the dokkaGenerate task to run, and instead produces the following error:

Some problems were found with the configuration of task ':dokkaGenerateModuleHtml' (type 'DokkaGenerateModuleTask').
  - Type 'org.jetbrains.dokka.gradle.engine.plugins.DokkaPluginParametersBuilder' method 'jsonEncode()' should not be annotated with: @Internal.

Environment

adam-enko commented 1 month ago

Hi, thank you very much for the report and investigation.

For some context, the DokkaPluginParametersBuilder was an experiment from Dokkatoo that I started, but never finished, because I never found a need for it. It was intended as an improvement over the DGPv1 approach of manually writing JSON, and to correctly registering task inputs for Gradle task avoidance.

I've looked into DokkaPluginParametersBuilder further and aside from the issues you already found (bugs, missing docs, and missing functionality) I also found some more problems. I've discussed this with the Dokka team, and we're going to remove DokkaPluginParametersBuilder.

Alternative approach

For now you will have to avoid DokkaPluginParametersBuilder and instead implement a custom DokkaPluginParametersBaseSpec class. Such a class can be implemented in a build.gradle.kts. For example:

// build.gradle.kts

plugins {
  id("org.jetbrains.dokka") version "2.0.0-Beta"
}

val dokkaScripts = layout.projectDirectory.dir("dokka-scripts")

dokka {
  pluginsConfiguration {
    registerBinding(DokkaScriptsPluginParameters::class, DokkaScriptsPluginParameters::class)
    register<DokkaScriptsPluginParameters>("DokkaScripts") {
      scripts.from(dokkaScripts.asFileTree)
    }
  }
}

@OptIn(DokkaInternalApi::class)
abstract class DokkaScriptsPluginParameters @Inject constructor(
  name: String
) : DokkaPluginParametersBaseSpec(name, "ca.solostudios.dokkascript.plugin.DokkaScriptsPlugin") {

  @get:InputFiles
  @get:PathSensitive(PathSensitivity.RELATIVE)
  @get:NormalizeLineEndings
  abstract val scripts: ConfigurableFileCollection

  override fun jsonEncode(): String {
    val encodedScriptFiles = scripts.files.joinToString { "\"${it.canonicalFile.invariantSeparatorsPath}\"" }
    return """
      {
        "scripts": [ $encodedScriptFiles ]
      }
    """.trimIndent()
  }
}

If you find you need to re-use DokkaScriptsPluginParameters in multiple buildscripts, then I recommend moving the class into a shared location, like buildSrc (or another included-build for conventions).

Presently DokkaPluginParametersBaseSpec requires an opt-in for the internal Dokka Gradle API, but I'll look at removing this restriction.

Tasks

solonovamax commented 1 month ago

For now you will have to avoid DokkaPluginParametersBuilder and instead implement a custom DokkaPluginParametersBaseSpec class. Such a class can be implemented in a build.gradle.kts. For example:

// build.gradle.kts

plugins {
  id("org.jetbrains.dokka") version "2.0.0-Beta"
}

val dokkaScripts = layout.projectDirectory.dir("dokka-scripts")

dokka {
  pluginsConfiguration {
    registerBinding(DokkaScriptsPluginParameters::class, DokkaScriptsPluginParameters::class)
    register<DokkaScriptsPluginParameters>("DokkaScripts") {
      scripts.from(dokkaScripts.asFileTree)
    }
  }
}

@OptIn(DokkaInternalApi::class)
abstract class DokkaScriptsPluginParameters @Inject constructor(
  name: String
) : DokkaPluginParametersBaseSpec(name, "ca.solostudios.dokkascript.plugin.DokkaScriptsPlugin") {

  @get:InputFiles
  @get:PathSensitive(PathSensitivity.RELATIVE)
  @get:NormalizeLineEndings
  abstract val scripts: ConfigurableFileCollection

  override fun jsonEncode(): String {
    val encodedScriptFiles = scripts.files.joinToString { "\"${it.canonicalFile.invariantSeparatorsPath}\"" }
    return """
      {
        "scripts": [ $encodedScriptFiles ]
      }
    """.trimIndent()
  }
}

If you find you need to re-use DokkaScriptsPluginParameters in multiple buildscripts, then I recommend moving the class into a shared location, like buildSrc (or another included-build for conventions).

I mentioned this in the slack, however this does not work and gradle will error with this. this is because it wants the class to be a static class, and for whatever reason if a class is declared in a buildscript, it is a non-static inner class

I also don't particularly want to introduce a buildSrc or a build-logic to my project, as that will increase builds needlessly, as my project does not utilize multiple subprojects.

solonovamax commented 1 month ago

I mentioned this in the slack, however this does not work and gradle will error with this. this is because it wants the class to be a static class, and for whatever reason if a class is declared in a buildscript, it is a non-static inner class

I also don't particularly want to introduce a buildSrc or a build-logic to my project, as that will increase builds needlessly, as my project does not utilize multiple subprojects.

this is the specific issue, btw:

Could not create domain object 'scripts' (DokkaScriptsPluginParameters)
> Could not create an instance of type Build_gradle$DokkaScriptsPluginParameters.
   > Class Build_gradle.DokkaScriptsPluginParameters is a non-static inner class.

this is happening specifically because of the use of an ExtensiblePolymorphicDomainObjectContainer

adam-enko commented 1 month ago

this is the specific issue, btw:

Could not create domain object 'scripts' (DokkaScriptsPluginParameters)
> Could not create an instance of type Build_gradle$DokkaScriptsPluginParameters.
   > Class Build_gradle.DokkaScriptsPluginParameters is a non-static inner class.

this is happening specifically because of the use of an ExtensiblePolymorphicDomainObjectContainer

Thanks for sharing the specific error. It looks like that's a Gradle error https://github.com/gradle/gradle/issues/25494, caused by referencing something from the buildscript inside of the class.

I'm working on a demonstration project here to show how to configure a custom DokkaScriptsPluginParameters.

If something still isn't working, please share an example (or link to an open source project) and I can take a look.