1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
|
/*
* Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/
package org.jetbrains.dokka.base.generation
import kotlinx.coroutines.*
import org.jetbrains.dokka.CoreExtensions
import org.jetbrains.dokka.DokkaConfiguration
import org.jetbrains.dokka.DokkaException
import org.jetbrains.dokka.Timer
import org.jetbrains.dokka.base.DokkaBase
import org.jetbrains.dokka.generation.Generation
import org.jetbrains.dokka.generation.exitGenerationGracefully
import org.jetbrains.dokka.model.DModule
import org.jetbrains.dokka.pages.RootPageNode
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.plugin
import org.jetbrains.dokka.plugability.query
import org.jetbrains.dokka.transformers.sources.AsyncSourceToDocumentableTranslator
import org.jetbrains.dokka.utilities.parallelMap
import org.jetbrains.dokka.utilities.report
public class SingleModuleGeneration(private val context: DokkaContext) : Generation {
override fun Timer.generate() {
report("Validity check")
validityCheck(context)
// Step 1: translate sources into documentables & transform documentables (change internally)
report("Creating documentation models")
val modulesFromPlatforms = createDocumentationModels()
report("Transforming documentation model before merging")
val transformedDocumentationBeforeMerge = transformDocumentationModelBeforeMerge(modulesFromPlatforms)
report("Merging documentation models")
val transformedDocumentationAfterMerge = mergeDocumentationModels(transformedDocumentationBeforeMerge)
?: exitGenerationGracefully("Nothing to document")
report("Transforming documentation model after merging")
val transformedDocumentation = transformDocumentationModelAfterMerge(transformedDocumentationAfterMerge)
// Step 2: Generate pages & transform them (change internally)
report("Creating pages")
val pages = createPages(transformedDocumentation)
report("Transforming pages")
val transformedPages = transformPages(pages)
// Step 3: Rendering
report("Rendering")
render(transformedPages)
report("Running post-actions")
runPostActions()
reportAfterRendering()
}
override val generationName: String = "documentation for ${context.configuration.moduleName}"
/**
* Implementation note: it runs in a separated single thread due to existing support of coroutines (see #2936)
*/
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
public fun createDocumentationModels(): List<DModule> = newSingleThreadContext("Generating documentable model").use { coroutineContext -> // see https://github.com/Kotlin/dokka/issues/3151
runBlocking(coroutineContext) {
context.configuration.sourceSets.parallelMap { sourceSet -> translateSources(sourceSet, context) }.flatten()
.also { modules -> if (modules.isEmpty()) exitGenerationGracefully("Nothing to document") }
}
}
public fun transformDocumentationModelBeforeMerge(modulesFromPlatforms: List<DModule>): List<DModule> {
return context.plugin<DokkaBase>()
.query { preMergeDocumentableTransformer }
.fold(modulesFromPlatforms) { acc, t -> t(acc) }
}
public fun mergeDocumentationModels(modulesFromPlatforms: List<DModule>): DModule? =
context.single(CoreExtensions.documentableMerger).invoke(modulesFromPlatforms)
public fun transformDocumentationModelAfterMerge(documentationModel: DModule): DModule =
context[CoreExtensions.documentableTransformer].fold(documentationModel) { acc, t -> t(acc, context) }
public fun createPages(transformedDocumentation: DModule): RootPageNode =
context.single(CoreExtensions.documentableToPageTranslator).invoke(transformedDocumentation)
public fun transformPages(pages: RootPageNode): RootPageNode =
context[CoreExtensions.pageTransformer].fold(pages) { acc, t -> t(acc) }
public fun render(transformedPages: RootPageNode) {
context.single(CoreExtensions.renderer).render(transformedPages)
}
public fun runPostActions() {
context[CoreExtensions.postActions].forEach { it() }
}
public fun validityCheck(context: DokkaContext) {
val (preGenerationCheckResult, checkMessages) = context[CoreExtensions.preGenerationCheck].fold(
Pair(true, emptyList<String>())
) { acc, checker -> checker() + acc }
if (!preGenerationCheckResult) throw DokkaException(
"Pre-generation validity check failed: ${checkMessages.joinToString(",")}"
)
}
public fun reportAfterRendering() {
context.unusedPoints.takeIf { it.isNotEmpty() }?.also {
context.logger.info("Unused extension points found: ${it.joinToString(", ")}")
}
context.logger.report()
if (context.configuration.failOnWarning && (context.logger.warningsCount > 0 || context.logger.errorsCount > 0)) {
throw DokkaException(
"Failed with warningCount=${context.logger.warningsCount} and errorCount=${context.logger.errorsCount}"
)
}
}
private suspend fun translateSources(sourceSet: DokkaConfiguration.DokkaSourceSet, context: DokkaContext) =
context[CoreExtensions.sourceToDocumentableTranslator].parallelMap { translator ->
when (translator) {
is AsyncSourceToDocumentableTranslator -> translator.invokeSuspending(sourceSet, context)
else -> translator.invoke(sourceSet, context)
}
}
}
|