Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add global settings to JSON dokka cli input #2292

Merged
merged 3 commits into from Jan 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 17 additions & 0 deletions core/api/core.api
Expand Up @@ -4,6 +4,8 @@ public final class org/jetbrains/dokka/ConfigurationKt {
public static final fun ExternalDocumentationLink (Ljava/net/URL;Ljava/net/URL;)Lorg/jetbrains/dokka/ExternalDocumentationLinkImpl;
public static synthetic fun ExternalDocumentationLink$default (Ljava/lang/String;Ljava/lang/String;ILjava/lang/Object;)Lorg/jetbrains/dokka/ExternalDocumentationLinkImpl;
public static synthetic fun ExternalDocumentationLink$default (Ljava/net/URL;Ljava/net/URL;ILjava/lang/Object;)Lorg/jetbrains/dokka/ExternalDocumentationLinkImpl;
public static final fun GlobalDokkaConfiguration (Ljava/lang/String;)Lorg/jetbrains/dokka/GlobalDokkaConfiguration;
public static final fun apply (Lorg/jetbrains/dokka/DokkaConfiguration;Lorg/jetbrains/dokka/GlobalDokkaConfiguration;)Lorg/jetbrains/dokka/DokkaConfiguration;
public static final fun build (Ljava/lang/Iterable;)Ljava/util/List;
public static final fun toJsonString (Lorg/jetbrains/dokka/DokkaConfiguration;)Ljava/lang/String;
public static final fun toJsonString (Lorg/jetbrains/dokka/plugability/ConfigurableBlock;)Ljava/lang/String;
Expand Down Expand Up @@ -343,6 +345,21 @@ public final class org/jetbrains/dokka/ExternalDocumentationLinkImpl : org/jetbr
public fun toString ()Ljava/lang/String;
}

public final class org/jetbrains/dokka/GlobalDokkaConfiguration {
public fun <init> (Ljava/util/List;Ljava/util/List;Ljava/util/List;)V
public final fun component1 ()Ljava/util/List;
public final fun component2 ()Ljava/util/List;
public final fun component3 ()Ljava/util/List;
public final fun copy (Ljava/util/List;Ljava/util/List;Ljava/util/List;)Lorg/jetbrains/dokka/GlobalDokkaConfiguration;
public static synthetic fun copy$default (Lorg/jetbrains/dokka/GlobalDokkaConfiguration;Ljava/util/List;Ljava/util/List;Ljava/util/List;ILjava/lang/Object;)Lorg/jetbrains/dokka/GlobalDokkaConfiguration;
public fun equals (Ljava/lang/Object;)Z
public final fun getExternalDocumentationLinks ()Ljava/util/List;
public final fun getPerPackageOptions ()Ljava/util/List;
public final fun getSourceLinks ()Ljava/util/List;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}

