diff options
author | sebastian.sellmair <sebastian.sellmair@jetbrains.com> | 2020-07-18 12:18:59 +0200 |
---|---|---|
committer | Sebastian Sellmair <34319766+sellmair@users.noreply.github.com> | 2020-08-14 17:51:11 +0200 |
commit | eae1ce49d18c2978b49166ea502bf2c109a85504 (patch) | |
tree | 477f39e33f14c71042f06eecc938d6efaa95e66c /runners/gradle-plugin/src/main/kotlin/org | |
parent | 6c635551ed3ea0cfe5f04b54a98cb28225061d26 (diff) | |
download | dokka-eae1ce49d18c2978b49166ea502bf2c109a85504.tar.gz dokka-eae1ce49d18c2978b49166ea502bf2c109a85504.tar.bz2 dokka-eae1ce49d18c2978b49166ea502bf2c109a85504.zip |
Simplify Dokka Gradle Plugin
Diffstat (limited to 'runners/gradle-plugin/src/main/kotlin/org')
24 files changed, 751 insertions, 941 deletions
diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt new file mode 100644 index 00000000..bf8308bf --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTask.kt @@ -0,0 +1,73 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Project +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.jetbrains.dokka.DokkaBootstrap +import org.jetbrains.dokka.DokkaBootstrapImpl +import kotlin.reflect.KClass + +// TODO NOW: Test UP-TO-DATE behaviour +abstract class AbstractDokkaParentTask( + bootstrapClass: KClass<out DokkaBootstrap> = DokkaBootstrapImpl::class +) : AbstractDokkaTask(bootstrapClass) { + + @Input + open var dokkaTaskNames: Set<String> = setOf() + + @Input + var subprojectPaths: Set<String> = project.subprojects.map { project -> project.path }.toSet() + + @get:Internal + val subprojects: List<Project> + get() = subprojectPaths.map { path -> project.project(path) }.distinct() + + @get:Nested + internal val dokkaTasks: List<AbstractDokkaTask> + get() = dokkaTaskNames.flatMap { dokkaTaskName -> findSubprojectDokkaTasks(dokkaTaskName) } + + + /** + * Will remove a single project from participating in this parent task. + * Note: This will not remove the [project]s subprojects. + * + * @see removeAllProjects + */ + fun removeSubproject(project: Project) { + subprojectPaths = subprojectPaths - project.path + } + + /** + * Will remove the [project] and all its subprojects from participating in this parent task. + * @see removeSubproject + */ + fun removeAllProjects(project: Project) { + project.allprojects.forEach(::removeSubproject) + } + + /** + * Includes the [project] to participate in this parent task. + * Note: This will not include any of the [project]s subprojects. + * @see addAllProjects + */ + fun addSubproject(project: Project) { + subprojectPaths = (subprojectPaths + project.path) + } + + /** + * Includes the [project] and all its subprojects to participate in this parent task. + * @see addSubproject + */ + fun addAllProjects(project: Project) { + project.allprojects.forEach(::addSubproject) + } + + protected fun findSubprojectDokkaTasks(dokkaTaskNames: Set<String>): List<AbstractDokkaTask> { + return dokkaTaskNames.flatMap { dokkaTaskName -> findSubprojectDokkaTasks(dokkaTaskName) } + } + + private fun findSubprojectDokkaTasks(dokkaTaskName: String): List<AbstractDokkaTask> { + return subprojects.mapNotNull { subproject -> subproject.tasks.findByName(dokkaTaskName) as? DokkaTask } + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt index 1269b305..6413d788 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaTask.kt @@ -1,21 +1,33 @@ package org.jetbrains.dokka.gradle import org.gradle.api.DefaultTask -import org.gradle.api.Project import org.gradle.api.artifacts.Configuration -import org.gradle.api.attributes.Usage -import org.gradle.api.tasks.Classpath -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.TaskAction -import org.gradle.kotlin.dsl.dependencies +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.tasks.* import org.jetbrains.dokka.DokkaBootstrap +import org.jetbrains.dokka.DokkaConfigurationImpl import org.jetbrains.dokka.plugability.Configurable +import org.jetbrains.dokka.toJsonString +import java.io.File +import java.util.function.BiConsumer +import kotlin.reflect.KClass +abstract class AbstractDokkaTask( + private val bootstrapClass: KClass<out DokkaBootstrap> = DokkaBootstrap::class +) : DefaultTask(), Configurable { + + @OutputDirectory + var outputDirectory: File = defaultDokkaOutputDirectory() + + @Optional + @InputDirectory + var cacheRoot: File? = null + + @Input + var failOnWarning: Boolean = false -abstract class AbstractDokkaTask : DefaultTask(), Configurable { @Input - var outputDirectory: String = defaultDokkaOutputDirectory().absolutePath + var offlineMode: Boolean = false @Input override val pluginsConfiguration: MutableMap<String, String> = mutableMapOf() @@ -27,19 +39,26 @@ abstract class AbstractDokkaTask : DefaultTask(), Configurable { val runtime: Configuration = project.maybeCreateDokkaRuntimeConfiguration(name) @TaskAction - protected fun run() { - val kotlinColorsEnabledBefore = System.getProperty(DokkaTask.COLORS_ENABLED_PROPERTY) ?: "false" - System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, "false") - try { + protected open fun generateDocumentation() { + DokkaBootstrap(runtime, bootstrapClass).apply { + configure(buildDokkaConfiguration().toJsonString(), createProxyLogger()) generate() - } finally { - System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, kotlinColorsEnabledBefore) } } - protected abstract fun generate() + internal abstract fun buildDokkaConfiguration(): DokkaConfigurationImpl + + private fun createProxyLogger(): BiConsumer<String, String> = BiConsumer { level, message -> + when (level) { + "debug" -> logger.debug(message) + "info" -> logger.info(message) + "progress" -> logger.lifecycle(message) + "warn" -> logger.warn(message) + "error" -> logger.error(message) + } + } - protected fun DokkaBootstrap(bootstrapClassFQName: String): DokkaBootstrap { - return DokkaBootstrap(runtime, bootstrapClassFQName) + init { + group = JavaBasePlugin.DOCUMENTATION_GROUP } } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt deleted file mode 100644 index 6217703f..00000000 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ConfigurationExtractor.kt +++ /dev/null @@ -1,185 +0,0 @@ -package org.jetbrains.dokka.gradle - -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.UnknownDomainObjectException -import org.gradle.api.artifacts.ResolveException -import org.gradle.api.file.FileCollection -import org.gradle.api.plugins.JavaPluginConvention -import org.gradle.api.tasks.SourceSet -import org.gradle.api.tasks.compile.AbstractCompile -import org.jetbrains.dokka.ReflectDsl -import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension -import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension -import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation -import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet -import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeCompilation -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.io.File -import java.io.Serializable - -class ConfigurationExtractor(private val project: Project) { - fun extractConfiguration(sourceSetName: String): PlatformData? { - val projectExtension = project.extensions.findByType(KotlinProjectExtension::class.java) ?: run { - project.logger.error("Missing kotlin project extension") - return null - } - - val sourceSet = projectExtension.sourceSets.findByName(sourceSetName) ?: run { - project.logger.error("No source set with name '$sourceSetName' found") - return null - } - - val compilation = try { - when (projectExtension) { - is KotlinMultiplatformExtension -> { - val targets = projectExtension.targets.flatMap { it.compilations } - targets.find { it.name == sourceSetName } - ?: targets.find { it.kotlinSourceSets.contains(sourceSet) } - } - is KotlinSingleTargetExtension -> projectExtension.target.compilations.find { - it.kotlinSourceSets.contains(sourceSet) - } - else -> null - } - } catch (e: NoClassDefFoundError) { // Old Kotlin plugin versions - null - } - - val sourceRoots = sourceSet.sourceFiles - val classpath = compilation?.classpath - ?: sourceRoots + sourceSet.allParentSourceFiles() - - return PlatformData( - sourceSetName, - classpath.filter { it.exists() }, - sourceRoots, - sourceSet.dependsOn.map { it.name }, - compilation?.target?.platformType?.name ?: "common" - ) - } - - private fun KotlinSourceSet.allParentSourceFiles(): List<File> = - sourceFiles + dependsOn.flatMap { it.allParentSourceFiles() } - - fun extractFromJavaPlugin(): PlatformData? = - project.convention.findPlugin(JavaPluginConvention::class.java) - ?.run { sourceSets.findByName(SourceSet.MAIN_SOURCE_SET_NAME)?.allSource?.srcDirs } - ?.let { PlatformData(null, emptyList(), it.toList(), emptyList(), "") } - - fun extractFromKotlinTasks(kotlinTasks: List<Task>): List<PlatformData> = - try { - kotlinTasks.map { extractFromKotlinTask(it) } - } catch (e: Throwable) { - when (e) { - is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - listOfNotNull(extractFromKotlinTasksTheHardWay(kotlinTasks)) - else -> throw e - } - } - - private fun extractFromKotlinTask(task: Task): PlatformData = - try { - project.extensions.getByType(KotlinSingleTargetExtension::class.java).target - .compilations - .find { it.compileKotlinTask == task } - } catch (e: Throwable) { - when (e) { - is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - project.extensions.getByType(KotlinMultiplatformExtension::class.java).targets - .flatMap { it.compilations }.firstOrNull { it.compileKotlinTask == task } - else -> throw e - } - }.let { compilation -> - PlatformData( - task.name, - compilation?.classpath.orEmpty(), - compilation?.sourceFiles.orEmpty(), - compilation?.dependentSourceSets?.map { it.name }.orEmpty(), - compilation?.platformType?.toString() ?: "" - ) - } - - private fun extractFromKotlinTasksTheHardWay(kotlinTasks: List<Task>): PlatformData? { - val allClasspath = mutableSetOf<File>() - var allClasspathFileCollection: FileCollection = project.files() - val allSourceRoots = mutableSetOf<File>() - - kotlinTasks.forEach { - with(ReflectDsl) { - val taskSourceRoots: List<File> - val abstractKotlinCompileClz: Class<out Any> - try { - taskSourceRoots = it["sourceRootsContainer"]["sourceRoots"].v() - abstractKotlinCompileClz = DokkaTask.getAbstractKotlinCompileFor(it)!! - } catch (e: NullPointerException) { - println("Error during extraction of sources from kotlinTasks. This may be a result of outdated Kotlin Gradle Plugin") - return null - } - - 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() } - } - } - val classpath: MutableList<File> = try { - allClasspathFileCollection.toMutableList() - } catch (e: ResolveException) { - mutableListOf() - } - classpath.addAll(project.files(allClasspath).toList()) - - return PlatformData(null, classpath, allSourceRoots.toList(), emptyList(), "") - } - - private val KotlinCompilation<*>.sourceFiles: List<File> - get() = kotlinSourceSets.flatMap { it.sourceFiles } - - private val KotlinSourceSet.sourceFiles: List<File> - get() = kotlin.sourceDirectories.filter { it.exists() }.toList() - - private val KotlinCompilation<*>.dependentSourceSets: Set<KotlinSourceSet> - get() = (allKotlinSourceSets - kotlinSourceSets) - - private val KotlinCompilation<*>.classpath: List<File> - get() = if (target.isAndroidTarget()) { - getClasspathFromAndroidTask(this) - } else { - getClasspathFromRegularTask(this) - } - - // This is a workaround for KT-33893 - private fun getClasspathFromAndroidTask(compilation: KotlinCompilation<*>): List<File> = (compilation - .compileKotlinTask as? KotlinCompile) - ?.classpath?.files?.toList() ?: getClasspathFromRegularTask(compilation) - - private fun getClasspathFromRegularTask(compilation: KotlinCompilation<*>): List<File> { - // explicit dependencies of the compilation - val ownDependencyFiles: Set<File> = compilation.compileDependencyFiles.files - - // the dependencies provided by the platform (e.g. Kotlin/Native platform libs) - val platformDependencyFiles: Set<File> = (compilation as? KotlinNativeCompilation) - ?.target?.project?.configurations - ?.findByName(compilation.defaultSourceSet.implementationMetadataConfigurationName)?.files - ?: emptySet() - - return (ownDependencyFiles + platformDependencyFiles).toList().filter { it.exists() } - } - - data class PlatformData( - val name: String?, - val classpath: List<File>, - val sourceRoots: List<File>, - val dependentSourceSets: List<String>, - val platform: String - ) : Serializable -} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt index 8d337795..7a73d633 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTask.kt @@ -1,59 +1,24 @@ package org.jetbrains.dokka.gradle -import com.google.gson.GsonBuilder -import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP -import org.gradle.api.tasks.Input -import org.jetbrains.dokka.toJsonString - -open class DokkaCollectorTask : AbstractDokkaTask() { - - @Input - var modules: List<String> = emptyList() - - @Input - var dokkaTaskNames: Set<String> = setOf() - - override fun generate() { - val configurations = getSubprojectDokkaTasks(dokkaTaskNames) - .mapNotNull { dokkaTask -> dokkaTask.getConfigurationOrNull() } - - val initial = GradleDokkaConfigurationImpl().apply { - outputDir = outputDirectory - cacheRoot = configurations.first().cacheRoot - } - - val configuration = configurations.fold(initial) { acc, it: GradleDokkaConfigurationImpl -> - if (acc.cacheRoot != it.cacheRoot) - throw IllegalStateException("Dokka task configurations differ on core argument cacheRoot") - acc.sourceSets = acc.sourceSets + it.sourceSets - acc.pluginsClasspath = (acc.pluginsClasspath + it.pluginsClasspath).distinct() - acc +import org.jetbrains.dokka.DokkaConfigurationImpl + +open class DokkaCollectorTask : AbstractDokkaParentTask() { + + override fun buildDokkaConfiguration(): DokkaConfigurationImpl { + val initialDokkaConfiguration = DokkaConfigurationImpl( + outputDir = outputDirectory, + cacheRoot = cacheRoot, + failOnWarning = failOnWarning, + offlineMode = offlineMode, + pluginsClasspath = plugins.resolve().toList(), + ) + + val subprojectDokkaConfigurations = dokkaTasks.map { dokkaTask -> dokkaTask.buildDokkaConfiguration() } + return subprojectDokkaConfigurations.fold(initialDokkaConfiguration) { acc, it: DokkaConfigurationImpl -> + acc.copy( + sourceSets = acc.sourceSets + it.sourceSets, + pluginsClasspath = acc.pluginsClasspath + it.pluginsClasspath + ) } - - val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaBootstrapImpl") - bootstrap.configure(configuration.toJsonString()) { level, message -> - when (level) { - "debug" -> logger.debug(message) - "info" -> logger.info(message) - "progress" -> logger.lifecycle(message) - "warn" -> logger.warn(message) - "error" -> logger.error(message) - } - } - bootstrap.generate() - } - - private fun getSubprojectDokkaTasks(dokkaTaskName: String): List<DokkaTask> { - return project.subprojects - .filter { subproject -> subproject.name in modules } - .mapNotNull { subproject -> subproject.tasks.findByName(dokkaTaskName) as? DokkaTask } - } - - private fun getSubprojectDokkaTasks(dokkaTaskNames: Set<String>): List<DokkaTask> { - return dokkaTaskNames.flatMap { dokkaTaskName -> getSubprojectDokkaTasks(dokkaTaskName) } - } - - init { - group = DOCUMENTATION_GROUP } } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt index 986b883a..8369954b 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaMultimoduleTask.kt @@ -1,66 +1,40 @@ package org.jetbrains.dokka.gradle -import org.gradle.api.plugins.JavaBasePlugin.DOCUMENTATION_GROUP +import org.gradle.api.internal.tasks.TaskDependencyInternal import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.DokkaModuleDescriptionImpl +import org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl import org.jetbrains.dokka.plugability.Configurable -import org.jetbrains.dokka.toJsonString -open class DokkaMultimoduleTask : AbstractDokkaTask(), Configurable { +open class DokkaMultimoduleTask : AbstractDokkaParentTask(DokkaMultimoduleBootstrapImpl::class), Configurable { + /** + * Name of the file containing all necessary module information. + * This file has to be placed inside the subrpojects root directory. + */ @Input var documentationFileName: String = "README.md" - - @Input - var dokkaTaskNames: Set<String> = setOf() - set(value) { - field = value.toSet() - setDependsOn(getSubprojectDokkaTasks(value)) - } - - - override fun generate() { - val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl") - val configuration = getConfiguration() - bootstrap.configure(configuration.toJsonString()) { level, message -> - when (level) { - "debug" -> logger.debug(message) - "info" -> logger.info(message) - "progress" -> logger.lifecycle(message) - "warn" -> logger.warn(message) - "error" -> logger.error(message) - } - } - bootstrap.generate() + override fun getTaskDependencies(): TaskDependencyInternal { + return super.getTaskDependencies() + dokkaTasks } - @Internal - internal fun getConfiguration(): GradleDokkaConfigurationImpl = - GradleDokkaConfigurationImpl().apply { - outputDir = project.file(outputDirectory).absolutePath - pluginsClasspath = plugins.resolve().toList() - pluginsConfiguration = this@DokkaMultimoduleTask.pluginsConfiguration - modules = getSubprojectDokkaTasks(dokkaTaskNames).map { dokkaTask -> - GradleDokkaModuleDescription().apply { - name = dokkaTask.project.name - path = dokkaTask.project.projectDir.resolve(dokkaTask.outputDirectory) - .toRelativeString(project.file(outputDirectory)) - docFile = dokkaTask.project.projectDir.resolve(documentationFileName).absolutePath - } + override fun buildDokkaConfiguration(): DokkaConfigurationImpl { + return DokkaConfigurationImpl( + outputDir = outputDirectory, + cacheRoot = cacheRoot, + pluginsConfiguration = pluginsConfiguration, + failOnWarning = failOnWarning, + offlineMode = offlineMode, + pluginsClasspath = plugins.resolve().toList(), + modules = dokkaTasks.map { dokkaTask -> + DokkaModuleDescriptionImpl( + name = dokkaTask.project.name, + path = dokkaTask.outputDirectory.relativeTo(outputDirectory), + docFile = dokkaTask.project.projectDir.resolve(documentationFileName).absoluteFile + ) } - } - - private fun getSubprojectDokkaTasks(dokkaTaskName: String): List<DokkaTask> { - return project.subprojects - .mapNotNull { subproject -> subproject.tasks.findByName(dokkaTaskName) as? DokkaTask } - } - - private fun getSubprojectDokkaTasks(dokkaTaskNames: Set<String>): List<DokkaTask> { - return dokkaTaskNames.flatMap { dokkaTaskName -> getSubprojectDokkaTasks(dokkaTaskName) } - } - - init { - group = DOCUMENTATION_GROUP + ) } } 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 index b4601acf..a74068ae 100644 --- 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 @@ -1,306 +1,35 @@ package org.jetbrains.dokka.gradle import org.gradle.api.NamedDomainObjectContainer -import org.gradle.api.Project -import org.gradle.api.Task -import org.gradle.api.file.FileCollection import org.gradle.api.internal.plugins.DslObject -import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.api.tasks.* -import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink.Builder -import org.jetbrains.dokka.DokkaConfiguration.SourceRoot -import org.jetbrains.dokka.DokkaException -import org.jetbrains.dokka.Platform -import org.jetbrains.dokka.ReflectDsl -import org.jetbrains.dokka.ReflectDsl.isNotInstance -import org.jetbrains.dokka.gradle.ConfigurationExtractor.PlatformData -import org.jetbrains.dokka.toJsonString -import java.io.File -import java.net.URL -import java.util.concurrent.Callable +import org.gradle.api.tasks.Nested +import org.jetbrains.dokka.DokkaBootstrapImpl +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.build -open class DokkaTask : AbstractDokkaTask() { - private val ANDROID_REFERENCE_URL = Builder("https://developer.android.com/reference/").build() - - private val ANDROIDX_REFERENCE_URL = Builder( - url = URL("https://developer.android.com/reference/kotlin/"), - packageListUrl = URL("https://developer.android.com/reference/androidx/package-list") - ).build() - - private val configExtractor = ConfigurationExtractor(project) - - @Suppress("MemberVisibilityCanBePrivate") - fun defaultKotlinTasks(): List<Task> = 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 } }) - } - - @Optional - @Input - var cacheRoot: String? = null - - - /** - * Hack used by DokkaCollector to enforce a different configuration to be used. - */ - @get:Internal - internal var enforcedConfiguration: GradleDokkaConfigurationImpl? = null +open class DokkaTask : AbstractDokkaTask(DokkaBootstrapImpl::class) { @get:Nested - val dokkaSourceSets: NamedDomainObjectContainer<GradleDokkaSourceSet> = - project.container(GradleDokkaSourceSet::class.java) { name -> GradleDokkaSourceSet(name, project) } - .also { container -> DslObject(this).extensions.add("dokkaSourceSets", container) } - - - private val kotlinTasks: List<Task> by lazy { - extractKotlinCompileTasks( - dokkaSourceSets.mapNotNull { - it.collectKotlinTasks?.invoke() - }.takeIf { it.isNotEmpty() }?.flatten() ?: defaultKotlinTasks() - ) - } - - @Input - var disableAutoconfiguration: Boolean = false - - @Input - var failOnWarning: Boolean = false - - @Input - var offlineMode: Boolean = false - - private var outputDiagnosticInfo: Boolean = - false // Workaround for Gradle, which fires some methods (like collectConfigurations()) multiple times in its lifecycle - - protected fun extractKotlinCompileTasks(collectTasks: List<Any?>?): List<Task> { - val inputList = (collectTasks ?: emptyList()).filterNotNull() - val (paths, other) = inputList.partition { it is String } - - val tasksByPath = paths.map { - project.tasks.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") } - - @Suppress("UNCHECKED_CAST") - return (tasksByPath + other) as List<Task> - } - - private fun Iterable<File>.toSourceRoots(): List<GradleSourceRootImpl> = - this.filter { it.exists() }.map { GradleSourceRootImpl().apply { path = it.path } } - - private fun Iterable<String>.toProjects(): List<Project> = - project.subprojects.toList().filter { this.contains(it.name) } - - protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>) = - if (project.isAndroidProject()) { - val generatedRoot = project.buildDir.resolve("generated").absoluteFile - sourceRoots - .map { File(it.path) } - .filter { it.startsWith(generatedRoot) } - .flatMap { it.walk().toList() } - .map { it.absolutePath } - } else { - emptyList() - } - - override fun generate() = enforcedConfiguration?.let { generate(it) } ?: generate(getConfigurationOrThrow()) - - protected open fun generate(configuration: GradleDokkaConfigurationImpl) { - outputDiagnosticInfo = true - val bootstrap = DokkaBootstrap("org.jetbrains.dokka.DokkaBootstrapImpl") - - bootstrap.configure(configuration.toJsonString()) { level, message -> - when (level) { - "debug" -> logger.debug(message) - "info" -> logger.info(message) - "progress" -> logger.lifecycle(message) - "warn" -> logger.warn(message) - "error" -> logger.error(message) - } - } - bootstrap.generate() - } - - - @Internal - internal fun getConfigurationOrNull(): GradleDokkaConfigurationImpl? { - val defaultModulesConfiguration = configuredDokkaSourceSets - .map { configureDefault(it) }.takeIf { it.isNotEmpty() } - ?: listOf( - configureDefault(configureDokkaSourceSet(dokkaSourceSets.create("main"))) - ).takeIf { project.isNotMultiplatformProject() } ?: emptyList() - - if (defaultModulesConfiguration.isEmpty()) { - return null - } - - return GradleDokkaConfigurationImpl().apply { - outputDir = project.file(outputDirectory).absolutePath - cacheRoot = this@DokkaTask.cacheRoot - offlineMode = this@DokkaTask.offlineMode - sourceSets = defaultModulesConfiguration - pluginsClasspath = plugins.resolve().toList() - pluginsConfiguration = this@DokkaTask.pluginsConfiguration - failOnWarning = this@DokkaTask.failOnWarning - } - } - - @Internal - internal fun getConfigurationOrThrow(): GradleDokkaConfigurationImpl { - return getConfigurationOrNull() ?: throw DokkaException( - """ - No source sets to document found. - Make source to configure at least one source set e.g. - - tasks { - dokkaHtml { - dokkaSourceSets { - register("commonMain") { - displayName = "common" - platform = "common" - } - } + val dokkaSourceSets: NamedDomainObjectContainer<GradleDokkaSourceSetBuilder> = + project.container(GradleDokkaSourceSetBuilder::class.java, GradleDokkaSourceSetBuilderFactory()) + .also { container -> + DslObject(this).extensions.add("dokkaSourceSets", container) + project.findKotlinSourceSets().orEmpty().forEach { kotlinSourceSet -> + container.register(kotlinSourceSet.name) { dokkaSourceSet -> + dokkaSourceSet.configureWithKotlinSourceSetGist(kotlinSourceSet) } } - """ - ) - } - - @get:Internal - protected val configuredDokkaSourceSets: List<GradleDokkaSourceSet> - get() = dokkaSourceSets.map { configureDokkaSourceSet(it) } - - private fun configureDokkaSourceSet(config: GradleDokkaSourceSet): GradleDokkaSourceSet { - val userConfig = config - .apply { - collectKotlinTasks?.let { - configExtractor.extractFromKotlinTasks(extractKotlinCompileTasks(it())) - .fold(this) { config, platformData -> - mergeUserConfigurationAndPlatformData(config, platformData) - } - } - } - - if (disableAutoconfiguration) return userConfig - - return configExtractor.extractConfiguration(userConfig.name) - ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } - ?: if (this.dokkaSourceSets.isNotEmpty()) { - if (outputDiagnosticInfo) - logger.warn( - "Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin, " + - "using only user provided configuration for this source set" - ) - userConfig - } else { - if (outputDiagnosticInfo) - logger.warn("Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin") - collectFromSinglePlatformOldPlugin(userConfig.name, userConfig) } - } - - private fun collectFromSinglePlatformOldPlugin(name: String, userConfig: GradleDokkaSourceSet) = - kotlinTasks.find { it.name == name } - ?.let { configExtractor.extractFromKotlinTasks(listOf(it)) } - ?.singleOrNull() - ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } - ?: configExtractor.extractFromJavaPlugin() - ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } - ?: userConfig - - private fun mergeUserConfigurationAndPlatformData( - userConfig: GradleDokkaSourceSet, - autoConfig: PlatformData - ) = userConfig.copy().apply { - sourceRoots.addAll(userConfig.sourceRoots.union(autoConfig.sourceRoots.toSourceRoots()).distinct()) - dependentSourceSets.addAll(userConfig.dependentSourceSets) - dependentSourceSets.addAll(autoConfig.dependentSourceSets.map { DokkaSourceSetID(project, it) }) - classpath = userConfig.classpath.union(autoConfig.classpath.map { it.absolutePath }).distinct() - if (userConfig.platform == null && autoConfig.platform != "") - platform = autoConfig.platform - } - - private fun configureDefault(config: GradleDokkaSourceSet): GradleDokkaSourceSet { - if (config.moduleDisplayName.isBlank()) { - config.moduleDisplayName = project.name - } - - if (config.displayName.isBlank()) { - config.displayName = config.name.substringBeforeLast("Main", config.platform.toString()) - } - - if (project.isAndroidProject() && !config.noAndroidSdkLink) { - config.externalDocumentationLinks.add(ANDROID_REFERENCE_URL) - config.externalDocumentationLinks.add(ANDROIDX_REFERENCE_URL) - } - - if (config.platform?.isNotBlank() == true) { - config.analysisPlatform = dokkaPlatformFromString(config.platform.toString()) - } - // Workaround for Groovy's GStringImpl - config.classpath = (config.classpath as List<Any>).map { it.toString() }.distinct() - config.sourceRoots = config.sourceRoots.distinct().toMutableList() - config.samples = config.samples.map { project.file(it).absolutePath } - config.includes = config.includes.map { project.file(it).absolutePath } - config.suppressedFiles += collectSuppressedFiles(config.sourceRoots) - config.suppressedFiles = config.suppressedFiles.map { project.file(it).absolutePath } - - return config - } - - private fun dokkaPlatformFromString(platform: String) = when (platform.toLowerCase()) { - "androidjvm", "android" -> Platform.jvm - "metadata" -> Platform.common - else -> Platform.fromString(platform) - } - - // Needed for Gradle incremental build - @OutputDirectory - fun getOutputDirectoryAsFile(): File = project.file(outputDirectory) - - // Needed for Gradle incremental build - @InputFiles - fun getInputFiles(): FileCollection = configuredDokkaSourceSets.let { config -> - project.files(config.flatMap { it.sourceRoots }.map { project.fileTree(File(it.path)) }) + - project.files(config.flatMap { it.includes }) + - project.files(config.flatMap { it.samples }.map { project.fileTree(File(it)) }) - } - - @Classpath - fun getInputClasspath(): FileCollection = - project.files((configuredDokkaSourceSets.flatMap { it.classpath } as List<Any>) - .map { project.fileTree(File(it.toString())) }) - - companion object { - const val COLORS_ENABLED_PROPERTY = "kotlin.colors.enabled" - const val ABSTRACT_KOTLIN_COMPILE = "org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile" - - internal fun getAbstractKotlinCompileFor(task: Task) = try { - task.project.buildscript.classLoader.loadClass(ABSTRACT_KOTLIN_COMPILE) - } catch (e: ClassNotFoundException) { - null - } + override fun buildDokkaConfiguration(): DokkaConfigurationImpl { + return DokkaConfigurationImpl( + outputDir = outputDirectory, + cacheRoot = cacheRoot, + offlineMode = offlineMode, + failOnWarning = failOnWarning, + sourceSets = dokkaSourceSets.build(), + pluginsConfiguration = pluginsConfiguration, + pluginsClasspath = plugins.resolve().toList() + ) } } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt new file mode 100644 index 00000000..e420e1a5 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleDokkaSourceSetBuilder.kt @@ -0,0 +1,298 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.gradle + +import com.android.build.gradle.api.AndroidSourceSet +import com.fasterxml.jackson.annotation.JsonIgnore +import groovy.lang.Closure +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.Task +import org.gradle.api.tasks.* +import org.gradle.util.ConfigureUtil +import org.jetbrains.dokka.* +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import java.io.File +import java.net.URL +import org.jetbrains.kotlin.gradle.model.SourceSet as KotlinModelSourceSet + + +internal fun Task.GradleDokkaSourceSetBuilderFactory(): (name: String) -> GradleDokkaSourceSetBuilder = + { name -> GradleDokkaSourceSetBuilder(name, project) } + +open class GradleDokkaSourceSetBuilder constructor( + @get:JsonIgnore @Transient @get:Input val name: String, + @get:JsonIgnore @Transient @get:Internal internal val project: Project +) : DokkaConfigurationBuilder<DokkaSourceSetImpl> { + + @Classpath + @Optional + var classpath: List<File> = emptyList() + + @Input + @Optional + var moduleDisplayName: String? = null + + @Input + @Optional + var displayName: String? = null + + @get:Internal + val sourceSetID: DokkaSourceSetID = DokkaSourceSetID(project, name) + + @Nested + var sourceRoots: MutableList<GradleSourceRootBuilder> = mutableListOf() + + @Input + var dependentSourceSets: MutableSet<DokkaSourceSetID> = mutableSetOf() + + @InputFiles + @Optional + var samples: List<File> = emptyList() + + @InputFiles + @Optional + var includes: List<File> = emptyList() + + @Input + var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic + + @Input + var includeRootPackage: Boolean = DokkaDefaults.includeRootPackage + + @Input + var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented + + @Input + var skipEmptyPackages: Boolean = DokkaDefaults.skipEmptyPackages + + @Input + var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + + @Input + var jdkVersion: Int = DokkaDefaults.jdkVersion + + @Nested + var sourceLinks: MutableList<GradleSourceLinkBuilder> = mutableListOf() + + @Nested + var perPackageOptions: MutableList<GradlePackageOptionsBuilder> = mutableListOf() + + @Nested + var externalDocumentationLinks: MutableList<GradleExternalDocumentationLinkBuilder> = mutableListOf() + + @Input + @Optional + var languageVersion: String? = null + + @Input + @Optional + var apiVersion: String? = null + + @Input + var noStdlibLink: Boolean = DokkaDefaults.noStdlibLink + + @Input + var noJdkLink: Boolean = DokkaDefaults.noJdkLink + + @Input + var noAndroidSdkLink: Boolean = false + + @Input + var suppressedFiles: List<File> = emptyList() + + @Input + @Optional + var analysisPlatform: Platform? = null + + @Input + @Optional + var platform: String? = null + + fun DokkaSourceSetID(sourceSetName: String): DokkaSourceSetID { + return DokkaSourceSetID(project, sourceSetName) + } + + fun dependsOn(sourceSet: SourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) + } + + fun dependsOn(sourceSet: GradleDokkaSourceSetBuilder) { + dependsOn(sourceSet.sourceSetID) + } + + fun dependsOn(sourceSet: DokkaConfiguration.DokkaSourceSet) { + dependsOn(sourceSet.sourceSetID) + } + + fun dependsOn(sourceSetName: String) { + dependsOn(DokkaSourceSetID(sourceSetName)) + } + + fun dependsOn(sourceSetID: DokkaSourceSetID) { + dependentSourceSets.add(sourceSetID) + } + + // TODO NOW: Cover with tests + fun sourceRoot(c: Closure<Unit>) { + val configured = ConfigureUtil.configure(c, GradleSourceRootBuilder()) + sourceRoots.add(configured) + } + + fun sourceRoot(action: Action<in GradleSourceRootBuilder>) { + val sourceRoot = GradleSourceRootBuilder() + action.execute(sourceRoot) + sourceRoots.add(sourceRoot) + } + + fun sourceLink(c: Closure<Unit>) { + val configured = ConfigureUtil.configure(c, GradleSourceLinkBuilder()) + sourceLinks.add(configured) + } + + fun sourceLink(action: Action<in GradleSourceLinkBuilder>) { + val sourceLink = GradleSourceLinkBuilder() + action.execute(sourceLink) + sourceLinks.add(sourceLink) + } + + fun perPackageOption(c: Closure<Unit>) { + val configured = ConfigureUtil.configure(c, GradlePackageOptionsBuilder()) + perPackageOptions.add(configured) + } + + fun perPackageOption(action: Action<in GradlePackageOptionsBuilder>) { + val option = GradlePackageOptionsBuilder() + action.execute(option) + perPackageOptions.add(option) + } + + fun externalDocumentationLink(c: Closure<Unit>) { + val link = ConfigureUtil.configure(c, GradleExternalDocumentationLinkBuilder()) + externalDocumentationLinks.add(link) + } + + fun externalDocumentationLink(action: Action<in GradleExternalDocumentationLinkBuilder>) { + val link = GradleExternalDocumentationLinkBuilder() + action.execute(link) + externalDocumentationLinks.add(link) + } + + fun externalDocumentationLink(url: String, packageListUrl: String? = null) { + externalDocumentationLinks.add( + GradleExternalDocumentationLinkBuilder().apply { + this.url = URL(url) + this.packageListUrl = URL(packageListUrl) + } + ) + } + + fun externalDocumentationLink(url: URL, packageListUrl: URL? = null) { + externalDocumentationLinks.add( + GradleExternalDocumentationLinkBuilder().apply { + this.url = url + if (packageListUrl != null) { + this.packageListUrl = packageListUrl + } + } + ) + } + + override fun build(): DokkaSourceSetImpl { + val moduleDisplayName = moduleDisplayName ?: project.name + + val displayName = displayName ?: name.substringBeforeLast("Main", platform.toString()) + + val externalDocumentationLinks = externalDocumentationLinks.map { it.build() } + .run { + if (noJdkLink) this + else this + ExternalDocumentationLink( + url = + if (jdkVersion < 11) "https://docs.oracle.com/javase/${jdkVersion}/docs/api/" + else "https://docs.oracle.com/en/java/javase/${jdkVersion}/docs/api/java.base/", + packageListUrl = + if (jdkVersion < 11) "https://docs.oracle.com/javase/${jdkVersion}/docs/api/package-list" + else "https://docs.oracle.com/en/java/javase/${jdkVersion}/docs/api/element-list" + ) + } + .run { + if (noStdlibLink) this + else this + ExternalDocumentationLink("https://kotlinlang.org/api/latest/jvm/stdlib/") + } + .run { + if (noAndroidSdkLink || !project.isAndroidProject()) this + else this + + ExternalDocumentationLink("https://developer.android.com/reference/") + + ExternalDocumentationLink( + url = URL("https://developer.android.com/reference/kotlin/"), + packageListUrl = URL("https://developer.android.com/reference/androidx/package-list") + ) + } + + val analysisPlatform = when { + analysisPlatform != null -> checkNotNull(analysisPlatform) + + platform?.isNotBlank() == true -> when (val platform = platform.toString().toLowerCase()) { + "androidjvm", "android" -> Platform.jvm + "metadata" -> Platform.common + else -> Platform.fromString(platform) + } + + else -> Platform.DEFAULT + } + + val sourceRoots = sourceRoots.build().distinct() + + val suppressedFiles = suppressedFiles + project.collectSuppressedFiles(sourceRoots) + + return DokkaSourceSetImpl( + classpath = classpath.distinct().toList(), + moduleDisplayName = moduleDisplayName, + displayName = displayName, + sourceSetID = sourceSetID, + sourceRoots = sourceRoots, + dependentSourceSets = dependentSourceSets.toSet(), + samples = samples.toList(), + includes = includes.toList(), + includeNonPublic = includeNonPublic, + includeRootPackage = includeRootPackage, + reportUndocumented = reportUndocumented, + skipEmptyPackages = skipEmptyPackages, + skipDeprecated = skipDeprecated, + jdkVersion = jdkVersion, + sourceLinks = sourceLinks.build(), + perPackageOptions = perPackageOptions.build(), + externalDocumentationLinks = externalDocumentationLinks, + languageVersion = languageVersion, + apiVersion = apiVersion, + noStdlibLink = noStdlibLink, + noJdkLink = noJdkLink, + suppressedFiles = suppressedFiles, + analysisPlatform = analysisPlatform + ) + } +} + +fun GradleDokkaSourceSetBuilder.dependsOn(sourceSet: KotlinModelSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + +fun GradleDokkaSourceSetBuilder.dependsOn(sourceSet: KotlinSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + +fun GradleDokkaSourceSetBuilder.dependsOn(sourceSet: AndroidSourceSet) { + dependsOn(DokkaSourceSetID(sourceSet.name)) +} + +// TODO NOW: Test +private fun Project.collectSuppressedFiles(sourceRoots: List<DokkaConfiguration.SourceRoot>): List<File> = + if (project.isAndroidProject()) { + val generatedRoot = project.buildDir.resolve("generated").absoluteFile + sourceRoots + .map { it.directory } + .filter { it.startsWith(generatedRoot) } + .flatMap { it.walk().toList() } + } else { + emptyList() + } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleExternalDocumentationLinkBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleExternalDocumentationLinkBuilder.kt new file mode 100644 index 00000000..84ad6c1e --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleExternalDocumentationLinkBuilder.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.DokkaConfigurationBuilder +import org.jetbrains.dokka.ExternalDocumentationLink +import org.jetbrains.dokka.ExternalDocumentationLinkImpl +import java.net.URL + +class GradleExternalDocumentationLinkBuilder : DokkaConfigurationBuilder<ExternalDocumentationLinkImpl> { + @Input + var url: URL? = null + + @Input + var packageListUrl: URL? = null + + override fun build(): ExternalDocumentationLinkImpl { + return ExternalDocumentationLink( + url = checkNotNull(url) { "url not specified " }, + packageListUrl = packageListUrl + ) + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradlePackageOptionsBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradlePackageOptionsBuilder.kt new file mode 100644 index 00000000..fdc0275e --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradlePackageOptionsBuilder.kt @@ -0,0 +1,36 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.gradle + +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.DokkaConfigurationBuilder +import org.jetbrains.dokka.DokkaDefaults +import org.jetbrains.dokka.PackageOptionsImpl + + +class GradlePackageOptionsBuilder : DokkaConfigurationBuilder<PackageOptionsImpl> { + @Input + var prefix: String = "" + + @Input + var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic + + @Input + var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented + + @Input + var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated + + @Input + var suppress: Boolean = DokkaDefaults.suppress + + override fun build(): PackageOptionsImpl { + return PackageOptionsImpl( + prefix = prefix, + includeNonPublic = includeNonPublic, + reportUndocumented = reportUndocumented, + skipDeprecated = skipDeprecated, + suppress = suppress + ) + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceLinkBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceLinkBuilder.kt new file mode 100644 index 00000000..007575ec --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceLinkBuilder.kt @@ -0,0 +1,25 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.DokkaConfigurationBuilder +import org.jetbrains.dokka.SourceLinkDefinitionImpl + +class GradleSourceLinkBuilder : DokkaConfigurationBuilder<SourceLinkDefinitionImpl> { + // TODO NOW: CHECK UP TO DATE + @Input + var path: String = "" + + @Input + var url: String = "" + + @Input + var lineSuffix: String? = null + + override fun build(): SourceLinkDefinitionImpl { + return SourceLinkDefinitionImpl( + path = path, + url = url, + lineSuffix = lineSuffix + ) + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceRootBuilder.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceRootBuilder.kt new file mode 100644 index 00000000..687dec9c --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/GradleSourceRootBuilder.kt @@ -0,0 +1,15 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.tasks.InputDirectory +import org.jetbrains.dokka.DokkaConfigurationBuilder +import org.jetbrains.dokka.SourceRootImpl +import java.io.File + +class GradleSourceRootBuilder : DokkaConfigurationBuilder<SourceRootImpl> { + @InputDirectory + var directory: File? = null + + override fun build(): SourceRootImpl { + return SourceRootImpl(checkNotNull(directory) { "directory not set" }) + } +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGist.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGist.kt new file mode 100644 index 00000000..334aae15 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/KotlinSourceSetGist.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Project +import org.jetbrains.dokka.utilities.cast +import org.jetbrains.kotlin.gradle.dsl.KotlinCommonOptions +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinSingleTargetExtension +import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile +import java.io.File + +private typealias KotlinCompilation = + org.jetbrains.kotlin.gradle.plugin.KotlinCompilation<KotlinCommonOptions> + +internal data class KotlinSourceSetGist( + val name: String, + val platform: String, + val classpath: List<File>, + val sourceRoots: List<File>, + val dependentSourceSets: List<String>, +) + +/** + * @return null if the kotlin extension cannot be found, + * A list of [KotlinSourceSetGist] for every currently registered kotlin source set + */ +internal fun Project.findKotlinSourceSets(): List<KotlinSourceSetGist>? { + val kotlin = kotlinExtensionOrNull ?: return null + return kotlin.sourceSets.map { sourceSet -> kotlin.gistOf(sourceSet) } +} + +internal fun KotlinProjectExtension.gistOf(sourceSet: KotlinSourceSet): KotlinSourceSetGist { + return KotlinSourceSetGist( + name = sourceSet.name, + platform = platformOf(sourceSet), + classpath = classpathOf(sourceSet).filter(File::exists), + sourceRoots = sourceSet.kotlin.sourceDirectories.toList().filter(File::exists), + dependentSourceSets = sourceSet.dependsOn.map { dependentSourceSet -> dependentSourceSet.name }, + ) +} + +private fun KotlinProjectExtension.classpathOf(sourceSet: KotlinSourceSet): List<File> { + val compilations = compilationsOf(sourceSet) + if (compilations.isNotEmpty()) { + return compilations + .flatMap { compilation -> compileClasspathOf(compilation) } + .distinct() + } + + return sourceSet.withAllDependentSourceSets() + .toList() + .flatMap { it.kotlin.sourceDirectories } +} + +private fun KotlinProjectExtension.platformOf(sourceSet: KotlinSourceSet): String { + val targetNames = compilationsOf(sourceSet).map { compilation -> compilation.target.platformType.name }.distinct() + return when (targetNames.size) { + 0 -> KotlinPlatformType.common.name + 1 -> targetNames.single() + else -> throw IllegalArgumentException( + "Source set ${sourceSet.name} is expected to have only one target. Found $targetNames" + ) + } +} + +private fun KotlinProjectExtension.compilationsOf( + sourceSet: KotlinSourceSet +): List<KotlinCompilation> { + return when (this) { + is KotlinMultiplatformExtension -> compilationsOf(sourceSet) + is KotlinSingleTargetExtension -> compilationsOf(sourceSet) + else -> emptyList() + } +} + +private fun KotlinMultiplatformExtension.compilationsOf(sourceSet: KotlinSourceSet): List<KotlinCompilation> { + val allCompilations = targets.flatMap { target -> target.compilations } + return allCompilations.filter { compilation -> sourceSet in compilation.kotlinSourceSets } +} + +private fun KotlinSingleTargetExtension.compilationsOf(sourceSet: KotlinSourceSet): List<KotlinCompilation> { + return target.compilations.filter { compilation -> sourceSet in compilation.kotlinSourceSets } +} + +private fun compileClasspathOf(compilation: KotlinCompilation): List<File> { + if (compilation.target.isAndroidTarget()) { + // This is a workaround for https://youtrack.jetbrains.com/issue/KT-33893 + return compilation.compileKotlinTask.cast<KotlinCompile>().classpath.files.toList() + } + return compilation.compileDependencyFiles.files.toList() +} + +private fun KotlinSourceSet.withAllDependentSourceSets(): Sequence<KotlinSourceSet> { + return sequence { + yield(this@withAllDependentSourceSets) + for (dependentSourceSet in dependsOn) { + yieldAll(dependentSourceSet.withAllDependentSourceSets()) + } + } +} + + + diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt deleted file mode 100644 index 4b511022..00000000 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ReflectDsl.kt +++ /dev/null @@ -1,72 +0,0 @@ -package org.jetbrains.dokka - -import kotlin.reflect.* -import kotlin.reflect.full.memberFunctions -import kotlin.reflect.full.memberProperties -import kotlin.reflect.jvm.isAccessible - -internal object ReflectDsl { - - class CallOrPropAccess(private val receiver: Any?, - private val clz: KClass<*>, - private val selector: String) { - - @Suppress("UNCHECKED_CAST") - operator fun <T : Any?> invoke(vararg a: Any?): T { - return func!!.call(receiver, *a) as T - } - - operator fun get(s: String): CallOrPropAccess { - return v<Any?>()!![s] - } - - val func: KFunction<*>? by lazy { clz.memberFunctions.find { it.name == selector } } - val prop: KProperty<*>? by lazy { clz.memberProperties.find { it.name == selector } } - - fun takeIfIsFunc(): CallOrPropAccess? = if (func != null) this else null - - fun takeIfIsProp(): CallOrPropAccess? = if (prop != null) this else null - - @Suppress("UNCHECKED_CAST") - fun <T : Any?> v(): T { - val prop = prop!! - return try { - prop.getter.apply { isAccessible = true }.call(receiver) as T - } catch (e: KotlinNullPointerException) { - // Hack around kotlin-reflect bug KT-18480 - val jclass = clz.java - val customGetterName = prop.getter.name - val getterName = if (customGetterName.startsWith("<")) "get" + prop.name.capitalize() else customGetterName - val getter = jclass.getDeclaredMethod(getterName) - getter.isAccessible = true - - getter.invoke(receiver) as T - - } - } - - @Suppress("UNCHECKED_CAST") - fun v(x: Any?) { - (prop as KMutableProperty).setter.apply { isAccessible = true }.call(receiver, x) - } - - - } - - operator fun Any.get(s: String): CallOrPropAccess { - val clz = this.javaClass.kotlin - return CallOrPropAccess(this, clz, s) - } - - operator fun Any.get(s: String, clz: Class<*>): CallOrPropAccess { - val kclz = clz.kotlin - return CallOrPropAccess(this, kclz, s) - } - - operator fun Any.get(s: String, clz: KClass<*>): CallOrPropAccess { - return CallOrPropAccess(this, clz, s) - } - - inline infix fun Any.isInstance(clz: Class<*>?): Boolean = clz != null && clz.isAssignableFrom(this.javaClass) - inline infix fun Any.isNotInstance(clz: Class<*>?): Boolean = !(this isInstance clz) -}
\ No newline at end of file diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/TaskDependencyInternalWithAdditions.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/TaskDependencyInternalWithAdditions.kt new file mode 100644 index 00000000..fb648c02 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/TaskDependencyInternalWithAdditions.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.Task +import org.gradle.api.internal.tasks.AbstractTaskDependency +import org.gradle.api.internal.tasks.TaskDependencyInternal +import org.gradle.api.internal.tasks.TaskDependencyResolveContext + +operator fun TaskDependencyInternal.plus(tasks: Iterable<Task>): TaskDependencyInternal { + return TaskDependencyInternalWithAdditions(this, tasks.toSet()) +} + +private class TaskDependencyInternalWithAdditions( + private val dependency: TaskDependencyInternal, + private val additionalTaskDependencies: Set<Task> +) : AbstractTaskDependency() { + override fun visitDependencies(context: TaskDependencyResolveContext) { + dependency.visitDependencies(context) + additionalTaskDependencies.forEach(context::add) + } +} 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/automagicTypedProxy.kt index 468f597f..13f9c2ff 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/ProxyUtils.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/automagicTypedProxy.kt @@ -1,6 +1,9 @@ package org.jetbrains.dokka.gradle -import java.lang.reflect.* +import java.lang.reflect.InvocationHandler +import java.lang.reflect.InvocationTargetException +import java.lang.reflect.Method +import java.lang.reflect.Proxy /** @@ -21,14 +24,14 @@ internal inline fun <reified T : Any> automagicTypedProxy(targetClassLoader: Cla * to create access proxy for [delegate] into [targetClassLoader]. * */ -internal fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any = +private fun automagicProxy(targetClassLoader: ClassLoader, targetType: Class<*>, delegate: Any): Any = Proxy.newProxyInstance( targetClassLoader, arrayOf(targetType), DelegatedInvocationHandler(delegate) ) -internal class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler { +private class DelegatedInvocationHandler(private val delegate: Any) : InvocationHandler { @Throws(Throwable::class) override fun invoke(proxy: Any, method: Method, args: Array<Any?>?): Any? { 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 deleted file mode 100644 index bed73d6d..00000000 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/configurationImplementations.kt +++ /dev/null @@ -1,259 +0,0 @@ -@file:Suppress("FunctionName") - -package org.jetbrains.dokka.gradle - -import com.android.build.gradle.api.AndroidSourceSet -import com.fasterxml.jackson.annotation.JsonIgnore -import groovy.lang.Closure -import org.gradle.api.Action -import org.gradle.api.Project -import org.gradle.api.tasks.Input -import org.gradle.api.tasks.Internal -import org.gradle.api.tasks.Optional -import org.gradle.util.ConfigureUtil -import org.jetbrains.dokka.DokkaConfiguration -import org.jetbrains.dokka.DokkaConfiguration.* -import org.jetbrains.dokka.DokkaDefaults -import org.jetbrains.dokka.DokkaSourceSetID -import org.jetbrains.dokka.Platform -import java.io.File -import java.net.URL -import java.util.concurrent.Callable -import kotlin.reflect.KMutableProperty -import kotlin.reflect.full.memberProperties -import org.gradle.api.tasks.SourceSet as GradleSourceSet -import org.jetbrains.kotlin.gradle.model.SourceSet as KotlinSourceSet - -class GradleSourceRootImpl : SourceRoot { - override var path: String = "" - set(value) { - field = File(value).absolutePath - } - - override fun toString(): String = path -} - -open class GradleDokkaSourceSet constructor( - @get:JsonIgnore @Transient @get:Input val name: String, - @get:JsonIgnore @Transient @get:Internal internal val project: Project -) : DokkaSourceSet { - - @Input - @Optional - override var classpath: List<String> = emptyList() - - @Input - override var moduleDisplayName: String = "" - - @Input - override var displayName: String = "" - - @get:Internal - override val sourceSetID: DokkaSourceSetID = DokkaSourceSetID(project, name) - - @Input - override var sourceRoots: MutableList<SourceRoot> = mutableListOf() - - @Input - override var dependentSourceSets: MutableSet<DokkaSourceSetID> = mutableSetOf() - - @Input - override var samples: List<String> = emptyList() - - @Input - override var includes: List<String> = emptyList() - - @Input - override var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic - - @Input - override var includeRootPackage: Boolean = DokkaDefaults.includeRootPackage - - @Input - override var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented - - @Input - override var skipEmptyPackages: Boolean = DokkaDefaults.skipEmptyPackages - - @Input - override var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated - - @Input - override var jdkVersion: Int = DokkaDefaults.jdkVersion - - @Input - override var sourceLinks: MutableList<SourceLinkDefinition> = mutableListOf() - - @Input - override var perPackageOptions: MutableList<PackageOptions> = mutableListOf() - - @Input - override var externalDocumentationLinks: MutableList<ExternalDocumentationLink> = mutableListOf() - - @Input - @Optional - override var languageVersion: String? = null - - @Input - @Optional - override var apiVersion: String? = null - - @Input - override var noStdlibLink: Boolean = DokkaDefaults.noStdlibLink - - @Input - override var noJdkLink: Boolean = DokkaDefaults.noJdkLink - - @Input - var noAndroidSdkLink: Boolean = false - - @Input - override var suppressedFiles: List<String> = emptyList() - - @Input - override var analysisPlatform: Platform = DokkaDefaults.analysisPlatform - - @Input - @Optional - var platform: String? = null - - @JsonIgnore - @Internal - @Transient - var collectKotlinTasks: (() -> List<Any?>?)? = null - - fun DokkaSourceSetID(sourceSetName: String): DokkaSourceSetID { - return DokkaSourceSetID(project, sourceSetName) - } - - fun dependsOn(sourceSet: GradleSourceSet) { - dependsOn(DokkaSourceSetID(sourceSet.name)) - } - - fun dependsOn(sourceSet: DokkaSourceSet) { - dependsOn(sourceSet.sourceSetID) - } - - fun dependsOn(sourceSetName: String) { - dependsOn(DokkaSourceSetID(sourceSetName)) - } - - fun dependsOn(sourceSetID: DokkaSourceSetID) { - dependentSourceSets.add(sourceSetID) - } - - fun kotlinTasks(taskSupplier: Callable<List<Any>>) { - collectKotlinTasks = { taskSupplier.call() } - } - - fun kotlinTasks(closure: Closure<Any?>) { - collectKotlinTasks = { closure.call() as? List<Any?> } - } - - 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(ExternalDocumentationLink.Builder(link.url, link.packageListUrl).build()) - } - - fun externalDocumentationLink(action: Action<in GradleExternalDocumentationLinkImpl>) { - val link = GradleExternalDocumentationLinkImpl() - action.execute(link) - externalDocumentationLinks.add(ExternalDocumentationLink.Builder(link.url, link.packageListUrl).build()) - } -} - -fun GradleDokkaSourceSet.dependsOn(sourceSet: KotlinSourceSet) { - dependsOn(DokkaSourceSetID(sourceSet.name)) -} - -fun GradleDokkaSourceSet.dependsOn(sourceSet: org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet) { - dependsOn(DokkaSourceSetID(sourceSet.name)) -} - -fun GradleDokkaSourceSet.dependsOn(sourceSet: AndroidSourceSet) { - dependsOn(DokkaSourceSetID(sourceSet.name)) -} - -class GradleSourceLinkDefinitionImpl : SourceLinkDefinition { - override var path: String = "" - override var url: String = "" - override var lineSuffix: String? = null -} - -class GradleExternalDocumentationLinkImpl : ExternalDocumentationLink { - override var url: URL = URL("http://") - override var packageListUrl: URL = URL("http://") -} - -class GradleDokkaModuleDescription : DokkaModuleDescription { - override var name: String = "" - override var path: String = "" - override var docFile: String = "" -} - -class GradleDokkaConfigurationImpl : DokkaConfiguration { - override var outputDir: String = "" - override var cacheRoot: String? = DokkaDefaults.cacheRoot - override var offlineMode: Boolean = DokkaDefaults.offlineMode - override var failOnWarning: Boolean = DokkaDefaults.failOnWarning - override var sourceSets: List<GradleDokkaSourceSet> = emptyList() - override var pluginsClasspath: List<File> = emptyList() - override var pluginsConfiguration: Map<String, String> = mutableMapOf() - override var modules: List<GradleDokkaModuleDescription> = emptyList() -} - -class GradlePackageOptionsImpl : PackageOptions { - override var prefix: String = "" - override var includeNonPublic: Boolean = DokkaDefaults.includeNonPublic - override var reportUndocumented: Boolean = DokkaDefaults.reportUndocumented - override var skipDeprecated: Boolean = DokkaDefaults.skipDeprecated - override var suppress: Boolean = DokkaDefaults.suppress -} - -internal fun GradleDokkaSourceSet.copy(): GradleDokkaSourceSet { - val newObj = GradleDokkaSourceSet(this.name, this.project) - this::class.memberProperties.forEach { field -> - if (field is KMutableProperty<*>) { - when (val value = field.getter.call(this)) { - is List<*> -> field.setter.call(newObj, value.toMutableList()) - is Set<*> -> field.setter.call(newObj, value.toMutableSet()) - else -> field.setter.call(newObj, field.getter.call(this)) - } - - } - } - return newObj -} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokka.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokka.kt new file mode 100644 index 00000000..2625f64c --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokka.kt @@ -0,0 +1,7 @@ +import org.gradle.api.Project +import org.gradle.kotlin.dsl.withType +import org.jetbrains.dokka.gradle.DokkaTask + +fun Project.dokka(configuration: DokkaTask.() -> Unit) { + tasks.withType<DokkaTask>().configureEach(configuration) +} diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaBootstrapFactory.kt index df29c19b..8a40ac89 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaBootstrapFactory.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaBootstrapFactory.kt @@ -1,18 +1,21 @@ +@file:Suppress("FunctionName") + package org.jetbrains.dokka.gradle import org.gradle.api.artifacts.Configuration import org.jetbrains.dokka.DokkaBootstrap import java.net.URLClassLoader +import kotlin.reflect.KClass -fun DokkaBootstrap(configuration: Configuration, bootstrapClassFQName: String): DokkaBootstrap { +fun DokkaBootstrap(configuration: Configuration, bootstrapClass: KClass<out DokkaBootstrap>): DokkaBootstrap { val runtimeJars = configuration.resolve() val runtimeClassLoader = URLClassLoader( runtimeJars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent ) - val bootstrapClass = runtimeClassLoader.loadClass(bootstrapClassFQName) - val bootstrapInstance = bootstrapClass.constructors.first().newInstance() - return automagicTypedProxy(DokkaPlugin::class.java.classLoader, bootstrapInstance) + val runtimeClassloaderBootstrapClass = runtimeClassLoader.loadClass(bootstrapClass.qualifiedName) + val runtimeClassloaderBootstrapInstance = runtimeClassloaderBootstrapClass.constructors.first().newInstance() + return automagicTypedProxy(DokkaPlugin::class.java.classLoader, runtimeClassloaderBootstrapInstance) } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaDefaultOutputDirectory.kt index 0a7ab534..0a7ab534 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/defaultDokkaOutputDirectory.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaDefaultOutputDirectory.kt diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaSourceSetIDFactory.kt index 3fadb4fd..3fadb4fd 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/DokkaSourceSetIDFactory.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaSourceSetIDFactory.kt diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt index 20f54cc5..20f54cc5 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/dokkaConfigurations.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/gradleConfigurations.kt 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 index 51356703..71fad405 100644 --- 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 @@ -53,7 +53,6 @@ open class DokkaPlugin : Plugin<Project> { } if (collectorTaskSupported) { project.tasks.register<DokkaCollectorTask>("${name}Collector") { - modules = project.subprojects.map(Project::getName) dokkaTaskNames = dokkaTaskNames + name } } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/sourceSetKotlinGistConfiguration.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/sourceSetKotlinGistConfiguration.kt new file mode 100644 index 00000000..9bb11f36 --- /dev/null +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/sourceSetKotlinGistConfiguration.kt @@ -0,0 +1,21 @@ +package org.jetbrains.dokka.gradle + +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import java.io.File + +// TODO NOW: Test +fun GradleDokkaSourceSetBuilder.configureWithKotlinSourceSet(sourceSet: KotlinSourceSet) { + configureWithKotlinSourceSetGist(project.kotlinExtension.gistOf(sourceSet)) +} + +internal fun GradleDokkaSourceSetBuilder.configureWithKotlinSourceSetGist(sourceSet: KotlinSourceSetGist) { + sourceRoots.addAll(sourceRoots.union(sourceSet.sourceRoots.toSourceRoots()).distinct()) + dependentSourceSets.addAll(dependentSourceSets) + dependentSourceSets.addAll(sourceSet.dependentSourceSets.map { DokkaSourceSetID(project, it) }) + classpath = classpath.union(sourceSet.classpath).distinct() + if (platform == null && sourceSet.platform != "") + platform = sourceSet.platform +} + +private fun Iterable<File>.toSourceRoots(): List<GradleSourceRootBuilder> = + this.filter { it.exists() }.map { GradleSourceRootBuilder().apply { directory = it } } diff --git a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt index b6c5cbd8..12e22f5d 100644 --- a/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt +++ b/runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/gradle/utils.kt @@ -4,9 +4,20 @@ import org.gradle.api.NamedDomainObjectContainer import org.gradle.api.Project import org.gradle.api.UnknownDomainObjectException import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinProjectExtension import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import org.jetbrains.kotlin.gradle.plugin.KotlinTarget +internal val Project.kotlinExtensionOrNull: KotlinProjectExtension? + get() = try { + project.extensions.findByType(KotlinProjectExtension::class.java) + } catch (e: NoClassDefFoundError) { + null + } + +internal val Project.kotlinExtension: KotlinProjectExtension + get() = project.extensions.getByType(KotlinProjectExtension::class.java) + internal fun Project.isAndroidProject() = try { project.extensions.getByName("android") @@ -35,3 +46,4 @@ internal fun KotlinTarget.isAndroidTarget() = this.platformType == KotlinPlatfor internal fun <T : Any> NamedDomainObjectContainer<T>.maybeCreate(name: String, configuration: T.() -> Unit): T { return findByName(name) ?: create(name, configuration) } + |