• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download

<lambda>null1 package org.jetbrains.dokka.gradle
2 
3 import groovy.lang.Closure
4 import org.gradle.api.DefaultTask
5 import org.gradle.api.Plugin
6 import org.gradle.api.Project
7 import org.gradle.api.Task
8 import org.gradle.api.file.FileCollection
9 import org.gradle.api.plugins.JavaBasePlugin
10 import org.gradle.api.plugins.JavaPluginConvention
11 import org.gradle.api.tasks.*
12 import org.gradle.api.tasks.Optional
13 import org.gradle.api.tasks.compile.AbstractCompile
14 import org.jetbrains.dokka.*
15 import org.jetbrains.dokka.ReflectDsl.isNotInstance
16 import org.jetbrains.dokka.gradle.ClassloaderContainer.fatJarClassLoader
17 import org.jetbrains.dokka.gradle.DokkaVersion.version
18 import ru.yole.jkid.JsonExclude
19 import ru.yole.jkid.serialization.serialize
20 import java.io.File
21 import java.io.InputStream
22 import java.io.Serializable
23 import java.net.URLClassLoader
24 import java.util.*
25 import java.util.concurrent.Callable
26 import java.util.function.BiConsumer
27 
28 open class DokkaPlugin : Plugin<Project> {
29 
30     override fun apply(project: Project) {
31         DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties"))
32         project.tasks.create("dokka", DokkaTask::class.java).apply {
33             moduleName = project.name
34             outputDirectory = File(project.buildDir, "dokka").absolutePath
35         }
36     }
37 }
38 
39 object DokkaVersion {
40     var version: String? = null
41 
loadFromnull42     fun loadFrom(stream: InputStream) {
43         version = Properties().apply {
44             load(stream)
45         }.getProperty("dokka-version")
46     }
47 }
48 
49 
50 object ClassloaderContainer {
51     @JvmField
52     var fatJarClassLoader: ClassLoader? = null
53 }
54 
55 const val `deprecationMessage reportNotDocumented` = "Will be removed in 0.9.17, see dokka#243"
56 
57 open class DokkaTask : DefaultTask() {
58 
<lambda>null59     fun defaultKotlinTasks() = with(ReflectDsl) {
60         val abstractKotlinCompileClz = try {
61             project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
62         } catch (cnfe: ClassNotFoundException) {
63             logger.warn("$ABSTRACT_KOTLIN_COMPILE class not found, default kotlin tasks ignored")
64             return@with emptyList<Task>()
65         }
66 
67         return@with project.tasks.filter { it isInstance abstractKotlinCompileClz }.filter { "Test" !in it.name }
68     }
69 
70     init {
71         group = JavaBasePlugin.DOCUMENTATION_GROUP
72         description = "Generates dokka documentation for Kotlin"
73 
74         @Suppress("LeakingThis")
<lambda>null75         dependsOn(Callable { kotlinTasks.map { it.taskDependencies } })
76     }
77 
78     @Input
79     var moduleName: String = ""
80     @Input
81     var outputFormat: String = "html"
82     @get:Internal // handled by getOutputDirectoryAsFile
83     var outputDirectory: String = ""
84 
85 
86     @Deprecated("Going to be removed in 0.9.16, use classpath + sourceDirs instead if kotlinTasks is not suitable for you")
87     @Input var processConfigurations: List<Any?> = emptyList()
88 
89     @InputFiles var classpath: Iterable<File> = arrayListOf()
90 
91     @Input
92     var includes: List<Any?> = arrayListOf()
93     @Input
94     var linkMappings: ArrayList<LinkMapping> = arrayListOf()
95     @Input
96     var samples: List<Any?> = arrayListOf()
97     @Input
98     var jdkVersion: Int = 6
99 
100     @Input
101     var generateClassIndexPage = true
102 
103     @Input
104     var generatePackageIndexPage = true
105 
106     @Input
107     var sourceDirs: Iterable<File> = emptyList()
108 
109     @Input
110     var sourceRoots: MutableList<SourceRoot> = arrayListOf()
111 
112     @Input
113     var dokkaFatJar: Any = "org.jetbrains.dokka:dokka-fatjar:$version"
114 
115     @Input var includeNonPublic = false
116     @Input var skipDeprecated = false
117     @Input var skipEmptyPackages = true
118 
119     @Input var outlineRoot: String = ""
120     @Input var dacRoot: String = ""
121 
122     @get:Input
123     @Deprecated(`deprecationMessage reportNotDocumented`, replaceWith = ReplaceWith("reportUndocumented"))
124     var reportNotDocumented
125         get() = reportUndocumented
126         set(value) {
127             logger.warn("Dokka: reportNotDocumented is deprecated and " + `deprecationMessage reportNotDocumented`.decapitalize())
128             reportUndocumented = value
129         }
130 
131     @Input var reportUndocumented = true
132     @Input var perPackageOptions: MutableList<PackageOptions> = arrayListOf()
133     @Input var impliedPlatforms: MutableList<String> = arrayListOf()
134 
135     @Input var externalDocumentationLinks = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
136 
137     @Input var noStdlibLink: Boolean = false
138 
139     @Input
140     var noJdkLink: Boolean = false
141 
142     @Optional @Input
143     var cacheRoot: String? = null
144 
145 
146     @Optional @Input
147     var languageVersion: String? = null
148 
149     @Optional @Input
150     var apiVersion: String? = null
151 
152     @Input
153     var collectInheritedExtensionsFromLibraries: Boolean = false
154 
155     @get:Internal
<lambda>null156     internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
157 
158 
<lambda>null159     private var kotlinTasksConfigurator: () -> List<Any?>? = { defaultKotlinTasks() }
<lambda>null160     private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks() }
161 
kotlinTasksnull162     fun kotlinTasks(closure: Closure<Any?>) {
163         kotlinTasksConfigurator = { closure.call() as? List<Any?> }
164     }
165 
linkMappingnull166     fun linkMapping(closure: Closure<Unit>) {
167         val mapping = LinkMapping()
168         closure.delegate = mapping
169         closure.call()
170 
171         if (mapping.path.isEmpty()) {
172             throw IllegalArgumentException("Link mapping should have dir")
173         }
174         if (mapping.url.isEmpty()) {
175             throw IllegalArgumentException("Link mapping should have url")
176         }
177 
178         linkMappings.add(mapping)
179     }
180 
sourceRootnull181     fun sourceRoot(closure: Closure<Unit>) {
182         val sourceRoot = SourceRoot()
183         closure.delegate = sourceRoot
184         closure.call()
185         sourceRoots.add(sourceRoot)
186     }
187 
packageOptionsnull188     fun packageOptions(closure: Closure<Unit>) {
189         val packageOptions = PackageOptions()
190         closure.delegate = packageOptions
191         closure.call()
192         perPackageOptions.add(packageOptions)
193     }
194 
externalDocumentationLinknull195     fun externalDocumentationLink(closure: Closure<Unit>) {
196         val builder = DokkaConfiguration.ExternalDocumentationLink.Builder()
197         closure.delegate = builder
198         closure.call()
199         externalDocumentationLinks.add(builder.build())
200     }
201 
tryResolveFatJarnull202     fun tryResolveFatJar(project: Project): File {
203         return try {
204             val dependency = project.buildscript.dependencies.create(dokkaFatJar)
205             val configuration = project.buildscript.configurations.detachedConfiguration(dependency)
206             configuration.description = "Dokka main jar"
207             configuration.resolve().first()
208         } catch (e: Exception) {
209             project.parent?.let { tryResolveFatJar(it) } ?: throw e
210         }
211     }
212 
loadFatJarnull213     fun loadFatJar() {
214         if (fatJarClassLoader == null) {
215             val fatjar = if (dokkaFatJar is File)
216                 dokkaFatJar as File
217             else
218                 tryResolveFatJar(project)
219             fatJarClassLoader = URLClassLoader(arrayOf(fatjar.toURI().toURL()), ClassLoader.getSystemClassLoader().parent)
220         }
221     }
222 
223     internal data class ClasspathAndSourceRoots(val classpathFileCollection: FileCollection, val sourceRoots: List<File>) : Serializable
224 
extractKotlinCompileTasksnull225     private fun extractKotlinCompileTasks(): List<Task> {
226         val inputList = (kotlinTasksConfigurator.invoke() ?: emptyList()).filterNotNull()
227         val (paths, other) = inputList.partition { it is String }
228 
229         val taskContainer = project.tasks
230 
231         val tasksByPath = paths.map { taskContainer.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") }
232 
233         other
234                 .filter { it !is Task || it isNotInstance getAbstractKotlinCompileFor(it) }
235                 .forEach { throw IllegalArgumentException("Illegal entry in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE or String, but was $it") }
236 
237         tasksByPath
238                 .filter { it == null || it isNotInstance getAbstractKotlinCompileFor(it) }
239                 .forEach { throw IllegalArgumentException("Illegal task path in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE, but was $it") }
240 
241 
242         return (tasksByPath + other) as List<Task>
243     }
244 
extractClasspathAndSourceRootsFromKotlinTasksnull245     private fun extractClasspathAndSourceRootsFromKotlinTasks(): ClasspathAndSourceRoots {
246 
247         val allTasks = kotlinTasks
248 
249         val allClasspath = mutableSetOf<File>()
250         var allClasspathFileCollection: FileCollection = project.files()
251         val allSourceRoots = mutableSetOf<File>()
252 
253         allTasks.forEach {
254 
255             logger.debug("Dokka found AbstractKotlinCompile task: $it")
256             with(ReflectDsl) {
257                 val taskSourceRoots: List<File> = it["sourceRootsContainer"]["sourceRoots"].v()
258 
259                 val abstractKotlinCompileClz = getAbstractKotlinCompileFor(it)!!
260 
261                 val taskClasspath: Iterable<File> =
262                         (it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke()
263                                 ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v()
264                                 ?: it["getClasspath", abstractKotlinCompileClz]())
265 
266                 if (taskClasspath is FileCollection) {
267                     allClasspathFileCollection += taskClasspath
268                 } else {
269                     allClasspath += taskClasspath
270                 }
271                 allSourceRoots += taskSourceRoots.filter { it.exists() }
272             }
273         }
274 
275         return ClasspathAndSourceRoots(allClasspathFileCollection + project.files(allClasspath), allSourceRoots.toList())
276     }
277 
<lambda>null278     private fun Iterable<File>.toSourceRoots(): List<SourceRoot> = this.filter { it.exists() }.map { SourceRoot().apply { path = it.path } }
279 
collectSuppressedFilesnull280     protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>): List<String> = emptyList()
281 
282     @TaskAction
283     fun generate() {
284         val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false"
285         System.setProperty(COLORS_ENABLED_PROPERTY, "false")
286         try {
287             loadFatJar()
288 
289             val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
290 
291             val project = project
292             val sourceRoots = collectSourceRoots() + tasksSourceRoots.toSourceRoots()
293 
294             if (sourceRoots.isEmpty()) {
295                 logger.warn("No source directories found: skipping dokka generation")
296                 return
297             }
298 
299             val fullClasspath = collectClasspathFromOldSources() + tasksClasspath + classpath
300 
301             val bootstrapClass = fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl")
302 
303             val bootstrapInstance = bootstrapClass.constructors.first().newInstance()
304 
305             val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
306 
307             val configuration = SerializeOnlyDokkaConfiguration(
308                 moduleName,
309                 fullClasspath.map { it.absolutePath },
310                 sourceRoots,
311                 samples.filterNotNull().map { project.file(it).absolutePath },
312                 includes.filterNotNull().map { project.file(it).absolutePath },
313                 outputDirectory,
314                 outputFormat,
315                 includeNonPublic,
316                 false,
317                 reportUndocumented,
318                 skipEmptyPackages,
319                 skipDeprecated,
320                 jdkVersion,
321                 generateClassIndexPage,
322                     generatePackageIndexPage,
323                     linkMappings,
324                     impliedPlatforms,
325                     perPackageOptions,
326                     externalDocumentationLinks,
327                     noStdlibLink,
328                     noJdkLink,
329                     cacheRoot,
330                     collectSuppressedFiles(sourceRoots),
331                     languageVersion,
332                     apiVersion,
333                     collectInheritedExtensionsFromLibraries,
334                     outlineRoot,
335                     dacRoot)
336 
337 
338             bootstrapProxy.configure(
339                     BiConsumer { level, message ->
340                         when (level) {
341                             "info" -> logger.info(message)
342                             "warn" -> logger.warn(message)
343                             "error" -> logger.error(message)
344                         }
345                     },
346                     serialize(configuration)
347             )
348 
349             bootstrapProxy.generate()
350 
351         } finally {
352             System.setProperty(COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore)
353         }
354     }
355 
collectClasspathFromOldSourcesnull356     private fun collectClasspathFromOldSources(): List<File> {
357 
358         val allConfigurations = project.configurations
359 
360         val fromConfigurations =
361                 processConfigurations.flatMap { allConfigurations.getByName(it.toString()) }
362 
363         return fromConfigurations
364     }
365 
collectSourceRootsnull366     private fun collectSourceRoots(): List<SourceRoot> {
367         val sourceDirs = if (sourceDirs.any()) {
368             logger.info("Dokka: Taking source directories provided by the user")
369             sourceDirs.toSet()
370         } else if (kotlinTasks.isEmpty()) {
371             project.convention.findPlugin(JavaPluginConvention::class.java)?.let { javaPluginConvention ->
372                 logger.info("Dokka: Taking source directories from default java plugin")
373                 val sourceSets = javaPluginConvention.sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)
374                 sourceSets?.allSource?.srcDirs
375             }
376         } else {
377             emptySet()
378         }
379 
380         return sourceRoots + (sourceDirs?.toSourceRoots() ?: emptyList())
381     }
382 
383 
384     @Classpath
getInputClasspathnull385     fun getInputClasspath(): FileCollection {
386         val (classpathFileCollection) = extractClasspathAndSourceRootsFromKotlinTasks()
387         return project.files(collectClasspathFromOldSources() + classpath) + classpathFileCollection
388     }
389 
390     @InputFiles
getInputFilesnull391     fun getInputFiles(): FileCollection {
392         val (_, tasksSourceRoots) = extractClasspathAndSourceRootsFromKotlinTasks()
393         return project.files(tasksSourceRoots.map { project.fileTree(it) }) +
394                 project.files(collectSourceRoots().map { project.fileTree(File(it.path)) }) +
395                 project.files(includes) +
396                 project.files(samples.filterNotNull().map { project.fileTree(it) })
397     }
398 
399     @OutputDirectory
getOutputDirectoryAsFilenull400     fun getOutputDirectoryAsFile(): File = project.file(outputDirectory)
401 
402     companion object {
403         const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled"
404         const val ABSTRACT_KOTLIN_COMPILE = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile"
405 
406         private fun getAbstractKotlinCompileFor(task: Task) = try {
407             task.project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
408         } catch (e: ClassNotFoundException) {
409             null
410         }
411     }
412 }
413 
414 class SourceRoot : DokkaConfiguration.SourceRoot, Serializable {
415     override var path: String = ""
416         set(value) {
417             field = File(value).absolutePath
418         }
419 
420     override var platforms: List<String> = arrayListOf()
421 
toStringnull422     override fun toString(): String {
423         return "${platforms.joinToString()}::$path"
424     }
425 }
426 
427 open class LinkMapping : Serializable, DokkaConfiguration.SourceLinkDefinition {
428     @JsonExclude
429     var dir: String
430         get() = path
431         set(value) {
432             path = value
433         }
434 
435     override var path: String = ""
436     override var url: String = ""
437 
438     @JsonExclude
439     var suffix: String?
440         get() = lineSuffix
441         set(value) {
442             lineSuffix = value
443         }
444 
445     override var lineSuffix: String? = null
446 
equalsnull447     override fun equals(other: Any?): Boolean {
448         if (this === other) return true
449         if (other?.javaClass != javaClass) return false
450 
451         other as LinkMapping
452 
453         if (path != other.path) return false
454         if (url != other.url) return false
455         if (lineSuffix != other.lineSuffix) return false
456 
457         return true
458     }
459 
hashCodenull460     override fun hashCode(): Int {
461         var result = path.hashCode()
462         result = 31 * result + url.hashCode()
463         result = 31 * result + (lineSuffix?.hashCode() ?: 0)
464         return result
465     }
466 
467     companion object {
468         const val serialVersionUID: Long = -8133501684312445981L
469     }
470 }
471 
472 class PackageOptions : Serializable, DokkaConfiguration.PackageOptions {
473     override var prefix: String = ""
474     override var includeNonPublic: Boolean = false
475     override var reportUndocumented: Boolean = true
476     override var skipDeprecated: Boolean = false
477     override var suppress: Boolean = false
478 }
479