aboutsummaryrefslogtreecommitdiff
path: root/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks')
-rw-r--r--dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt187
-rw-r--r--dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt62
-rw-r--r--dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt22
-rw-r--r--dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt156
4 files changed, 427 insertions, 0 deletions
diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt
new file mode 100644
index 00000000..b27acbc5
--- /dev/null
+++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt
@@ -0,0 +1,187 @@
+package org.jetbrains.dokka.dokkatoo.tasks
+
+import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin.Companion.jsonMapper
+import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaGeneratorParametersSpec
+import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs
+import org.jetbrains.dokka.dokkatoo.dokka.parameters.builders.DokkaParametersBuilder
+import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer
+import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi
+import org.jetbrains.dokka.dokkatoo.workers.DokkaGeneratorWorker
+import java.io.IOException
+import javax.inject.Inject
+import kotlinx.serialization.json.JsonElement
+import org.gradle.api.file.ConfigurableFileCollection
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.file.RegularFileProperty
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.model.ReplacedBy
+import org.gradle.api.provider.ListProperty
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.*
+import org.gradle.kotlin.dsl.*
+import org.gradle.process.JavaForkOptions
+import org.gradle.workers.WorkerExecutor
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.toPrettyJsonString
+
+/**
+ * Executes the Dokka Generator, and produces documentation.
+ *
+ * The type of documentation generated is determined by the supplied Dokka Plugins in [generator].
+ */
+@CacheableTask
+abstract class DokkatooGenerateTask
+@DokkatooInternalApi
+@Inject
+constructor(
+ objects: ObjectFactory,
+ private val workers: WorkerExecutor,
+
+ /**
+ * Configurations for Dokka Generator Plugins. Must be provided from
+ * [org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.pluginsConfiguration].
+ */
+ pluginsConfiguration: DokkaPluginParametersContainer,
+) : DokkatooTask() {
+
+ @get:OutputDirectory
+ abstract val outputDirectory: DirectoryProperty
+
+ /**
+ * Classpath required to run Dokka Generator.
+ *
+ * Contains the Dokka Generator, Dokka plugins, and any transitive dependencies.
+ */
+ @get:Classpath
+ abstract val runtimeClasspath: ConfigurableFileCollection
+
+ @get:LocalState
+ abstract val cacheDirectory: DirectoryProperty
+
+ /**
+ * Generating a Dokka Module? Set this to [GenerationType.MODULE].
+ *
+ * Generating a Dokka Publication? [GenerationType.PUBLICATION].
+ */
+ @get:Input
+ abstract val generationType: Property<GenerationType>
+
+ /** @see org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.enabled */
+ @get:Input
+ abstract val publicationEnabled: Property<Boolean>
+
+ @get:Nested
+ val generator: DokkaGeneratorParametersSpec = objects.newInstance(pluginsConfiguration)
+
+ /** @see JavaForkOptions.getDebug */
+ @get:Input
+ abstract val workerDebugEnabled: Property<Boolean>
+ /** @see JavaForkOptions.getMinHeapSize */
+ @get:Input
+ @get:Optional
+ abstract val workerMinHeapSize: Property<String>
+ /** @see JavaForkOptions.getMaxHeapSize */
+ @get:Input
+ @get:Optional
+ abstract val workerMaxHeapSize: Property<String>
+ /** @see JavaForkOptions.jvmArgs */
+ @get:Input
+ abstract val workerJvmArgs: ListProperty<String>
+ @get:Internal
+ abstract val workerLogFile: RegularFileProperty
+
+ /**
+ * The [DokkaConfiguration] by Dokka Generator can be saved to a file for debugging purposes.
+ * To disable this behaviour set this property to `null`.
+ */
+ @DokkatooInternalApi
+ @get:Internal
+ abstract val dokkaConfigurationJsonFile: RegularFileProperty
+
+ enum class GenerationType {
+ MODULE,
+ PUBLICATION,
+ }
+
+ @TaskAction
+ internal fun generateDocumentation() {
+ val dokkaConfiguration = createDokkaConfiguration()
+ logger.info("dokkaConfiguration: $dokkaConfiguration")
+ dumpDokkaConfigurationJson(dokkaConfiguration)
+
+ logger.info("DokkaGeneratorWorker runtimeClasspath: ${runtimeClasspath.asPath}")
+
+ val workQueue = workers.processIsolation {
+ classpath.from(runtimeClasspath)
+ forkOptions {
+ defaultCharacterEncoding = "UTF-8"
+ minHeapSize = workerMinHeapSize.orNull
+ maxHeapSize = workerMaxHeapSize.orNull
+ enableAssertions = true
+ debug = workerDebugEnabled.get()
+ jvmArgs = workerJvmArgs.get()
+ }
+ }
+
+ workQueue.submit(DokkaGeneratorWorker::class) {
+ this.dokkaParameters.set(dokkaConfiguration)
+ this.logFile.set(workerLogFile)
+ }
+ }
+
+ /**
+ * Dump the [DokkaConfiguration] JSON to a file ([dokkaConfigurationJsonFile]) for debugging
+ * purposes.
+ */
+ private fun dumpDokkaConfigurationJson(
+ dokkaConfiguration: DokkaConfiguration,
+ ) {
+ val destFile = dokkaConfigurationJsonFile.asFile.orNull ?: return
+ destFile.parentFile.mkdirs()
+ destFile.createNewFile()
+
+ val compactJson = dokkaConfiguration.toPrettyJsonString()
+ val json = jsonMapper.decodeFromString(JsonElement.serializer(), compactJson)
+ val prettyJson = jsonMapper.encodeToString(JsonElement.serializer(), json)
+
+ destFile.writeText(prettyJson)
+
+ logger.info("[$path] Dokka Generator configuration JSON: ${destFile.toURI()}")
+ }
+
+ private fun createDokkaConfiguration(): DokkaConfiguration {
+ val outputDirectory = outputDirectory.get().asFile
+
+ val delayTemplateSubstitution = when (generationType.orNull) {
+ GenerationType.MODULE -> true
+ GenerationType.PUBLICATION -> false
+ null -> error("missing GenerationType")
+ }
+
+ val dokkaModuleDescriptors = dokkaModuleDescriptors()
+
+ return DokkaParametersBuilder.build(
+ spec = generator,
+ delayTemplateSubstitution = delayTemplateSubstitution,
+ outputDirectory = outputDirectory,
+ modules = dokkaModuleDescriptors,
+ cacheDirectory = cacheDirectory.asFile.orNull,
+ )
+ }
+
+ private fun dokkaModuleDescriptors(): List<DokkaModuleDescriptionKxs> {
+ return generator.dokkaModuleFiles.asFileTree
+ .matching { include("**/module_descriptor.json") }
+ .files.map { file ->
+ try {
+ val fileContent = file.readText()
+ jsonMapper.decodeFromString(
+ DokkaModuleDescriptionKxs.serializer(),
+ fileContent,
+ )
+ } catch (ex: Exception) {
+ throw IOException("Could not parse DokkaModuleDescriptionKxs from $file", ex)
+ }
+ }
+ }
+}
diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt
new file mode 100644
index 00000000..1247ebc7
--- /dev/null
+++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt
@@ -0,0 +1,62 @@
+package org.jetbrains.dokka.dokkatoo.tasks
+
+import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin.Companion.jsonMapper
+import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs
+import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi
+import javax.inject.Inject
+import kotlinx.serialization.encodeToString
+import org.gradle.api.file.*
+import org.gradle.api.provider.Property
+import org.gradle.api.tasks.*
+import org.gradle.api.tasks.PathSensitivity.RELATIVE
+
+/**
+ * Produces a Dokka Configuration that describes a single module of a multimodule Dokka configuration.
+ *
+ * @see org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs
+ */
+@CacheableTask
+abstract class DokkatooPrepareModuleDescriptorTask
+@DokkatooInternalApi
+@Inject
+constructor() : DokkatooTask() {
+
+ @get:OutputFile
+ abstract val dokkaModuleDescriptorJson: RegularFileProperty
+
+ @get:Input
+ abstract val moduleName: Property<String>
+
+ @get:Input
+ abstract val modulePath: Property<String>
+
+ @get:InputDirectory
+ @get:PathSensitive(RELATIVE)
+ abstract val moduleDirectory: DirectoryProperty
+
+ @get:InputFiles
+ @get:Optional
+ @get:PathSensitive(RELATIVE)
+ abstract val includes: ConfigurableFileCollection
+
+ @TaskAction
+ internal fun generateModuleConfiguration() {
+ val moduleName = moduleName.get()
+ val moduleDirectory = moduleDirectory.asFile.get()
+ val includes = includes.files
+ val modulePath = modulePath.get()
+
+ val moduleDesc = DokkaModuleDescriptionKxs(
+ name = moduleName,
+ sourceOutputDirectory = moduleDirectory,
+ includes = includes,
+ modulePath = modulePath,
+ )
+
+ val encodedModuleDesc = jsonMapper.encodeToString(moduleDesc)
+
+ logger.info("encodedModuleDesc: $encodedModuleDesc")
+
+ dokkaModuleDescriptorJson.get().asFile.writeText(encodedModuleDesc)
+ }
+}
diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt
new file mode 100644
index 00000000..c125a64e
--- /dev/null
+++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt
@@ -0,0 +1,22 @@
+package org.jetbrains.dokka.dokkatoo.tasks
+
+import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin
+import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi
+import javax.inject.Inject
+import org.gradle.api.DefaultTask
+import org.gradle.api.model.ObjectFactory
+import org.gradle.api.tasks.CacheableTask
+
+/** Base Dokkatoo task */
+@CacheableTask
+abstract class DokkatooTask
+@DokkatooInternalApi
+constructor() : DefaultTask() {
+
+ @get:Inject
+ abstract val objects: ObjectFactory
+
+ init {
+ group = DokkatooBasePlugin.TASK_GROUP
+ }
+}
diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt
new file mode 100644
index 00000000..c281ce56
--- /dev/null
+++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt
@@ -0,0 +1,156 @@
+package org.jetbrains.dokka.dokkatoo.tasks
+
+import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi
+import org.jetbrains.dokka.dokkatoo.internal.appendPath
+import org.jetbrains.dokka.dokkatoo.tasks.LogHtmlPublicationLinkTask.Companion.ENABLE_TASK_PROPERTY_NAME
+import java.net.URI
+import java.net.http.HttpClient
+import java.net.http.HttpRequest
+import java.net.http.HttpResponse
+import java.time.Duration
+import javax.inject.Inject
+import org.gradle.api.provider.Property
+import org.gradle.api.provider.ProviderFactory
+import org.gradle.api.provider.ValueSource
+import org.gradle.api.provider.ValueSourceParameters
+import org.gradle.api.tasks.Console
+import org.gradle.api.tasks.TaskAction
+import org.gradle.kotlin.dsl.*
+import org.gradle.work.DisableCachingByDefault
+
+/**
+ * Prints an HTTP link in the console when the HTML publication is generated.
+ *
+ * The HTML publication requires a web server, since it loads resources via javascript.
+ *
+ * By default, it uses
+ * [IntelliJ's built-in server](https://www.jetbrains.com/help/idea/php-built-in-web-server.html)
+ * to host the file.
+ *
+ * This task can be disabled using the [ENABLE_TASK_PROPERTY_NAME] project property.
+ */
+@DisableCachingByDefault(because = "logging-only task")
+abstract class LogHtmlPublicationLinkTask
+@Inject
+@DokkatooInternalApi
+constructor(
+ providers: ProviderFactory
+) : DokkatooTask() {
+
+ @get:Console
+ abstract val serverUri: Property<String>
+
+ /**
+ * Path to the `index.html` of the publication. Will be appended to [serverUri].
+ *
+ * The IntelliJ built-in server requires a relative path originating from the _parent_ directory
+ * of the IntelliJ project.
+ *
+ * For example,
+ *
+ * * given an IntelliJ project path of
+ * ```
+ * /Users/rachel/projects/my-project/
+ * ```
+ * * and the publication is generated with an index file
+ * ```
+ * /Users/rachel/projects/my-project/docs/build/dokka/html/index.html
+ * ````
+ * * then IntelliJ requires the [indexHtmlPath] is
+ * ```
+ * my-project/docs/build/dokka/html/index.html
+ * ```
+ * * so that (assuming [serverUri] is `http://localhost:63342`) the logged URL is
+ * ```
+ * http://localhost:63342/my-project/docs/build/dokka/html/index.html
+ * ```
+ */
+ @get:Console
+ abstract val indexHtmlPath: Property<String>
+
+ init {
+ // don't assign a group. This task is a 'finalizer' util task, so it doesn't make sense
+ // to display this task prominently.
+ group = "other"
+
+ val serverActive = providers.of(ServerActiveCheck::class) {
+ parameters.uri.convention(serverUri)
+ }
+ super.onlyIf("server URL is reachable") { serverActive.get() }
+
+ val logHtmlPublicationLinkTaskEnabled = providers
+ .gradleProperty(ENABLE_TASK_PROPERTY_NAME)
+ .orElse("true")
+ .map(String::toBoolean)
+ super.onlyIf("task is enabled via property") {
+ logHtmlPublicationLinkTaskEnabled.get()
+ }
+ }
+
+ @TaskAction
+ fun exec() {
+ val serverUri = serverUri.orNull
+ val filePath = indexHtmlPath.orNull
+
+ if (serverUri != null && !filePath.isNullOrBlank()) {
+ val link = URI(serverUri).appendPath(filePath).toString()
+
+ logger.lifecycle("Generated Dokka HTML publication: $link")
+ }
+ }
+
+ /**
+ * Check if the server URI that can host the generated Dokka HTML publication is accessible.
+ *
+ * Use the [HttpClient] included with Java 11 to avoid bringing in a new dependency for such
+ * a small util.
+ *
+ * The check uses a [ValueSource] source to attempt to be compatible with Configuration Cache, but
+ * I'm not certain that this is necessary, or if a [ValueSource] is the best way to achieve it.
+ */
+ internal abstract class ServerActiveCheck : ValueSource<Boolean, ServerActiveCheck.Parameters> {
+
+ interface Parameters : ValueSourceParameters {
+ /** E.g. `http://localhost:63342` */
+ val uri: Property<String>
+ }
+
+ override fun obtain(): Boolean {
+ try {
+ val uri = URI.create(parameters.uri.get())
+ val client = HttpClient.newHttpClient()
+ val request = HttpRequest
+ .newBuilder()
+ .uri(uri)
+ .timeout(Duration.ofSeconds(1))
+ .GET()
+ .build()
+ val response = client.send(request, HttpResponse.BodyHandlers.ofString())
+
+ // don't care about the status - only if the server is available
+ return response.statusCode() > 0
+ } catch (ex: Exception) {
+ return false
+ }
+ }
+ }
+
+ companion object {
+ /**
+ * Control whether the [LogHtmlPublicationLinkTask] task is enabled. Useful for disabling the
+ * task locally, or in CI/CD, or for tests.
+ *
+ * ```properties
+ * #$GRADLE_USER_HOME/gradle.properties
+ * org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false
+ * ```
+ *
+ * or via an environment variable
+ *
+ * ```env
+ * ORG_GRADLE_PROJECT_org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false
+ * ```
+ */
+ const val ENABLE_TASK_PROPERTY_NAME = "org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled"
+ }
+}