diff options
Diffstat (limited to 'dokka-runners/runner-cli')
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" + } + ] +} |