aboutsummaryrefslogtreecommitdiff
path: root/runners
diff options
context:
space:
mode:
authorKrystian Ujma <krystianujma@gmail.com>2019-04-16 14:47:26 +0200
committerGitHub <noreply@github.com>2019-04-16 14:47:26 +0200
commit0d5422f375de769bbf514a72d3549ee379241c60 (patch)
tree2fe31e5789edb3ff8c8c74dc14319198a260763a /runners
parentaf2a9f18965793f5d4cabc05df323782bd2c60c0 (diff)
parentd841e2dd60e6a784ac1863ddba21949460320fc3 (diff)
downloaddokka-0d5422f375de769bbf514a72d3549ee379241c60.tar.gz
dokka-0d5422f375de769bbf514a72d3549ee379241c60.tar.bz2
dokka-0d5422f375de769bbf514a72d3549ee379241c60.zip
Merge pull request #1 from kamilok1965/rework_gradle_plugin
Rework gradle plugin
Diffstat (limited to 'runners')
-rw-r--r--runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt1
-rw-r--r--runners/gradle-plugin/build.gradle2
-rw-r--r--runners/gradle-plugin/src/main/kotlin/main.kt612
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt275
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt (renamed from runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt)8
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt118
-rw-r--r--runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt51
7 files changed, 450 insertions, 617 deletions
diff --git a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
index b1996da0..367b9257 100644
--- a/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
+++ b/runners/android-gradle-plugin/src/main/kotlin/mainAndroid.kt
@@ -4,6 +4,7 @@ import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.tasks.Input
import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder
+import org.jetbrains.dokka.DokkaConfiguration.SourceRoot
import java.io.File
open class DokkaAndroidPlugin : Plugin<Project> {
diff --git a/runners/gradle-plugin/build.gradle b/runners/gradle-plugin/build.gradle
index 97a6b697..adcfd009 100644
--- a/runners/gradle-plugin/build.gradle
+++ b/runners/gradle-plugin/build.gradle
@@ -28,7 +28,7 @@ dependencies {
compileOnly gradleApi()
compileOnly localGroovy()
- implementation 'com.google.code.gson:gson:2.8.5'
+ implementation "com.google.code.gson:gson:$gson_version"
}
task sourceJar(type: Jar) {
diff --git a/runners/gradle-plugin/src/main/kotlin/main.kt b/runners/gradle-plugin/src/main/kotlin/main.kt
deleted file mode 100644
index 555dece4..00000000
--- a/runners/gradle-plugin/src/main/kotlin/main.kt
+++ /dev/null
@@ -1,612 +0,0 @@
-package org.jetbrains.dokka.gradle
-
-import com.google.gson.GsonBuilder
-import groovy.lang.Closure
-import org.gradle.api.*
-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.Optional
-import org.gradle.api.tasks.compile.AbstractCompile
-import org.jetbrains.dokka.*
-import org.jetbrains.dokka.ReflectDsl.isNotInstance
-import org.jetbrains.dokka.gradle.ClassloaderContainer.fatJarClassLoader
-import org.jetbrains.dokka.gradle.DokkaVersion.version
-
-import java.io.File
-import java.io.InputStream
-import java.io.Serializable
-import java.net.URL
-import java.net.URLClassLoader
-import java.util.*
-import java.util.concurrent.Callable
-import java.util.function.BiConsumer
-
-open class DokkaPlugin : Plugin<Project> {
-
- override fun apply(project: Project) {
- DokkaVersion.loadFrom(javaClass.getResourceAsStream("/META-INF/gradle-plugins/org.jetbrains.dokka.properties"))
-
- val passConfiguration= project.container(GradlePassConfigurationImpl::class.java)
- project.extensions.add("passConfigurations", passConfiguration)
-
- project.tasks.create("dokka", DokkaTask::class.java).apply {
- dokkaRuntime = project.configurations.create("dokkaRuntime")
- moduleName = project.name
- outputDirectory = File(project.buildDir, "dokka").absolutePath
- }
- }
-}
-
-
-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
-}
-
-
-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"
- var outputDirectory: String = ""
- var dokkaRuntime: Configuration? = null
-
- @InputFiles var classpath: Iterable<File> = arrayListOf()
-
- @Input
- var includes: List<Any?> = arrayListOf()
- @Input
- var linkMappings: ArrayList<LinkMapping> = arrayListOf()
- @Input
- var samples: List<Any?> = arrayListOf()
- @Input
- var jdkVersion: Int = 6
- @Input
- var sourceDirs: Iterable<File> = emptyList()
-
- @Input
- var sourceRoots: MutableList<SourceRoot> = arrayListOf()
-
- @Input
- var dokkaFatJar: Any = "org.jetbrains.dokka:dokka-fatjar:$version"
-
- @Input var includeNonPublic = false
- @Input var skipDeprecated = false
- @Input var skipEmptyPackages = true
- @Input var reportUndocumented = true
- @Input var perPackageOptions: MutableList<PackageOptions> = arrayListOf()
- @Input var impliedPlatforms: MutableList<String> = arrayListOf()
-
- @Input var externalDocumentationLinks = mutableListOf<DokkaConfiguration.ExternalDocumentationLink>()
-
- @Input var noStdlibLink: Boolean = false
-
- @Input
- var noJdkLink: Boolean = false
-
- @Optional @Input
- var cacheRoot: String? = null
-
-
- @Optional @Input
- var languageVersion: String? = null
-
- @Optional @Input
- var apiVersion: String? = null
-
- @Input
- var collectInheritedExtensionsFromLibraries: Boolean = false
-
- @get:Internal
- internal val kotlinCompileBasedClasspathAndSourceRoots: ClasspathAndSourceRoots by lazy { extractClasspathAndSourceRootsFromKotlinTasks() }
-
- @Optional @Input
- var targets: List<String> = listOf()
-
-// @Input var passConfigurations: MutableList<GradlePassConfigurationImpl> = arrayListOf()
-
-// fun passConfiguration(action: Action<GradlePassConfigurationImpl>) {
-// val passConfig = GradlePassConfigurationImpl()
-// action.execute(passConfig)
-// passConfigurations.add(passConfig)
-// }
-//
-// fun passConfiguration(closure: Closure<Unit>) {
-// passConfiguration(Action {
-// closure.delegate = it
-// closure.call()
-// })
-// }
-
-// fun passConfiguration(closure: Closure<Unit>) {
-// closure.call()
-// passConfiguration = mutableListOf(PassConfigurationImpl(targets = closure.getProperty("targets") as List<String>))
-// }
-
- 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 linkMapping(action: Action<LinkMapping>) {
- val mapping = LinkMapping()
- action.execute(mapping)
-
- if (mapping.path.isEmpty()) {
- throw IllegalArgumentException("Link mapping should have dir")
- }
- if (mapping.url.isEmpty()) {
- throw IllegalArgumentException("Link mapping should have url")
- }
-
- linkMappings.add(mapping)
- }
-
- fun linkMapping(closure: Closure<Unit>) {
- linkMapping(Action { mapping ->
- closure.delegate = mapping
- closure.call()
- })
- }
-
- fun sourceRoot(action: Action<SourceRoot>) {
- val sourceRoot = SourceRoot()
- action.execute(sourceRoot)
- sourceRoots.add(sourceRoot)
- }
-
- fun sourceRoot(closure: Closure<Unit>) {
- sourceRoot(Action { sourceRoot ->
- closure.delegate = sourceRoot
- closure.call()
- })
- }
-
- fun packageOptions(action: Action<PackageOptions>) {
- val packageOptions = PackageOptions()
- action.execute(packageOptions)
- perPackageOptions.add(packageOptions)
- }
-
- fun packageOptions(closure: Closure<Unit>) {
- packageOptions(Action { packageOptions ->
- closure.delegate = packageOptions
- closure.call()
- })
- }
-
- fun externalDocumentationLink(action: Action<DokkaConfiguration.ExternalDocumentationLink.Builder>) {
- val builder = DokkaConfiguration.ExternalDocumentationLink.Builder()
- action.execute(builder)
- externalDocumentationLinks.add(builder.build())
- }
-
- fun externalDocumentationLink(closure: Closure<Unit>) {
- externalDocumentationLink(Action { builder ->
- closure.delegate = builder
- closure.call()
- })
- }
-
- fun tryResolveFatJar(project: Project): Set<File> {
- return try {
- dokkaRuntime!!.resolve()
- } catch (e: Exception) {
- project.parent?.let { tryResolveFatJar(it) } ?: throw e
- }
- }
-
- fun loadFatJar() {
- if (fatJarClassLoader == null) {
- val jars = if (dokkaFatJar is File)
- setOf(dokkaFatJar as File)
- else
- tryResolveFatJar(project)
- 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<SourceRoot> = this.filter { it.exists() }.map { SourceRoot().apply { path = it.path } }
-
- protected open fun collectSuppressedFiles(sourceRoots: List<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()
-
- val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
-
- val sourceRoots = collectSourceRoots() + tasksSourceRoots.toSourceRoots()
- if (sourceRoots.isEmpty()) {
- logger.warn("No source directories found: skipping dokka generation")
- return
- }
-
- val fullClasspath = tasksClasspath + classpath
-
- val bootstrapClass = 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 configuration = GradleDokkaConfigurationImpl()
- configuration.outputDir = outputDirectory
- configuration.format = outputFormat
- configuration.generateIndexPages = true
- configuration.cacheRoot = cacheRoot
- configuration.impliedPlatforms = impliedPlatforms
- configuration.passesConfigurations =
- (project.extensions.getByName("passConfigurations") as Iterable<GradlePassConfigurationImpl>)
- .toList()
- .map { defaultPassConfiguration(it) }
-
-// listOf(PassConfigurationImpl(
-// classpath= fullClasspath.map { it.absolutePath },
-// sourceRoots = sourceRoots.map { SourceRootImpl(it.path) },
-// samples = samples.filterNotNull().map { project.file(it).absolutePath },
-// includes = includes.filterNotNull().map { project.file(it).absolutePath },
-// collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries,
-// perPackageOptions = perPackageOptions.map{PackageOptionsImpl(
-// prefix = it.prefix,
-// includeNonPublic = it.includeNonPublic,
-// reportUndocumented = it.reportUndocumented,
-// skipDeprecated = it.skipDeprecated,
-// suppress = it.suppress
-// )},
-// moduleName = moduleName,
-// includeNonPublic = includeNonPublic,
-// includeRootPackage = false,
-// reportUndocumented = reportUndocumented,
-// skipEmptyPackages = skipEmptyPackages,
-// skipDeprecated = skipDeprecated,
-// jdkVersion = jdkVersion,
-// languageVersion = languageVersion,
-// apiVersion = apiVersion,
-// noStdlibLink = noStdlibLink,
-// noJdkLink = noJdkLink,
-// suppressedFiles = collectSuppressedFiles(sourceRoots),
-// sinceKotlin = "1.0",
-// analysisPlatform = Platform.DEFAULT,
-// targets = emptyList(),
-// sourceLinks = mutableListOf(), // TODO: check this line
-// externalDocumentationLinks = externalDocumentationLinks.map {
-// ExternalDocumentationLinkImpl(it.url, it.packageListUrl)
-// }
-// )
-// )
-// )
-
- 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{
-
- fun Closure<Unit>.setDelegateAndCall(delegate: Any) {
- this.delegate = delegate
- this.call()
- this.delegate
- }
-
- val (tasksClasspath, tasksSourceRoots) = kotlinCompileBasedClasspathAndSourceRoots
-
- val fullClasspath = tasksClasspath + classpath
- passConfig.moduleName = moduleName
- passConfig.classpath = fullClasspath.map { it.absolutePath }
- passConfig.sourceRoots = sourceRoots.map { SourceRootImpl(it.path) }
- passConfig.samples = samples.filterNotNull().map { project.file(it).absolutePath }
- passConfig.includes = includes.filterNotNull().map { project.file(it).absolutePath }
- passConfig.collectInheritedExtensionsFromLibraries = collectInheritedExtensionsFromLibraries
-
- passConfig.perPackageOptions = ((passConfig.perPackageOptions as List<Closure<Unit>>).map {
- it.setDelegateAndCall(GradlePackageOptionsImpl())
- }) as List<PackageOptionsImpl>
-
- passConfig.suppressedFiles = collectSuppressedFiles(sourceRoots)
- passConfig.sourceLinks = ((passConfig.sourceLinks as List<Closure<Unit>>).map {
- it.setDelegateAndCall(GradleSourceLinkDefinitionImpl())
- }) as MutableList<GradleSourceLinkDefinitionImpl> // TODO: Parse source links?
-
- passConfig.externalDocumentationLinks = ((passConfig.externalDocumentationLinks as List<Closure<Unit>>).map {
- it.setDelegateAndCall(GradleExternalDocumentationLinkImpl())
- }) as List<ExternalDocumentationLinkImpl>
-
- return passConfig
- }
-
-
- private fun collectSourceRoots(): List<SourceRoot> {
- val sourceDirs = if (sourceDirs.any()) {
- logger.info("Dokka: Taking source directories provided by the user")
- sourceDirs.toSet()
- } else if (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())
- }
-
-
- @Classpath
- fun getInputClasspath(): FileCollection {
- val (classpathFileCollection) = extractClasspathAndSourceRootsFromKotlinTasks()
- return project.files(classpath) + classpathFileCollection
- }
-
- @InputFiles
- fun getInputFiles(): FileCollection {
- val (_, tasksSourceRoots) = extractClasspathAndSourceRootsFromKotlinTasks()
- return project.files(tasksSourceRoots.map { project.fileTree(it) }) +
- project.files(collectSourceRoots().map { project.fileTree(File(it.path)) }) +
- project.files(includes) +
- project.files(samples.filterNotNull().map { project.fileTree(it) })
- }
-
- @OutputDirectory
- fun getOutputDirectoryAsFile(): File = project.file(outputDirectory)
-
- 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
- }
- }
-
-}
-
-class SourceRoot : DokkaConfiguration.SourceRoot, Serializable {
- override var path: String = ""
- set(value) {
- field = File(value).absolutePath
- }
-
- override fun toString(): String = path
-}
-
-open class LinkMapping : Serializable, DokkaConfiguration.SourceLinkDefinition {
- var dir: String
- get() = path
- set(value) {
- if (value.contains("\\"))
- throw java.lang.IllegalArgumentException("Incorrect dir property, only Unix based path allowed.")
- else path = value
- }
-
- override var path: String = ""
- override var url: String = ""
-
- var suffix: String?
- get() = lineSuffix
- set(value) {
- lineSuffix = value
- }
-
- override var lineSuffix: String? = null
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (other?.javaClass != javaClass) return false
-
- other as LinkMapping
-
- if (path != other.path) return false
- if (url != other.url) return false
- if (lineSuffix != other.lineSuffix) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = path.hashCode()
- result = 31 * result + url.hashCode()
- result = 31 * result + (lineSuffix?.hashCode() ?: 0)
- return result
- }
-
- companion object {
- const val serialVersionUID: Long = -8133501684312445981L
- }
-}
-
-class PackageOptions : Serializable, DokkaConfiguration.PackageOptions {
- override var prefix: String = ""
- override var includeNonPublic: Boolean = false
- override var reportUndocumented: Boolean = true
- override var skipDeprecated: Boolean = false
- override var suppress: Boolean = false
-}
-
-open class GradlePassConfigurationImpl(@Transient val name: String = ""): DokkaConfiguration.PassConfiguration {
- override var classpath: List<String> = emptyList()
- override var moduleName: String = ""
- override var sourceRoots: List<SourceRootImpl> = emptyList()
- 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: List<GradleSourceLinkDefinitionImpl> = emptyList()
- override var perPackageOptions: List<PackageOptionsImpl> = emptyList()
- override var externalDocumentationLinks: List<ExternalDocumentationLinkImpl> = emptyList()
- 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"
-}
-
-class GradleSourceLinkDefinitionImpl : DokkaConfiguration.SourceLinkDefinition {
- override var path: String = ""
- override var url: String = ""
- override var lineSuffix: String? = null
-
- companion object {
- fun parseSourceLinkDefinition(srcLink: String): SourceLinkDefinitionImpl {
- val (path, urlAndLine) = srcLink.split('=')
- return SourceLinkDefinitionImpl(
- File(path).canonicalPath,
- urlAndLine.substringBefore("#"),
- urlAndLine.substringAfter("#", "").let { if (it.isEmpty()) null else "#$it" })
- }
- }
-}
-
-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..6c93188b
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaTask.kt
@@ -0,0 +1,275 @@
+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.DokkaConfiguration.SourceRoot
+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 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<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() + 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 = extensions.findByName(
+ CONFIGURATION_EXTENSION_NAME) as GradlePassConfigurationImpl
+ val passConfigurationsContainer =
+ (extensions.getByName(MULTIPLATFORM_EXTENSION_NAME) as Iterable<GradlePassConfigurationImpl>).toList()
+
+ passConfigurationExtension.sourceRoots.addAll(sourceRoots)
+
+ val passConfigurationList =
+ (if (passConfigurationsContainer.isEmpty()) listOf(passConfigurationExtension) else 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, _) = 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(): 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/ProxyUtils.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt
index 7bdf2f9d..f8965993 100644
--- a/runners/gradle-plugin/src/main/kotlin/ProxyUtils.kt
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt
@@ -1,4 +1,4 @@
-package org.jetbrains.dokka
+package org.jetbrains.dokka.gradle
import java.lang.reflect.InvocationHandler
import java.lang.reflect.InvocationTargetException
@@ -26,9 +26,9 @@ inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: ClassLoader,
*/
fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any =
Proxy.newProxyInstance(
- targetClassLoader,
- arrayOf(targetType),
- DelegatedInvocationHandler(delegate)
+ targetClassLoader,
+ arrayOf(targetType),
+ DelegatedInvocationHandler(delegate)
)
class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler {
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..56bfe262
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt
@@ -0,0 +1,118 @@
+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.DokkaConfiguration.*
+import org.jetbrains.dokka.Platform
+import java.io.File
+import java.io.Serializable
+import java.net.URL
+
+class GradleSourceRootImpl: SourceRoot, Serializable {
+ override var path: String = ""
+ set(value) {
+ field = File(value).absolutePath
+ }
+
+ override fun toString(): String = path
+}
+
+open class GradlePassConfigurationImpl(@Transient val name: String = ""): PassConfiguration {
+ override var classpath: List<String> = emptyList()
+ override var moduleName: String = ""
+ override var sourceRoots: MutableList<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<SourceLinkDefinition> = mutableListOf()
+ override var perPackageOptions: MutableList<PackageOptions> = mutableListOf()
+ override var externalDocumentationLinks: MutableList<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 : SourceLinkDefinition {
+ override var path: String = ""
+ override var url: String = ""
+ override var lineSuffix: String? = null
+}
+
+class GradleExternalDocumentationLinkImpl : 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: 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/main.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt
new file mode 100644
index 00000000..d04ed5f0
--- /dev/null
+++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/main.kt
@@ -0,0 +1,51 @@
+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.*
+
+/*
+* 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