public final class org/jetbrains/dokka/PackageOptionsImpl : org/jetbrains/dokka/DokkaConfiguration$PackageOptions {
public fun <init> (Ljava/lang/String;ZLjava/lang/Boolean;ZZLjava/util/Set;)V
public final fun component1 ()Ljava/lang/String;
Expand Down
29 changes: 29 additions & 0 deletions core/src/main/kotlin/configuration.kt
Expand Up @@ -3,6 +3,7 @@
package org.jetbrains.dokka

import org.jetbrains.dokka.plugability.ConfigurableBlock
import org.jetbrains.dokka.utilities.cast
import org.jetbrains.dokka.utilities.parseJson
import org.jetbrains.dokka.utilities.toJsonString
import java.io.File
Expand Down Expand Up @@ -86,6 +87,34 @@ data class DokkaSourceSetID(

fun DokkaConfigurationImpl(json: String): DokkaConfigurationImpl = parseJson(json)

/**
* Global options are applied to all packages and modules and overwrite package configuration.
*
* These are handy if we have multiple sourcesets sharing the same global options as it reduces the size of the boilerplate.
* Otherwise, the user would be enforced to repeat all these options per each sourceset.
*/
data class GlobalDokkaConfiguration(
val perPackageOptions: List<PackageOptionsImpl>?,
val externalDocumentationLinks: List<ExternalDocumentationLinkImpl>?,
val sourceLinks: List<SourceLinkDefinitionImpl>?
)

fun GlobalDokkaConfiguration(json: String): GlobalDokkaConfiguration = parseJson(json)

IgnatBeresnev marked this conversation as resolved.
Show resolved Hide resolved
fun DokkaConfiguration.apply(globals: GlobalDokkaConfiguration): DokkaConfiguration = this.apply {
sourceSets.forEach {
it.perPackageOptions.cast<MutableList<DokkaConfiguration.PackageOptions>>().addAll(globals.perPackageOptions ?: emptyList())
}

sourceSets.forEach {
it.externalDocumentationLinks.cast<MutableSet<DokkaConfiguration.ExternalDocumentationLink>>().addAll(globals.externalDocumentationLinks ?: emptyList())
}

sourceSets.forEach {
it.sourceLinks.cast<MutableSet<SourceLinkDefinitionImpl>>().addAll(globals.sourceLinks ?: emptyList())
}
}

fun DokkaConfiguration.toJsonString(): String = toJsonString(this)
fun <T : ConfigurableBlock> T.toJsonString(): String = toJsonString(this)

Expand Down
1 change: 0 additions & 1 deletion core/src/main/kotlin/utilities/json.kt
Expand Up @@ -34,7 +34,6 @@ internal fun toJsonString(value: Any): String = objectMapper.writeValueAsString(
@PublishedApi
internal inline fun <reified T : Any> parseJson(json: String): T = parseJson(json, TypeReference())


@PublishedApi
internal fun <T : Any> parseJson(json: String, typeReference: TypeReference<T>): T =
objectMapper.readValue(json, typeReference.jackson)
Expand Down
Expand Up @@ -2,6 +2,8 @@ package org.jetbrains.dokka.it.cli

import org.jetbrains.dokka.it.awaitProcessResult
import java.io.File
import java.io.PrintWriter
import java.lang.IllegalStateException
import kotlin.test.*

class CliIntegrationTest : AbstractCliIntegrationTest() {
Expand Down Expand Up @@ -193,7 +195,7 @@ class CliIntegrationTest : AbstractCliIntegrationTest() {
}

@Test
fun `logging level should be respected`(){
fun `logging level should be respected`() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val process = ProcessBuilder(
Expand Down Expand Up @@ -259,4 +261,111 @@ class CliIntegrationTest : AbstractCliIntegrationTest() {
)
)
}


@Test
fun `should accept json as input configuration`() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!")
PrintWriter(jsonPath).run {
write(jsonBuilder(dokkaOutputDir.path, basePluginJarFile.path, File(projectDir, "src").path, reportUndocumented = true))
close()
}

val process = ProcessBuilder(
"java", "-jar", cliJarFile.path, jsonPath
).redirectErrorStream(true).start()

val result = process.awaitProcessResult()
assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)")

val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""")
val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count()

assertTrue(
amountOfExtensionsLoaded > 10,
"Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)"
)

val undocumentedReportRegex = Regex("""Undocumented:""")
val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count()
assertTrue(
amountOfUndocumentedReports > 0,
"Expected at least one report of undocumented code (found $amountOfUndocumentedReports)"
)

assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
}

/**
* This test disables global `reportUndocumneted` property and set `reportUndocumented` via perPackageOptions to
* make sure that global settings apply to dokka context.
*/
@Test
fun `global settings should overwrite package options in configuration`() {
val dokkaOutputDir = File(projectDir, "output")
assertTrue(dokkaOutputDir.mkdirs())
val jsonPath = javaClass.getResource("/my-file.json")?.path ?: throw IllegalStateException("No JSON found!")
PrintWriter(jsonPath).run {
write(
jsonBuilder(
outputPath = dokkaOutputDir.path,
pluginsClasspath = basePluginJarFile.path,
projectPath = File(projectDir, "src").path,
globalSourceLinks = """
{
"localDirectory": "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin",
"remoteUrl": "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin",
"remoteLineSuffix": "#L"
}
""".trimIndent(),
globalExternalDocumentationLinks = """
{
"url": "https://docs.oracle.com/javase/8/docs/api/",
"packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list"
},
{
"url": "https://kotlinlang.org/api/latest/jvm/stdlib/",
"packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list"
}
""".trimIndent(),
globalPerPackageOptions = """
{
"matchingRegex": ".*",
"skipDeprecated": "true",
"reportUndocumented": "true",
"documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"]
}
""".trimIndent(),
reportUndocumented = false
),
)
close()
}

val process = ProcessBuilder(
"java", "-jar", cliJarFile.path, jsonPath
).redirectErrorStream(true).start()

val result = process.awaitProcessResult()
assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)")

val extensionLoadedRegex = Regex("""Extension: org\.jetbrains\.dokka\.base\.DokkaBase""")
val amountOfExtensionsLoaded = extensionLoadedRegex.findAll(result.output).count()

assertTrue(
amountOfExtensionsLoaded > 10,
"Expected more than 10 extensions being present (found $amountOfExtensionsLoaded)"
)

val undocumentedReportRegex = Regex("""Undocumented:""")
val amountOfUndocumentedReports = undocumentedReportRegex.findAll(result.output).count()
assertTrue(
amountOfUndocumentedReports > 0,
"Expected at least one report of undocumented code (found $amountOfUndocumentedReports)"
)

assertTrue(dokkaOutputDir.isDirectory, "Missing dokka output directory")
}
}
@@ -0,0 +1,52 @@
package org.jetbrains.dokka.it.cli

fun jsonBuilder(
outputPath: String,
pluginsClasspath: String,
projectPath: String,
globalSourceLinks: String = "",
globalExternalDocumentationLinks: String = "",
globalPerPackageOptions: String = "",
reportUndocumented: Boolean = false

): String {
return """{
"moduleName": "Dokka Example",
"moduleVersion": null,
"outputDir": "$outputPath",
"pluginsClasspath": ["$pluginsClasspath"],
"cacheRoot": null,
"offlineMode": false,
"sourceLinks": [$globalSourceLinks],
"externalDocumentationLinks": [$globalExternalDocumentationLinks],
"perPackageOptions": [$globalPerPackageOptions],
"sourceSets": [
{
"displayName": "jvm",
"sourceSetID": {
"scopeId": ":dokkaHtml",
"sourceSetName": "main"
},
"sourceRoots": [
"$projectPath"
],
"dependentSourceSets": [],
"samples": [],
"includes": [],
"includeNonPublic": false,
"reportUndocumented": $reportUndocumented,
"skipEmptyPackages": true,
"skipDeprecated": false,
"jdkVersion": 8,
"sourceLinks": [],
"perPackageOptions": [],
"externalDocumentationLinks": [],
"noStdlibLink": false,
"noJdkLink": false,
"suppressedFiles": [],
"analysisPlatform": "jvm"
}
]
}
"""
}
Empty file.
2 changes: 1 addition & 1 deletion plugins/base/frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions runners/cli/api/cli.api
Expand Up @@ -89,6 +89,7 @@ public final class org/jetbrains/dokka/GlobalArguments : org/jetbrains/dokka/Dok

public final class org/jetbrains/dokka/MainKt {
public static final fun defaultLinks (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Ljava/util/List;
public static final fun initializeConfiguration (Lorg/jetbrains/dokka/GlobalArguments;)Lorg/jetbrains/dokka/DokkaConfiguration;
public static final fun main ([Ljava/lang/String;)V
public static final fun parseLinks (Ljava/util/List;)Ljava/util/List;
}
Expand Down
1 change: 1 addition & 0 deletions runners/cli/build.gradle.kts
Expand Up @@ -10,6 +10,7 @@ dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-cli-jvm:0.3.3")
implementation(project(":core"))
implementation(kotlin("stdlib"))
testImplementation(kotlin("test-junit"))
}

tasks {
Expand Down
27 changes: 17 additions & 10 deletions runners/cli/src/main/kotlin/cli/main.kt
Expand Up @@ -2,10 +2,7 @@ package org.jetbrains.dokka

import kotlinx.cli.*
import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
import org.jetbrains.dokka.utilities.DokkaConsoleLogger
import org.jetbrains.dokka.utilities.DokkaLogger
import org.jetbrains.dokka.utilities.LoggingLevel
import org.jetbrains.dokka.utilities.cast
import org.jetbrains.dokka.utilities.*
import java.io.*
import java.net.MalformedURLException
import java.net.URL
Expand Down Expand Up @@ -80,6 +77,7 @@ class GlobalArguments(args: Array<String>) : DokkaConfiguration {

private val _includes by parser.option(
ArgTypeFile,
fullName = "includes",
description = "Markdown files that would be displayed in multi-module page separated by the semicolon `;`)"
).delimiter(";")

Expand Down Expand Up @@ -406,14 +404,23 @@ fun parseLinks(links: List<String>): List<ExternalDocumentationLink> {
}
}

fun initializeConfiguration(globalArguments: GlobalArguments): DokkaConfiguration = if (globalArguments.json != null) {
val jsonContent = Paths.get(checkNotNull(globalArguments.json)).toFile().readText()
val globals = GlobalDokkaConfiguration(jsonContent)
val dokkaConfigurationImpl = DokkaConfigurationImpl(jsonContent)

dokkaConfigurationImpl.apply(globals).apply {
sourceSets.forEach {
it.externalDocumentationLinks.cast<MutableSet<ExternalDocumentationLink>>().addAll(defaultLinks(it))
}
}
} else {
globalArguments
}

fun main(args: Array<String>) {
val globalArguments = GlobalArguments(args)
val configuration = if (globalArguments.json != null)
DokkaConfigurationImpl(
Paths.get(checkNotNull(globalArguments.json)).toFile().readText()
)
else
globalArguments
val configuration = initializeConfiguration(globalArguments)
DokkaGenerator(configuration, globalArguments.logger).generate()
}

30 changes: 30 additions & 0 deletions runners/cli/src/test/kotlin/cli/CliTest.kt
@@ -0,0 +1,30 @@
package org.jetbrains.dokka

import junit.framework.Assert.assertTrue
import org.junit.Test
import java.lang.IllegalStateException
import java.nio.file.Paths
import kotlin.test.assertEquals

class CliIntegrationTest {

@Test
fun `should apply global settings to all source sets`() {
val jsonPath = Paths.get(javaClass.getResource("/my-file.json")?.toURI() ?: throw IllegalStateException("No JSON found!")).toFile().toString()
val globalArguments = GlobalArguments(arrayOf(jsonPath))

val configuration = initializeConfiguration(globalArguments)

configuration.sourceSets.forEach {
assertTrue(it.perPackageOptions.isNotEmpty())
assertTrue(it.sourceLinks.isNotEmpty())
assertTrue(it.externalDocumentationLinks.isNotEmpty())

assertTrue(it.externalDocumentationLinks.any { it.url.toString() == "https://docs.oracle.com/javase/8/docs/api/" })
assertEquals(it.sourceLinks.single().localDirectory, "/home/Vadim.Mishenev/dokka/examples/cli/src/main/kotlin")
assertEquals(it.perPackageOptions.single().matchingRegex, "my-custom-regex")
}

}

}