diff options
author | Kamil Doległo <kamilok1965@interia.pl> | 2019-04-11 14:56:38 +0200 |
---|---|---|
committer | Kamil Doległo <kamilok1965@interia.pl> | 2019-04-11 14:56:38 +0200 |
commit | 64305246bf6a22a4111c11dc3c671297cf223393 (patch) | |
tree | acd0940b9fe9e212763c37dd86330c4e54097a43 /runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka | |
parent | 5de1d68ca8510f7bc3601c3516ecb111f7abe44b (diff) | |
download | dokka-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')
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 |