diff options
| author | Kamil Doległo <kamilok1965@interia.pl> | 2020-05-20 12:05:26 +0200 |
|---|---|---|
| committer | Paweł Marks <Kordyjan@users.noreply.github.com> | 2020-06-15 13:15:30 +0200 |
| commit | f3baf10b4c882230d382bfcdd94163d070bd0e25 (patch) | |
| tree | 128b63b10f05242cfce88da6714d9e04987b5651 /runners | |
| parent | 645e02fb42bbf1cd3ee2773a014ea1e553e09229 (diff) | |
| download | dokka-f3baf10b4c882230d382bfcdd94163d070bd0e25.tar.gz dokka-f3baf10b4c882230d382bfcdd94163d070bd0e25.tar.bz2 dokka-f3baf10b4c882230d382bfcdd94163d070bd0e25.zip | |
Rework dokka configuration and Gradle plugin
Diffstat (limited to 'runners')
9 files changed, 221 insertions, 288 deletions
diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index be42bc79..d217f83e 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -35,24 +35,22 @@ open class GlobalArguments(parser: DokkaArgumentsParser) : DokkaConfiguration { } } - override val generateIndexPages: Boolean by parser.singleFlag( - listOf("-generateIndexPages"), - "Generate index page" - ) - override val cacheRoot: String? by parser.stringOption( listOf("-cacheRoot"), "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled", null ) - override val impliedPlatforms: List<String> = emptyList() + override val offlineMode: Boolean by parser.singleFlag( + listOf("-offlineMode"), + "Offline mode (do not download package lists from the Internet)" + ) override val passesConfigurations: List<Arguments> by parser.repeatableFlag( listOf("-pass"), "Single dokka pass" ) { - Arguments(parser).also { if(it.moduleName.isEmpty()) DokkaConsoleLogger.warn("Not specified module name. It can result in unexpected behaviour while including documentation for module") } + Arguments(parser).also { if (it.moduleName.isEmpty()) DokkaConsoleLogger.warn("Not specified module name. It can result in unexpected behaviour while including documentation for module") } } override val modules: List<DokkaConfiguration.DokkaModuleDescription> = emptyList() @@ -67,12 +65,17 @@ class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfi "" ) - override val sourceSetName: String by parser.stringOption( - listOf("-sourceSetName"), - "Name of the source set", - "main" + override val displayName: String by parser.stringOption( + listOf("-displayName"), + "Name displayed in the generated documentation", + "" ) + override val sourceSetID: String by parser.stringOption( + listOf("-sourceSetID"), + "Source set ID used for declaring dependent source sets", + "main" + ) override val classpath: List<String> by parser.repeatableOption<String>( listOf("-classpath"), @@ -84,11 +87,6 @@ class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfi "Source file or directory (allows many paths separated by the system path separator)" ) { SourceRootImpl(it) } - override val dependentSourceRoots: List<DokkaConfiguration.SourceRoot> by parser.repeatableOption( - listOf("-dependentRoots"), - "Source roots of dependent source sets" - ) { SourceRootImpl(it) } - override val dependentSourceSets: List<String> by parser.repeatableOption<String>( listOf("-dependentSets"), "Names of dependent source sets" @@ -163,16 +161,6 @@ class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfi "" ) - override val sinceKotlin: String? by parser.stringOption( - listOf("-sinceKotlin"), - "Kotlin Api version to use as base version, if none specified", - null - ) - - override val collectInheritedExtensionsFromLibraries: Boolean by parser.singleFlag( - listOf("-collectInheritedExtensionsFromLibraries"), - "Search for applicable extensions in libraries" - ) override val analysisPlatform: Platform by parser.singleOption( listOf("-analysisPlatform"), @@ -181,10 +169,6 @@ class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfi { Platform.DEFAULT } ) - override val targets: List<String> by parser.repeatableOption<String>( - listOf("-target"), - "Generation targets" - ) override val perPackageOptions: MutableList<DokkaConfiguration.PackageOptions> by parser.singleOption( listOf("-packageOptions"), @@ -215,16 +199,16 @@ class Arguments(val parser: DokkaArgumentsParser) : DokkaConfiguration.PassConfi object MainKt { fun defaultLinks(config: DokkaConfiguration.PassConfiguration): MutableList<ExternalDocumentationLink> = mutableListOf<ExternalDocumentationLink>().apply { - if (!config.noJdkLink) - this += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/") - .build() - - if (!config.noStdlibLink) - this += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") - .build() - } + if (!config.noJdkLink) + this += DokkaConfiguration.ExternalDocumentationLink + .Builder("https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/") + .build() + + if (!config.noStdlibLink) + this += DokkaConfiguration.ExternalDocumentationLink + .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") + .build() + } fun parseLinks(links: String): List<ExternalDocumentationLink> { val (parsedLinks, parsedOfflineLinks) = links.split("^^") 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 index c69c6f67..44a0635f 100644 --- 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 @@ -1,9 +1,5 @@ package org.jetbrains.dokka.gradle -import com.android.build.gradle.* -import com.android.build.gradle.api.BaseVariant -import com.android.builder.core.BuilderConstants -import org.gradle.api.NamedDomainObjectCollection import org.gradle.api.Project import org.gradle.api.Task import org.gradle.api.UnknownDomainObjectException @@ -13,57 +9,60 @@ 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.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.* +import org.jetbrains.kotlin.gradle.plugin.KotlinCompilation +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import java.io.File import java.io.Serializable class ConfigurationExtractor(private val project: Project) { - fun extractConfiguration(targetName: String, variantName: String?) = - extractFromKotlinProject(targetName, variantName) - - fun extractFromKotlinProject(sourceSetName: String, variantName: String?): PlatformData? { + fun extractConfiguration(sourceSetName: String): PlatformData? { val projectExtension = project.extensions.getByType(KotlinProjectExtension::class.java) val sourceSet = projectExtension.sourceSets.findByName(sourceSetName) ?: run { project.logger.error("No source set with name '$sourceSetName' found"); return null } val compilation = when (projectExtension) { - is KotlinMultiplatformExtension -> projectExtension.targets.flatMap { it.compilations } - .find { it.kotlinSourceSets.contains(sourceSet) } + 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 - } ?: run { project.logger.error("No compilation found for set with name '$sourceSetName'"); return null } + } + + val sourceRoots = sourceSet.sourceFiles + val classpath = compilation?.classpath + ?: sourceRoots + sourceSet.allParentSourceFiles() - val classpath = compilation.compileDependencyFiles.files.filter { it.exists() } - val dependencies = (compilation.allKotlinSourceSets - sourceSet).flatMap { it.kotlin.sourceDirectories } return PlatformData( sourceSetName, - classpath, - sourceSet.kotlin.sourceDirectories.filter { it.exists() }.toList(), - dependencies, - sourceSet.dependsOn.map { it.name }, - compilation.target.targetName + 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(), emptyList(), "") } + ?.let { PlatformData(null, emptyList(), it.toList(), emptyList(), "") } - fun extractFromKotlinTasks(passName: String, kotlinTasks: List<Task>): PlatformData? = + fun extractFromKotlinTasks(kotlinTasks: List<Task>): List<PlatformData> = try { - kotlinTasks.find { it.toString() == passName }?.let { extractFromKotlinTask(it) } + kotlinTasks.map { extractFromKotlinTask(it) } } catch (e: Throwable) { when (e) { is UnknownDomainObjectException, is NoClassDefFoundError, is ClassNotFoundException -> - extractFromKotlinTasksTheHardWay(passName, kotlinTasks) + listOfNotNull(extractFromKotlinTasksTheHardWay(kotlinTasks)) else -> throw e } } @@ -80,18 +79,17 @@ class ConfigurationExtractor(private val project: Project) { .flatMap { it.compilations }.firstOrNull { it.compileKotlinTask == task } else -> throw e } - }.let { + }.let { compilation -> PlatformData( task.name, - getClasspath(it), - getSourceSet(it), - getDependentSourceSetRoots(it), - getDependentSourceSet(it).map { it.name }, - it?.platformType?.toString() ?: "" + compilation?.classpath.orEmpty(), + compilation?.sourceFiles.orEmpty(), + compilation?.dependentSourceSets?.map { it.name }.orEmpty(), + compilation?.platformType?.toString() ?: "" ) } - private fun extractFromKotlinTasksTheHardWay(passName: String, kotlinTasks: List<Task>): PlatformData? { + private fun extractFromKotlinTasksTheHardWay(kotlinTasks: List<Task>): PlatformData? { val allClasspath = mutableSetOf<File>() var allClasspathFileCollection: FileCollection = project.files() val allSourceRoots = mutableSetOf<File>() @@ -128,93 +126,41 @@ class ConfigurationExtractor(private val project: Project) { } classpath.addAll(project.files(allClasspath).toList()) - return PlatformData(null, classpath, allSourceRoots.toList(), emptyList(), emptyList(),"") + return PlatformData(null, classpath, allSourceRoots.toList(), emptyList(), "") } - private fun getSourceSet(target: KotlinTarget, variantName: String? = null): List<File> = - if (variantName != null) - getSourceSet(getCompilation(target, variantName)) - else - getSourceSet(getMainCompilation(target)) - - private fun getClasspath(target: KotlinTarget, variantName: String? = null): List<File> = - if (target.isAndroidTarget()) { - if (variantName != null) - getClasspathFromAndroidTask(getCompilation(target, variantName)) - else - getClasspathFromAndroidTask(getMainCompilation(target)) - } else { - getClasspath(getMainCompilation(target)) - } - - private fun getSourceSet(compilation: KotlinCompilation<*>?): List<File> = compilation - ?.kotlinSourceSets - ?.flatMap { it.kotlin.sourceDirectories } - ?.filter { it.exists() } - .orEmpty() + private val KotlinCompilation<*>.sourceFiles: List<File> + get() = kotlinSourceSets.flatMap { it.sourceFiles } - private fun getDependentSourceSet(compilation: KotlinCompilation<*>?) = compilation - ?.let { it.allKotlinSourceSets - it.kotlinSourceSets }.orEmpty() + private val KotlinSourceSet.sourceFiles: List<File> + get() = kotlin.sourceDirectories.filter { it.exists() }.toList() - private fun getDependentSourceSetRoots(compilation: KotlinCompilation<*>?): List<File> = - getDependentSourceSet(compilation)?.flatMap { it.kotlin.sourceDirectories } - .filter { it.exists() } + private val KotlinCompilation<*>.dependentSourceSets: Set<KotlinSourceSet> + get() = (allKotlinSourceSets - kotlinSourceSets) - private fun getClasspath(compilation: KotlinCompilation<*>?): List<File> = compilation - ?.compileDependencyFiles - ?.files - ?.toList() - ?.filter { it.exists() } - .orEmpty() + 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() ?: getClasspath(compilation) - - private fun getMainCompilation(target: KotlinTarget) = - getCompilation(target, getMainCompilationName(target)) - - private fun getCompilation(target: KotlinTarget, name: String) = - target.compilations.getByName(name) - - private fun getMainCompilationName(target: KotlinTarget) = if (target.isAndroidTarget()) - getVariants(project).filter { it.buildType.name == BuilderConstants.RELEASE }.map { it.name }.first() - else - KotlinCompilation.MAIN_COMPILATION_NAME - - private fun getVariants(project: Project): Set<BaseVariant> { - val androidExtension = project.extensions.getByName("android") - val baseVariants = when (androidExtension) { - is AppExtension -> androidExtension.applicationVariants.toSet() - is LibraryExtension -> { - androidExtension.libraryVariants.toSet() + - if (androidExtension is FeatureExtension) { - androidExtension.featureVariants.toSet() - } else { - emptySet<BaseVariant>() - } - } - is TestExtension -> androidExtension.applicationVariants.toSet() - else -> emptySet() - } - val testVariants = if (androidExtension is TestedExtension) { - androidExtension.testVariants.toSet() + androidExtension.unitTestVariants.toSet() - } else { - emptySet<BaseVariant>() - } - - return baseVariants + testVariants - } + ?.classpath?.files?.toList() ?: getClasspathFromRegularTask(compilation) - private fun getPlatformName(platform: KotlinPlatformType): String = - if (platform == KotlinPlatformType.androidJvm) KotlinPlatformType.jvm.toString() else platform.toString() + private fun getClasspathFromRegularTask(compilation: KotlinCompilation<*>): List<File> = + compilation + .compileDependencyFiles + .files + .toList() + .filter { it.exists() } data class PlatformData( val name: String?, val classpath: List<File>, val sourceRoots: List<File>, - val dependentSourceRoots: 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 f4fa7aaa..8c4e0c4c 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 @@ -20,7 +20,7 @@ open class DokkaCollectorTask : DefaultTask() { @TaskAction fun collect() { - val passesConfigurations = getProjects(project).filter { it.name in modules }.map { + val passesConfigurations = getProjects(project).filter { it.name in modules }.mapNotNull { val task = try { it.tasks.getByName(DOKKA_TASK_NAME, DokkaTask::class) } catch (e: UnknownTaskException) { @@ -33,12 +33,11 @@ open class DokkaCollectorTask : DefaultTask() { outputDir = outputDirectory cacheRoot = passesConfigurations.first().cacheRoot format = passesConfigurations.first().format - generateIndexPages = passesConfigurations.first().generateIndexPages } configuration = passesConfigurations.fold(initial) { acc, it: GradleDokkaConfigurationImpl -> - if(acc.format != it.format || acc.generateIndexPages != it.generateIndexPages || acc.cacheRoot != it.cacheRoot) - throw IllegalStateException("Dokka task configurations differ on core arguments (format, generateIndexPages, cacheRoot)") + if(acc.format != it.format || acc.cacheRoot != it.cacheRoot) + throw IllegalStateException("Dokka task configurations differ on core arguments (format, cacheRoot)") acc.passesConfigurations = acc.passesConfigurations + it.passesConfigurations acc.pluginsClasspath = (acc.pluginsClasspath + it.pluginsClasspath).distinct() acc 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 a1bfdb96..2ef85de2 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 @@ -35,9 +35,9 @@ open class DokkaMultimoduleTask : DefaultTask(), Configurable { System.setProperty(DokkaTask.COLORS_ENABLED_PROPERTY, "false") try { - loadFatJar() + loadCore() val bootstrapClass = - ClassloaderContainer.fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl") + ClassloaderContainer.coreClassLoader!!.loadClass("org.jetbrains.dokka.DokkaMultimoduleBootstrapImpl") val bootstrapInstance = bootstrapClass.constructors.first().newInstance() val bootstrapProxy: DokkaBootstrap = automagicTypedProxy( javaClass.classLoader, @@ -85,10 +85,10 @@ open class DokkaMultimoduleTask : DefaultTask(), Configurable { } } - private fun loadFatJar() { - if (ClassloaderContainer.fatJarClassLoader == null) { + private fun loadCore() { + if (ClassloaderContainer.coreClassLoader == null) { val jars = dokkaRuntime!!.resolve() - ClassloaderContainer.fatJarClassLoader = URLClassLoader( + ClassloaderContainer.coreClassLoader = URLClassLoader( jars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent ) 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 2de95493..5a420d4d 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 @@ -15,15 +15,16 @@ import org.jetbrains.dokka.ReflectDsl import org.jetbrains.dokka.ReflectDsl.isNotInstance import org.jetbrains.dokka.gradle.ConfigurationExtractor.PlatformData import org.jetbrains.dokka.plugability.Configurable -import org.jetbrains.kotlin.gradle.plugin.KotlinPlatformType import java.io.File import java.net.URLClassLoader import java.util.concurrent.Callable import java.util.function.BiConsumer +import kotlin.system.exitProcess open class DokkaTask : DefaultTask(), Configurable { private val ANDROID_REFERENCE_URL = Builder("https://developer.android.com/reference/").build() private val GLOBAL_CONFIGURATION_NAME = "global" // Used for copying perPackageOptions to other platforms + private val configExtractor = ConfigurationExtractor(project) @Suppress("MemberVisibilityCanBePrivate") fun defaultKotlinTasks(): List<Task> = with(ReflectDsl) { @@ -57,8 +58,6 @@ open class DokkaTask : DefaultTask(), Configurable { var subProjects: List<String> = emptyList() @Input - var impliedPlatforms: MutableList<String> = arrayListOf() - override val pluginsConfiguration: Map<String, String> = mutableMapOf() @Optional @@ -66,54 +65,47 @@ open class DokkaTask : DefaultTask(), Configurable { var cacheRoot: String? = null @Classpath - lateinit var pluginsConfig: Configuration + lateinit var pluginsClasspathConfiguration: Configuration var dokkaSourceSets: NamedDomainObjectContainer<GradlePassConfigurationImpl> @Suppress("UNCHECKED_CAST") @Nested get() = (DslObject(this).extensions.getByName(SOURCE_SETS_EXTENSION_NAME) as NamedDomainObjectContainer<GradlePassConfigurationImpl>) internal set(value) = DslObject(this).extensions.add(SOURCE_SETS_EXTENSION_NAME, value) - var configuration: GradlePassConfigurationImpl - @Suppress("UNCHECKED_CAST") - @Nested get() = DslObject(this).extensions.getByType(GradlePassConfigurationImpl::class.java) - internal set(value) = DslObject(this).extensions.add(CONFIGURATION_EXTENSION_NAME, value) - - var config: GradleDokkaConfigurationImpl? = null - - // Configure Dokka with closure in Gradle Kotlin DSL - fun configuration(action: Action<in GradlePassConfigurationImpl>) = action.execute(configuration) - private val kotlinTasks: List<Task> by lazy { - extractKotlinCompileTasks( - configuration.collectKotlinTasks ?: { defaultKotlinTasks() }) + extractKotlinCompileTasks({ + dokkaSourceSets.map { + it.collectKotlinTasks?.invoke() + } + }.takeIf { it().isNotEmpty() } ?: { defaultKotlinTasks() } + ) } - private val configExtractor = ConfigurationExtractor(project) - @Input var disableAutoconfiguration: 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 - private fun loadFatJar() { - if (ClassloaderContainer.fatJarClassLoader == null) { + private fun loadCore() { + if (ClassloaderContainer.coreClassLoader == null) { val jars = dokkaRuntime!!.resolve() - ClassloaderContainer.fatJarClassLoader = URLClassLoader( + ClassloaderContainer.coreClassLoader = URLClassLoader( jars.map { it.toURI().toURL() }.toTypedArray(), ClassLoader.getSystemClassLoader().parent ) } } - private fun extractKotlinCompileTasks(collectTasks: () -> List<Any?>?): List<Task> { + protected fun extractKotlinCompileTasks(collectTasks: () -> List<Any?>?): List<Task> { val inputList = (collectTasks.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") + project.tasks.findByPath(it as String) ?: throw IllegalArgumentException("Task with path '$it' not found") } other @@ -134,7 +126,7 @@ open class DokkaTask : DefaultTask(), Configurable { private fun Iterable<String>.toProjects(): List<Project> = project.subprojects.toList().filter { this.contains(it.name) } - private fun collectSuppressedFiles(sourceRoots: List<SourceRoot>) = + protected open fun collectSuppressedFiles(sourceRoots: List<SourceRoot>) = if (project.isAndroidProject()) { val generatedRoot = project.buildDir.resolve("generated").absoluteFile sourceRoots @@ -147,25 +139,21 @@ open class DokkaTask : DefaultTask(), Configurable { } @TaskAction - fun generate() { - generateForConfig(config ?: getConfiguration()) - } + fun generate() = getConfiguration()?.let { generate(it) } ?: exitProcess(0) - internal fun generateForConfig(configuration: GradleDokkaConfigurationImpl) { + protected open fun generate(configuration: GradleDokkaConfigurationImpl) { outputDiagnosticInfo = true val kotlinColorsEnabledBefore = System.getProperty(COLORS_ENABLED_PROPERTY) ?: "false" System.setProperty(COLORS_ENABLED_PROPERTY, "false") try { - loadFatJar() + loadCore() val bootstrapClass = - ClassloaderContainer.fatJarClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl") + ClassloaderContainer.coreClassLoader!!.loadClass("org.jetbrains.dokka.DokkaBootstrapImpl") val bootstrapInstance = bootstrapClass.constructors.first().newInstance() val bootstrapProxy: DokkaBootstrap = automagicTypedProxy(javaClass.classLoader, bootstrapInstance) - val gson = GsonBuilder().setPrettyPrinting().create() - bootstrapProxy.configure( BiConsumer { level, message -> when (level) { @@ -176,7 +164,7 @@ open class DokkaTask : DefaultTask(), Configurable { "error" -> logger.error(message) } }, - gson.toJson(configuration) + GsonBuilder().setPrettyPrinting().create().toJson(configuration) ) bootstrapProxy.generate() @@ -186,68 +174,78 @@ open class DokkaTask : DefaultTask(), Configurable { } } - internal fun getConfiguration(): GradleDokkaConfigurationImpl { + internal open fun getConfiguration(): GradleDokkaConfigurationImpl? { val globalConfig = dokkaSourceSets.toList().find { it.name.toLowerCase() == GLOBAL_CONFIGURATION_NAME } - val defaultModulesConfiguration = collectConfigurations() - .map { defaultPassConfiguration(it, globalConfig) } + val defaultModulesConfiguration = passConfigurations + .map { defaultPassConfiguration(it, globalConfig) }.takeIf { it.isNotEmpty() } + ?: listOf( + defaultPassConfiguration( + collectSinglePassConfiguration(GradlePassConfigurationImpl("main")), + null + ) + ).takeIf { project.isNotMultiplatformProject() } ?: emptyList() + + if (defaultModulesConfiguration.isEmpty()) { + logger.error("No source sets to document found, exiting") + return null + } + return GradleDokkaConfigurationImpl().apply { outputDir = project.file(outputDirectory).absolutePath format = outputFormat - generateIndexPages = true - cacheRoot = cacheRoot - impliedPlatforms = impliedPlatforms + cacheRoot = this@DokkaTask.cacheRoot + offlineMode = this@DokkaTask.offlineMode passesConfigurations = defaultModulesConfiguration - pluginsClasspath = pluginsConfig.resolve().toList() + pluginsClasspath = pluginsClasspathConfiguration.resolve().toList() pluginsConfiguration = this@DokkaTask.pluginsConfiguration } } - private fun collectConfigurations() = - if (this.dokkaSourceSets.isNotEmpty()) collectMultipassConfiguration() else listOf(collectSinglePassConfiguration(configuration)) - private fun collectMultipassConfiguration() = dokkaSourceSets - .filterNot { it.name.toLowerCase() == GLOBAL_CONFIGURATION_NAME } - .map { collectSinglePassConfiguration(it) } + protected val passConfigurations: List<GradlePassConfigurationImpl> + get() = dokkaSourceSets + .filterNot { it.name.toLowerCase() == GLOBAL_CONFIGURATION_NAME } + .map { collectSinglePassConfiguration(it) } - private fun collectSinglePassConfiguration(config: GradlePassConfigurationImpl): GradlePassConfigurationImpl { + protected fun collectSinglePassConfiguration(config: GradlePassConfigurationImpl): GradlePassConfigurationImpl { val userConfig = config - /*.let { - if (it.collectKotlinTasks != null) { - configExtractor.extractFromKotlinTasks(extractKotlinCompileTasks(it.collectKotlinTasks!!)) - ?.let { platformData -> mergeUserConfigurationAndPlatformData(it, platformData) } ?: it - } else { - it + .apply { + collectKotlinTasks?.let { + configExtractor.extractFromKotlinTasks(extractKotlinCompileTasks(it)) + .fold(this) { config, platformData -> + mergeUserConfigurationAndPlatformData(config, platformData) + } + } } - }*/ if (disableAutoconfiguration) return userConfig - val baseConfig = configExtractor.extractConfiguration(userConfig.name, userConfig.androidVariant) + val baseConfig = configExtractor.extractConfiguration(userConfig.name) ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } ?: if (this.dokkaSourceSets.isNotEmpty()) { if (outputDiagnosticInfo) logger.warn( - "Could not find target with name: ${userConfig.name} in Kotlin Gradle Plugin, " + - "using only user provided configuration for this target" + "Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin, " + + "using only user provided configuration for this source set" ) userConfig } else { - logger.warn("Could not find target with name: ${userConfig.name} in Kotlin Gradle Plugin") - collectFromSinglePlatformOldPlugin(userConfig.name) + if (outputDiagnosticInfo) + logger.warn("Could not find source set with name: ${userConfig.name} in Kotlin Gradle Plugin") + collectFromSinglePlatformOldPlugin(userConfig.name, userConfig) } return if (subProjects.isNotEmpty()) { try { - subProjects.toProjects().fold(baseConfig) { config, subProject -> + subProjects.toProjects().fold(baseConfig) { configAcc, subProject -> mergeUserConfigurationAndPlatformData( - config, - ConfigurationExtractor(subProject).extractConfiguration(config.name, config.androidVariant)!! + configAcc, + ConfigurationExtractor(subProject).extractConfiguration(userConfig.name)!! ) } } catch (e: NullPointerException) { logger.warn( - "Cannot extract sources from subProjects. Do you have the Kotlin plugin in version 1.3.30+ " + - "and the Kotlin plugin applied in the root project?" + "Cannot extract sources from subProjects. Do you have the Kotlin plugin applied in the root project?" ) baseConfig } @@ -256,49 +254,52 @@ open class DokkaTask : DefaultTask(), Configurable { } } - private fun collectFromSinglePlatformOldPlugin(name: String) = - configExtractor.extractFromKotlinTasks(name, kotlinTasks) - ?.let { mergeUserConfigurationAndPlatformData(configuration, it) } + protected fun collectFromSinglePlatformOldPlugin(name: String, userConfig: GradlePassConfigurationImpl) = + kotlinTasks.find { it.name == name } + ?.let { configExtractor.extractFromKotlinTasks(listOf(it)) } + ?.singleOrNull() + ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } ?: configExtractor.extractFromJavaPlugin() - ?.let { mergeUserConfigurationAndPlatformData(configuration, it) } - ?: configuration + ?.let { mergeUserConfigurationAndPlatformData(userConfig, it) } + ?: userConfig - private fun mergeUserConfigurationAndPlatformData( + protected fun mergeUserConfigurationAndPlatformData( userConfig: GradlePassConfigurationImpl, autoConfig: PlatformData ) = userConfig.copy().apply { - sourceSetName = autoConfig.name ?: "" + sourceSetID = autoConfig.name ?: "" sourceRoots.addAll(userConfig.sourceRoots.union(autoConfig.sourceRoots.toSourceRoots()).distinct()) - dependentSourceRoots.addAll(userConfig.dependentSourceRoots.union(autoConfig.dependentSourceRoots.toSourceRoots()).distinct()) dependentSourceSets.addAll(userConfig.dependentSourceSets.union(autoConfig.dependentSourceSets).distinct()) classpath = userConfig.classpath.union(autoConfig.classpath.map { it.absolutePath }).distinct() if (userConfig.platform == null && autoConfig.platform != "") platform = autoConfig.platform } - private fun defaultPassConfiguration( + protected fun defaultPassConfiguration( config: GradlePassConfigurationImpl, globalConfig: GradlePassConfigurationImpl? ): GradlePassConfigurationImpl { - if (config.moduleName == "") { + if (config.moduleName.isBlank()) { config.moduleName = project.name } - if (config.sourceSetName.isEmpty()) { - config.sourceSetName = config.name.substringBeforeLast("Main").takeIf(String::isNotBlank) ?: config.platform.toString() + if (config.sourceSetID.isBlank()) { + config.sourceSetID = config.name.takeIf(String::isNotBlank) ?: config.analysisPlatform.key } - config.classpath = (config.classpath as List<Any>).map { it.toString() }.distinct() // Workaround for Groovy's GStringImpl + config.displayName = config.sourceSetID.substringBeforeLast("Main") + config.classpath = + (config.classpath as List<Any>).map { it.toString() }.distinct() // Workaround for Groovy's GStringImpl 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) - if (project.isAndroidProject() && !config.noAndroidSdkLink) { // TODO: introduce Android as a separate Dokka platform? + if (project.isAndroidProject() && !config.noAndroidSdkLink) { config.externalDocumentationLinks.add(ANDROID_REFERENCE_URL) |
