aboutsummaryrefslogtreecommitdiff
path: root/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka
diff options
context:
space:
mode:
authorKamil Doległo <kamilok1965@interia.pl>2019-04-11 14:56:38 +0200
committerKamil Doległo <kamilok1965@interia.pl>2019-04-11 14:56:38 +0200
commit64305246bf6a22a4111c11dc3c671297cf223393 (patch)
treeacd0940b9fe9e212763c37dd86330c4e54097a43 /runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka
parent5de1d68ca8510f7bc3601c3516ecb111f7abe44b (diff)
downloaddokka-64305246bf6a22a4111c11dc3c671297cf223393.tar.gz
dokka-64305246bf6a22a4111c11dc3c671297cf223393.tar.bz2
dokka-64305246bf6a22a4111c11dc3c671297cf223393.zip
Remove dead code, create package, add comment
Diffstat (limited to 'runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka')
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt46
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt117
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaTask.kt265
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt50
4 files changed, 478 insertions, 0 deletions
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt
new file mode 100644
index 00000000..61c9e773
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt
@@ -0,0 +1,46 @@
+package org.jetbrains.dokka.gradle
+
+import java.lang.reflect.InvocationHandler
+import java.lang.reflect.InvocationTargetException
+import java.lang.reflect.Method
+import java.lang.reflect.Proxy
+
+
+/**
+ * Warning! Hard reflection magic used here.
+ *
+ * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm,
+ * to create access proxy for [delegate] into [targetClassLoader].
+ */
+@Suppress("UNCHECKED_CAST")
+inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader, delegate: Any): T =
+ automagicProxy(targetClassLoader, T::class.java, delegate) as T
+
+
+/**
+ * Warning! Hard reflection magic used here.
+ *
+ * Creates [java.lang.reflect.Proxy] with pass through invocation algorithm,
+ * to create access proxy for [delegate] into [targetClassLoader].
+ *
+ */
+fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any =
+ Proxy.newProxyInstance(
+ targetClassLoader,
+ arrayOf(targetType),
+ DelegatedInvocationHandler(delegate)
+ )
+
+class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler {
+
+ @Throws(Throwable::class)
+ override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any? {
+ val delegateMethod = delegate.javaClass.getMethod(method.name, *method.parameterTypes)
+ try {
+ delegateMethod.isAccessible = true
+ return delegateMethod.invoke(delegate, *(args ?: emptyArray()))
+ } catch (ex: InvocationTargetException) {
+ throw ex.targetException
+ }
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt
new file mode 100644
index 00000000..bab36f29
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt
@@ -0,0 +1,117 @@
+package org.jetbrains.dokka.gradle
+
+import groovy.lang.Closure
+import org.gradle.api.Action
+import org.gradle.util.ConfigureUtil
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.Platform
+import java.io.File
+import java.io.Serializable
+import java.net.URL
+
+class GradleSourceRootImpl: DokkaConfiguration.SourceRoot, Serializable{
+ override var path: String = ""
+ set(value) {
+ field = File(value).absolutePath
+ }
+
+ override fun toString(): String = path
+}
+
+open class GradlePassConfigurationImpl(@Transient val name: String = ""): DokkaConfiguration.PassConfiguration {
+ override var classpath: List<String> = emptyList()
+ override var moduleName: String = ""
+ override var sourceRoots: MutableList<DokkaConfiguration.SourceRoot> = mutableListOf()
+ override var samples: List<String> = emptyList()
+ override var includes: List<String> = emptyList()
+ override var includeNonPublic: Boolean = false
+ override var includeRootPackage: Boolean = false
+ override var reportUndocumented: Boolean = false
+ override var skipEmptyPackages: Boolean = false
+ override var skipDeprecated: Boolean = false
+ override var jdkVersion: Int = 6
+ override var sourceLinks: MutableList<DokkaConfiguration.SourceLinkDefinition> = mutableListOf()
+ override var perPackageOptions: MutableList<DokkaConfiguration.PackageOptions> = mutableListOf()
+ override var externalDocumentationLinks: MutableList<DokkaConfiguration.ExternalDocumentationLink> = mutableListOf()
+ override var languageVersion: String? = null
+ override var apiVersion: String? = null
+ override var noStdlibLink: Boolean = false
+ override var noJdkLink: Boolean = false
+ override var suppressedFiles: List<String> = emptyList()
+ override var collectInheritedExtensionsFromLibraries: Boolean = false
+ override var analysisPlatform: Platform = Platform.DEFAULT
+ override var targets: List<String> = listOf("JVM")
+ override var sinceKotlin: String = "1.0"
+
+ fun sourceRoot(c: Closure<Unit>){
+ val configured = ConfigureUtil.configure(c, GradleSourceRootImpl())
+ sourceRoots.add(configured)
+ }
+
+ fun sourceRoot(action: Action<in GradleSourceRootImpl>){
+ val sourceRoot = GradleSourceRootImpl()
+ action.execute(sourceRoot)
+ sourceRoots.add(sourceRoot)
+ }
+
+ fun sourceLink(c: Closure<Unit>){
+ val configured = ConfigureUtil.configure(c, GradleSourceLinkDefinitionImpl())
+ sourceLinks.add(configured)
+ }
+
+ fun sourceLink(action: Action<in GradleSourceLinkDefinitionImpl>){
+ val sourceLink = GradleSourceLinkDefinitionImpl()
+ action.execute(sourceLink)
+ sourceLinks.add(sourceLink)
+ }
+
+ fun perPackageOption(c: Closure<Unit>){
+ val configured = ConfigureUtil.configure(c, GradlePackageOptionsImpl())
+ perPackageOptions.add(configured)
+ }
+
+ fun perPackageOption(action: Action<in GradlePackageOptionsImpl>){
+ val option = GradlePackageOptionsImpl()
+ action.execute(option)
+ perPackageOptions.add(option)
+ }
+
+ fun externalDocumentationLink(c: Closure<Unit>){
+ val link = ConfigureUtil.configure(c, GradleExternalDocumentationLinkImpl())
+ externalDocumentationLinks.add(link)
+ }
+
+ fun externalDocumentationLink(action: Action<in GradleExternalDocumentationLinkImpl>){
+ val link = GradleExternalDocumentationLinkImpl()
+ action.execute(link)
+ externalDocumentationLinks.add(link)
+ }
+}
+
+class GradleSourceLinkDefinitionImpl : DokkaConfiguration.SourceLinkDefinition {
+ override var path: String = ""
+ override var url: String = ""
+ override var lineSuffix: String? = null
+}
+
+class GradleExternalDocumentationLinkImpl : DokkaConfiguration.ExternalDocumentationLink {
+ override var url: URL = URL("")
+ override var packageListUrl: URL = URL("")
+}
+
+class GradleDokkaConfigurationImpl: DokkaConfiguration {
+ override var outputDir: String = ""
+ override var format: String = "html"
+ override var generateIndexPages: Boolean = false
+ override var cacheRoot: String? = null
+ override var impliedPlatforms: List<String> = emptyList()
+ override var passesConfigurations: List<GradlePassConfigurationImpl> = emptyList()
+}
+
+class GradlePackageOptionsImpl: DokkaConfiguration.PackageOptions {
+ override var prefix: String = ""
+ override val includeNonPublic: Boolean = false
+ override val reportUndocumented: Boolean = true
+ override val skipDeprecated: Boolean = true
+ override val suppress: Boolean = false
+} \ No newline at end of file
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaTask.kt
new file mode 100644
index 00000000..0d78461c
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaTask.kt
@@ -0,0 +1,265 @@
+package org.jetbrains.dokka.gradle
+
+import com.google.gson.GsonBuilder
+import groovy.lang.Closure
+import org.gradle.api.DefaultTask
+import org.gradle.api.Project
+import org.gradle.api.Task
+import org.gradle.api.artifacts.Configuration
+import org.gradle.api.file.FileCollection
+import org.gradle.api.plugins.JavaBasePlugin
+import org.gradle.api.plugins.JavaPluginConvention
+import org.gradle.api.tasks.*
+import org.gradle.api.tasks.compile.AbstractCompile
+import org.jetbrains.dokka.DokkaBootstrap
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.ReflectDsl
+import org.jetbrains.dokka.ReflectDsl.isNotInstance
+import java.io.File
+import java.io.Serializable
+import java.net.URLClassLoader
+import java.util.concurrent.Callable
+import java.util.function.BiConsumer
+
+open class DokkaTask : DefaultTask() {
+
+ fun defaultKotlinTasks() = with(ReflectDsl) {
+ val abstractKotlinCompileClz = try {
+ project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
+ } catch (cnfe: ClassNotFoundException) {
+ logger.warn("$ABSTRACT_KOTLIN_COMPILE class not found, default kotlin tasks ignored")
+ return@with emptyList<Task>()
+ }
+
+ return@with project.tasks.filter { it isInstance abstractKotlinCompileClz }.filter { "Test" !in it.name }
+ }
+
+ init {
+ group = JavaBasePlugin.DOCUMENTATION_GROUP
+ description = "Generates dokka documentation for Kotlin"
+
+ @Suppress("LeakingThis")
+ dependsOn(Callable { kotlinTasks.map { it.taskDependencies } })
+ }
+
+ @Input
+ var moduleName: String = ""
+ @Input
+ var outputFormat: String = "html"
+ @OutputDirectory
+ var outputDirectory: String = ""
+ var dokkaRuntime: Configuration? = null
+
+ @InputFiles
+ var classpath: Iterable<File> = arrayListOf()
+
+ @Input
+ var sourceDirs: Iterable<File> = emptyList()
+ @Input
+ var sourceRoots: MutableList<DokkaConfiguration.SourceRoot> = arrayListOf()
+ @Input
+ var dokkaFatJar: Any = "org.jetbrains.dokka:dokka-fatjar:${DokkaVersion.version}"
+ @Input
+ var impliedPlatforms: MutableList<String> = arrayListOf()
+ @Optional
+ @Input
+ var cacheRoot: String? = null
+ @Input
+ var collectInheritedExtensionsFromLibraries: Boolean = false
+ @get:Internal
+ internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
+
+ protected var externalDocumentationLinks: MutableList<DokkaConfiguration.ExternalDocumentationLink> = mutableListOf()
+
+ private var kotlinTasksConfigurator: () -> List<Any?>? = { defaultKotlinTasks() }
+ private val kotlinTasks: List<Task> by lazy { extractKotlinCompileTasks() }
+
+ fun kotlinTasks(taskSupplier: Callable<List<Any>>) {
+ kotlinTasksConfigurator = { taskSupplier.call() }
+ }
+
+ fun kotlinTasks(closure: Closure<Any?>) {
+ kotlinTasksConfigurator = { closure.call() as? List<Any?> }
+ }
+
+
+ fun tryResolveFatJar(project: Project): Set<File> {
+ return try {
+ dokkaRuntime!!.resolve()
+ } catch (e: Exception) {
+ project.parent?.let { tryResolveFatJar(it) } ?: throw e
+ }
+ }
+
+ fun loadFatJar() {
+ if (ClassloaderContainer.fatJarClassLoader == null) {
+ val jars = if (dokkaFatJar is File)
+ setOf(dokkaFatJar as File)
+ else
+ tryResolveFatJar(project)
+ ClassloaderContainer.fatJarClassLoader = URLClassLoader(jars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent)
+ }
+ }
+
+ internal data class ClasspathAndSourceRoots(val classpathFileCollection: FileCollection, val sourceRoots: List<File>) :
+ Serializable
+
+ private fun extractKotlinCompileTasks(): List<Task> {
+ val inputList = (kotlinTasksConfigurator.invoke() ?: emptyList()).filterNotNull()
+ val (paths, other) = inputList.partition { it is String }
+
+ val taskContainer = project.tasks
+
+ val tasksByPath = paths.map { taskContainer.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") }
+
+ other
+ .filter { it !is Task || it isNotInstance getAbstractKotlinCompileFor(it) }
+ .forEach { throw IllegalArgumentException("Illegal entry in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE or String, but was $it") }
+
+ tasksByPath
+ .filter { it == null || it isNotInstance getAbstractKotlinCompileFor(it) }
+ .forEach { throw IllegalArgumentException("Illegal task path in kotlinTasks, must be subtype of $ABSTRACT_KOTLIN_COMPILE, but was $it") }
+
+
+ return (tasksByPath + other) as List<Task>
+ }
+
+ private fun extractClasspathAndSourceRootsFromKotlinTasks(): ClasspathAndSourceRoots {
+
+ val allTasks = kotlinTasks
+
+ val allClasspath = mutableSetOf<File>()
+ var allClasspathFileCollection: FileCollection = project.files()
+ val allSourceRoots = mutableSetOf<File>()
+
+ allTasks.forEach {
+ logger.debug("Dokka found AbstractKotlinCompile task: $it")
+ with(ReflectDsl) {
+ val taskSourceRoots: List<File> = it["sourceRootsContainer"]["sourceRoots"].v()
+
+ val abstractKotlinCompileClz = getAbstractKotlinCompileFor(it)!!
+
+ val taskClasspath: Iterable<File> =
+ (it["getClasspath", AbstractCompile::class].takeIfIsFunc()?.invoke()
+ ?: it["compileClasspath", abstractKotlinCompileClz].takeIfIsProp()?.v()
+ ?: it["getClasspath", abstractKotlinCompileClz]())
+
+ if (taskClasspath is FileCollection) {
+ allClasspathFileCollection += taskClasspath
+ } else {
+ allClasspath += taskClasspath
+ }
+ allSourceRoots += taskSourceRoots.filter { it.exists() }
+ }
+ }
+
+ return ClasspathAndSourceRoots(allClasspathFileCollection + project.files(allClasspath), allSourceRoots.toList())
+ }
+
+ private fun Iterable<File>.toSourceRoots(): List<GradleSourceRootImpl> = this.filter { it.exists() }.map { GradleSourceRootImpl().apply { path = it.path } }
+
+ protected open fun collectSuppressedFiles(sourceRoots: List<DokkaConfiguration.SourceRoot>): List<String> = emptyList()
+
+ @TaskAction
+ fun generate() {
+ if (dokkaRuntime == null){
+ dokkaRuntime = project.configurations.getByName("dokkaRuntime")
+ }
+
+ dokkaRuntime?.defaultDependencies{ dependencies -> dependencies.add(project.dependencies.create(dokkaFatJar)) }
+ val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false"
+ System.setProperty(COLORS_ENABLED_PROPERTY, "false")
+ try {
+ loadFatJar()
+ // TODO: implement extracting source roots from kotlin tasks
+ val (_, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
+
+ val sourceRoots = collectSourceRoots(sourceDirs, sourceRoots) + tasksSourceRoots.toSourceRoots()
+
+ val bootstrapClass = ClassloaderContainer.fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl")
+ val bootstrapInstance = bootstrapClass.constructors.first().newInstance()
+ val bootstrapProxy: DokkaBootstrap =
+ automagicTypedProxy(javaClass.classLoader, bootstrapInstance)
+
+ val gson = GsonBuilder().setPrettyPrinting().create()
+
+ val passConfigurationExtension: GradlePassConfigurationImpl? = this.extensions.findByName(
+ CONFIGURATION_EXTENSION_NAME) as GradlePassConfigurationImpl?
+ val passConfigurationsContainer by lazy {
+ (this.extensions.getByName(MULTIPLATFORM_EXTENSION_NAME) as Iterable<GradlePassConfigurationImpl>).toList()
+ }
+ passConfigurationExtension?.sourceRoots?.addAll(sourceRoots)
+
+ val passConfigurationList =
+ (passConfigurationExtension?.let {passConfigurationsContainer + it } ?: passConfigurationsContainer)
+ .map { defaultPassConfiguration(it) }
+
+ val configuration = GradleDokkaConfigurationImpl()
+ configuration.outputDir = outputDirectory
+ configuration.format = outputFormat
+ configuration.generateIndexPages = true
+ configuration.cacheRoot = cacheRoot
+ configuration.impliedPlatforms = impliedPlatforms
+ configuration.passesConfigurations = passConfigurationList
+
+ bootstrapProxy.configure(
+ BiConsumer { level, message ->
+ when (level) {
+ "info" -> logger.info(message)
+ "warn" -> logger.warn(message)
+ "error" -> logger.error(message)
+ }
+ },
+ gson.toJson(configuration)
+ )
+
+ bootstrapProxy.generate()
+
+ } finally {
+ System.setProperty(COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore)
+ }
+ }
+
+ private fun defaultPassConfiguration(passConfig: GradlePassConfigurationImpl): GradlePassConfigurationImpl{
+ val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
+
+ val fullClasspath = tasksClasspath + classpath
+ passConfig.moduleName = moduleName
+ passConfig.classpath = fullClasspath.map { it.absolutePath }
+ passConfig.samples = passConfig.samples.map { project.file(it).absolutePath }
+ passConfig.includes = passConfig.includes.map { project.file(it).absolutePath }
+ passConfig.collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
+ passConfig.suppressedFiles = collectSuppressedFiles(passConfig.sourceRoots)
+ passConfig.externalDocumentationLinks.addAll(externalDocumentationLinks)
+
+ return passConfig
+ }
+
+ private fun collectSourceRoots(sourceDirs: Iterable<File>, sourceRoots: List<DokkaConfiguration.SourceRoot>): List<DokkaConfiguration.SourceRoot> {
+ val sourceDirs = when {
+ sourceDirs.any() -> {
+ logger.info("Dokka: Taking source directories provided by the user")
+ sourceDirs.toSet()
+ }
+ kotlinTasks.isEmpty() -> project.convention.findPlugin(JavaPluginConvention::class.java)?.let { javaPluginConvention ->
+ logger.info("Dokka: Taking source directories from default java plugin")
+ val sourceSets = javaPluginConvention.sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)
+ sourceSets?.allSource?.srcDirs
+ }
+ else -> emptySet()
+ }
+
+ return sourceRoots + (sourceDirs?.toSourceRoots() ?: emptyList())
+ }
+
+ companion object {
+ const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled"
+ const val ABSTRACT_KOTLIN_COMPILE = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile"
+
+ private fun getAbstractKotlinCompileFor(task: Task) = try {
+ task.project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE)
+ } catch (e: ClassNotFoundException) {
+ null
+ }
+ }
+}
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt
new file mode 100644
index 00000000..defddd31
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt
@@ -0,0 +1,50 @@
+package org.jetbrains.dokka.gradle
+
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import java.io.File
+import java.io.InputStream
+import java.util.*
+
+/*
+* Those are extension names, which are used in a build.gradle file as closure names:
+* dokka {
+* configuration { <- extension name
+* }
+* }
+* */
+internal const val CONFIGURATION_EXTENSION_NAME = "configuration"
+internal const val MULTIPLATFORM_EXTENSION_NAME = "multiplatform"
+
+open class DokkaPlugin : Plugin<Project> {
+
+ override fun apply(project: Project) {
+ DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties"))
+
+ project.tasks.create("dokka", DokkaTask::class.java).apply {
+ dokkaRuntime = project.configurations.create("dokkaRuntime")
+ moduleName = project.name
+ outputDirectory = File(project.buildDir, "dokka").absolutePath
+ }
+ project.tasks.withType(DokkaTask::class.java) { task ->
+ val passConfiguration = project.container(GradlePassConfigurationImpl::class.java)
+ task.extensions.add(MULTIPLATFORM_EXTENSION_NAME, passConfiguration)
+ task.extensions.create(CONFIGURATION_EXTENSION_NAME, GradlePassConfigurationImpl::class.java, "")
+ }
+ }
+}
+
+object DokkaVersion {
+ var version: String? = null
+
+ fun loadFrom(stream: InputStream) {
+ version = Properties().apply {
+ load(stream)
+ }.getProperty("dokka-version")
+ }
+}
+
+object ClassloaderContainer {
+ @JvmField
+ var fatJarClassLoader: ClassLoader? = null
+} \ No newline at end of file