aboutsummaryrefslogtreecommitdiff
path: root/dokka-runners/runner-cli
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-runners/runner-cli')
-rw-r--r--dokka-runners/runner-cli/api/runner-cli.api100
-rw-r--r--dokka-runners/runner-cli/build.gradle.kts26
-rw-r--r--dokka-runners/runner-cli/gradle.properties11
-rw-r--r--dokka-runners/runner-cli/settings.gradle.kts31
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/CliArgumentTypes.kt97
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/GlobalArguments.kt168
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/LinkMapper.kt41
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/PackageOptionsParser.kt38
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/SourceSetArgumentsParser.kt165
-rw-r--r--dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/main.kt32
-rw-r--r--dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt46
-rw-r--r--dokka-runners/runner-cli/src/test/resources/my-file-no-sourceset-options.json13
-rw-r--r--dokka-runners/runner-cli/src/test/resources/my-file.json51
13 files changed, 819 insertions, 0 deletions
diff --git a/dokka-runners/runner-cli/api/runner-cli.api b/dokka-runners/runner-cli/api/runner-cli.api
new file mode 100644
index 00000000..cfa173c5
--- /dev/null
+++ b/dokka-runners/runner-cli/api/runner-cli.api
@@ -0,0 +1,100 @@
+public final class org/jetbrains/dokka/ArgTypeArgument : kotlinx/cli/ArgType {
+ public fun <init> (Lkotlinx/cli/CLIEntity;)V
+ public final fun component1 ()Lkotlinx/cli/CLIEntity;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;
+ public final fun copy (Lkotlinx/cli/CLIEntity;)Lorg/jetbrains/dokka/ArgTypeArgument;
+ public static synthetic fun copy$default (Lorg/jetbrains/dokka/ArgTypeArgument;Lkotlinx/cli/CLIEntity;ILjava/lang/Object;)Lorg/jetbrains/dokka/ArgTypeArgument;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getDescription ()Ljava/lang/String;
+ public final fun getModuleName ()Lkotlinx/cli/CLIEntity;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypeFile : kotlinx/cli/ArgType {
+ public static final field INSTANCE Lorg/jetbrains/dokka/ArgTypeFile;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/io/File;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun getDescription ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypeHelpSourceSet : kotlinx/cli/ArgType {
+ public fun <init> (Lkotlinx/cli/CLIEntity;)V
+ public final fun component1 ()Lkotlinx/cli/CLIEntity;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public final fun copy (Lkotlinx/cli/CLIEntity;)Lorg/jetbrains/dokka/ArgTypeHelpSourceSet;
+ public static synthetic fun copy$default (Lorg/jetbrains/dokka/ArgTypeHelpSourceSet;Lkotlinx/cli/CLIEntity;ILjava/lang/Object;)Lorg/jetbrains/dokka/ArgTypeHelpSourceSet;
+ public fun equals (Ljava/lang/Object;)Z
+ public fun getDescription ()Ljava/lang/String;
+ public final fun getModuleName ()Lkotlinx/cli/CLIEntity;
+ public fun hashCode ()I
+ public fun toString ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypePlatform : kotlinx/cli/ArgType {
+ public static final field INSTANCE Lorg/jetbrains/dokka/ArgTypePlatform;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/Platform;
+ public fun getDescription ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypePlugin : kotlinx/cli/ArgType {
+ public static final field INSTANCE Lorg/jetbrains/dokka/ArgTypePlugin;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/DokkaConfiguration$PluginConfiguration;
+ public fun getDescription ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypeSourceLinkDefinition : kotlinx/cli/ArgType {
+ public static final field INSTANCE Lorg/jetbrains/dokka/ArgTypeSourceLinkDefinition;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/DokkaConfiguration$SourceLinkDefinition;
+ public fun getDescription ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/ArgTypeVisibility : kotlinx/cli/ArgType {
+ public static final field INSTANCE Lorg/jetbrains/dokka/ArgTypeVisibility;
+ public synthetic fun convert (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
+ public fun convert (Ljava/lang/String;Ljava/lang/String;)Lorg/jetbrains/dokka/DokkaConfiguration$Visibility;
+ public fun getDescription ()Ljava/lang/String;
+}
+
+public final class org/jetbrains/dokka/GlobalArguments : org/jetbrains/dokka/DokkaConfiguration {
+ public fun <init> ([Ljava/lang/String;)V
+ public fun getCacheRoot ()Ljava/io/File;
+ public fun getDelayTemplateSubstitution ()Z
+ public fun getFailOnWarning ()Z
+ public fun getFinalizeCoroutines ()Z
+ public final fun getGlobalLinks ()Ljava/util/List;
+ public final fun getGlobalPackageOptions ()Ljava/util/List;
+ public final fun getGlobalSrcLink ()Ljava/util/List;
+ public final fun getHelpSourceSet ()Ljava/lang/Object;
+ public fun getIncludes ()Ljava/util/Set;
+ public final fun getJson ()Ljava/lang/String;
+ public final fun getLogger ()Lorg/jetbrains/dokka/utilities/DokkaLogger;
+ public final fun getLoggingLevel ()Lorg/jetbrains/dokka/utilities/LoggingLevel;
+ public fun getModuleName ()Ljava/lang/String;
+ public fun getModuleVersion ()Ljava/lang/String;
+ public fun getModules ()Ljava/util/List;
+ public final fun getNoSuppressObviousFunctions ()Z
+ public fun getOfflineMode ()Z
+ public fun getOutputDir ()Ljava/io/File;
+ public final fun getParser ()Lkotlinx/cli/ArgParser;
+ public fun getPluginsClasspath ()Ljava/util/List;
+ public fun getPluginsConfiguration ()Ljava/util/List;
+ public fun getSourceSets ()Ljava/util/List;
+ public fun getSuppressInheritedMembers ()Z
+ public fun getSuppressObviousFunctions ()Z
+}
+
+public final class org/jetbrains/dokka/LinkMapperKt {
+ public static final fun defaultLinks (Lorg/jetbrains/dokka/DokkaConfiguration$DokkaSourceSet;)Ljava/util/List;
+ public static final fun parseLinks (Ljava/util/List;)Ljava/util/List;
+}
+
+public final class org/jetbrains/dokka/MainKt {
+ public static final fun initializeConfiguration (Lorg/jetbrains/dokka/GlobalArguments;)Lorg/jetbrains/dokka/DokkaConfiguration;
+ public static final fun main ([Ljava/lang/String;)V
+}
+
diff --git a/dokka-runners/runner-cli/build.gradle.kts b/dokka-runners/runner-cli/build.gradle.kts
new file mode 100644
index 00000000..c078e22c
--- /dev/null
+++ b/dokka-runners/runner-cli/build.gradle.kts
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+import dokkabuild.overridePublicationArtifactId
+
+plugins {
+ id("dokkabuild.kotlin-jvm")
+ id("dokkabuild.publish-shadow")
+ alias(libs.plugins.kotlinx.binaryCompatibilityValidator)
+}
+
+overridePublicationArtifactId("dokka-cli")
+
+dependencies {
+ implementation("org.jetbrains.dokka:dokka-core")
+ implementation(libs.kotlinx.cli)
+
+ testImplementation(kotlin("test"))
+}
+
+tasks.shadowJar {
+ manifest {
+ attributes("Main-Class" to "org.jetbrains.dokka.MainKt")
+ }
+}
diff --git a/dokka-runners/runner-cli/gradle.properties b/dokka-runners/runner-cli/gradle.properties
new file mode 100644
index 00000000..08f2ce2a
--- /dev/null
+++ b/dokka-runners/runner-cli/gradle.properties
@@ -0,0 +1,11 @@
+#
+# Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+#
+
+# Project Settings
+group=org.jetbrains.dokka
+version=1.9.20-SNAPSHOT
+
+org.jetbrains.dokka.javaToolchain.mainCompiler=8
+org.jetbrains.dokka.javaToolchain.testLauncher=8
+org.jetbrains.dokka.kotlinLanguageLevel=1.4
diff --git a/dokka-runners/runner-cli/settings.gradle.kts b/dokka-runners/runner-cli/settings.gradle.kts
new file mode 100644
index 00000000..37b36fda
--- /dev/null
+++ b/dokka-runners/runner-cli/settings.gradle.kts
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+@file:Suppress("UnstableApiUsage")
+
+rootProject.name = "runner-cli"
+
+pluginManagement {
+ includeBuild("../../build-logic")
+
+ repositories {
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+dependencyResolutionManagement {
+ repositories {
+ mavenCentral()
+ google()
+ }
+
+ versionCatalogs {
+ create("libs") {
+ from(files("../../gradle/libs.versions.toml"))
+ }
+ }
+}
+
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/CliArgumentTypes.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/CliArgumentTypes.kt
new file mode 100644
index 00000000..1c6b0ba4
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/CliArgumentTypes.kt
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import kotlinx.cli.ArgParser
+import kotlinx.cli.ArgType
+import kotlinx.cli.CLIEntity
+import java.io.File
+import java.nio.file.Paths
+
+
+public object ArgTypeFile : ArgType<File>(true) {
+ override fun convert(value: kotlin.String, name: kotlin.String): File = Paths.get(value).toRealPath().toFile()
+ override val description: kotlin.String
+ get() = "{ String that represents a directory / file path }"
+}
+
+public object ArgTypePlatform : ArgType<Platform>(true) {
+ override fun convert(value: kotlin.String, name: kotlin.String): Platform = Platform.fromString(value)
+ override val description: kotlin.String
+ get() = "{ String that represents a Kotlin platform. Possible values: jvm/js/native/common/android }"
+}
+
+public object ArgTypeVisibility : ArgType<DokkaConfiguration.Visibility>(true) {
+ override fun convert(value: kotlin.String, name: kotlin.String): DokkaConfiguration.Visibility {
+ return DokkaConfiguration.Visibility.fromString(value)
+ }
+
+ override val description: kotlin.String
+ get() = "{ String that represents a visibility modifier. Possible values: ${getPossibleVisibilityValues()}"
+
+ private fun getPossibleVisibilityValues(): kotlin.String =
+ DokkaConfiguration.Visibility.values().joinToString(separator = ", ")
+}
+
+public object ArgTypePlugin : ArgType<DokkaConfiguration.PluginConfiguration>(true) {
+ override fun convert(
+ value: kotlin.String,
+ name: kotlin.String
+ ): DokkaConfiguration.PluginConfiguration {
+ return value.split("=").let {
+ PluginConfigurationImpl(
+ fqPluginName = it[0],
+ serializationFormat = DokkaConfiguration.SerializationFormat.JSON,
+ values = it[1]
+ )
+ }
+ }
+
+ override val description: kotlin.String
+ get() = "{ String that represents plugin configuration. " +
+ "Format is {fullyQualifiedPluginName}={jsonConfiguration}. " +
+ "Quotation marks (`\"`) inside json must be escaped. }"
+}
+
+public object ArgTypeSourceLinkDefinition : ArgType<DokkaConfiguration.SourceLinkDefinition>(true) {
+ override fun convert(value: kotlin.String, name: kotlin.String): DokkaConfiguration.SourceLinkDefinition {
+ return if (value.isNotEmpty() && value.contains("="))
+ SourceLinkDefinitionImpl.parseSourceLinkDefinition(value)
+ else {
+ throw IllegalArgumentException(
+ "Warning: Invalid -srcLink syntax. " +
+ "Expected: <path>=<url>[#lineSuffix]. No source links will be generated."
+ )
+ }
+ }
+
+ override val description: kotlin.String
+ get() = "{ String that represent source links. Format: {srcPath}={remotePath#lineSuffix} }"
+}
+
+public data class ArgTypeArgument(val moduleName: CLIEntity<kotlin.String>) :
+ ArgType<DokkaConfiguration.DokkaSourceSet>(true) {
+ override fun convert(value: kotlin.String, name: kotlin.String): DokkaConfiguration.DokkaSourceSet =
+ (if (moduleName.valueOrigin != ArgParser.ValueOrigin.UNSET && moduleName.valueOrigin != ArgParser.ValueOrigin.UNDEFINED) {
+ moduleName.value
+ } else {
+ DokkaDefaults.moduleName
+ }).let { moduleNameOrDefault ->
+ parseSourceSet(moduleNameOrDefault, value.split(" ").filter { it.isNotBlank() }.toTypedArray())
+ }
+
+ override val description: kotlin.String
+ get() = ""
+}
+
+// Workaround for printing nested parsers help
+public data class ArgTypeHelpSourceSet(val moduleName: CLIEntity<kotlin.String>) : ArgType<Any>(false) {
+ override fun convert(value: kotlin.String, name: kotlin.String): Any = Any().also {
+ parseSourceSet(moduleName.value, arrayOf("-h"))
+ }
+
+ override val description: kotlin.String
+ get() = ""
+}
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/GlobalArguments.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/GlobalArguments.kt
new file mode 100644
index 00000000..5c95f63f
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/GlobalArguments.kt
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import kotlinx.cli.*
+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 java.io.File
+
+public class GlobalArguments(args: Array<String>) : DokkaConfiguration {
+
+ public val parser: ArgParser = ArgParser("dokka-cli", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
+
+ public val json: String? by parser.argument(ArgType.String, description = "JSON configuration file path").optional()
+
+ private val _moduleName = parser.option(
+ ArgType.String,
+ description = "Name of the project/module",
+ fullName = "moduleName"
+ ).default(DokkaDefaults.moduleName)
+
+ override val moduleName: String by _moduleName
+
+ override val moduleVersion: String? by parser.option(
+ ArgType.String,
+ description = "Documented version",
+ fullName = "moduleVersion"
+ )
+
+ override val outputDir: File by parser.option(ArgTypeFile, description = "Output directory path, ./dokka by default")
+ .default(DokkaDefaults.outputDir)
+
+ override val cacheRoot: File? = null
+
+ override val sourceSets: List<DokkaConfiguration.DokkaSourceSet> by parser.option(
+ ArgTypeArgument(_moduleName),
+ description = "Configuration for a Dokka source set. Contains nested configuration.",
+ fullName = "sourceSet"
+ ).multiple()
+
+ override val pluginsConfiguration: List<DokkaConfiguration.PluginConfiguration> by parser.option(
+ ArgTypePlugin,
+ description = "Configuration for Dokka plugins. Accepts multiple values separated by `^^`."
+ ).delimiter("^^")
+
+ override val pluginsClasspath: List<File> by parser.option(
+ ArgTypeFile,
+ fullName = "pluginsClasspath",
+ description = "List of jars with Dokka plugins and their dependencies. Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ override val offlineMode: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Whether to resolve remote files/links over network"
+ ).default(DokkaDefaults.offlineMode)
+
+ override val failOnWarning: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Whether to fail documentation generation if Dokka has emitted a warning or an error"
+ ).default(DokkaDefaults.failOnWarning)
+
+ override val delayTemplateSubstitution: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Delay substitution of some elements. Used in incremental builds of multimodule projects"
+ ).default(DokkaDefaults.delayTemplateSubstitution)
+
+ public val noSuppressObviousFunctions: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Whether to suppress obvious functions such as inherited from `kotlin.Any` and `java.lang.Object`"
+ ).default(!DokkaDefaults.suppressObviousFunctions)
+
+ override val suppressObviousFunctions: Boolean by lazy { !noSuppressObviousFunctions }
+
+ private val _includes by parser.option(
+ ArgTypeFile,
+ fullName = "includes",
+ description = "Markdown files that contain module and package documentation. " +
+ "Accepts multiple values separated by semicolons"
+ ).delimiter(";")
+
+ override val includes: Set<File> by lazy { _includes.toSet() }
+
+ override val suppressInheritedMembers: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Whether to suppress inherited members that aren't explicitly overridden in a given class"
+ ).default(DokkaDefaults.suppressInheritedMembers)
+
+ override val finalizeCoroutines: Boolean = true
+
+ public val globalPackageOptions: List<String> by parser.option(
+ ArgType.String,
+ description = "Global list of package configurations in format " +
+ "\"matchingRegexp,-deprecated,-privateApi,+warnUndocumented,+suppress;...\". " +
+ "Accepts multiple values separated by semicolons. "
+ ).delimiter(";")
+
+ public val globalLinks: List<String> by parser.option(
+ ArgType.String,
+ description = "Global external documentation links in format {url}^{packageListUrl}. " +
+ "Accepts multiple values separated by `^^`"
+ ).delimiter("^^")
+
+ public val globalSrcLink: List<String> by parser.option(
+ ArgType.String,
+ description = "Global mapping between a source directory and a Web service for browsing the code. " +
+ "Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ public val helpSourceSet: Any? by parser.option(
+ ArgTypeHelpSourceSet(_moduleName),
+ description = "Prints help for nested -sourceSet configuration"
+ )
+
+ public val loggingLevel: LoggingLevel by parser.option(
+ ArgType.Choice(toVariant = {
+ when (it.toUpperCase().trim()) {
+ "DEBUG", "" -> LoggingLevel.DEBUG
+ "PROGRESS" -> LoggingLevel.PROGRESS
+ "INFO" -> LoggingLevel.INFO
+ "WARN" -> LoggingLevel.WARN
+ "ERROR" -> LoggingLevel.ERROR
+ else -> {
+ println("""Failed to deserialize logging level, got $it expected one of
+ |"DEBUG", "PROGRESS", "INFO", "WARN", "ERROR", falling back to PROGRESS""".trimMargin())
+ LoggingLevel.PROGRESS
+ }
+ }
+ }, toString = { it.toString() }
+ )).default(LoggingLevel.PROGRESS)
+
+ override val modules: List<DokkaConfiguration.DokkaModuleDescription> = emptyList()
+
+ public val logger: DokkaLogger by lazy {
+ DokkaConsoleLogger(loggingLevel)
+ }
+
+ init {
+ parser.parse(args)
+
+ sourceSets.forEach {
+ it.perPackageOptions.cast<MutableList<DokkaConfiguration.PackageOptions>>()
+ .addAll(parsePerPackageOptions(globalPackageOptions))
+ }
+
+ sourceSets.forEach {
+ it.externalDocumentationLinks.cast<MutableSet<DokkaConfiguration.ExternalDocumentationLink>>().addAll(parseLinks(globalLinks))
+ }
+
+ globalSrcLink.forEach {
+ if (it.isNotEmpty() && it.contains("="))
+ sourceSets.all { sourceSet ->
+ sourceSet.sourceLinks.cast<MutableSet<SourceLinkDefinitionImpl>>()
+ .add(SourceLinkDefinitionImpl.parseSourceLinkDefinition(it))
+ }
+ else {
+ logger.warn("Invalid -srcLink syntax. Expected: <path>=<url>[#lineSuffix]. No source links will be generated.")
+ }
+ }
+
+ sourceSets.forEach {
+ it.externalDocumentationLinks.cast<MutableSet<DokkaConfiguration.ExternalDocumentationLink>>().addAll(defaultLinks(it))
+ }
+ }
+}
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/LinkMapper.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/LinkMapper.kt
new file mode 100644
index 00000000..dbfa8db1
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/LinkMapper.kt
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import java.io.File
+import java.net.MalformedURLException
+import java.net.URL
+
+@OptIn(ExperimentalStdlibApi::class) // for buildList
+public fun defaultLinks(config: DokkaConfiguration.DokkaSourceSet): MutableList<DokkaConfiguration.ExternalDocumentationLink> =
+ buildList<DokkaConfiguration.ExternalDocumentationLink> {
+ if (!config.noJdkLink) {
+ add(DokkaConfiguration.ExternalDocumentationLink.jdk(config.jdkVersion))
+ }
+
+ if (!config.noStdlibLink) {
+ add(DokkaConfiguration.ExternalDocumentationLink.kotlinStdlib())
+ }
+ }.toMutableList()
+
+
+public fun parseLinks(links: List<String>): List<DokkaConfiguration.ExternalDocumentationLink> {
+ val (parsedLinks, parsedOfflineLinks) = links
+ .map { it.split("^").map { it.trim() }.filter { it.isNotBlank() } }
+ .filter { it.isNotEmpty() }
+ .partition { it.size == 1 }
+
+ return parsedLinks.map { (root) -> ExternalDocumentationLink(root) } +
+ parsedOfflineLinks.map { (root, packageList) ->
+ val rootUrl = URL(root)
+ val packageListUrl =
+ try {
+ URL(packageList)
+ } catch (ex: MalformedURLException) {
+ File(packageList).toURI().toURL()
+ }
+ ExternalDocumentationLink(rootUrl, packageListUrl)
+ }
+}
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/PackageOptionsParser.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/PackageOptionsParser.kt
new file mode 100644
index 00000000..377e32e6
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/PackageOptionsParser.kt
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+internal fun parsePerPackageOptions(args: List<String>): List<DokkaConfiguration.PackageOptions> = args.map { it.split(",") }.map {
+ val matchingRegex = it.first()
+
+ val options = it.subList(1, it.size)
+
+ val deprecated = options.find { it.endsWith("skipDeprecated") }?.startsWith("+")
+ ?: DokkaDefaults.skipDeprecated
+
+ val reportUndocumented = options.find { it.endsWith("reportUndocumented") }?.startsWith("+")
+ ?: DokkaDefaults.reportUndocumented
+
+ val privateApi = options.find { it.endsWith("includeNonPublic") }?.startsWith("+")
+ ?: DokkaDefaults.includeNonPublic
+
+ val suppress = options.find { it.endsWith("suppress") }?.startsWith("+")
+ ?: DokkaDefaults.suppress
+
+ val documentedVisibilities = options
+ .filter { it.matches(Regex("\\+visibility:.+")) } // matches '+visibility:' with at least one symbol after the semicolon
+ .map { DokkaConfiguration.Visibility.fromString(it.split(":")[1]) }
+ .toSet()
+ .ifEmpty { DokkaDefaults.documentedVisibilities }
+
+ PackageOptionsImpl(
+ matchingRegex,
+ includeNonPublic = privateApi,
+ documentedVisibilities = documentedVisibilities,
+ reportUndocumented = reportUndocumented,
+ skipDeprecated = !deprecated,
+ suppress = suppress
+ )
+}
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/SourceSetArgumentsParser.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/SourceSetArgumentsParser.kt
new file mode 100644
index 00000000..a1c44a13
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/SourceSetArgumentsParser.kt
@@ -0,0 +1,165 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import kotlinx.cli.ArgParser
+import kotlinx.cli.ArgType
+import kotlinx.cli.default
+import kotlinx.cli.delimiter
+
+internal fun parseSourceSet(moduleName: String, args: Array<String>): DokkaConfiguration.DokkaSourceSet {
+
+ if (moduleName.contains(',')) {
+ // To figure out why this is needed and if it is still relevant, see the comment here:
+ // https://github.com/Kotlin/dokka/issues/3011#issuecomment-1568620493
+ throw IllegalArgumentException("Module name cannot contain commas as it is used internally as a delimiter.")
+ }
+
+ val parser = ArgParser("sourceSet", prefixStyle = ArgParser.OptionPrefixStyle.JVM)
+
+ val sourceSetName by parser.option(
+ ArgType.String,
+ description = "Name of the source set"
+ ).default("main")
+
+ val displayName by parser.option(
+ ArgType.String,
+ description = "Display name of the source set, used both internally and externally"
+ ).default(DokkaDefaults.sourceSetDisplayName)
+
+ val classpath by parser.option(
+ ArgTypeFile,
+ description = "Classpath for analysis and interactive samples. Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ val sourceRoots by parser.option(
+ ArgTypeFile,
+ description = "Source code roots to be analyzed and documented. Accepts multiple paths separated by semicolons",
+ fullName = "src"
+ ).delimiter(";")
+
+ val dependentSourceSets by parser.option(
+ ArgType.String,
+ description = "Names of dependent source sets in format \"moduleName/sourceSetName\". " +
+ "Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ val samples by parser.option(
+ ArgTypeFile,
+ description = "List of directories or files that contain sample functions. " +
+ "Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ val includes by parser.option(
+ ArgTypeFile,
+ description = "Markdown files that contain module and package documentation. " +
+ "Accepts multiple paths separated by semicolons"
+ ).delimiter(";")
+
+ val includeNonPublic: Boolean by parser.option(
+ ArgType.Boolean,
+ description = "Deprecated, use documentedVisibilities")
+ .default(DokkaDefaults.includeNonPublic)
+
+ val documentedVisibilities by parser.option(
+ ArgTypeVisibility,
+ description = "Visibilities to be documented. Accepts multiple values separated by semicolons"
+ ).delimiter(";")
+
+ val reportUndocumented by parser.option(ArgType.Boolean, description = "Whether to report undocumented declarations")
+ .default(DokkaDefaults.reportUndocumented)
+
+ val noSkipEmptyPackages by parser.option(
+ ArgType.Boolean,
+ description = "Whether to create pages for empty packages"
+ ).default(!DokkaDefaults.skipEmptyPackages)
+
+ val skipEmptyPackages by lazy { !noSkipEmptyPackages }
+
+ val skipDeprecated by parser.option(ArgType.Boolean, description = "Whether to skip deprecated declarations")
+ .default(DokkaDefaults.skipDeprecated)
+
+ val jdkVersion by parser.option(
+ ArgType.Int,
+ description = "Version of JDK to use for linking to JDK Javadocs"
+ ).default(DokkaDefaults.jdkVersion)
+
+ val languageVersion by parser.option(
+ ArgType.String,
+ description = "Language version used for setting up analysis and samples"
+ )
+
+ val apiVersion by parser.option(
+ ArgType.String,
+ description = "Kotlin API version used for setting up analysis and samples"
+ )
+
+ val noStdlibLink by parser.option(ArgType.Boolean, description = "Whether to generate links to Standard library")
+ .default(DokkaDefaults.noStdlibLink)
+
+ val noJdkLink by parser.option(ArgType.Boolean, description = "Whether to generate links to JDK Javadocs")
+ .default(DokkaDefaults.noJdkLink)
+
+ val suppressedFiles by parser.option(
+ ArgTypeFile,
+ description = "Paths to files to be suppressed. Accepts multiple paths separated by semicolons."
+ ).delimiter(";")
+
+ val analysisPlatform: Platform by parser.option(
+ ArgTypePlatform,
+ description = "Platform used for setting up analysis"
+ ).default(DokkaDefaults.analysisPlatform)
+
+ val perPackageOptions by parser.option(
+ ArgType.String,
+ description = "List of package source set configuration in format " +
+ "\"matchingRegexp,-deprecated,-privateApi,+warnUndocumented,+suppress;...\". " +
+ "Accepts multiple values separated by semicolons. "
+ ).delimiter(";")
+
+ val externalDocumentationLinks by parser.option(
+ ArgType.String,
+ description = "External documentation links in format {url}^{packageListUrl}. " +
+ "Accepts multiple values separated by `^^`"
+ ).delimiter("^^")
+
+ val sourceLinks by parser.option(
+ ArgTypeSourceLinkDefinition,
+ description = "Mapping between a source directory and a Web service for browsing the code. " +
+ "Accepts multiple paths separated by semicolons",
+ fullName = "srcLink"
+ ).delimiter(";")
+
+ parser.parse(args)
+
+ return object : DokkaConfiguration.DokkaSourceSet {
+ override val displayName = displayName
+ override val sourceSetID = DokkaSourceSetID(moduleName, sourceSetName)
+ override val classpath = classpath.toMutableList()
+ override val sourceRoots = sourceRoots.toMutableSet()
+ override val dependentSourceSets = dependentSourceSets
+ .map { dependentSourceSetName -> dependentSourceSetName.split('/').let { DokkaSourceSetID(it[0], it[1]) } }
+ .toMutableSet()
+ override val samples = samples.toMutableSet()
+ override val includes = includes.toMutableSet()
+ @Deprecated("Use [documentedVisibilities] property for a more flexible control over documented visibilities")
+ override val includeNonPublic = includeNonPublic
+ override val reportUndocumented = reportUndocumented
+ override val skipEmptyPackages = skipEmptyPackages
+ override val skipDeprecated = skipDeprecated
+ override val jdkVersion = jdkVersion
+ override val sourceLinks = sourceLinks.toMutableSet()
+ override val analysisPlatform = analysisPlatform
+ override val perPackageOptions = parsePerPackageOptions(perPackageOptions).toMutableList()
+ override val externalDocumentationLinks = parseLinks(externalDocumentationLinks).toMutableSet()
+ override val languageVersion = languageVersion
+ override val apiVersion = apiVersion
+ override val noStdlibLink = noStdlibLink
+ override val noJdkLink = noJdkLink
+ override val suppressedFiles = suppressedFiles.toMutableSet()
+ override val documentedVisibilities: Set<DokkaConfiguration.Visibility> = documentedVisibilities.toSet()
+ .ifEmpty { DokkaDefaults.documentedVisibilities }
+ }
+}
diff --git a/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/main.kt b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/main.kt
new file mode 100644
index 00000000..e1949a93
--- /dev/null
+++ b/dokka-runners/runner-cli/src/main/kotlin/org/jetbrains/dokka/main.kt
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import org.jetbrains.dokka.DokkaConfiguration.ExternalDocumentationLink
+import org.jetbrains.dokka.utilities.*
+import java.nio.file.Paths
+
+public fun main(args: Array<String>) {
+ val globalArguments = GlobalArguments(args)
+ val configuration = initializeConfiguration(globalArguments)
+ DokkaGenerator(configuration, globalArguments.logger).generate()
+}
+
+public fun initializeConfiguration(globalArguments: GlobalArguments): DokkaConfiguration {
+ return 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
+ }
+}
+
diff --git a/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt b/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt
new file mode 100644
index 00000000..48441e4c
--- /dev/null
+++ b/dokka-runners/runner-cli/src/test/kotlin/org/jetbrains/dokka/CliTest.kt
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package org.jetbrains.dokka
+
+import java.nio.file.Paths
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+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")
+ }
+
+ }
+
+ @Test
+ fun `should not fail when no sourceset options are specified`() {
+ val jsonPath = Paths.get(javaClass.getResource("/my-file-no-sourceset-options.json")?.toURI() ?: throw IllegalStateException("No JSON found!")).toFile().toString()
+ val globalArguments = GlobalArguments(arrayOf(jsonPath))
+
+ val configuration = initializeConfiguration(globalArguments)
+
+ configuration.sourceSets.forEach {
+ assertTrue(it.perPackageOptions.isEmpty())
+ assertTrue(it.sourceLinks.isEmpty())
+ assertTrue(it.externalDocumentationLinks.size == 2) // there are default values, java and kotlin stdlibs
+ }
+ }
+}
diff --git a/dokka-runners/runner-cli/src/test/resources/my-file-no-sourceset-options.json b/dokka-runners/runner-cli/src/test/resources/my-file-no-sourceset-options.json
new file mode 100644
index 00000000..3a8643d1
--- /dev/null
+++ b/dokka-runners/runner-cli/src/test/resources/my-file-no-sourceset-options.json
@@ -0,0 +1,13 @@
+{
+ "outputDir": "build/docs",
+ "sourceSets": [
+ {
+ "moduleDisplayName": "Sample",
+ "sourceSetID": {
+ "scopeId": "sample",
+ "sourceSetName": "main"
+ },
+ "sourceRoots": ["sample"]
+ }
+ ]
+}
diff --git a/dokka-runners/runner-cli/src/test/resources/my-file.json b/dokka-runners/runner-cli/src/test/resources/my-file.json
new file mode 100644
index 00000000..49dda814
--- /dev/null
+++ b/dokka-runners/runner-cli/src/test/resources/my-file.json
@@ -0,0 +1,51 @@
+{
+ "moduleName": "Dokka Example",
+ "moduleVersion": null,
+ "outputDir": "$outputPath",
+ "pluginsClasspath": ["$pluginsClasspath"],
+ "cacheRoot": null,
+ "offlineMode": false,
+ "sourceLinks": [{
+ "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"
+ }],
+ "externalDocumentationLinks": [{
+ "url": "https://docs.oracle.com/javase/8/docs/api/",
+ "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list"
+ }],
+ "perPackageOptions": [{
+ "matchingRegex": "my-custom-regex",
+ "skipDeprecated": "true",
+ "reportUndocumented": "true",
+ "includeNonPublic": "true",
+ "documentedVisibilities": ["PUBLIC", "PRIVATE", "PROTECTED", "INTERNAL", "PACKAGE"]
+ }],
+ "sourceSets": [
+ {
+ "displayName": "jvm",
+ "sourceSetID": {
+ "scopeId": ":dokkaHtml",
+ "sourceSetName": "main"
+ },
+ "sourceRoots": [
+ "$projectPath"
+ ],
+ "dependentSourceSets": [],
+ "samples": [],
+ "includes": [],
+ "includeNonPublic": false,
+ "reportUndocumented": false,
+ "skipEmptyPackages": true,
+ "skipDeprecated": false,
+ "jdkVersion": 8,
+ "sourceLinks": [],
+ "perPackageOptions": [],
+ "externalDocumentationLinks": [],
+ "noStdlibLink": false,
+ "noJdkLink": false,
+ "suppressedFiles": [],
+ "analysisPlatform": "jvm"
+ }
+ ]
+}