diff options
62 files changed, 1272 insertions, 1187 deletions
diff --git a/core/src/main/kotlin/DokkaBootstrapImpl.kt b/core/src/main/kotlin/DokkaBootstrapImpl.kt index a2efab41..584d8b8c 100644 --- a/core/src/main/kotlin/DokkaBootstrapImpl.kt +++ b/core/src/main/kotlin/DokkaBootstrapImpl.kt @@ -70,38 +70,8 @@ class DokkaBootstrapImpl : DokkaBootstrap { private lateinit var generator: DokkaGenerator - fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) = with(configuration) { - - fun defaultLinks(config: DokkaSourceSetImpl): List<ExternalDocumentationLinkImpl> { - val links = mutableListOf<ExternalDocumentationLinkImpl>() - if (!config.noJdkLink) { - val javadocLink = - if (config.jdkVersion < 11) "https://docs.oracle.com/javase/${config.jdkVersion}/docs/api/" - else "https://docs.oracle.com/en/java/javase/${config.jdkVersion}/docs/api/java.base/" - val packageListLink = - if (config.jdkVersion < 11) "${javadocLink}/package-list" - else "https://docs.oracle.com/en/java/javase/${config.jdkVersion}/docs/api/element-list" - links += DokkaConfiguration.ExternalDocumentationLink - .Builder(javadocLink, packageListLink) - .build() as ExternalDocumentationLinkImpl - } - - if (!config.noStdlibLink) - links += DokkaConfiguration.ExternalDocumentationLink - .Builder("https://kotlinlang.org/api/latest/jvm/stdlib/") - .build() as ExternalDocumentationLinkImpl - return links - } - - val configurationWithLinks = configuration.copy( - sourceSets = sourceSets.map { - val links: List<ExternalDocumentationLinkImpl> = - it.externalDocumentationLinks + defaultLinks(it) - it.copy(externalDocumentationLinks = links) - } - ) - - generator = DokkaGenerator(configurationWithLinks, logger) + fun configure(logger: DokkaLogger, configuration: DokkaConfigurationImpl) { + generator = DokkaGenerator(configuration, logger) } override fun configure(serializedConfigurationJSON: String, logger: BiConsumer<String, String>) = configure( diff --git a/core/src/main/kotlin/configuration.kt b/core/src/main/kotlin/configuration.kt index 91ee4d64..67c55861 100644 --- a/core/src/main/kotlin/configuration.kt +++ b/core/src/main/kotlin/configuration.kt @@ -2,16 +2,16 @@ package org.jetbrains.dokka -import org.jetbrains.dokka.utilities.toJsonString import org.jetbrains.dokka.utilities.parseJson +import org.jetbrains.dokka.utilities.toJsonString import java.io.File import java.io.Serializable import java.net.URL object DokkaDefaults { - const val outputDir = "./dokka" + val outputDir = File("./dokka") const val format: String = "html" - val cacheRoot: String? = null + val cacheRoot: File? = null const val offlineMode: Boolean = false const val failOnWarning: Boolean = false @@ -51,6 +51,14 @@ enum class Platform(val key: String) { } } +interface DokkaConfigurationBuilder<T : Any> { + fun build(): T +} + +fun <T : Any> Iterable<DokkaConfigurationBuilder<T>>.build(): List<T> { + return this.map { it.build() } +} + data class DokkaSourceSetID( val moduleName: String, val sourceSetName: String @@ -65,8 +73,8 @@ fun DokkaConfigurationImpl(json: String): DokkaConfigurationImpl = parseJson(jso fun DokkaConfiguration.toJsonString(): String = toJsonString(this) interface DokkaConfiguration : Serializable { - val outputDir: String - val cacheRoot: String? + val outputDir: File + val cacheRoot: File? val offlineMode: Boolean val failOnWarning: Boolean val sourceSets: List<DokkaSourceSet> @@ -78,11 +86,11 @@ interface DokkaConfiguration : Serializable { val sourceSetID: DokkaSourceSetID val displayName: String val moduleDisplayName: String - val classpath: List<String> + val classpath: List<File> val sourceRoots: List<SourceRoot> val dependentSourceSets: Set<DokkaSourceSetID> - val samples: List<String> - val includes: List<String> + val samples: List<File> + val includes: List<File> val includeNonPublic: Boolean val includeRootPackage: Boolean val reportUndocumented: Boolean @@ -96,12 +104,12 @@ interface DokkaConfiguration : Serializable { val apiVersion: String? val noStdlibLink: Boolean val noJdkLink: Boolean - val suppressedFiles: List<String> + val suppressedFiles: List<File> val analysisPlatform: Platform } interface SourceRoot : Serializable { - val path: String + val directory: File } interface SourceLinkDefinition : Serializable { @@ -112,8 +120,8 @@ interface DokkaConfiguration : Serializable { interface DokkaModuleDescription : Serializable { val name: String - val path: String - val docFile: String + val path: File + val docFile: File } interface PackageOptions : Serializable { @@ -135,7 +143,7 @@ interface DokkaConfiguration : Serializable { constructor(root: String, packageList: String? = null) : this(URL(root), packageList?.let { URL(it) }) - fun build(): ExternalDocumentationLink = + fun build(): ExternalDocumentationLinkImpl = if (packageListUrl != null && url != null) ExternalDocumentationLinkImpl(url!!, packageListUrl!!) else if (url != null) @@ -146,4 +154,16 @@ interface DokkaConfiguration : Serializable { } } +fun ExternalDocumentationLink( + url: URL? = null, + packageListUrl: URL? = null +): ExternalDocumentationLinkImpl = + DokkaConfiguration.ExternalDocumentationLink.Builder(url = url, packageListUrl = packageListUrl).build() + + +fun ExternalDocumentationLink( + url: String, packageListUrl: String? = null +): ExternalDocumentationLinkImpl = + DokkaConfiguration.ExternalDocumentationLink.Builder(root = url, packageList = packageListUrl).build() + diff --git a/core/src/main/kotlin/defaultConfiguration.kt b/core/src/main/kotlin/defaultConfiguration.kt index 8e38e8d5..384ce392 100644 --- a/core/src/main/kotlin/defaultConfiguration.kt +++ b/core/src/main/kotlin/defaultConfiguration.kt @@ -3,10 +3,12 @@ package org.jetbrains.dokka import org.jetbrains.dokka.DokkaConfiguration.DokkaSourceSet import java.io.File import java.net.URL +import kotlin.reflect.full.memberProperties +import kotlin.reflect.full.primaryConstructor data class DokkaConfigurationImpl( - override val outputDir: String = DokkaDefaults.outputDir, - override val cacheRoot: String? = DokkaDefaults.cacheRoot, + override val outputDir: File = DokkaDefaults.outputDir, + override val cacheRoot: File? = DokkaDefaults.cacheRoot, override val offlineMode: Boolean = DokkaDefaults.offlineMode, override val sourceSets: List<DokkaSourceSetImpl> = emptyList(), override val pluginsClasspath: List<File> = emptyList(), @@ -15,15 +17,16 @@ data class DokkaConfigurationImpl( override val failOnWarning: Boolean = DokkaDefaults.failOnWarning ) : DokkaConfiguration + data class DokkaSourceSetImpl( override val moduleDisplayName: String, override val displayName: String = DokkaDefaults.sourceSetDisplayName, override val sourceSetID: DokkaSourceSetID, - override val classpath: List<String> = emptyList(), + override val classpath: List<File> = emptyList(), override val sourceRoots: List<SourceRootImpl>, override val dependentSourceSets: Set<DokkaSourceSetID> = emptySet(), - override val samples: List<String> = emptyList(), - override val includes: List<String> = emptyList(), + override val samples: List<File> = emptyList(), + override val includes: List<File> = emptyList(), override val includeNonPublic: Boolean = DokkaDefaults.includeNonPublic, override val includeRootPackage: Boolean = DokkaDefaults.includeRootPackage, override val reportUndocumented: Boolean = DokkaDefaults.reportUndocumented, @@ -37,19 +40,21 @@ data class DokkaSourceSetImpl( override val apiVersion: String? = null, override val noStdlibLink: Boolean = DokkaDefaults.noStdlibLink, override val noJdkLink: Boolean = DokkaDefaults.noJdkLink, - override val suppressedFiles: List<String> = emptyList(), + override val suppressedFiles: List<File> = emptyList(), override val analysisPlatform: Platform = DokkaDefaults.analysisPlatform ) : DokkaSourceSet data class DokkaModuleDescriptionImpl( override val name: String, - override val path: String, - override val docFile: String + override val path: File, + override val docFile: File ) : DokkaConfiguration.DokkaModuleDescription data class SourceRootImpl( - override val path: String -) : DokkaConfiguration.SourceRoot + override val directory: File +) : DokkaConfiguration.SourceRoot { + constructor(directoryPath: String) : this(File(directoryPath)) +} data class SourceLinkDefinitionImpl( override val path: String, diff --git a/core/src/main/kotlin/utilities/json.kt b/core/src/main/kotlin/utilities/json.kt index 9e4677aa..feac2b23 100644 --- a/core/src/main/kotlin/utilities/json.kt +++ b/core/src/main/kotlin/utilities/json.kt @@ -56,10 +56,10 @@ private object FileSerializer : StdScalarSerializer<File>(File::class.java) { private object SourceRootSerializer : StdScalarSerializer<SourceRoot>(SourceRoot::class.java) { override fun serialize(value: SourceRoot, g: JsonGenerator, provider: SerializerProvider) { - g.writeString(value.path) + g.writeString(value.directory.path) } } private object SourceRootImplDeserializer : StdScalarDeserializer<SourceRootImpl>(SourceRootImpl::class.java) { - override fun deserialize(p: JsonParser, ctxt: DeserializationContext): SourceRootImpl = SourceRootImpl(p.text) + override fun deserialize(p: JsonParser, ctxt: DeserializationContext): SourceRootImpl = SourceRootImpl(File(p.text)) } diff --git a/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt b/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt index e954c82c..35755f50 100644 --- a/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt +++ b/core/src/test/kotlin/utilities/DokkaConfigurationJsonTest.kt @@ -9,12 +9,12 @@ class DokkaConfigurationJsonTest { @Test fun `simple configuration toJsonString then parseJson`() { val configuration = DokkaConfigurationImpl( - outputDir = "customOutputDir", + outputDir = File("customOutputDir"), pluginsClasspath = listOf(File("plugins/customPlugin.jar")), sourceSets = listOf( DokkaSourceSetImpl( moduleDisplayName = "customModuleDisplayName", - sourceRoots = listOf(SourceRootImpl("customSourceRoot")), + sourceRoots = listOf(SourceRootImpl(File("customSourceRoot"))), sourceSetID = DokkaSourceSetID("customModuleName", "customSourceSetName") ) ) @@ -48,14 +48,14 @@ class DokkaConfigurationJsonTest { val parsedConfiguration = DokkaConfigurationImpl(json) assertEquals( DokkaConfigurationImpl( - outputDir = "customOutputDir", + outputDir = File("customOutputDir"), pluginsClasspath = listOf(File("plugins/customPlugin.jar")), sourceSets = listOf( DokkaSourceSetImpl( moduleDisplayName = "customModuleDisplayName", - sourceRoots = listOf(SourceRootImpl("customSourceRoot")), + sourceRoots = listOf(SourceRootImpl(File("customSourceRoot"))), sourceSetID = DokkaSourceSetID("customModuleName", "customSourceSetName"), - classpath = listOf("classpath/custom1.jar", "classpath/custom2.jar") + classpath = listOf(File("classpath/custom1.jar"), File("classpath/custom2.jar")) ) ) ), diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bb8b2fc2..29271bed 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Tue Jul 21 13:56:13 CEST 2020 +distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-all.zip distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5.1-bin.zip -zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/integration-tests/build.gradle.kts b/integration-tests/build.gradle.kts index 86472aa9..aaa8b153 100644 --- a/integration-tests/build.gradle.kts +++ b/integration-tests/build.gradle.kts @@ -38,7 +38,6 @@ subprojects { ?: System.getenv("DOKKA_INTEGRATION_TEST_IS_EXHAUSTIVE")?.toBoolean() ?: false.toString() ) - } tasks.check { diff --git a/integration-tests/gradle/projects/it-basic-groovy/build.gradle b/integration-tests/gradle/projects/it-basic-groovy/build.gradle index 13a34619..405b55ed 100644 --- a/integration-tests/gradle/projects/it-basic-groovy/build.gradle +++ b/integration-tests/gradle/projects/it-basic-groovy/build.gradle @@ -10,28 +10,29 @@ dependencies { } dokkaHtml { - outputDirectory = "$buildDir/dokka/customHtml" + outputDirectory = new File(buildDir, "/dokka/customHtml") pluginsConfiguration.put("pluginA", "configA") failOnWarning = false dokkaSourceSets { customSourceSet { - sourceRoot { path = "$projectDir/src/main/java" } - sourceRoot { path = "$projectDir/src/main/kotlin" } + sourceRoot { directory = file("src/main/java") } + sourceRoot { directory = file("src/main/kotlin") } displayName = "custom" reportUndocumented = true } } + } dokkaJavadoc { - outputDirectory = "$buildDir/dokka/customJavadoc" + outputDirectory = new File(buildDir, "dokka/customJavadoc") } dokkaGfm { - outputDirectory = "$buildDir/dokka/customGfm" + outputDirectory = new File(buildDir, "dokka/customGfm") } dokkaJekyll { - outputDirectory = "$buildDir/dokka/customJekyll" + outputDirectory = new File(buildDir, "dokka/customJekyll") } diff --git a/integration-tests/gradle/projects/it-basic/build.gradle.kts b/integration-tests/gradle/projects/it-basic/build.gradle.kts index b3ddde18..45454e29 100644 --- a/integration-tests/gradle/projects/it-basic/build.gradle.kts +++ b/integration-tests/gradle/projects/it-basic/build.gradle.kts @@ -14,11 +14,11 @@ dependencies { tasks.withType<DokkaTask> { dokkaSourceSets { configureEach { - moduleDisplayName = "Basic Project" - suppressedFiles = listOf("src/main/kotlin/it/suppressedByPath") + moduleDisplayName.set("Basic Project") + suppressedFiles.from(file("src/main/kotlin/it/suppressedByPath")) perPackageOption { - prefix = "it.suppressedByPackage" - suppress = true + prefix.set("it.suppressedByPackage") + suppress.set(true) } } } diff --git a/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts b/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts index 52aa9d36..26200399 100644 --- a/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts +++ b/integration-tests/gradle/projects/it-multiplatform-0/build.gradle.kts @@ -1,5 +1,3 @@ -import org.jetbrains.dokka.gradle.DokkaTask - plugins { kotlin("multiplatform") id("org.jetbrains.dokka") @@ -20,13 +18,3 @@ kotlin { } } } - -tasks.withType<DokkaTask> { - dokkaSourceSets { - create("commonMain") - create("jvmMain") - create("linuxMain") - create("macosMain") - create("jsMain") - } -} diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt index 8e7fa96e..6de30de6 100644 --- a/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt @@ -2,4 +2,10 @@ package it.mpp0 actual class ExpectedClass { actual val platform: String = "jvm" + + /** + * This function can only be used by JVM consumers + */ + fun jvmOnlyFunction() = Unit + } diff --git a/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/JvmOnlyClass.kt b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/JvmOnlyClass.kt new file mode 100644 index 00000000..21101a89 --- /dev/null +++ b/integration-tests/gradle/projects/it-multiplatform-0/src/jvmMain/kotlin/it/mpp0/JvmOnlyClass.kt @@ -0,0 +1,13 @@ +@file:Suppress("unused") + +package it.mpp0 + +/** + * This class can only be used by JVM consumers + */ +class JvmOnlyClass { + /** + * This function can only be used by JVM consumers + */ + fun myJvm() = println("HI") +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt index d6dfe451..a657e9c7 100644 --- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Android0GradleIntegrationTest.kt @@ -62,12 +62,6 @@ class Android0GradleIntegrationTest(override val versions: BuildVersions) : Abst "Expected html files in html output directory" ) - htmlOutputDir.allHtmlFiles().forEach { file -> - assertContainsNoErrorClass(file) - assertNoUnresolvedLinks(file) - assertNoHrefToMissingLocalFileOrDirectory(file) - } - assertTrue( htmlOutputDir.allHtmlFiles().any { file -> "https://developer.android.com/reference/android/content/Context.html" in file.readText() @@ -80,5 +74,11 @@ class Android0GradleIntegrationTest(override val versions: BuildVersions) : Abst file.readText() }, "Expected link to developer.android.com/.../androidx/" ) + + htmlOutputDir.allHtmlFiles().forEach { file -> + assertContainsNoErrorClass(file) + assertNoUnresolvedLinks(file) + assertNoHrefToMissingLocalFileOrDirectory(file) + } } } diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt index fc7a6a81..70b5832d 100644 --- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/Multimodule0IntegrationTest.kt @@ -36,6 +36,13 @@ class Multimodule0IntegrationTest(override val versions: BuildVersions) : Abstra assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaHtmlMultimodule")).outcome) assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaGfmMultimodule")).outcome) assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:dokkaJekyllMultimodule")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaHtml")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaHtml")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaGfm")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaGfm")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleB:dokkaJekyll")).outcome) + assertEquals(TaskOutcome.SUCCESS, assertNotNull(result.task(":moduleA:moduleC:dokkaJekyll")).outcome) + val outputDir = File(projectDir, "moduleA/build/dokka/htmlMultimodule") assertTrue(outputDir.isDirectory, "Missing dokka output directory") diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt index c4914652..d21106fa 100644 --- a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt @@ -3,10 +3,13 @@ package org.jetbrains.dokka.it import org.jsoup.Jsoup import org.junit.Rule import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 import java.io.File import kotlin.test.assertFalse import kotlin.test.assertTrue +@RunWith(JUnit4::class) abstract class AbstractIntegrationTest { @get:Rule diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt index 685bfe82..14067f43 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/AnalysisEnvironment.kt @@ -540,12 +540,11 @@ class AnalysisEnvironment(val messageCollector: MessageCollector, val analysisPl * Adds list of paths to source roots. * $list: collection of files to add */ - fun addSources(list: List<String>) { - list.forEach { - configuration.addKotlinSourceRoot(it) - val file = File(it) - if (file.isDirectory || file.extension == ".java") { - configuration.addJavaSourceRoot(file) + fun addSources(sourceDirectories: List<File>) { + sourceDirectories.forEach { directory -> + configuration.addKotlinSourceRoot(directory.path) + if (directory.isDirectory || directory.extension == ".java") { + configuration.addJavaSourceRoot(directory) } } } diff --git a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt index d9a89194..009bc66c 100644 --- a/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt +++ b/kotlin-analysis/src/main/kotlin/org/jetbrains/dokka/analysis/EnvironmentAndFacade.kt @@ -17,12 +17,12 @@ internal fun createEnvironmentAndFacade( if (analysisPlatform == Platform.jvm) { addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) } - sourceSet.classpath.forEach { addClasspath(File(it)) } + sourceSet.classpath.forEach(::addClasspath) addSources( (sourceSet.sourceRoots + configuration.sourceSets.filter { it.sourceSetID in sourceSet.dependentSourceSets } .flatMap { it.sourceRoots }) - .map { it.path } + .map { it.directory } ) loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) diff --git a/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt index de2242f2..4be84749 100644 --- a/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt +++ b/plugins/base/src/main/kotlin/allModulePage/MultimodulePageCreator.kt @@ -43,7 +43,7 @@ class MultimodulePageCreator( header(2, "All modules:") table(styles = setOf(MultimoduleTable)) { modules.mapNotNull { module -> - val paragraph = module.docFile.let(::File).readText().let { parser.parse(it).firstParagraph() } + val paragraph = module.docFile.readText().let { parser.parse(it).firstParagraph() } paragraph?.let { val dri = DRI(packageName = MULTIMODULE_PACKAGE_PLACEHOLDER, classNames = module.name) val dci = DCI(setOf(dri), ContentKind.Main) @@ -68,10 +68,9 @@ class MultimodulePageCreator( } private fun throwOnMissingModuleDocFile(module: DokkaConfiguration.DokkaModuleDescription) { - val docFile = File(module.docFile) - if (!docFile.exists() || !docFile.isFile) { + if (!module.docFile.exists() || !module.docFile.isFile) { throw DokkaException( - "Missing documentation file for module ${module.name}: ${docFile.absolutePath}" + "Missing documentation file for module ${module.name}: ${module.docFile.absolutePath}" ) } } diff --git a/plugins/base/src/main/kotlin/renderers/FileWriter.kt b/plugins/base/src/main/kotlin/renderers/FileWriter.kt index 181295c0..cd38f1b9 100644 --- a/plugins/base/src/main/kotlin/renderers/FileWriter.kt +++ b/plugins/base/src/main/kotlin/renderers/FileWriter.kt @@ -21,10 +21,10 @@ class FileWriter(val context: DokkaContext): OutputWriter { createdFiles.add(path) try { - val dir = Paths.get(root, path.dropLastWhile { it != '/' }).toFile() + val dir = Paths.get(root.absolutePath, path.dropLastWhile { it != '/' }).toFile() withContext(Dispatchers.IO) { dir.mkdirsOrFail() - Files.write(Paths.get(root, "$path$ext"), text.lines()) + Files.write(Paths.get(root.absolutePath, "$path$ext"), text.lines()) } } catch (e: Throwable) { context.logger.error("Failed to write $this. ${e.message}") @@ -41,7 +41,7 @@ class FileWriter(val context: DokkaContext): OutputWriter { private suspend fun copyFromDirectory(pathFrom: String, pathTo: String) { - val dest = Paths.get(root, pathTo).toFile() + val dest = Paths.get(root.path, pathTo).toFile() val uri = javaClass.getResource(pathFrom).toURI() withContext(Dispatchers.IO) { File(uri).copyRecursively(dest, true) @@ -51,7 +51,7 @@ class FileWriter(val context: DokkaContext): OutputWriter { private suspend fun copyFromJar(pathFrom: String, pathTo: String) { val rebase = fun(path: String) = "$pathTo/${path.removePrefix(pathFrom)}" - val dest = Paths.get(root, pathTo).toFile() + val dest = Paths.get(root.path, pathTo).toFile() dest.mkdirsOrFail() val uri = javaClass.getResource(pathFrom).toURI() val fs = getFileSystemForURI(uri) @@ -60,12 +60,12 @@ class FileWriter(val context: DokkaContext): OutputWriter { if (Files.isDirectory(file)) { val dirPath = file.toAbsolutePath().toString() withContext(Dispatchers.IO) { - Paths.get(root, rebase(dirPath)).toFile().mkdirsOrFail() + Paths.get(root.path, rebase(dirPath)).toFile().mkdirsOrFail() } } else { val filePath = file.toAbsolutePath().toString() withContext(Dispatchers.IO) { - Paths.get(root, rebase(filePath)).toFile().writeBytes( + Paths.get(root.path, rebase(filePath)).toFile().writeBytes( this@FileWriter.javaClass.getResourceAsStream(filePath).readBytes() ) } @@ -85,4 +85,4 @@ class FileWriter(val context: DokkaContext): OutputWriter { } catch (e: FileSystemAlreadyExistsException) { FileSystems.getFileSystem(uri) } -}
\ No newline at end of file +} diff --git a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt index 4a98a5e0..71824922 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/ModuleAndPackageDocumentationTransformer.kt @@ -22,9 +22,9 @@ internal class ModuleAndPackageDocumentationTransformer( val modulesAndPackagesDocumentation = context.configuration.sourceSets - .map { - Pair(it.moduleDisplayName, it) to - it.includes.map { Paths.get(it) } + .map { sourceSet -> + Pair(sourceSet.moduleDisplayName, sourceSet) to + sourceSet.includes.map { it.toPath() } .also { it.forEach { if (Files.notExists(it)) diff --git a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt index 79feb832..7febabbb 100644 --- a/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/documentables/SuppressedDocumentableFilterTransformer.kt @@ -45,7 +45,7 @@ class SuppressedDocumentableFilterTransformer(val context: DokkaContext) : PreMe if (documentable !is WithExpectActual) return false val sourceFile = File(source(documentable).path).absoluteFile return sourceSet(documentable).suppressedFiles.any { suppressedFile -> - sourceFile.startsWith(File(suppressedFile).absoluteFile) + sourceFile.startsWith(suppressedFile.absoluteFile) } } } diff --git a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt index 695ef050..f13e52ab 100644 --- a/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt +++ b/plugins/base/src/main/kotlin/transformers/pages/samples/SamplesTransformer.kt @@ -43,16 +43,16 @@ abstract class SamplesTransformer(val context: DokkaContext) : PageTransformer { } } - private fun setUpAnalysis(context: DokkaContext) = context.configuration.sourceSets.map { - it to AnalysisEnvironment(DokkaMessageCollector(context.logger), it.analysisPlatform).run { + private fun setUpAnalysis(context: DokkaContext) = context.configuration.sourceSets.map { sourceSet -> + sourceSet to AnalysisEnvironment(DokkaMessageCollector(context.logger), sourceSet.analysisPlatform).run { if (analysisPlatform == Platform.jvm) { addClasspath(PathUtil.getJdkClassesRootsFromCurrentJre()) } - it.classpath.forEach { addClasspath(File(it)) } + sourceSet.classpath.forEach(::addClasspath) - addSources(it.samples.map { it }) + addSources(sourceSet.samples.toList()) - loadLanguageVersionSettings(it.languageVersion, it.apiVersion) + loadLanguageVersionSettings(sourceSet.languageVersion, sourceSet.apiVersion) val environment = createCoreEnvironment() val (facade, _) = createResolutionFacade(environment) diff --git a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt index c4c6483f..1ac4edf7 100644 --- a/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/descriptors/DefaultDescriptorToDocumentableTranslator.kt @@ -95,7 +95,7 @@ private class DokkaDescriptorVisitor( private fun Collection<DeclarationDescriptor>.filterDescriptorsInSourceSet() = filter { it.toSourceElement.containingFile.toString().let { path -> path.isNotBlank() && sourceSet.sourceRoots.any { root -> - Paths.get(path).startsWith(Paths.get(root.path)) + Paths.get(path).startsWith(root.directory.toPath()) } } } @@ -356,7 +356,8 @@ private class DokkaDescriptorVisitor( sources = actual, visibility = descriptor.visibility.toDokkaVisibility().toSourceSetDependent(), generics = descriptor.typeParameters.map { it.toTypeParameter() }, - documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED }?.resolveDescriptorData() ?: emptyMap(), + documentation = descriptor.takeIf { it.kind != CallableMemberDescriptor.Kind.SYNTHESIZED } + ?.resolveDescriptorData() ?: emptyMap(), modifier = descriptor.modifier().toSourceSetDependent(), type = descriptor.returnType!!.toBound(), sourceSets = setOf(sourceSet), @@ -746,11 +747,19 @@ private class DokkaDescriptorVisitor( private data class InheritanceLevel(val level: Int, val superclass: DRI?, val interfaces: List<DRI>) - private data class ClassInfo(val inheritance: List<InheritanceLevel>, val docs: SourceSetDependent<DocumentationNode>){ + private data class ClassInfo( + val inheritance: List<InheritanceLevel>, + val docs: SourceSetDependent<DocumentationNode> + ) { val supertypes: List<DriWithKind> get() = inheritance.firstOrNull { it.level == 0 }?.let { - listOfNotNull(it.superclass?.let { DriWithKind(it, KotlinClassKindTypes.CLASS) }) + it.interfaces.map { DriWithKind(it, KotlinClassKindTypes.INTERFACE) } - }.orEmpty() + listOfNotNull(it.superclass?.let { + DriWithKind( + it, + KotlinClassKindTypes.CLASS + ) + }) + it.interfaces.map { DriWithKind(it, KotlinClassKindTypes.INTERFACE) } + }.orEmpty() val allImplementedInterfaces: List<DRI> get() = inheritance.flatMap { it.interfaces }.distinct() diff --git a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt index 9ed37c30..cd43e635 100644 --- a/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt +++ b/plugins/base/src/main/kotlin/translators/psi/DefaultPsiToDocumentableTranslator.kt @@ -43,8 +43,8 @@ class DefaultPsiToDocumentableTranslator( override fun invoke(sourceSet: DokkaSourceSet, context: DokkaContext): DModule { - fun isFileInSourceRoots(file: File) : Boolean { - return sourceSet.sourceRoots.any { root -> file.path.startsWith(File(root.path).absolutePath) } + fun isFileInSourceRoots(file: File): Boolean { + return sourceSet.sourceRoots.any { root -> file.startsWith(root.directory) } } val (environment, _) = kotlinAnalysis[sourceSet] @@ -133,7 +133,7 @@ class DefaultPsiToDocumentableTranslator( val superMethods = mutableListOf<Pair<PsiMethod, DRI>>() methods.forEach { superMethodsKeys.add(it.hash) } fun parseSupertypes(superTypes: Array<PsiClassType>, level: Int = 0) { - if(superTypes.isEmpty()) return + if (superTypes.isEmpty()) return val parsedClasses = superTypes.filter { !it.shouldBeIgnored }.mapNotNull { it.resolve()?.let { when { @@ -178,7 +178,8 @@ class DefaultPsiToDocumentableTranslator( }) + it.interfaces.map { DriWithKind(dri = it, kind = JavaClassKindTypes.INTERFACE) } }.toSourceSetDependent() val modifiers = getModifier().toSourceSetDependent() - val implementedInterfacesExtra = ImplementedInterfaces(inheritanceTree.flatMap { it.interfaces }.distinct().toSourceSetDependent()) + val implementedInterfacesExtra = + ImplementedInterfaces(inheritanceTree.flatMap { it.interfaces }.distinct().toSourceSetDependent()) return when { isAnnotationType -> DAnnotation( @@ -195,8 +196,11 @@ class DefaultPsiToDocumentableTranslator( constructors.map { parseFunction(it, true) }, mapTypeParameters(dri), setOf(sourceSetData), - PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations()) + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) ) isEnum -> DEnum( dri, @@ -211,8 +215,11 @@ class DefaultPsiToDocumentableTranslator( emptyList(), emptyList(), setOf(sourceSetData), - PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations()) + PropertyContainer.withAll( + implementedInterfacesExtra, + annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) ) }, documentation, @@ -226,8 +233,10 @@ class DefaultPsiToDocumentableTranslator( constructors.map { parseFunction(it, true) }, ancestors, setOf(sourceSetData), - PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations()) + PropertyContainer.withAll( + implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) ) isInterface -> DInterface( dri, @@ -243,8 +252,10 @@ class DefaultPsiToDocumentableTranslator( mapTypeParameters(dri), ancestors, setOf(sourceSetData), - PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations()) + PropertyContainer.withAll( + implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) ) else -> DClass( dri, @@ -262,8 +273,10 @@ class DefaultPsiToDocumentableTranslator( null, modifiers, setOf(sourceSetData), - PropertyContainer.withAll(implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() - .toAnnotations()) + PropertyContainer.withAll( + implementedInterfacesExtra, annotations.toList().toListOfAnnotations().toSourceSetDependent() + .toAnnotations() + ) ) } } @@ -305,7 +318,8 @@ class DefaultPsiToDocumentableTranslator( PropertyContainer.withAll( InheritedFunction(inheritedFrom.toSourceSetDependent()), it.toSourceSetDependent().toAdditionalModifiers(), - (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() + (psi.annotations.toList() + .toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() .toAnnotations() ) } @@ -434,7 +448,8 @@ class DefaultPsiToDocumentableTranslator( psi.additionalExtras().let { PropertyContainer.withAll<DProperty>( it.toSourceSetDependent().toAdditionalModifiers(), - (psi.annotations.toList().toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() + (psi.annotations.toList() + .toListOfAnnotations() + it.toListOfAnnotations()).toSourceSetDependent() .toAnnotations() ) } diff --git a/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt index 4dfdc410..57571cd3 100644 --- a/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt +++ b/plugins/base/src/test/kotlin/expect/AbstractExpectTest.kt @@ -24,7 +24,7 @@ abstract class AbstractExpectTest( var result: Path? = null testFromData(config, cleanupOutput = false) { - renderingStage = { _, context -> result = Paths.get(context.configuration.outputDir) } + renderingStage = { _, context -> result = context.configuration.outputDir.toPath() } } return result } diff --git a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt index b6765fda..ae494929 100644 --- a/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt +++ b/plugins/base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt @@ -52,7 +52,7 @@ abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() { DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory }, DokkaBase().tabSortingStrategy to { DefaultTabSortingStrategy() }, testConfiguration = DokkaConfigurationImpl( - "", null, false, listOf(js, jvm, native), emptyList(), emptyMap(), emptyList(), false + sourceSets = listOf(js, jvm, native) ) ) diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt index 0c8b942e..2381ad07 100644 --- a/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/DivergentTest.kt @@ -9,6 +9,7 @@ import org.jetbrains.dokka.pages.ContentDivergentGroup import org.junit.jupiter.api.Test import renderers.defaultSourceSet import renderers.TestPage +import java.io.File class DivergentTest : GfmRenderingOnlyTestBase() { private val js = defaultSourceSet.copy( @@ -371,4 +372,4 @@ class DivergentTest : GfmRenderingOnlyTestBase() { CommonmarkRenderer(context).render(page) assert(renderedContent == expect) } -}
\ No newline at end of file +} diff --git a/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt index 6d0dd3a6..7c9590c6 100644 --- a/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt +++ b/plugins/gfm/src/test/kotlin/renderers/gfm/GfmRenderingOnlyTestBase.kt @@ -21,9 +21,7 @@ abstract class GfmRenderingOnlyTestBase : RenderingOnlyTestBase<String>() { DokkaBase().externalLocationProviderFactory to { ::DokkaExternalLocationProviderFactory }, GfmPlugin().gfmPreprocessors to { _ -> RootCreator }, - testConfiguration = DokkaConfigurationImpl( - "", null, false, emptyList(), emptyList(), emptyMap(), emptyList(), false - ) + testConfiguration = DokkaConfigurationImpl() ) override val renderedContent: String by lazy { diff --git a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocPackageTemplateMapTest.kt b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocPackageTemplateMapTest.kt index 447be3f8..89d5b286 100644 --- a/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocPackageTemplateMapTest.kt +++ b/plugins/javadoc/src/test/kotlin/org/jetbrains/dokka/javadoc/JavadocPackageTemplateMapTest.kt @@ -8,6 +8,7 @@ import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Disabled import org.junit.jupiter.api.Test import org.jetbrains.dokka.testApi.utils.assertIsInstance +import java.io.File internal class JavadocPackageTemplateMapTest : AbstractJavadocTemplateMapTest() { @@ -109,7 +110,7 @@ internal class JavadocPackageTemplateMapTest : AbstractJavadocTemplateMapTest() configuration = config.copy( sourceSets = config.sourceSets.map { sourceSet -> sourceSet.copy( - includes = listOf("packages.md") + includes = listOf(File("packages.md")) ) } ) diff --git a/runners/cli/src/main/kotlin/cli/main.kt b/runners/cli/src/main/kotlin/cli/main.kt index f8d5c55c..df763596 100644 --- a/runners/cli/src/main/kotlin/cli/main.kt +++ b/runners/cli/src/main/kotlin/cli/main.kt @@ -17,11 +17,11 @@ class GlobalArguments(args: Array<String>) : DokkaConfiguration { val json: String? by parser.argument(ArgType.String, description = "Json file name").optional() - override val outputDir by parser.option(ArgType.String, description = "Output directory path") + override val outputDir by parser.option(ArgTypeFile, description = "Output directory path") .default(DokkaDefaults.outputDir) override val cacheRoot by parser.option( - ArgType.String, + ArgTypeFile, description = "Path to cache folder, or 'default' to use ~/.cache/dokka, if not provided caching is disabled" ) @@ -132,12 +132,12 @@ private fun parseSourceSet(args: Array<String>): DokkaConfiguration.DokkaSourceS ).default(DokkaDefaults.sourceSetDisplayName) val classpath by parser.option( - ArgType.String, + ArgTypeFile, description = "Classpath for symbol resolution (allows many paths separated by the semicolon `;`)" ).delimiter(";") val sourceRoots by parser.option( - ArgType.String, + ArgTypeFile, description = "Source file or directory (allows many paths separated by the semicolon `;`)", fullName = "src" ).delimiter(";") @@ -148,12 +148,12 @@ private fun parseSourceSet(args: Array<String>): DokkaConfiguration.DokkaSourceS ).delimiter(";") val samples by parser.option( - ArgType.String, + ArgTypeFile, description = "Source root for samples (allows many paths separated by the semicolon `;`)" ).delimiter(";") val includes by parser.option( - ArgType.String, + ArgTypeFile, description = "Markdown files to load (allows many paths separated by the semicolon `;`)" ).delimiter(";") @@ -196,7 +196,7 @@ private fun parseSourceSet(args: Array<String>): DokkaConfiguration.DokkaSourceS .default(DokkaDefaults.noJdkLink) val suppressedFiles by parser.option( - ArgType.String, + ArgTypeFile, description = "Paths to files to be suppressed (allows many paths separated by the semicolon `;`)" ).delimiter(";") @@ -228,12 +228,12 @@ private fun parseSourceSet(args: Array<String>): DokkaConfiguration.DokkaSourceS override val displayName = displayName override val sourceSetID = DokkaSourceSetID(moduleName, sourceSetName) override val classpath = classpath - override val sourceRoots = sourceRoots.map { SourceRootImpl(it.toAbsolutePath()) } + override val sourceRoots = sourceRoots.map { SourceRootImpl(it) } override val dependentSourceSets: Set<DokkaSourceSetID> = dependentSourceSets .map { dependentSourceSetName -> dependentSourceSetName.split('/').let { DokkaSourceSetID(it[0], it[1]) } } .toSet() - override val samples = samples.map { it.toAbsolutePath() } - override val includes = includes.map { it.toAbsolutePath() } + override val samples = samples + override val includes = includes override val includeNonPublic = includeNonPublic override val includeRootPackage = includeRootPackage override val reportUndocumented = reportUndocumented 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) } + diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTaskTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTaskTest.kt new file mode 100644 index 00000000..da0f3f35 --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/AbstractDokkaParentTaskTest.kt @@ -0,0 +1,107 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.withType +import org.gradle.testfixtures.ProjectBuilder +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class AbstractDokkaParentTaskTest { + + private val rootProject = ProjectBuilder.builder().build() + private val subproject0 = ProjectBuilder.builder().withName("subproject0").withParent(rootProject).build() + private val subproject1 = ProjectBuilder.builder().withName("subproject1").withParent(rootProject).build() + private val subSubproject0a = ProjectBuilder.builder().withName("subSubproject0a").withParent(subproject0).build() + + init { + rootProject.allprojects { project -> project.plugins.apply("org.jetbrains.dokka") } + } + + private val parentTasks = rootProject.tasks.withType<AbstractDokkaParentTask>().toList() + + @Test + fun `at least one parent task is registered`() { + assertTrue( + parentTasks.isNotEmpty(), + "Expected at least one ${AbstractDokkaParentTask::class.simpleName} task in rootProject" + ) + } + + @Test + fun `configuring subprojects`() { + parentTasks.forEach { task -> + assertEquals( + setOf(":subproject0", ":subproject1", ":subproject0:subSubproject0a"), task.subprojectPaths, + "Expected all sub projects registered by default" + ) + + assertEquals( + listOf(subproject0, subproject1, subSubproject0a), task.subprojects, + "Expected all sub projects registered by default" + ) + + assertEquals(3, task.dokkaTasks.size, "Expected three referenced dokka tasks") + assertTrue(listOf(subproject0, subproject1, subSubproject0a).all { project -> + task.dokkaTasks.any { task -> task in project.tasks } + }, "Expected all sub projects to contribute to referenced dokka tasks") + + task.removeSubproject(subproject0) + assertEquals( + setOf(":subproject1", ":subproject0:subSubproject0a"), task.subprojectPaths, + "Expected subproject0 to be removed (without removing its children)" + ) + + task.addSubproject(subproject0) + assertEquals( + setOf(":subproject1", ":subproject0:subSubproject0a", ":subproject0"), task.subprojectPaths, + "Expected subproject0 being added again" + ) + + task.addSubproject(subproject0) + assertEquals( + setOf(":subproject1", ":subproject0:subSubproject0a", ":subproject0"), task.subprojectPaths, + "Expected adding same project twice to be ignored" + ) + + task.removeAllProjects(subproject0) + assertEquals( + setOf(":subproject1"), task.subprojectPaths, + "Expected subproject0 and subSubproject0a to be removed" + ) + + task.addAllProjects(subproject0) + assertEquals( + setOf(":subproject1", ":subproject0", ":subproject0:subSubproject0a"), task.subprojectPaths, + "Expected subproject0 and subSubproject0a to be added again" + ) + } + } + + @Test + fun `configure dokkaTaskNames`() { + parentTasks.forEach { task -> + assertEquals( + 3, task.dokkaTasks.size, + "Expected 3 tasks referenced by default" + ) + + val customDokkaTaskName = "custom${task.name}" + task.dokkaTaskNames = setOf(customDokkaTaskName) + assertTrue(task.dokkaTasks.isEmpty(), "Expected no $customDokkaTaskName. Found: ${task.dokkaTasks}") + + rootProject.subprojects { subproject -> + subproject.tasks.register<DokkaTask>(customDokkaTaskName) + } + + assertEquals( + 3, task.dokkaTasks.size, + "Expected three $customDokkaTaskName found" + ) + + task.dokkaTasks.forEach { dokkaTask -> + assertEquals(customDokkaTaskName, dokkaTask.name) + } + } + } +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTaskTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTaskTest.kt new file mode 100644 index 00000000..2d8dc21a --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaCollectorTaskTest.kt @@ -0,0 +1,60 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.FileCollectionDependency +import org.gradle.kotlin.dsl.withType +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.dokka.DokkaConfigurationImpl +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class DokkaCollectorTaskTest { + + @Test + fun buildDokkaConfiguration() { + val rootProject = ProjectBuilder.builder().build() + val childProject = ProjectBuilder.builder().withParent(rootProject).build() + childProject.plugins.apply("org.jetbrains.kotlin.jvm") + + rootProject.allprojects { project -> + project.plugins.apply("org.jetbrains.dokka") + project.tasks.withType<AbstractDokkaTask>().configureEach { task -> + task.plugins.withDependencies { dependencies -> dependencies.clear() } + } + } + + val collectorTasks = rootProject.tasks.withType<DokkaCollectorTask>() + collectorTasks.configureEach { task -> + task.outputDirectory = File("customOutputDirectory") + task.cacheRoot = File("customCacheRoot") + task.failOnWarning = true + task.offlineMode = true + } + + assertTrue(collectorTasks.isNotEmpty(), "Expected at least one collector task") + + collectorTasks.forEach { task -> + val dokkaConfiguration = task.buildDokkaConfiguration() + assertEquals( + DokkaConfigurationImpl( + outputDir = File("customOutputDirectory"), + cacheRoot = File("customCacheRoot"), + failOnWarning = true, + offlineMode = true, + sourceSets = task.dokkaTasks + .map { it.buildDokkaConfiguration() } + .map { it.sourceSets } + .reduce { acc, list -> acc + list }, + pluginsClasspath = task.dokkaTasks + .map { it.plugins.resolve() } + .reduce { acc, mutableSet -> acc + mutableSet } + .toList() + ), + dokkaConfiguration + ) + } + + } +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationJsonTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationJsonTest.kt index d80f43e4..f6a9c909 100644 --- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationJsonTest.kt +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationJsonTest.kt @@ -3,6 +3,7 @@ package org.jetbrains.dokka.gradle import org.gradle.kotlin.dsl.withType import org.gradle.testfixtures.ProjectBuilder import org.jetbrains.dokka.* +import java.io.File import java.net.URL import kotlin.test.Test import kotlin.test.assertEquals @@ -20,8 +21,8 @@ class DokkaConfigurationJsonTest { dokkaTask.apply { this.failOnWarning = true this.offlineMode = true - this.outputDirectory = "customOutputDir" - this.cacheRoot = "customCacheRoot" + this.outputDirectory = File("customOutputDir") + this.cacheRoot = File("customCacheRoot") this.pluginsConfiguration["0"] = "a" this.pluginsConfiguration["1"] = "b" this.dokkaSourceSets.create("main") { sourceSet -> @@ -33,12 +34,6 @@ class DokkaConfigurationJsonTest { link.packageListUrl = URL("http://some.url") link.url = URL("http://some.other.url") } - sourceSet.collectKotlinTasks = { - println(this@DokkaConfigurationJsonTest) - println("This lambda is capturing the entire test") - emptyList() - } - sourceSet.perPackageOption { packageOption -> packageOption.includeNonPublic = true packageOption.reportUndocumented = true @@ -47,47 +42,11 @@ class DokkaConfigurationJsonTest { } } - val sourceConfiguration = dokkaTask.getConfigurationOrThrow() + val sourceConfiguration = dokkaTask.buildDokkaConfiguration() val configurationJson = sourceConfiguration.toJsonString() val parsedConfiguration = DokkaConfigurationImpl(configurationJson) - assertEquals( - DokkaConfigurationImpl( - failOnWarning = sourceConfiguration.failOnWarning, - offlineMode = sourceConfiguration.offlineMode, - outputDir = sourceConfiguration.outputDir, - cacheRoot = sourceConfiguration.cacheRoot, - pluginsClasspath = emptyList(), - pluginsConfiguration = sourceConfiguration.pluginsConfiguration.toMap(), - sourceSets = listOf( - DokkaSourceSetImpl( - moduleDisplayName = sourceConfiguration.sourceSets.single().moduleDisplayName, - displayName = sourceConfiguration.sourceSets.single().displayName, - reportUndocumented = sourceConfiguration.sourceSets.single().reportUndocumented, - externalDocumentationLinks = sourceConfiguration.sourceSets.single().externalDocumentationLinks - .map { link -> - ExternalDocumentationLinkImpl( - url = link.url, - packageListUrl = link.packageListUrl - ) - }, - perPackageOptions = sourceConfiguration.sourceSets.single().perPackageOptions.map { option -> - PackageOptionsImpl( - prefix = option.prefix, - includeNonPublic = option.includeNonPublic, - reportUndocumented = option.reportUndocumented, - skipDeprecated = option.skipDeprecated, - suppress = option.suppress - ) - }, - sourceSetID = sourceConfiguration.sourceSets.single().sourceSetID, - sourceRoots = sourceConfiguration.sourceSets.single().sourceRoots.map { sourceRoot -> - SourceRootImpl(sourceRoot.path) - } - ) - ) - ), parsedConfiguration - ) + assertEquals(sourceConfiguration, parsedConfiguration) println(parsedConfiguration) } } diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationSerializableTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationSerializableTest.kt index 70ecfcf0..e4fd0676 100644 --- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationSerializableTest.kt +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaConfigurationSerializableTest.kt @@ -6,6 +6,7 @@ import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.toJsonString import org.junit.Rule import org.junit.rules.TemporaryFolder +import java.io.File import java.io.ObjectInputStream import java.io.ObjectOutputStream import java.net.URL @@ -28,8 +29,8 @@ class DokkaConfigurationSerializableTest { dokkaTask.apply { this.failOnWarning = true this.offlineMode = true - this.outputDirectory = "customOutputDir" - this.cacheRoot = "customCacheRoot" + this.outputDirectory = File("customOutputDir") + this.cacheRoot = File("customCacheRoot") this.pluginsConfiguration["0"] = "a" this.pluginsConfiguration["1"] = "b" this.dokkaSourceSets.create("main") { sourceSet -> @@ -41,9 +42,6 @@ class DokkaConfigurationSerializableTest { link.packageListUrl = URL("http://some.url") link.url = URL("http://some.other.url") } - sourceSet.collectKotlinTasks = { - emptyList() - } sourceSet.perPackageOption { packageOption -> packageOption.includeNonPublic = true @@ -53,7 +51,7 @@ class DokkaConfigurationSerializableTest { } } - val sourceConfiguration = dokkaTask.getConfigurationOrThrow() + val sourceConfiguration = dokkaTask.buildDokkaConfiguration() val configurationFile = temporaryFolder.root.resolve("config.bin") ObjectOutputStream(configurationFile.outputStream()).use { stream -> stream.writeObject(sourceConfiguration) @@ -62,7 +60,6 @@ class DokkaConfigurationSerializableTest { stream.readObject() as DokkaConfiguration } - /* Abusing toJsonString, since there is no proper .equals on the gradle implementations yet */ - assertEquals(sourceConfiguration.toJsonString(), parsedConfiguration.toJsonString()) + assertEquals(sourceConfiguration, parsedConfiguration) } } diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTaskTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTaskTest.kt new file mode 100644 index 00000000..ff318bdf --- /dev/null +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/DokkaMultiModuleTaskTest.kt @@ -0,0 +1,112 @@ +package org.jetbrains.dokka.gradle + +import org.gradle.kotlin.dsl.create +import org.gradle.kotlin.dsl.withType +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.DokkaModuleDescriptionImpl +import java.io.File +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue + +class DokkaMultiModuleTaskTest { + + private val rootProject = ProjectBuilder.builder() + .withName("root") + .build() + + private val childProject = ProjectBuilder.builder() + .withName("child") + .withProjectDir(rootProject.projectDir.resolve("child")) + .withParent(rootProject).build() + + init { + rootProject.allprojects { project -> + project.plugins.apply("org.jetbrains.kotlin.jvm") + project.plugins.apply("org.jetbrains.dokka") + project.tasks.withType<AbstractDokkaTask>().configureEach { task -> + task.plugins.withDependencies { dependencies -> dependencies.clear() } + } + } + } + + @Test + fun `child project is withing root project`() { + assertEquals( + rootProject.projectDir, childProject.projectDir.parentFile, + "Expected child project being inside the root project" + ) + + assertEquals( + childProject.projectDir.name, "child", + "Expected folder of child project to be called 'child'" + ) + } + + @Test + fun buildDokkaConfiguration() { + childProject.tasks.withType<DokkaTask>().configureEach { task -> + task.outputDirectory = task.project.buildDir.resolve("output") + } + + val multimoduleTasks = rootProject.tasks.withType<DokkaMultimoduleTask>() + assertTrue(multimoduleTasks.isNotEmpty(), "Expected at least one multimodule task") + + multimoduleTasks.configureEach { task -> + task.documentationFileName = "customDocumentationFileName.md" + task.outputDirectory = task.project.buildDir.resolve("customOutputDirectory") + task.cacheRoot = File("customCacheRoot") + task.pluginsConfiguration["pluginA"] = "configA" + task.failOnWarning = true + task.offlineMode = true + } + + multimoduleTasks.forEach { task -> + val dokkaConfiguration = task.buildDokkaConfiguration() + assertEquals( + DokkaConfigurationImpl( + outputDir = task.project.buildDir.resolve("customOutputDirectory"), + cacheRoot = File("customCacheRoot"), + pluginsConfiguration = mapOf("pluginA" to "configA"), + pluginsClasspath = emptyList(), + failOnWarning = true, + offlineMode = true, + modules = listOf( + DokkaModuleDescriptionImpl( + name = "child", + path = File("../../child/build/output"), + docFile = childProject.projectDir.resolve("customDocumentationFileName.md") + ) + ) + ), + dokkaConfiguration + ) + } + } + + @Test + fun `setting dokkaTaskNames declares proper task dependencies`() { + val multimoduleTasks = rootProject.tasks.withType<DokkaMultimoduleTask>() + assertTrue(multimoduleTasks.isNotEmpty(), "Expected at least one multimodule task") + + multimoduleTasks.toList().forEach { task -> + val dependencies = task.taskDependencies.getDependencies(task).toSet() + assertEquals(1, dependencies.size, "Expected one dependency") + val dependency = dependencies.single() + + assertTrue(dependency is DokkaTask, "Expected dependency to be of Type ${DokkaTask::class.simpleName}") + assertEquals(childProject, dependency.project, "Expected dependency from child project") + } + + val customDokkaTask = childProject.tasks.create<DokkaTask>("customDokkaTask") + + multimoduleTasks.toList().forEach { task -> + task.dokkaTaskNames += "customDokkaTask" + val dependencies = task.taskDependencies.getDependencies(task).toSet() + + assertEquals(2, dependencies.size, "Expected two dependencies") + assertTrue(customDokkaTask in dependencies, "Expected 'customDokkaTask' in dependencies") + } + } +} diff --git a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt index 7b78fb55..f4029bd5 100644 --- a/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt +++ b/runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/gradle/KotlinDslDokkaTaskConfigurationTest.kt @@ -3,6 +3,7 @@ package org.jetbrains.dokka.gradle import org.gradle.testfixtures.ProjectBuilder import org.jetbrains.dokka.DokkaSourceSetID import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import java.io.File import kotlin.test.Test import kotlin.test.assertEquals @@ -13,11 +14,11 @@ class KotlinDslDokkaTaskConfigurationTest { val project = ProjectBuilder.builder().build() project.plugins.apply("org.jetbrains.dokka") project.tasks.withType(DokkaTask::class.java).forEach { dokkaTask -> - dokkaTask.outputDirectory = "test" + dokkaTask.outputDirectory = File("test") } project.tasks.withType(DokkaTask::class.java).forEach { dokkaTask -> - assertEquals("test", dokkaTask.outputDirectory) + assertEquals(File("test"), dokkaTask.outputDirectory) } } diff --git a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt index 514df151..3df2a17e 100644 --- a/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt +++ b/runners/maven-plugin/src/main/kotlin/DokkaMojo.kt @@ -53,9 +53,12 @@ class ExternalDocumentationLinkBuilder : DokkaConfiguration.ExternalDocumentatio } abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependency>) : AbstractMojo() { - class SourceRoot : DokkaConfiguration.SourceRoot { + class SourceRoot : DokkaConfigurationBuilder<DokkaConfiguration.SourceRoot> { @Parameter(required = true) - override var path: String = "" + var path: String = "" + override fun build(): DokkaConfiguration.SourceRoot { + return SourceRootImpl(File(path)) + } } @Parameter(defaultValue = "\${project}", readonly = true) @@ -200,11 +203,11 @@ abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependenc moduleDisplayName = moduleDisplayName.takeIf(String::isNotBlank) ?: moduleName, displayName = displayName, sourceSetID = DokkaSourceSetID(moduleName, sourceSetName), - classpath = classpath, - sourceRoots = sourceDirectories.map { SourceRootImpl(it) }, + classpath = classpath.map(::File), + sourceRoots = sourceDirectories.map(::File).map(::SourceRootImpl), dependentSourceSets = emptySet(), - samples = samples, - includes = includes, + samples = samples.map(::File), + includes = includes.map(::File), includeNonPublic = includeNonPublic, includeRootPackage = includeRootPackage, reportUndocumented = reportUndocumented, @@ -226,7 +229,7 @@ abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependenc apiVersion = apiVersion, noStdlibLink = noStdlibLink, noJdkLink = noJdkLink, - suppressedFiles = suppressedFiles, + suppressedFiles = suppressedFiles.map(::File), analysisPlatform = if (platform.isNotEmpty()) Platform.fromString(platform) else Platform.DEFAULT ).let { it.copy( @@ -237,9 +240,9 @@ abstract class AbstractDokkaMojo(private val defaultDokkaPlugins: List<Dependenc val logger = MavenDokkaLogger(log) val configuration = DokkaConfigurationImpl( - outputDir = getOutDir(), + outputDir = File(getOutDir()), offlineMode = offlineMode, - cacheRoot = cacheRoot, + cacheRoot = cacheRoot?.let(::File), sourceSets = listOf(sourceSet).also { if (sourceSet.moduleDisplayName.isEmpty()) logger.warn("Not specified module name. It can result in unexpected behaviour while including documentation for module") }, diff --git a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt index 457325cf..6d80d15a 100644 --- a/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt +++ b/testApi/src/main/kotlin/testApi/testRunner/TestRunner.kt @@ -38,7 +38,7 @@ abstract class AbstractCoreTest( logger.info("Output generated under: ${tempDir.root.absolutePath}") val newConfiguration = configuration.copy( - outputDir = tempDir.root.toPath().toAbsolutePath().toString() + outputDir = tempDir.root ) DokkaTestGenerator( newConfiguration, @@ -62,20 +62,21 @@ abstract class AbstractCoreTest( fileMap.materializeFiles(testDirPath.toAbsolutePath()) if (!cleanupOutput) loggerForTest.info("Output generated under: ${testDirPath.toAbsolutePath()}") - val newConfiguration = - configuration.copy( - outputDir = testDirPath.toAbsolutePath().toString(), - sourceSets = configuration.sourceSets.map { sourceSet -> - sourceSet.copy( - sourceRoots = sourceSet.sourceRoots.map { sourceRoot -> - sourceRoot.copy(path = "${testDirPath.toAbsolutePath()}/${sourceRoot.path}") - }, - suppressedFiles = sourceSet.suppressedFiles.map { suppressedFile -> - testDirPath.toAbsolutePath().toFile().resolve(suppressedFile).absolutePath - } - ) - }, - ) + val newConfiguration = configuration.copy( + outputDir = testDirPath.toFile(), + sourceSets = configuration.sourceSets.map { sourceSet -> + sourceSet.copy( + sourceRoots = sourceSet.sourceRoots.map { sourceRoot -> + sourceRoot.copy( + directory = testDirPath.toFile().resolve(sourceRoot.directory) + ) + }, + suppressedFiles = sourceSet.suppressedFiles.map { file -> + testDirPath.toFile().resolve(file) + } + ) + } + ) DokkaTestGenerator( newConfiguration, loggerForTest, @@ -172,8 +173,8 @@ abstract class AbstractCoreTest( var failOnWarning: Boolean = false private val sourceSets = mutableListOf<DokkaSourceSetImpl>() fun build() = DokkaConfigurationImpl( - outputDir = outputDir, - cacheRoot = cacheRoot, + outputDir = File(outputDir), + cacheRoot = cacheRoot?.let(::File), offlineMode = offlineMode, sourceSets = sourceSets, pluginsClasspath = pluginsClasspath, @@ -224,11 +225,11 @@ abstract class AbstractCoreTest( moduleDisplayName = moduleDisplayName ?: moduleName, displayName = displayName, sourceSetID = DokkaSourceSetID(moduleName, name), - classpath = classpath, - sourceRoots = sourceRoots.map { SourceRootImpl(it) }, + classpath = classpath.map(::File), + sourceRoots = sourceRoots.map(::File).map(::SourceRootImpl), dependentSourceSets = dependentSourceSets, - samples = samples, - includes = includes, + samples = samples.map(::File), + includes = includes.map(::File), includeNonPublic = includeNonPublic, includeRootPackage = includeRootPackage, reportUndocumented = reportUndocumented, @@ -242,7 +243,7 @@ abstract class AbstractCoreTest( apiVersion = apiVersion, noStdlibLink = noStdlibLink, noJdkLink = noJdkLink, - suppressedFiles = suppressedFiles, + suppressedFiles = suppressedFiles.map(::File), analysisPlatform = Platform.fromString(analysisPlatform) ) } |