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
|
package org.jetbrains.dokka
import org.jetbrains.dokka.analysis.AnalysisEnvironment
import org.jetbrains.dokka.analysis.DokkaResolutionFacade
import org.jetbrains.dokka.model.Module
import org.jetbrains.dokka.pages.PlatformData
import org.jetbrains.dokka.plugability.DokkaContext
import org.jetbrains.dokka.plugability.single
import org.jetbrains.dokka.renderers.FileWriter
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation
import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity
import org.jetbrains.kotlin.cli.common.messages.MessageCollector
import org.jetbrains.kotlin.cli.common.messages.MessageRenderer
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
class DokkaGenerator(
private val configuration: DokkaConfiguration,
private val logger: DokkaLogger
) {
fun generate() {
logger.debug("Setting up analysis environments")
val platforms: Map<PlatformData, EnvironmentAndFacade> = configuration.passesConfigurations.map {
PlatformData(it.analysisPlatform, it.targets) to createEnvironmentAndFacade(it)
}.toMap()
logger.debug("Initializing plugins")
val context = DokkaContext.create(configuration.pluginsClasspath, logger, platforms)
logger.debug("Creating documentation models")
val modulesFromPlatforms = platforms.map { (pdata, _) -> translateDescriptors(pdata, context) }
logger.debug("Merging documentation models")
val documentationModel = context.single(CoreExtensions.documentationMerger)
.invoke(modulesFromPlatforms, context)
logger.debug("Transforming documentation model")
val transformedDocumentation = context[CoreExtensions.documentationTransformer]
.fold(documentationModel) { acc, t -> t(acc, context) }
logger.debug("Creating pages")
val pages = context.single(CoreExtensions.documentationToPageTranslator)
.invoke(transformedDocumentation, context)
logger.debug("Transforming pages")
val transformedPages = context[CoreExtensions.pageTransformer]
.fold(pages) { acc, t -> t(acc, context) }
logger.debug("Rendering")
val fileWriter = FileWriter(configuration.outputDir, "")
val locationProvider = context.single(CoreExtensions.locationProviderFactory)
.invoke(transformedPages, configuration, context)
val renderer = context.single(CoreExtensions.rendererFactory)
.invoke(fileWriter, locationProvider, context)
renderer.render(transformedPages)
}
private fun createEnvironmentAndFacade(pass: DokkaConfiguration.PassConfiguration): EnvironmentAndFacade =
AnalysisEnvironment(DokkaMessageCollector(logger), pass.analysisPlatform).run {
if (analysisPlatform == Platform.jvm) {
addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre())
}
pass.classpath.forEach { addClasspath(File(it)) }
addSources(pass.sourceRoots.map { it.path })
loadLanguageVersionSettings(pass.languageVersion, pass.apiVersion)
val environment = createCoreEnvironment()
val (facade, _) = createResolutionFacade(environment)
EnvironmentAndFacade(environment, facade)
}
private fun translateDescriptors(platformData: PlatformData, context: DokkaContext): Module {
val (environment, facade) = context.platforms.getValue(platformData)
val packageFragments = environment.getSourceFiles().asSequence()
.map { it.packageFqName }
.distinct()
.mapNotNull { facade.resolveSession.getPackageFragment(it) }
.toList()
return context.single(CoreExtensions.descriptorToDocumentationTranslator)
.invoke(packageFragments, platformData, context)
}
private class DokkaMessageCollector(private val logger: DokkaLogger) : MessageCollector {
override fun clear() {
seenErrors = false
}
private var seenErrors = false
override fun report(severity: CompilerMessageSeverity, message: String, location: CompilerMessageLocation?) {
if (severity == CompilerMessageSeverity.ERROR) {
seenErrors = true
}
logger.error(MessageRenderer.PLAIN_FULL_PATHS.render(severity, message, location))
}
override fun hasErrors() = seenErrors
}
}
// It is not data class due to ill-defined equals
class EnvironmentAndFacade(val environment: KotlinCoreEnvironment, val facade: DokkaResolutionFacade) {
operator fun component1() = environment
operator fun component2() = facade
}
|