diff options
Diffstat (limited to 'dokka-runners')
365 files changed, 15465 insertions, 0 deletions
diff --git a/dokka-runners/dokkatoo/.gitattributes b/dokka-runners/dokkatoo/.gitattributes new file mode 100644 index 00000000..2ba525d5 --- /dev/null +++ b/dokka-runners/dokkatoo/.gitattributes @@ -0,0 +1,51 @@ +# https://help.github.com/articles/dealing-with-line-endings/ +# https://github.com/alexkaratarakis/gitattributes + +* text=auto + +# The above will handle all files NOT found below + +*.json text +*.toml text +*.xml text +*.yaml text +*.yml text +.editorconfig text +.env text + +# Documentation +*.md text diff=markdown +*.txt text +LICENSE text + +# JVM +*.java text diff=java +*.kt text diff=kotlin +*.kts text diff=kotlin +*.properties text +*.jar binary + + +# Linux start script should use lf +gradlew text eol=lf +*.bash text eol=lf +*.sh text eol=lf + +# These are Windows script files and should use crlf +*.bat text eol=crlf +*.cmd text eol=crlf + +# SVG treated as an asset (binary) by default. +*.svg text + +# Exclude external libs from GitHub language stats https://github.com/github/linguist/blob/v7.24.1/docs/overrides.md +examples/** linguist-documentation +examples/*/dokka linguist-vendored +modules/dokkatoo-plugin-integration-tests/projects/**dokka/ linguist-vendored +modules/dokkatoo-plugin-integration-tests/projects/**dokkatoo/ linguist-documentation + +# Exclude files from exporting + +.gitattributes export-ignore +.gitignore export-ignore +.gitkeep export-ignore diff --git a/dokka-runners/dokkatoo/.gitignore b/dokka-runners/dokkatoo/.gitignore new file mode 100644 index 00000000..e733d08a --- /dev/null +++ b/dokka-runners/dokkatoo/.gitignore @@ -0,0 +1,71 @@ +### Gradle ### +.gradle +build/ + +!gradle/wrapper/gradle-wrapper.jar +!gradle/wrapper/gradle-wrapper.properties + + +### Kotlin/JVM ### +*.class +*.log + +hs_err_pid* +replay_pid* +*.hprof + +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + + +### IntelliJ ### +.idea/** +!.idea/codeStyles/ +!.idea/codeStyles/** + + +### Eclipse ### +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +.settings/ +.loadpath +.recommenders +.classpath + +.apt_generated/ +.apt_generated_test/ +.project + + +### Linux ### +*~ +.fuse_hidden* +.Trash-* +.nfs* + + +### Windows ### +[Dd]esktop.ini +$RECYCLE.BIN/ +*.lnk + + +### macOS ### +.DS_Store +._* + +# Icon must end with two \r +Icon + + +########################### diff --git a/dokka-runners/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..67184998 --- /dev/null +++ b/dokka-runners/dokkatoo/build.gradle.kts @@ -0,0 +1,47 @@ +import buildsrc.utils.excludeGeneratedGradleDsl +import buildsrc.utils.initIdeProjectLogo + +plugins { + buildsrc.conventions.base + idea +} + +group = "org.jetbrains.dokka.dokkatoo" +version = "2.1.0-SNAPSHOT" + + +idea { + module { + excludeGeneratedGradleDsl(layout) + + excludeDirs.apply { + // exclude .gradle, IDE dirs from nested projects (e.g. example & template projects) + // so IntelliJ project-wide search isn't cluttered with irrelevant files + val excludedDirs = setOf( + ".idea", + ".gradle", + "build", + "gradle/wrapper", + "ANDROID_SDK", + ) + addAll( + projectDir.walk().filter { file -> + excludedDirs.any { + file.invariantSeparatorsPath.endsWith(it) + } + } + ) + } + } +} + +initIdeProjectLogo("modules/docs/images/logo-icon.svg") + +val dokkatooVersion by tasks.registering { + description = "prints the Dokkatoo project version (used during release to verify the version)" + group = "help" + val version = providers.provider { project.version } + doLast { + logger.quiet("${version.orNull}") + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/build.gradle.kts b/dokka-runners/dokkatoo/buildSrc/build.gradle.kts new file mode 100644 index 00000000..832a98b9 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/build.gradle.kts @@ -0,0 +1,19 @@ +import org.gradle.kotlin.dsl.support.expectedKotlinDslPluginsVersion + +plugins { + `kotlin-dsl` +} + +dependencies { + implementation("org.gradle.kotlin:gradle-kotlin-dsl-plugins:$expectedKotlinDslPluginsVersion") + implementation(libs.gradlePlugin.bcvMu) + implementation(libs.gradlePlugin.dokkatoo) + implementation(libs.gradlePlugin.gradlePublishPlugin) + implementation("org.jetbrains.kotlin:kotlin-serialization:$embeddedKotlinVersion") +} + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/settings.gradle.kts b/dokka-runners/dokkatoo/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..855fe5d9 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/settings.gradle.kts @@ -0,0 +1,25 @@ +rootProject.name = "buildSrc" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + + repositories { + mavenCentral() + gradlePluginPortal() + } + + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts new file mode 100644 index 00000000..ed22d799 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts @@ -0,0 +1,78 @@ +package buildsrc.conventions + +import org.jetbrains.kotlin.util.suffixIfNot + + +/** + * Utilities for preparing Android projects + */ + +plugins { + base + id("buildsrc.conventions.base") +} + + +val androidSdkDirPath: Provider<String> = providers + // first try getting the SDK installed on via GitHub step setup-android + .environmentVariable("ANDROID_SDK_ROOT").map(::File) + // else get the project-local SDK + .orElse(layout.projectDirectory.file("projects/ANDROID_SDK").asFile) + .map { it.invariantSeparatorsPath } + + +val createAndroidLocalPropertiesFile by tasks.registering { + + val localPropertiesFile = temporaryDir.resolve("local.properties") + outputs.file(localPropertiesFile).withPropertyName("localPropertiesFile") + + val androidSdkDirPath = androidSdkDirPath + inputs.property("androidSdkDirPath", androidSdkDirPath) + + doLast { + localPropertiesFile.apply { + parentFile.mkdirs() + createNewFile() + writeText( + """ + |# DO NOT EDIT - Generated by $path + | + |sdk.dir=${androidSdkDirPath.get()} + | + """.trimMargin() + ) + } + } +} + + +val updateAndroidLocalProperties by tasks.registering { + + // find all local.properties files + val localPropertiesFiles = layout.projectDirectory.dir("projects") + .asFileTree + .matching { include("**/local.properties") } + .files + + outputs.files(localPropertiesFiles).withPropertyName("localPropertiesFiles") + + val androidSdkDirPath = androidSdkDirPath + inputs.property("androidSdkDirPath", androidSdkDirPath) + + doLast { + localPropertiesFiles + .filter { it.exists() } + .forEach { file -> + file.writeText( + file.useLines { lines -> + lines.joinToString("\n") { line -> + when { + line.startsWith("sdk.dir=") -> "sdk.dir=${androidSdkDirPath.get()}" + else -> line + } + }.suffixIfNot("\n") + } + ) + } + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts new file mode 100644 index 00000000..60bfa2fe --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts @@ -0,0 +1,155 @@ +package buildsrc.conventions + +import java.time.Duration +import org.gradle.api.tasks.testing.logging.TestLogEvent + +/** + * A convention plugin that sets up common config and sensible defaults for all subprojects. + */ + +plugins { + base +} + +if (project != rootProject) { + project.version = rootProject.version + project.group = rootProject.group +} + +tasks.withType<AbstractArchiveTask>().configureEach { + // https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives + isPreserveFileTimestamps = false + isReproducibleFileOrder = true +} + +tasks.withType<AbstractTestTask>().configureEach { + timeout.set(Duration.ofMinutes(60)) + + testLogging { + showCauses = true + showExceptions = true + showStackTraces = true + showStandardStreams = true + events( + TestLogEvent.PASSED, + TestLogEvent.FAILED, + TestLogEvent.SKIPPED, + TestLogEvent.STARTED, + TestLogEvent.STANDARD_ERROR, + TestLogEvent.STANDARD_OUT, + ) + } +} + +tasks.withType<AbstractCopyTask>().configureEach { + includeEmptyDirs = false +} + +val updateTestReportCss by tasks.registering { + description = "Hack so the Gradle test reports have dark mode" + // the CSS is based on https://github.com/gradle/gradle/pull/12177 + + mustRunAfter(tasks.withType<Test>()) + mustRunAfter(tasks.withType<TestReport>()) + + val cssFiles = layout.buildDirectory.asFileTree.matching { + include("reports/**/css/base-style.css") + include("reports/**/css/style.css") + } + + outputs.files(cssFiles.files) + + doLast { + cssFiles.forEach { cssFile -> + val fileContent = cssFile.readText() + + if ("/* Dark mode */" in fileContent) { + return@forEach + } else { + when (cssFile.name) { + "base-style.css" -> cssFile.writeText( + fileContent + """ + + /* Dark mode */ + @media (prefers-color-scheme: dark) { + html { + background: black; + } + body, a, a:visited { + color: #E7E7E7FF; + } + #footer, #footer a { + color: #cacaca; + } + ul.tabLinks li { + border: solid 1px #cacaca; + background-color: #151515; + } + ul.tabLinks li:hover { + background-color: #383838; + } + ul.tabLinks li.selected { + background-color: #002d32; + border-color: #007987; + } + div.tab th, div.tab table { + border-bottom: solid #d0d0d0 1px; + } + span.code pre { + background-color: #0a0a0a; + border: solid 1px #5f5f5f; + } + } + """.trimIndent() + ) + + "style.css" -> cssFile.writeText( + fileContent + """ + + /* Dark mode */ + @media (prefers-color-scheme: dark) { + .breadcrumbs, .breadcrumbs a { + color: #9b9b9b; + } + #successRate, .summaryGroup { + border: solid 2px #d0d0d0; + } + .success, .success a { + color: #7fff7f; + } + div.success, #successRate.success { + background-color: #001c00; + border-color: #7fff7f; + } + .failures, .failures a { + color: #a30000; + } + .skipped, .skipped a { + color: #a26d13; + } + div.failures, #successRate.failures { + background-color: #170000; + border-color: #a30000; + } + } + """.trimIndent() + ) + } + } + } + } +} + +tasks.withType<Test>().configureEach { + finalizedBy(updateTestReportCss) +} + +tasks.withType<TestReport>().configureEach { + finalizedBy(updateTestReportCss) +} + +tasks.matching { it.name == "validatePlugins" }.configureEach { + // prevent warning + // Task ':validatePlugins' uses this output of task ':updateTestReportCss' without declaring an explicit or implicit dependency. + mustRunAfter(updateTestReportCss) +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts new file mode 100644 index 00000000..69e384e1 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts @@ -0,0 +1,68 @@ +package buildsrc.conventions + +import buildsrc.settings.DokkaSourceDownloaderSettings +import buildsrc.utils.asConsumer +import buildsrc.utils.asProvider +import buildsrc.utils.dropDirectories +import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE +import org.gradle.kotlin.dsl.support.serviceOf + +plugins { + id("buildsrc.conventions.base") +} + +val dsdExt: DokkaSourceDownloaderSettings = extensions.create<DokkaSourceDownloaderSettings>( + DokkaSourceDownloaderSettings.EXTENSION_NAME +) + +val kotlinDokkaSource by configurations.creating<Configuration> { + asConsumer() + attributes { + attribute(USAGE_ATTRIBUTE, objects.named("externals-dokka-src")) + } +} + +val kotlinDokkaSourceElements by configurations.registering { + asProvider() + attributes { + attribute(USAGE_ATTRIBUTE, objects.named("externals-dokka-src")) + } +} + +dependencies { + kotlinDokkaSource(dsdExt.dokkaVersion.map { "kotlin:dokka:$it@zip" }) +} + +val prepareDokkaSource by tasks.registering(Sync::class) { + group = "dokka setup" + description = "Download & unpack Kotlin Dokka source code" + + inputs.property("dokkaVersion", dsdExt.dokkaVersion).optional(false) + + val archives = serviceOf<ArchiveOperations>() + + from( + kotlinDokkaSource.incoming + .artifacts + .resolvedArtifacts + .map { artifacts -> + artifacts.map { archives.zipTree(it.file) } + } + ) { + // drop the first dir (dokka-$version) + eachFile { + relativePath = relativePath.dropDirectories(1) + } + } + + into(temporaryDir) + + exclude( + "*.github", + "*.gradle", + "**/gradlew", + "**/gradlew.bat", + "**/gradle/wrapper/gradle-wrapper.jar", + "**/gradle/wrapper/gradle-wrapper.properties", + ) +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts new file mode 100644 index 00000000..5c2c45fa --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts @@ -0,0 +1,27 @@ +package buildsrc.conventions + +import buildsrc.utils.asConsumer +import buildsrc.utils.asProvider + +plugins { + id("buildsrc.conventions.base") +} + + +val exampleProjectsAttribute: Attribute<String> = + Attribute.of("example-projects", String::class.java) + +dependencies.attributesSchema { + attribute(exampleProjectsAttribute) +} + + +val exampleProjects by configurations.registering { + asConsumer() + attributes { attribute(exampleProjectsAttribute, "dokka") } +} + +val exampleProjectsElements by configurations.registering { + asProvider() + attributes { attribute(exampleProjectsAttribute, "dokka") } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts new file mode 100644 index 00000000..c6994a83 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts @@ -0,0 +1,160 @@ +package buildsrc.conventions + +import buildsrc.settings.* +import buildsrc.tasks.* +import buildsrc.utils.* + +plugins { + id("buildsrc.conventions.base") + id("buildsrc.conventions.dokka-source-downloader") + id("buildsrc.conventions.maven-publish-test") + id("buildsrc.conventions.dokkatoo-example-projects-base") +} + +val mavenPublishTestExtension = extensions.getByType<MavenPublishTestSettings>() +val dokkaTemplateProjectSettings = + extensions.create<DokkaTemplateProjectSettings>( + DokkaTemplateProjectSettings.EXTENSION_NAME, + { project.copySpec() } + ).apply { + this.destinationBaseDir.convention(layout.projectDirectory) + } + +val prepareDokkaSource by tasks.existing(Sync::class) + +dokkaTemplateProjectSettings.dokkaSourceDir.convention( + prepareDokkaSource.flatMap { + layout.dir(providers.provider { + it.destinationDir + }) + } +) + +tasks.withType<SetupDokkaProjects>().configureEach { + dependsOn(prepareDokkaSource) + + dokkaSourceDir.convention(dokkaTemplateProjectSettings.dokkaSourceDir) + destinationBaseDir.convention(dokkaTemplateProjectSettings.destinationBaseDir) + + templateProjects.addAllLater(provider { + dokkaTemplateProjectSettings.templateProjects + }) +} + +val setupDokkaTemplateProjects by tasks.registering(SetupDokkaProjects::class) + +fun createDokkatooExampleProjectsSettings( + projectDir: Directory = project.layout.projectDirectory +): DokkatooExampleProjectsSettings { + return extensions.create<DokkatooExampleProjectsSettings>( + DokkatooExampleProjectsSettings.EXTENSION_NAME + ).apply { + + // find all Gradle settings files + val settingsFiles = projectDir.asFileTree + .matching { + include( + "**/*dokkatoo*/**/settings.gradle.kts", + "**/*dokkatoo*/**/settings.gradle", + ) + }.files + + // for each settings file, create a DokkatooExampleProjectSpec + settingsFiles.forEach { + val destinationDir = it.parentFile + val name = destinationDir.toRelativeString(projectDir.asFile).toAlphaNumericCamelCase() + exampleProjects.register(name) { + this.exampleProjectDir.set(destinationDir) + } + } + + exampleProjects.configureEach { + gradlePropertiesContent.add( + mavenPublishTestExtension.testMavenRepoPath.map { testMavenRepoPath -> + "testMavenRepo=$testMavenRepoPath" + } + ) + } + } +} + +val dokkatooExampleProjectsSettings = createDokkatooExampleProjectsSettings() + +val updateDokkatooExamplesGradleProperties by tasks.registering( + UpdateDokkatooExampleProjects::class +) { + group = DokkatooExampleProjectsSettings.TASK_GROUP + + mustRunAfter(tasks.withType<SetupDokkaProjects>()) + + exampleProjects.addAllLater(providers.provider { + dokkatooExampleProjectsSettings.exampleProjects + }) +} + +val dokkatooVersion = provider { project.version.toString() } + +val updateDokkatooExamplesBuildFiles by tasks.registering { + group = DokkatooExampleProjectsSettings.TASK_GROUP + description = "Update the Gradle build files in the Dokkatoo examples" + + outputs.upToDateWhen { false } + + mustRunAfter(tasks.withType<SetupDokkaProjects>()) + shouldRunAfter(updateDokkatooExamplesGradleProperties) + + val dokkatooVersion = dokkatooVersion + + val dokkatooDependencyVersionMatcher = """ + \"dev\.adamko\.dokkatoo\:dokkatoo\-plugin\:([^"]+?)\" + """.trimIndent().toRegex() + + val dokkatooPluginVersionMatcher = """ + id[^"]+?"dev\.adamko\.dokkatoo".+?version "([^"]+?)" + """.trimIndent().toRegex() + + val gradleBuildFiles = + layout.projectDirectory.asFileTree + .matching { + include( + "**/*dokkatoo*/**/build.gradle.kts", + "**/*dokkatoo*/**/build.gradle", + ) + }.elements + outputs.files(gradleBuildFiles) + + doLast { + gradleBuildFiles.get().forEach { fileLocation -> + val file = fileLocation.asFile + if (file.exists()) { + file.writeText( + file.readText() + .replace(dokkatooPluginVersionMatcher) { + val oldVersion = it.groupValues[1] + it.value.replace(oldVersion, dokkatooVersion.get()) + } + .replace(dokkatooDependencyVersionMatcher) { + val oldVersion = it.groupValues[1] + it.value.replace(oldVersion, dokkatooVersion.get()) + } + ) + } + } + } +} + + +val updateDokkatooExamples by tasks.registering { + group = DokkatooExampleProjectsSettings.TASK_GROUP + description = "lifecycle task for all '${DokkatooExampleProjectsSettings.TASK_GROUP}' tasks" + dependsOn( + setupDokkaTemplateProjects, + updateDokkatooExamplesGradleProperties, + updateDokkatooExamplesBuildFiles, + ) +} + +tasks.assemble { + dependsOn(updateDokkatooExamples) + dependsOn(setupDokkaTemplateProjects) +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts new file mode 100644 index 00000000..1d9fc43b --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts @@ -0,0 +1,44 @@ +package buildsrc.conventions + +import org.gradle.api.attributes.plugin.GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE + +plugins { + id("buildsrc.conventions.base") + `java-gradle-plugin` +} + +fun registerGradleVariant(name: String, gradleVersion: String) { + val variantSources = sourceSets.create(name) + + java { + registerFeature(variantSources.name) { + usingSourceSet(variantSources) + capability("${project.group}", "${project.name}", "${project.version}") + + withJavadocJar() + withSourcesJar() + } + } + + configurations + .matching { it.isCanBeConsumed && it.name.startsWith(variantSources.name) } + .configureEach { + attributes { + attribute(GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named(gradleVersion)) + } + } + + tasks.named<Copy>(variantSources.processResourcesTaskName) { + val copyPluginDescriptors = rootSpec.addChild() + copyPluginDescriptors.into("META-INF/gradle-plugins") +// copyPluginDescriptors.into(tasks.pluginDescriptors.flatMap { it.outputDirectory }) + copyPluginDescriptors.from(tasks.pluginDescriptors) + } + + dependencies { + add(variantSources.compileOnlyConfigurationName, gradleApi()) + } +} + +registerGradleVariant("gradle7", "7.6") +registerGradleVariant("gradle8", "8.0") diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts new file mode 100644 index 00000000..203b80f2 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts @@ -0,0 +1,19 @@ +package buildsrc.conventions + +import org.gradle.api.JavaVersion +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.jvm.toolchain.JavaLanguageVersion +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.`java-base` + +plugins { + id("buildsrc.conventions.base") + `java` +} + +extensions.getByType<JavaPluginExtension>().apply { + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } + withSourcesJar() +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts new file mode 100644 index 00000000..4174088a --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts @@ -0,0 +1,37 @@ +package buildsrc.conventions + +plugins { + id("buildsrc.conventions.base") + id("buildsrc.conventions.java-base") + id("org.gradle.kotlin.kotlin-dsl") + id("com.gradle.plugin-publish") +} + +tasks.validatePlugins { + enableStricterValidation.set(true) +} + +val createJavadocJarReadme by tasks.registering(Sync::class) { + description = "generate a readme.txt for the Javadoc JAR" + from( + resources.text.fromString( + """ + This Javadoc JAR is intentionally empty. + + For documentation, see the sources JAR or https://github.com/adamko-dev/dokkatoo/ + + """.trimIndent() + ) + ) { + rename { "readme.txt" } + } + into(temporaryDir) +} + + +// The Gradle Publish Plugin enables the Javadoc JAR in afterEvaluate, so find it lazily +tasks.withType<Jar>() + .matching { it.name == "javadocJar" } + .configureEach { + from(createJavadocJarReadme) + } diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts new file mode 100644 index 00000000..38678b5b --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts @@ -0,0 +1,93 @@ +package buildsrc.conventions + +import buildsrc.settings.MavenPublishTestSettings +import buildsrc.utils.* + + +/** Utility for publishing a project to a local Maven directory for use in integration tests. */ + +plugins { + base +} + +val Gradle.rootGradle: Gradle get() = generateSequence(gradle) { it.parent }.last() + +val mavenPublishTestExtension = extensions.create<MavenPublishTestSettings>( + "mavenPublishTest", + gradle.rootGradle.rootProject.layout.buildDirectory.dir("test-maven-repo"), +) + + +val publishToTestMavenRepo by tasks.registering { + group = PublishingPlugin.PUBLISH_TASK_GROUP + description = "Publishes all Maven publications to the test Maven repository." +} + + +plugins.withType<MavenPublishPlugin>().all { + extensions + .getByType<PublishingExtension>() + .publications + .withType<MavenPublication>().all publication@{ + val publicationName = this@publication.name + val installTaskName = "publish${publicationName.uppercaseFirstChar()}PublicationToTestMavenRepo" + + // Register a publication task for each publication. + // Use PublishToMavenLocal, because the PublishToMavenRepository task will *always* create + // a new jar, even if nothing has changed, and append a timestamp, which results in a large + // directory and tasks are never up-to-date. + // PublishToMavenLocal does not append a timestamp, so the target directory is smaller, and + // up-to-date checks work. + val installTask = tasks.register<PublishToMavenLocal>(installTaskName) { + description = "Publishes Maven publication '$publicationName' to the test Maven repository." + group = PublishingPlugin.PUBLISH_TASK_GROUP + outputs.cacheIf { true } + publication = this@publication + val destinationDir = mavenPublishTestExtension.testMavenRepo.get().asFile + inputs.property("testMavenRepoTempDir", destinationDir.invariantSeparatorsPath) + doFirst { + /** + * `maven.repo.local` will set the destination directory for this [PublishToMavenLocal] task. + * + * @see org.gradle.api.internal.artifacts.mvnsettings.DefaultLocalMavenRepositoryLocator.getLocalMavenRepository + */ + System.setProperty("maven.repo.local", destinationDir.absolutePath) + } + } + + publishToTestMavenRepo.configure { + dependsOn(installTask) + } + + tasks.check { + mustRunAfter(installTask) + } + } +} + + +val testMavenPublication by configurations.registering { + asConsumer() + attributes { + attribute(MavenPublishTestSettings.attribute, "testMavenRepo") + } +} + +val testMavenPublicationElements by configurations.registering { + asProvider() + extendsFrom(testMavenPublication.get()) + attributes { + attribute(MavenPublishTestSettings.attribute, "testMavenRepo") + } + outgoing { + artifact(mavenPublishTestExtension.testMavenRepo) { + builtBy(publishToTestMavenRepo) + } + } +} + +dependencies { + attributesSchema { + attribute(MavenPublishTestSettings.attribute) + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts new file mode 100644 index 00000000..7af7b69f --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts @@ -0,0 +1,137 @@ +package buildsrc.conventions + +import buildsrc.settings.MavenPublishingSettings + +plugins { + `maven-publish` + signing +} + +val mavenPublishing = + extensions.create<MavenPublishingSettings>(MavenPublishingSettings.EXTENSION_NAME, project) + + +//region POM convention +publishing { + publications.withType<MavenPublication>().configureEach { + pom { + name.convention("Dokkatoo") + description.convention("Dokkatoo is a Gradle plugin that generates documentation for your Kotlin projects") + url.convention("https://github.com/adamko-dev/dokkatoo") + + scm { + connection.convention("scm:git:https://github.com/adamko-dev/dokkatoo") + developerConnection.convention("scm:git:https://github.com/adamko-dev/dokkatoo") + url.convention("https://github.com/adamko-dev/dokkatoo") + } + + licenses { + license { + name.convention("Apache-2.0") + url.convention("https://www.apache.org/licenses/LICENSE-2.0.txt") + } + } + + developers { + developer { + email.set("adam@adamko.dev") + } + } + } + } +} +//endregion + + +//region GitHub branch publishing +publishing { + repositories { + maven(mavenPublishing.githubPublishDir) { + name = "GitHubPublish" + } + } +} +//endregion + + +//region Maven Central publishing/signing +publishing { + repositories { + val mavenCentralUsername = mavenPublishing.mavenCentralUsername.orNull + val mavenCentralPassword = mavenPublishing.mavenCentralPassword.orNull + if (!mavenCentralUsername.isNullOrBlank() && !mavenCentralPassword.isNullOrBlank()) { + maven(mavenPublishing.sonatypeReleaseUrl) { + name = "SonatypeRelease" + credentials { + username = mavenCentralUsername + password = mavenCentralPassword + } + } + } + } + + // com.gradle.plugin-publish automatically adds a Javadoc jar +} + +signing { + logger.info("maven-publishing.gradle.kts enabled signing for ${project.path}") + + val keyId = mavenPublishing.signingKeyId.orNull + val key = mavenPublishing.signingKey.orNull + val password = mavenPublishing.signingPassword.orNull + + if (!keyId.isNullOrBlank() && !key.isNullOrBlank() && !password.isNullOrBlank()) { + useInMemoryPgpKeys(keyId, key, password) + } + + setRequired({ + gradle.taskGraph.allTasks.filterIsInstance<PublishToMavenRepository>().any { + it.repository.name == "SonatypeRelease" + } + }) +} + +//afterEvaluate { +// com.gradle.plugin-publish automatically signs tasks in a weird way, that stops this from working: +// signing { +// sign(publishing.publications) +// } +//} +//endregion + + +//region Fix Gradle warning about signing tasks using publishing task outputs without explicit dependencies +// https://youtrack.jetbrains.com/issue/KT-46466 https://github.com/gradle/gradle/issues/26091 +tasks.withType<AbstractPublishToMaven>().configureEach { + val signingTasks = tasks.withType<Sign>() + mustRunAfter(signingTasks) +} +//endregion + + +//region publishing logging +tasks.withType<AbstractPublishToMaven>().configureEach { + val publicationGAV = provider { publication?.run { "$group:$artifactId:$version" } } + doLast("log publication GAV") { + if (publicationGAV.isPresent) { + logger.lifecycle("[task: ${path}] ${publicationGAV.get()}") + } + } +} +//endregion + + +//region IJ workarounds +// manually define the Kotlin DSL accessors because IntelliJ _still_ doesn't load them properly +fun Project.publishing(configure: PublishingExtension.() -> Unit): Unit = + extensions.configure(configure) + +val Project.publishing: PublishingExtension + get() = extensions.getByType() + +fun Project.signing(configure: SigningExtension.() -> Unit): Unit = + extensions.configure(configure) + +val Project.signing: SigningExtension + get() = extensions.getByType() +//endregion diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt new file mode 100644 index 00000000..c3f9906c --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt @@ -0,0 +1,13 @@ +package buildsrc.settings + +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property + +abstract class DokkaSourceDownloaderSettings : ExtensionAware { + + abstract val dokkaVersion: Property<String> + + companion object { + const val EXTENSION_NAME = "dokkaSourceDownload" + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt new file mode 100644 index 00000000..7bacafb9 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt @@ -0,0 +1,96 @@ +package buildsrc.settings + +import buildsrc.utils.adding +import buildsrc.utils.domainObjectContainer +import buildsrc.utils.toAlphaNumericCamelCase +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.CopySpec +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFiles +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.kotlin.dsl.* + +private typealias TemplateProjectsContainer = NamedDomainObjectContainer<DokkaTemplateProjectSettings.DokkaTemplateProjectSpec> + +abstract class DokkaTemplateProjectSettings @Inject constructor( + private val objects: ObjectFactory, + private val copySpecs: () -> CopySpec +) : ExtensionAware { + + /** Directory that will contain the projects downloaded from the Dokka source code. */ + abstract val dokkaSourceDir: DirectoryProperty + + abstract val destinationBaseDir: DirectoryProperty + + internal val templateProjects: TemplateProjectsContainer = + // create an extension so Gradle will generate DSL accessors + extensions.adding("templateProjects", objects.domainObjectContainer { name -> + objects.newInstance<DokkaTemplateProjectSpec>(name, copySpecs()) + }) + + /** + * Copy a directory from the Dokka source project into a local directory. + * + * @param[source] Source dir, relative to [templateProjectsDir] + * @param[destination] Destination dir, relative to [destinationBaseDir] + */ + fun register( + source: String, + destination: String, + configure: DokkaTemplateProjectSpec.() -> Unit = {}, + ) { + val name = source.toAlphaNumericCamelCase() + templateProjects.register(name) { + this.sourcePath.set(source) + this.destinationPath.set(destination) + configure() + } + } + + fun configureEach(configure: DokkaTemplateProjectSpec.() -> Unit) { + templateProjects.configureEach(configure) + } + + /** + * Details for how to copy a Dokka template project from the Dokka project to a local directory. + */ + abstract class DokkaTemplateProjectSpec @Inject constructor( + private val named: String, + @get:Internal + internal val copySpec: CopySpec, + ) : Named { + + @get:Input + abstract val sourcePath: Property<String> + + @get:Input + @get:Optional + abstract val destinationPath: Property<String> + + @get:Input + abstract val additionalPaths: SetProperty<String> + + @get:InputFiles + abstract val additionalFiles: ConfigurableFileCollection + + fun configureCopy(configure: CopySpec.() -> Unit) { + copySpec.configure() + } + + @Input + override fun getName(): String = named + } + + companion object { + const val EXTENSION_NAME = "dokkaTemplateProjects" + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt new file mode 100644 index 00000000..a3124904 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt @@ -0,0 +1,62 @@ +package buildsrc.settings + +import buildsrc.utils.adding +import buildsrc.utils.domainObjectContainer +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFile +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.gradle.api.tasks.OutputFile + +/** + * Settings for the [buildsrc.conventions.Dokkatoo_example_projects_gradle] convention plugin + */ +abstract class DokkatooExampleProjectsSettings @Inject constructor( + objects: ObjectFactory, +) : ExtensionAware { + + val exampleProjects: NamedDomainObjectContainer<DokkatooExampleProjectSpec> = + // create an extension so Gradle will generate DSL accessors + extensions.adding("exampleProjects", objects.domainObjectContainer()) + + abstract class DokkatooExampleProjectSpec( + private val name: String + ): Named { + + /** The `gradle.properties` file of the example project */ + @get:OutputFile + val gradlePropertiesFile: Provider<RegularFile> + get() = exampleProjectDir.file("gradle.properties") + + /** The directory that contains the example project */ + @get:Internal + abstract val exampleProjectDir: DirectoryProperty + + /** + * Content to add to the `gradle.properties` file. + * + * Elements may span multiple lines. + * + * Elements will be sorted before appending to the file (to improve caching & reproducibility). + */ + @get:Input + @get:Optional + abstract val gradlePropertiesContent: ListProperty<String> + + @Input + override fun getName(): String = name + } + + companion object { + const val TASK_GROUP = "dokkatoo examples" + const val EXTENSION_NAME = "dokkatooExampleProjects" + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt new file mode 100644 index 00000000..0a701986 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt @@ -0,0 +1,19 @@ +package buildsrc.settings + +import org.gradle.api.attributes.Attribute +import org.gradle.api.file.Directory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Provider + +/** + * Settings for the [buildsrc.conventions.Maven_publish_test_gradle] convention plugin. + */ +abstract class MavenPublishTestSettings( + val testMavenRepo: Provider<Directory> +) : ExtensionAware { + val testMavenRepoPath: Provider<String> = testMavenRepo.map { it.asFile.invariantSeparatorsPath } + + companion object { + val attribute = Attribute.of("maven-publish-test", String::class.java) + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt new file mode 100644 index 00000000..9ec28faa --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt @@ -0,0 +1,68 @@ +package buildsrc.settings + +import java.io.File +import javax.inject.Inject +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.* + + +/** + * Settings for the [buildsrc.conventions.Maven_publish_test_gradle] convention plugin. + */ +abstract class MavenPublishingSettings @Inject constructor( + private val project: Project, + private val providers: ProviderFactory, +) { + + private val isReleaseVersion: Provider<Boolean> = + providers.provider { !project.version.toString().endsWith("-SNAPSHOT") } + + val sonatypeReleaseUrl: Provider<String> = + isReleaseVersion.map { isRelease -> + if (isRelease) { + "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" + } else { + "https://s01.oss.sonatype.org/content/repositories/snapshots/" + } + } + + val mavenCentralUsername: Provider<String> = + d2Prop("mavenCentralUsername") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_USERNAME")) + val mavenCentralPassword: Provider<String> = + d2Prop("mavenCentralPassword") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_PASSWORD")) + + val signingKeyId: Provider<String> = + d2Prop("signing.keyId") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY_ID")) + val signingKey: Provider<String> = + d2Prop("signing.key") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY")) + val signingPassword: Provider<String> = + d2Prop("signing.password") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_PASSWORD")) + + val githubPublishDir: Provider<File> = + providers.environmentVariable("GITHUB_PUBLISH_DIR").map { File(it) } + + private fun d2Prop(name: String): Provider<String> = + providers.gradleProperty("org.jetbrains.dokka.dokkatoo.$name") + + private fun <T : Any> d2Prop(name: String, convert: (String) -> T): Provider<T> = + d2Prop(name).map(convert) + + companion object { + const val EXTENSION_NAME = "mavenPublishing" + + /** Retrieve the [KayrayBuildProperties] extension. */ + internal val Project.mavenPublishing: MavenPublishingSettings + get() = extensions.getByType() + + /** Configure the [KayrayBuildProperties] extension. */ + internal fun Project.mavenPublishing(configure: MavenPublishingSettings.() -> Unit) = + extensions.configure(configure) + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt new file mode 100644 index 00000000..d473d287 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt @@ -0,0 +1,73 @@ +package buildsrc.tasks + +import buildsrc.settings.DokkaTemplateProjectSettings.DokkaTemplateProjectSpec +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.FileCollection +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.file.ProjectLayout +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.* + +abstract class SetupDokkaProjects @Inject constructor( + private val fs: FileSystemOperations, + private val layout: ProjectLayout, + private val providers: ProviderFactory, +) : DefaultTask() { + + @get:OutputDirectories + val destinationDirs: FileCollection + get() = layout.files( + destinationBaseDir.map { base -> + templateProjects.map { spec -> base.dir(spec.destinationPath) } + } + ) + + @get:Internal // tracked by destinationDirs + abstract val destinationBaseDir: DirectoryProperty + + @get:Nested + abstract val templateProjects: NamedDomainObjectContainer<DokkaTemplateProjectSpec> + + @get:InputDirectory + abstract val dokkaSourceDir: DirectoryProperty + + @get:InputFiles + val additionalFiles: FileCollection + get() = layout.files( + providers.provider { + templateProjects.map { it.additionalFiles } + } + ) + + init { + group = "dokka examples" + } + + @TaskAction + internal fun action() { + val dokkaSourceDir = dokkaSourceDir.get() + val destinationBaseDir = destinationBaseDir.get() + val templateProjects = templateProjects.filter { it.destinationPath.isPresent } + + templateProjects.forEach { spec -> + fs.sync { + with(spec.copySpec) + + from(dokkaSourceDir.dir(spec.sourcePath)) + + from( + spec.additionalPaths.get().map { additionalPath -> + dokkaSourceDir.asFile.resolve(additionalPath) + } + ) + + from(spec.additionalFiles) + + into(destinationBaseDir.dir(spec.destinationPath)) + } + } + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt new file mode 100644 index 00000000..7737e098 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt @@ -0,0 +1,49 @@ +package buildsrc.tasks + +import buildsrc.settings.DokkatooExampleProjectsSettings.DokkatooExampleProjectSpec +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Nested +import org.gradle.api.tasks.TaskAction + +/** + * Utility for updating the `gradle.properties` of projects used in automated tests. + */ +@CacheableTask +abstract class UpdateDokkatooExampleProjects @Inject constructor( + @get:Internal + val objects: ObjectFactory +) : DefaultTask() { + + @get:Nested + abstract val exampleProjects: NamedDomainObjectContainer<DokkatooExampleProjectSpec> + + private val taskPath: String = path // renamed for clarity + + @TaskAction + fun update() { + exampleProjects.forEach { exampleProject -> + updateGradleProperties(exampleProject) + } + } + + private fun updateGradleProperties(exampleProject: DokkatooExampleProjectSpec) { + + val gradlePropertiesContent = exampleProject.gradlePropertiesContent.orNull?.sorted() ?: return + + val content = buildString { + appendLine("# DO NOT EDIT - Generated by $taskPath") + appendLine() + + gradlePropertiesContent.forEach { + appendLine(it) + } + } + + exampleProject.gradlePropertiesFile.get().asFile.writeText(content) + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt new file mode 100644 index 00000000..0af662d4 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt @@ -0,0 +1,118 @@ +package buildsrc.utils + +import java.io.File +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.NamedDomainObjectFactory +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.component.AdhocComponentWithVariants +import org.gradle.api.file.RelativePath +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.kotlin.dsl.* + +/** + * Mark this [Configuration] as one that will be consumed by other subprojects. + * + * ``` + * isCanBeResolved = false + * isCanBeConsumed = true + * ``` + */ +fun Configuration.asProvider( + visible: Boolean = true +) { + isVisible = visible + isCanBeResolved = false + isCanBeConsumed = true +} + +/** + * Mark this [Configuration] as one that will consume artifacts from other subprojects (also known as 'resolving') + * + * ``` + * isCanBeResolved = true + * isCanBeConsumed = false + * ``` + * */ +fun Configuration.asConsumer( + visible: Boolean = false +) { + isVisible = visible + isCanBeResolved = true + isCanBeConsumed = false +} + + +/** Drop the first [count] directories from the path */ +fun RelativePath.dropDirectories(count: Int): RelativePath = + RelativePath(true, *segments.drop(count).toTypedArray()) + + +/** Drop the first directory from the path */ +fun RelativePath.dropDirectory(): RelativePath = + dropDirectories(1) + + +/** Drop the first directory from the path */ +fun RelativePath.dropDirectoriesWhile( + segmentPrediate: (segment: String) -> Boolean +): RelativePath = + RelativePath( + true, + *segments.dropWhile(segmentPrediate).toTypedArray(), + ) + + +/** + * Don't publish test fixtures (which causes warnings when publishing) + * + * https://docs.gradle.org/current/userguide/java_testing.html#publishing_test_fixtures + */ +fun Project.skipTestFixturesPublications() { + val javaComponent = components["java"] as AdhocComponentWithVariants + javaComponent.withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() } + javaComponent.withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() } +} + + +/** + * Add an extension to the [ExtensionContainer], and return the value. + * + * Adding an extension is especially useful for improving the DSL in build scripts when [T] is a + * [NamedDomainObjectContainer]. + * Using an extension will allow Gradle to generate + * [type-safe model accessors](https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:accessor_applicability) + * for added types. + * + * ([name] should match the property name. This has to be done manually. I tried using a + * delegated-property provider but then Gradle can't introspect the types properly, so it fails to + * create accessors). + */ +internal inline fun <reified T : Any> ExtensionContainer.adding( + name: String, + value: T, +): T { + add<T>(name, value) + return value +} + +/** + * Create a new [NamedDomainObjectContainer], using + * [org.gradle.kotlin.dsl.domainObjectContainer] + * (but [T] is `reified`). + * + * @param[factory] an optional factory for creating elements + * @see org.gradle.kotlin.dsl.domainObjectContainer + */ +internal inline fun <reified T : Any> ObjectFactory.domainObjectContainer( + factory: NamedDomainObjectFactory<T>? = null +): NamedDomainObjectContainer<T> = + if (factory == null) { + domainObjectContainer(T::class) + } else { + domainObjectContainer(T::class, factory) + } + +/** workaround for the overly verbose replacement for the deprecated [Project.getBuildDir] property */ +val Project.buildDir_: File get() = layout.buildDirectory.get().asFile diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt new file mode 100644 index 00000000..f93e7683 --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt @@ -0,0 +1,45 @@ +package buildsrc.utils + +import org.gradle.api.Project +import org.gradle.api.file.ProjectLayout +import org.gradle.plugins.ide.idea.model.IdeaModule + + +/** exclude generated Gradle code, so it doesn't clog up search results */ +fun IdeaModule.excludeGeneratedGradleDsl(layout: ProjectLayout) { + + val generatedSrcDirs = listOf( + "kotlin-dsl-accessors", + "kotlin-dsl-external-plugin-spec-builders", + "kotlin-dsl-plugins", + ) + + excludeDirs.addAll( + layout.projectDirectory.asFile.walk() + .filter { it.isDirectory && it.parentFile.name in generatedSrcDirs } + .flatMap { file -> + file.walk().maxDepth(1).filter { it.isDirectory }.toList() + } + ) +} + + +/** Sets a logo for project IDEs */ +fun Project.initIdeProjectLogo( + svgLogoPath: String +) { + val logoSvg = rootProject.layout.projectDirectory.file(svgLogoPath) + val ideaDir = rootProject.layout.projectDirectory.dir(".idea") + + if ( + logoSvg.asFile.exists() + && ideaDir.asFile.exists() + && !ideaDir.file("icon.png").asFile.exists() + && !ideaDir.file("icon.svg").asFile.exists() + ) { + copy { + from(logoSvg) { rename { "icon.svg" } } + into(ideaDir) + } + } +} diff --git a/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt new file mode 100644 index 00000000..6a0749ce --- /dev/null +++ b/dokka-runners/dokkatoo/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt @@ -0,0 +1,26 @@ +package buildsrc.utils + + +/** Title case the first char of a string */ +internal fun String.uppercaseFirstChar(): String = mapFirstChar(Character::toTitleCase) + + +/** Lowercase the first char of a string */ +internal fun String.lowercaseFirstChar(): String = mapFirstChar(Character::toLowerCase) + + +private inline fun String.mapFirstChar( + transform: (Char) -> Char +): String = if (isNotEmpty()) transform(this[0]) + substring(1) else this + + +/** + * Exclude all non-alphanumeric characters and converts the result into a camelCase string. + */ +internal fun String.toAlphaNumericCamelCase(): String = + map { if (it.isLetterOrDigit()) it else ' ' } + .joinToString("") + .split(" ") + .filter { it.isNotBlank() } + .joinToString("") { it.uppercaseFirstChar() } + .lowercaseFirstChar() diff --git a/dokka-runners/dokkatoo/devOps/release.main.kts b/dokka-runners/dokkatoo/devOps/release.main.kts new file mode 100644 index 00000000..a7555719 --- /dev/null +++ b/dokka-runners/dokkatoo/devOps/release.main.kts @@ -0,0 +1,415 @@ +#!/usr/bin/env kotlin +@file:DependsOn("com.github.ajalt.clikt:clikt-jvm:3.5.2") +@file:DependsOn("me.alllex.parsus:parsus-jvm:0.4.0") +@file:DependsOn("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3") + +import Release_main.SemVer.Companion.SemVer +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.options.flag +import com.github.ajalt.clikt.parameters.options.option +import java.io.File +import java.util.concurrent.TimeUnit.MINUTES +import kotlin.system.exitProcess +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking +import me.alllex.parsus.parser.* +import me.alllex.parsus.token.literalToken +import me.alllex.parsus.token.regexToken + +try { + Release.main(args) + exitProcess(0) +} catch (ex: Exception) { + println("${ex::class.simpleName}: ${ex.message}") + exitProcess(1) +} + +/** + * Release a new version. + * + * Requires: + * * [gh cli](https://cli.github.com/manual/gh) + * * [kotlin](https://kotlinlang.org/docs/command-line.html) + * * [git](https://git-scm.com/) + */ +// based on https://github.com/apollographql/apollo-kotlin/blob/v4.0.0-dev.2/scripts/release.main.kts +object Release : CliktCommand() { + private val skipGitValidation by option( + "--skip-git-validation", + help = "skips git status validation" + ).flag(default = false) + + override fun run() { + echo("Current Dokkatoo version is $dokkatooVersion") + echo("git dir is ${Git.rootDir}") + + val startBranch = Git.currentBranch() + + validateGitStatus(startBranch) + + val releaseVersion = semverPrompt( + text = "version to release?", + default = dokkatooVersion.copy(snapshot = false), + ) { + if (it.snapshot) { + echo("versionToRelease must not be a snapshot version, but was $it") + } + !it.snapshot + } + val nextVersion = semverPrompt( + text = "post-release version?", + default = releaseVersion.incrementMinor(snapshot = true), + ) + updateVersionCreatePR(releaseVersion) + + // switch back to the main branch + Git.switch(startBranch) + Git.pull(startBranch) + + // Tag the release + createAndPushTag(releaseVersion) + + confirm("Publish plugins to Gradle Plugin Portal?", abort = true) + Gradle.publishPlugins() + + // Bump the version to the next snapshot + updateVersionCreatePR(nextVersion) + + // Go back and pull the changes + Git.switch(startBranch) + Git.pull(startBranch) + + echo("Released version $releaseVersion") + } + + private fun validateGitStatus(startBranch: String) { + if (skipGitValidation) { + echo("skipping git status validation") + return + } + check(Git.status().isEmpty()) { + "git repo is not clean. Stash or commit changes before making a release." + } + check(dokkatooVersion.snapshot) { + "Current version must be a SNAPSHOT, but was $dokkatooVersion" + } + check(startBranch == "main") { + "Must be on the main branch to make a release, but current branch is $startBranch" + } + } + + /** + * @param[validate] returns `null` if the provided SemVer is valid, or else an error message + * explaining why it is invalid. + */ + private tailrec fun semverPrompt( + text: String, + default: SemVer, + validate: (candidate: SemVer) -> Boolean = { true }, + ): SemVer { + val response = prompt( + text = text, + default = default.toString(), + requireConfirmation = true, + ) { + SemVer.of(it) + } + + return if (response == null || !validate(response)) { + if (response == null) echo("invalid SemVer") + semverPrompt(text, default, validate) + } else { + response + } + } + + private fun updateVersionCreatePR(version: SemVer) { + // checkout a release branch + val releaseBranch = "release/v$version" + echo("checkout out new branch...") + Git.switch(releaseBranch, create = true) + + // update the version & run tests + dokkatooVersion = version + echo("running Gradle check...") + Gradle.check() + + // commit and push + echo("committing...") + Git.commit("release $version") + echo("pushing...") + Git.push(releaseBranch) + + // create a new PR + echo("creating PR...") + GitHub.createPr(releaseBranch) + + confirm("Merge the PR for branch $releaseBranch?", abort = true) + mergeAndWait(releaseBranch) + echo("$releaseBranch PR merged") + } + + private fun createAndPushTag(version: SemVer) { + // Tag the release + require(dokkatooVersion == version) { + "tried to create a tag, but project version does not match provided version. Expected $version but got $dokkatooVersion" + } + val tagName = "v$version" + Git.tag(tagName) + confirm("Push tag $tagName?", abort = true) + Git.push(tagName) + echo("Tag pushed") + + confirm("Publish plugins to Gradle Plugin Portal?", abort = true) + Gradle.publishPlugins() + } + + private val buildGradleKts: File by lazy { + val rootDir = Git.rootDir + File("$rootDir/build.gradle.kts").apply { + require(exists()) { "could not find build.gradle.kts in $rootDir" } + } + } + + /** Read/write the version set in the root `build.gradle.kts` file */ + private var dokkatooVersion: SemVer + get() { + val rawVersion = Gradle.dokkatooVersion() + return SemVer(rawVersion) + } + set(value) { + val updatedFile = buildGradleKts.useLines { lines -> + lines.joinToString(separator = "\n", postfix = "\n") { line -> + if (line.startsWith("version = ")) { + "version = \"${value}\"" + } else { + line + } + } + } + buildGradleKts.writeText(updatedFile) + } + + private fun mergeAndWait(branchName: String): Unit = runBlocking { + GitHub.autoMergePr(branchName) + echo("Waiting for the PR to be merged...") + while (GitHub.prState(branchName) != "MERGED") { + delay(1.seconds) + echo(".", trailingNewline = false) + } + } +} + +private abstract class CliTool { + + protected fun runCommand( + cmd: String, + dir: File? = Git.rootDir, + logOutput: Boolean = true, + ): String { + val args = parseSpaceSeparatedArgs(cmd) + + val process = ProcessBuilder(args).apply { + redirectOutput(ProcessBuilder.Redirect.PIPE) + redirectInput(ProcessBuilder.Redirect.PIPE) + redirectErrorStream(true) + if (dir != null) directory(dir) + }.start() + + val processOutput = process.inputStream + .bufferedReader() + .lineSequence() + .onEach { if (logOutput) println("\t$it") } + .joinToString("\n") + .trim() + + process.waitFor(10, MINUTES) + + val exitCode = process.exitValue() + + if (exitCode != 0) { + error("command '$cmd' failed:\n${processOutput}") + } + + return processOutput + } + + private data class ProcessResult( + val exitCode: Int, + val output: String, + ) + + companion object { + private fun parseSpaceSeparatedArgs(argsString: String): List<String> { + val parsedArgs = mutableListOf<String>() + var inQuotes = false + var currentCharSequence = StringBuilder() + fun saveArg(wasInQuotes: Boolean) { + if (wasInQuotes || currentCharSequence.isNotBlank()) { + parsedArgs.add(currentCharSequence.toString()) + currentCharSequence = StringBuilder() + } + } + argsString.forEach { char -> + if (char == '"') { + inQuotes = !inQuotes + // Save value which was in quotes. + if (!inQuotes) { + saveArg(true) + } + } else if (char.isWhitespace() && !inQuotes) { + // Space is separator + saveArg(false) + } else { + currentCharSequence.append(char) + } + } + if (inQuotes) { + error("No close-quote was found in $currentCharSequence.") + } + saveArg(false) + return parsedArgs + } + } +} + +/** git commands */ +private object Git : CliTool() { + val rootDir = File(runCommand("git rev-parse --show-toplevel", dir = null)) + + init { + require(rootDir.exists()) { "could not determine root git directory" } + } + + fun switch(branch: String, create: Boolean = false): String { + return runCommand( + buildString { + append("git switch ") + if (create) append("--create ") + append(branch) + } + ) + } + + fun commit(message: String): String = runCommand("git commit -a -m \"$message\"") + fun currentBranch(): String = runCommand("git symbolic-ref --short HEAD") + fun pull(ref: String): String = runCommand("git pull origin $ref") + fun push(ref: String): String = runCommand("git push origin $ref") + fun status(): String { + runCommand("git fetch --all") + return runCommand("git status --porcelain=v2") + } + + fun tag(tag: String): String { + return runCommand("git tag $tag") + } +} + +/** GitHub commands */ +private object GitHub : CliTool() { + + init { + setRepo("adamko-dev/dokkatoo") + } + + fun setRepo(repo: String): String = + runCommand("gh repo set-default $repo") + + fun prState(branchName: String): String = + runCommand("gh pr view $branchName --json state --jq .state", logOutput = false) + + fun createPr(branch: String): String = + runCommand("gh pr create --head $branch --fill") + + fun autoMergePr(branch: String): String = + runCommand("gh pr merge $branch --squash --auto --delete-branch") + + fun waitForPrChecks(branch: String): String = + runCommand("gh pr checks $branch --watch --interval 30") +} + +/** GitHub commands */ +private object Gradle : CliTool() { + + val gradlew: String + + init { + val osName = System.getProperty("os.name").lowercase() + gradlew = if ("win" in osName) "./gradlew.bat" else "./gradlew" + } + + fun stopDaemons(): String = runCommand("$gradlew --stop") + + fun dokkatooVersion(): String { + stopDaemons() + return runCommand("$gradlew :dokkatooVersion --quiet --no-daemon") + } + + fun check(): String { + stopDaemons() + return runCommand("$gradlew check --no-daemon") + } + + fun publishPlugins(): String { + stopDaemons() + return runCommand("$gradlew publishPlugins --no-daemon --no-configuration-cache") + } +} + +private data class SemVer( + val major: Int, + val minor: Int, + val patch: Int, + val snapshot: Boolean, +) { + + fun incrementMinor(snapshot: Boolean): SemVer = + copy(minor = minor + 1, snapshot = snapshot) + + override fun toString(): String = + "$major.$minor.$patch" + if (snapshot) "-SNAPSHOT" else "" + + companion object { + fun SemVer(input: String): SemVer = + SemVerParser.parseEntire(input).getOrElse { error -> + error("provided version to release must be SemVer X.Y.Z, but got error while parsing: $error") + } + + fun of(input: String): SemVer? = + SemVerParser.parseEntire(input).getOrElse { return null } + + fun isValid(input: String): Boolean = + try { + SemVerParser.parseEntireOrThrow(input) + true + } catch (ex: ParseException) { + false + } + } + + private object SemVerParser : Grammar<SemVer>() { + private val dotSeparator by literalToken(".") + private val dashSeparator by literalToken("-") + + /** Non-negative number that is either 0, or does not start with 0 */ + private val number: Parser<Int> by regexToken("""0|[1-9]\d*""").map { it.text.toInt() } + + private val snapshot by -dashSeparator * literalToken("SNAPSHOT") + + override val root: Parser<SemVer> by parser { + val major = number() + dotSeparator() + val minor = number() + dotSeparator() + val patch = number() + val snapshot = checkPresent(snapshot) + SemVer( + major = major, + minor = minor, + patch = patch, + snapshot = snapshot, + ) + } + } +} diff --git a/dokka-runners/dokkatoo/examples/.gitignore b/dokka-runners/dokkatoo/examples/.gitignore new file mode 100644 index 00000000..4a1da3f8 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/.gitignore @@ -0,0 +1,10 @@ +.idea +**/gradle/wrapper/** +gradlew.bat +gradlew +gradle.properties + +versioning-multimodule-example/dokka/previousDocVersions + +# these are test projects, so don't commit the lock file +**/kotlin-js-store/yarn.lock diff --git a/dokka-runners/dokkatoo/examples/README.md b/dokka-runners/dokkatoo/examples/README.md new file mode 100644 index 00000000..f39abb5b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/README.md @@ -0,0 +1,18 @@ +# Dokkatoo Examples + +Examples of how to use Dokkatoo. + +The examples are copied from the Dokka project. + +### Set up + +The Dokka examples are synced automatically from the Dokka source code. + +### Tests + +The projects are tested in the +[`:modules:dokkatoo-plugin-integration-tests`](./../modules/dokkatoo-plugin-integration-tests/) +project. + +The Dokka Publications generated by the Dokka examples are compared against the Dokka +Publications generated by the Dokkatoo projects. diff --git a/dokka-runners/dokkatoo/examples/build.gradle.kts b/dokka-runners/dokkatoo/examples/build.gradle.kts new file mode 100644 index 00000000..da366c88 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/build.gradle.kts @@ -0,0 +1,48 @@ +plugins { + buildsrc.conventions.`maven-publish-test` + buildsrc.conventions.`dokkatoo-example-projects` +} + +dokkaTemplateProjects { + register( + source = "examples/gradle/dokka-customFormat-example", + destination = "custom-format-example/dokka" + ) + register( + source = "examples/gradle/dokka-gradle-example", + destination = "gradle-example/dokka" + ) + register( + source = "examples/gradle/dokka-kotlinAsJava-example", + destination = "kotlin-as-java-example/dokka" + ) + register( + source = "examples/gradle/dokka-library-publishing-example", + destination = "library-publishing-example/dokka" + ) + register( + source = "examples/gradle/dokka-multimodule-example", + destination = "multimodule-example/dokka" + ) + register( + source = "examples/gradle/dokka-multiplatform-example", + destination = "multiplatform-example/dokka" + ) + register( + source = "examples/gradle/dokka-versioning-multimodule-example", + destination = "versioning-multimodule-example/dokka" + ) +} + +configurations.exampleProjectsElements.configure { + outgoing { + artifact(projectDir) { + builtBy(tasks.updateDokkatooExamples) + type = "directory" + } + } +} + +dokkaSourceDownload { + dokkaVersion.set(libs.versions.kotlin.dokka) +} diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/README.md b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/README.md new file mode 100644 index 00000000..a25cd80e --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/README.md @@ -0,0 +1,17 @@ +## Dokka custom format example + +This example demonstrates how to override `.css` styles and add custom images as assets, allowing +you to change the logo used in the header. + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-customFormat-example/html/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtml` task to generate documentation with the custom logo in place: + +```bash +./gradlew dokkaHtml +``` diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/build.gradle.kts new file mode 100644 index 00000000..27540ee6 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/build.gradle.kts @@ -0,0 +1,35 @@ +import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.DokkaBaseConfiguration + +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" +} + +buildscript { + dependencies { + classpath("org.jetbrains.dokka:dokka-base:1.9.0") + } +} + +repositories { + mavenCentral() +} + +tasks.dokkaHtml { + pluginConfiguration<DokkaBase, DokkaBaseConfiguration> { + // Dokka's stylesheets and assets with conflicting names will be overriden. + // In this particular case, logo-styles.css will be overriden and ktor-logo.png will + // be added as an additional image asset + customStyleSheets = listOf(file("logo-styles.css")) + customAssets = listOf(file("ktor-logo.png")) + + // Text used in the footer + footerMessage = "(c) Custom Format Dokka example" + } +} + +dependencies { + testImplementation(kotlin("test-junit")) +} diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..8f9b88b0 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/ktor-logo.png b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/ktor-logo.png Binary files differnew file mode 100644 index 00000000..ef943896 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/ktor-logo.png diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/logo-styles.css b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/logo-styles.css new file mode 100644 index 00000000..ffe4d503 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/logo-styles.css @@ -0,0 +1,20 @@ +/* + * All Margins and sizes are custom for the ktor-logo.png file. + * You may need to override it and find what works best for your case. + */ +:root { + --dokka-logo-image-url: url('../images/ktor-logo.png'); + --dokka-logo-height: 125px; + --dokka-logo-width: 50px; +} + +/* link custom rules styles */ +.library-name--link { + /* ... */ +} + +/* logo custom rules styles */ +.library-name--link::before { + background-position: left; + width: 52px; +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..9855e823 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "dokka-customFormat-example" diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokka/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokka/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..7832a8f9 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka.dokkatoo") version "2.1.0-SNAPSHOT" +} + +dokkatoo { + moduleName.set("customFormat-example") + pluginsConfiguration.html { + // Dokka's stylesheets and assets with conflicting names will be overridden. + // In this particular case, logo-styles.css will be overridden + // and ktor-logo.png will be added as an additional image asset + customStyleSheets.from("logo-styles.css") + customAssets.from("ktor-logo.png") + + // Text used in the footer + footerMessage.set("(c) Custom Format Dokka example") + } +} diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/ktor-logo.png b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/ktor-logo.png Binary files differnew file mode 100644 index 00000000..ef943896 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/ktor-logo.png diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/logo-styles.css b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/logo-styles.css new file mode 100644 index 00000000..ffe4d503 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/logo-styles.css @@ -0,0 +1,20 @@ +/* + * All Margins and sizes are custom for the ktor-logo.png file. + * You may need to override it and find what works best for your case. + */ +:root { + --dokka-logo-image-url: url('../images/ktor-logo.png'); + --dokka-logo-height: 125px; + --dokka-logo-width: 50px; +} + +/* link custom rules styles */ +.library-name--link { + /* ... */ +} + +/* logo custom rules styles */ +.library-name--link::before { + background-position: left; + width: 52px; +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..31ad8c91 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "custom-format-example" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/custom-format-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/Module.md b/dokka-runners/dokkatoo/examples/gradle-example/dokka/Module.md new file mode 100644 index 00000000..0d051cb1 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/Module.md @@ -0,0 +1,7 @@ +# Module Dokka Gradle Example + +This is an example of how you can write module documentation with Dokka. + +# Package demo + +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/README.md b/dokka-runners/dokkatoo/examples/gradle-example/dokka/README.md new file mode 100644 index 00000000..3401e8e9 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/README.md @@ -0,0 +1,22 @@ +# Dokka Gradle example + +This example demonstrates how to apply Dokka in a simple single-project Gradle build, as well as how to configure it. + +Configuration changes: + +* Custom project name used in the header, `Dokka Gradle Example`. +* Description for the project and the packages taken from [Module.md](Module.md). +* Documentation contains source links that lead to declarations in this GitHub repository. + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-gradle-example/html/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtml` task to generate documentation for this example: + +```bash +./gradlew dokkaHtml +``` diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/gradle-example/dokka/build.gradle.kts new file mode 100644 index 00000000..5f54ad9d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/build.gradle.kts @@ -0,0 +1,37 @@ +import org.jetbrains.dokka.gradle.DokkaTask +import java.net.URL + +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test-junit")) +} + +tasks.withType<DokkaTask>().configureEach { + dokkaSourceSets { + named("main") { + // used as project name in the header + moduleName.set("Dokka Gradle Example") + + // contains descriptions for the module and the packages + includes.from("Module.md") + + // adds source links that lead to this repository, allowing readers + // to easily find source code for inspected declarations + sourceLink { + localDirectory.set(file("src/main/kotlin")) + remoteUrl.set(URL("https://github.com/Kotlin/dokka/tree/master/" + + "examples/gradle/dokka-gradle-example/src/main/kotlin" + )) + remoteLineSuffix.set("#L") + } + } + } +} diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/gradle-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..4462f3b5 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/gradle-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..5b8c3c92 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "dokka-gradle-example" diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokka/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/gradle-example/dokka/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokka/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/Module.md b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/Module.md new file mode 100644 index 00000000..0d051cb1 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/Module.md @@ -0,0 +1,7 @@ +# Module Dokka Gradle Example + +This is an example of how you can write module documentation with Dokka. + +# Package demo + +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..2cfc30bd --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka.dokkatoo") version "2.1.0-SNAPSHOT" +} + +dokkatoo { + // used as project name in the header + moduleName.set("Dokka Gradle Example") + + dokkatooSourceSets.main { + + // contains descriptions for the module and the packages + includes.from("Module.md") + + // adds source links that lead to this repository, allowing readers + // to easily find source code for inspected declarations + sourceLink { + localDirectory.set(file("src/main/kotlin")) + remoteUrl("https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-gradle-example/src/main/kotlin") + remoteLineSuffix.set("#L") + } + } +} diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..02c75bfb --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "gradle-example" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/gradle-example/dokkatoo/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/README.md b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/README.md new file mode 100644 index 00000000..647b0e6d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/README.md @@ -0,0 +1,19 @@ +# Dokka Kotlin-as-Java plugin example + +This example demonstrates how you can apply a Dokka plugin in a simple Gradle project. + +In particular, it applies [Kotlin as Java](../../../plugins/kotlin-as-java) Dokka plugin that renders all +Kotlin signatures as Java signatures. + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-kotlinAsJava-example/html/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtml` task to generate documentation for this example: + +```bash +./gradlew dokkaHtml +``` diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/build.gradle.kts new file mode 100644 index 00000000..b339cec3 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test-junit")) + + // Will apply the plugin to all Dokka tasks + dokkaPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.9.0") + + // Will apply the plugin only to the `:dokkaHtml` task + //dokkaHtmlPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.9.0") + + // Will apply the plugin only to the `:dokkaGfm` task + //dokkaGfmPlugin("org.jetbrains.dokka:kotlin-as-java-plugin:1.9.0") +} diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..92f1170f --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..0a0b8c0e --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "dokka-kotlinAsJava-example" diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokka/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..019ac938 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/kotlin-as-java-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "kotlin-as-java-example" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/README.md b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/README.md new file mode 100644 index 00000000..60d967b8 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/README.md @@ -0,0 +1,41 @@ +# Dokka Library publishing example + +This example demonstrates how you can integrate Dokka into the publishing process of your library, adding +documentation generated by Dokka as artifacts. + +This is useful because some repositories, like Maven Central, require documentation (`javadoc.jar`) to be published +alongside library artifacts. + +You can also use services like [javadoc.io](https://javadoc.io/) to host of your library's API documentation for free +and without any additional setup - it will take documentation pages straight from the published artifact. It works with +both HTML and Javadoc formats as demonstrated by +[com.trib3's Javadocs](https://javadoc.io/doc/com.trib3/server/latest/index.html). + +## Running + +Run `dokkaHtml` task to generate documentation for this example: + +```bash +./gradlew dokkaHtml +``` + +### Javadoc jar + +Run `dokkaJavadocJar` task to create a jar file that contains documentation generated in Dokka's Javadoc format. + +```Bash +./gradlew dokkaJavadocJar +``` + +After that, you can find the jar under `build/libs/dokka-library-publishing-example-javadoc.jar` + +### HTML jar + + +Run `dokkaHtmlJar` task to create a jar file that contains documentation generated in Dokka's HTML format. + +```Bash +./gradlew dokkaHtmlJar +``` + +After that, you can find the jar under `build/libs/dokka-library-publishing-example-html.jar` diff --git a/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/build.gradle.kts new file mode 100644 index 00000000..731de85b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" + `java-library` + `maven-publish` +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation(kotlin("test-junit")) +} + +val dokkaJavadocJar by tasks.register<Jar>("dokkaJavadocJar") { + dependsOn(tasks.dokkaJavadoc) + from(tasks.dokkaJavadoc.flatMap { it.outputDirectory }) + archiveClassifier.set("javadoc") +} + +val dokkaHtmlJar by tasks.register<Jar>("dokkaHtmlJar") { + dependsOn(tasks.dokkaHtml) + from(tasks.dokkaHtml.flatMap { it.outputDirectory }) + archiveClassifier.set("html-doc") +} + +publishing { + publications { + register<MavenPublication>("library") { + from(components["java"]) + version = "1.0.0" + groupId = "demo" + artifactId = "dokka-library-publishing-example" + artifact(dokkaJavadocJar) + artifact(dokkaHtmlJar) + } + } +} diff --git a/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..e0847ac9 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/settings.gradle.kts @@ -0,0 +1 @@ +rootProject.name = "dokka-library-publishing-example" diff --git a/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/src/main/kotlin/demo/HelloWorld.kt b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/src/main/kotlin/demo/HelloWorld.kt new file mode 100644 index 00000000..172e18f7 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/library-publishing-example/dokka/src/main/kotlin/demo/HelloWorld.kt @@ -0,0 +1,20 @@ +package demo + +/** + * This class supports greeting people by name. + * + * @property name The name of the person to be greeted. + */ +class Greeter(val name: String) { + + /** + * Prints the greeting to the standard output. + */ + fun greet() { + println("Hello $name!") + } +} + +fun main(args: Array<String>) { + Greeter(args[0]).greet() +} diff --git a/dokka-runners/dokkatoo/examples/library-publishing-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/library-publishing-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..198cfbdf --- /dev/null +++ b/dokka-runners/dokkatoo/examples/library-publishing-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "library-publishing-example" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/README.md b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/README.md new file mode 100644 index 00000000..c8b224ec --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/README.md @@ -0,0 +1,25 @@ +# Dokka MultiModule example + +This example demonstrates how to apply and configure Dokka in a +[multi-project build](https://docs.gradle.org/current/userguide/multi_project_builds.html). + +You can also learn how to set Dokka's version in [gradle.properties](gradle.properties) using `pluginManagement` +configuration block in [settings.gradle.kts](settings.gradle.kts). + +____ + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-multimodule-example/htmlMultiModule/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtmlMultiModule` task to generate documentation for this example: + +```bash +./gradlew dokkaHtmlMultiModule +``` + +It will generate complete documentation for the root project and its subprojects, with a common +Table of Contents. diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/build.gradle.kts new file mode 100644 index 00000000..6b416abc --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/build.gradle.kts @@ -0,0 +1,5 @@ +subprojects { + repositories { + mavenCentral() + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..d25576b8 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/build.gradle.kts new file mode 100644 index 00000000..3563ecca --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/build.gradle.kts @@ -0,0 +1,38 @@ +import org.jetbrains.dokka.DokkaConfiguration.Visibility +import org.jetbrains.dokka.gradle.DokkaTaskPartial +import java.net.URL + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +// You can apply and configure Dokka in each subproject +// individially or configure all subprojects at once +subprojects { + apply(plugin = "org.jetbrains.dokka") + + tasks.withType<DokkaTaskPartial>().configureEach { + dokkaSourceSets.configureEach { + documentedVisibilities.set(setOf( + Visibility.PUBLIC, + Visibility.PROTECTED + )) + + // Read docs for more details: https://kotlinlang.org/docs/dokka-gradle.html#source-link-configuration + sourceLink { + val exampleDir = "https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-multimodule-example" + + localDirectory.set(rootProject.projectDir) + remoteUrl.set(URL("$exampleDir")) + remoteLineSuffix.set("#L") + } + } + } +} + +// Configures only the parent MultiModule task, +// this will not affect subprojects +tasks.dokkaHtmlMultiModule { + moduleName.set("Dokka MultiModule Example") +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/ModuleA.md b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/ModuleA.md new file mode 100644 index 00000000..12712d97 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/ModuleA.md @@ -0,0 +1,5 @@ +# Module childProjectA +This is the child module A + +# Package demo +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts new file mode 100644 index 00000000..7b3b1e23 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts @@ -0,0 +1,16 @@ +import org.jetbrains.dokka.gradle.DokkaTaskPartial + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +// configuration specific to this subproject. +// notice the use of Partial task +tasks.withType<DokkaTaskPartial>().configureEach { + dokkaSourceSets { + configureEach { + includes.from("ModuleA.md") + } + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt new file mode 100644 index 00000000..533b305c --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child project a + */ +class ChildProjectAClass diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/ModuleB.md b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/ModuleB.md new file mode 100644 index 00000000..18a92a33 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/ModuleB.md @@ -0,0 +1,5 @@ +# Module childProjectB +This is the child module B + +# Package demo +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts new file mode 100644 index 00000000..e8b40d4a --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts @@ -0,0 +1,16 @@ +import org.jetbrains.dokka.gradle.DokkaTaskPartial + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +// configuration specific to this subproject. +// notice the use of Partial task +tasks.withType<DokkaTaskPartial>().configureEach { + dokkaSourceSets { + configureEach { + includes.from("ModuleB.md") + } + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt new file mode 100644 index 00000000..6bfd22eb --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child module b + */ +class ChildProjectBClass diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..9844b3cc --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokka/settings.gradle.kts @@ -0,0 +1,15 @@ +pluginManagement { + val kotlinVersion: String by settings + val dokkaVersion: String by settings + + plugins { + kotlin("jvm") version kotlinVersion + id("org.jetbrains.dokka") version dokkaVersion + } +} + +include(":parentProject") +include(":parentProject:childProjectA") +include(":parentProject:childProjectB") + +rootProject.name = "dokka-multimodule-example" diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/build.gradle.kts new file mode 100644 index 00000000..4ee5b079 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + `kotlin-dsl` +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.0") + implementation("org.jetbrains.dokka.dokkatoo:dokkatoo-plugin:2.1.0-SNAPSHOT") +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/settings.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/settings.gradle.kts new file mode 100644 index 00000000..b7510ccf --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/settings.gradle.kts @@ -0,0 +1,21 @@ +rootProject.name = "buildSrc" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + + repositories { + mavenCentral() + gradlePluginPortal() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/src/main/kotlin/dokka-convention.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/src/main/kotlin/dokka-convention.gradle.kts new file mode 100644 index 00000000..04970e8a --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/buildSrc/src/main/kotlin/dokka-convention.gradle.kts @@ -0,0 +1,17 @@ +/** + * Common conventions for generating documentation with Dokkatoo. + */ + +plugins { + id("org.jetbrains.dokka.dokkatoo") +} + +dokkatoo { + dokkatooSourceSets.configureEach { + sourceLink { + // Read docs for more details: https://kotlinlang.org/docs/dokka-gradle.html#source-link-configuration + remoteUrl("https://github.com/Kotlin/dokka/tree/master/examples/gradle/dokka-multimodule-example") + localDirectory.set(rootDir) + } + } +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/build.gradle.kts new file mode 100644 index 00000000..2a698a7b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/build.gradle.kts @@ -0,0 +1,23 @@ +plugins { + kotlin("jvm") apply false + `dokka-convention` +} + +dependencies { + dokkatoo(project(":parentProject:childProjectA")) + dokkatoo(project(":parentProject:childProjectB")) + dokkatooPluginHtml( + dokkatoo.versions.jetbrainsDokka.map { dokkaVersion -> + "org.jetbrains.dokka:all-modules-page-plugin:$dokkaVersion" + } + ) + dokkatooPluginHtml( + dokkatoo.versions.jetbrainsDokka.map { dokkaVersion -> + "org.jetbrains.dokka:templating-plugin:$dokkaVersion" + } + ) +} + +dokkatoo { + moduleName.set("Dokka MultiModule Example") +} diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/ModuleA.md b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/ModuleA.md new file mode 100644 index 00000000..12712d97 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/ModuleA.md @@ -0,0 +1,5 @@ +# Module childProjectA +This is the child module A + +# Package demo +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/build.gradle.kts new file mode 100644 index 00000000..51c17118 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") + `dokka-convention` +} + +dokkatoo { + dokkatooSourceSets.configureEach { + includes.from("ModuleA.md") + } +} + +//region DON'T COPY - this is only needed for internal Dokkatoo integration tests +dokkatoo { + modulePath.set("childProjectA") // match the original dokka default +} +tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + generator.dokkaSourceSets.configureEach { + sourceSetScope.set(":parentProject:childProjectA:dokkaHtmlPartial") + } +} +//endregion diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt new file mode 100644 index 00000000..533b305c --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child project a + */ +class ChildProjectAClass diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/ModuleB.md b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/ModuleB.md new file mode 100644 index 00000000..18a92a33 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/ModuleB.md @@ -0,0 +1,5 @@ +# Module childProjectB +This is the child module B + +# Package demo +This package contains a few examples of Dokka usage. diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/build.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/build.gradle.kts new file mode 100644 index 00000000..69e066bb --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + kotlin("jvm") + `dokka-convention` +} + +dokkatoo { + dokkatooSourceSets.configureEach { + includes.from("ModuleB.md") + } +} + +//region DON'T COPY - this is only needed for internal Dokkatoo integration tests +dokkatoo { + modulePath.set("childProjectB") // match the original dokka default +} +tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + generator.dokkaSourceSets.configureEach { + sourceSetScope.set(":parentProject:childProjectB:dokkaHtmlPartial") + } +} +//endregion diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt new file mode 100644 index 00000000..6bfd22eb --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child module b + */ +class ChildProjectBClass diff --git a/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..a091e8cc --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multimodule-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,21 @@ +rootProject.name = "dokkatoo-multimodule-example" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +include(":parentProject") +include(":parentProject:childProjectA") +include(":parentProject:childProjectB") diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/README.md b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/README.md new file mode 100644 index 00000000..9b8a85e6 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/README.md @@ -0,0 +1,29 @@ +# Dokka Multiplatform example + +This example demonstrates Dokka's configuration and output for a simple +[Kotlin Multiplatform](https://kotlinlang.org/docs/multiplatform.html) project. + +It contains [Kotlin source sets](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) for different +platforms that are automatically picked up by Dokka from the Kotlin Gradle Plugin, and an additional custom source +set known to Dokka only. + +The example demonstrates the following things: + +* Documentation for common code +* Documentation for expect/actual declarations available via tabs +* Documentation for platform-specific declarations, including functions from different source sets, but + with clashing names +* Use of Platform-specific API, such as `CPointer` from `kotlinx.cinterop` + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-multiplatform-example/html/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtml` task in order to generate documentation for this example: + +```bash +./gradlew dokkaHtml +``` diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/build.gradle.kts new file mode 100644 index 00000000..7c5f11cf --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/build.gradle.kts @@ -0,0 +1,42 @@ +@file:Suppress("UNUSED_VARIABLE") + +import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.dokka.Platform + +plugins { + kotlin("multiplatform") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" +} + +repositories { + mavenCentral() +} + +group = "org.dokka.example" +version = "1.0-SNAPSHOT" + +kotlin { + jvm() // Creates a JVM target with the default name "jvm" + linuxX64("linux") + macosX64("macos") + js() + sourceSets { + val commonMain by getting { + dependencies { + implementation(kotlin("stdlib-common")) + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") + } + } + } +} + +tasks.withType<DokkaTask>().configureEach { + dokkaSourceSets { + // Create a custom source set not known to the Kotlin Gradle Plugin + register("customSourceSet") { + this.jdkVersion.set(9) + this.displayName.set("custom") + this.sourceRoots.from(file("src/customJdk9/kotlin")) + } + } +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..58798ccf --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..e9daf094 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/settings.gradle.kts @@ -0,0 +1,2 @@ +rootProject.name = "dokka-multiplatform-example" + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt new file mode 100644 index 00000000..30bea657 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt @@ -0,0 +1,15 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Deferred + +/** + * Common `expect` declaration + */ +expect fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> + +/** + * Common coroutine extension + */ +fun CoroutineDispatcher.name(): String = TODO("Not implemented") diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt new file mode 100644 index 00000000..b241f5ea --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt @@ -0,0 +1,14 @@ +package org.kotlintestmpp.date + +/** + * Common `expect` declaration + */ +expect fun getCurrentDate(): String + +/** + * Common date util function + */ +fun getDate(): String { + return "Today's Date is ${getCurrentDate()}" +} + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt new file mode 100644 index 00000000..96c825c5 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt @@ -0,0 +1,7 @@ +package org.kotlintestmpp.common + +/** + * Common Foo class + */ +class Foo {} + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt new file mode 100644 index 00000000..d77b959b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt @@ -0,0 +1,11 @@ +package org.kotlintest.jdk9 + +/** + * This class demonstrates custom dokka source sets + */ +class CustomSourceSetFile { + /** + * This function will not do anything + */ + fun thisIsAFunction() {} +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt new file mode 100644 index 00000000..85d6beb0 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * JS actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt new file mode 100644 index 00000000..2f4f3c45 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * JS actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + return "test" +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt new file mode 100644 index 00000000..76757359 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt @@ -0,0 +1,18 @@ +package org.kotlintestmpp + +/** + * Function declares in JS source set + */ +fun js() {} + +/** + * Function declared in JS source set. + * + * Function with the same name exists in another source set as well. + */ +fun shared() {} + +/** + * Extension declared in JS source set + */ +fun String.myExtension() = println("test") diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java new file mode 100644 index 00000000..8b11ca09 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java @@ -0,0 +1,19 @@ +package org.kotlintestmpp; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This is a Java annotation + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface JavaAnnotation { + String usage(); + + String[] aliases(); + + String description(); +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt new file mode 100644 index 00000000..8f7fda49 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * JVM actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt new file mode 100644 index 00000000..db7f2d74 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * JVM actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + return "test" +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt new file mode 100644 index 00000000..0ef8a99d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt @@ -0,0 +1,35 @@ +package org.kotlintestmpp + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.kotlintestmpp.common.Foo + +/** + * Function declared in JVM source set + * + * also see the [Foo] class + * @see org.kotlintestmpp.common.Foo + */ +fun jvm() {} + +/** + * Function declared in JVM source set + * + * Function with the same name exists in another source set as well. + */ +fun shared() {} + +/** + * Extension declared in JVM source set + */ +fun CoroutineScope.startConnectionPipeline( + input: String +): Job = launch { TODO() } + +/** + * Extension declared in JVM source set + */ +fun String.myExtension() = println("test2") + + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt new file mode 100644 index 00000000..5c84780b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt @@ -0,0 +1,15 @@ +@file:Suppress("unused") + +package org.kotlintestmpp + +import kotlinx.cinterop.CPointed +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi + +/** + * Low-level Linux function + */ +@OptIn(ExperimentalForeignApi::class) +fun <T : CPointed> printPointerRawValue(pointer: CPointer<T>) { + println(pointer.rawValue) +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt new file mode 100644 index 00000000..b561272d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * Linux actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt new file mode 100644 index 00000000..8900f87b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * Linux actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt new file mode 100644 index 00000000..8576982c --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * MacOS actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt new file mode 100644 index 00000000..accf98a9 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokka/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * MacOS actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..3d5a9e0d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/build.gradle.kts @@ -0,0 +1,39 @@ +plugins { + kotlin("multiplatform") version "1.9.0" + id("org.jetbrains.dokka.dokkatoo") version "2.1.0-SNAPSHOT" +} + +group = "org.dokka.example" +version = "1.0-SNAPSHOT" + +kotlin { + jvm() // Creates a JVM target with the default name "jvm" + linuxX64("linux") + macosX64("macos") + js(IR) { + browser() + } + sourceSets { + commonMain { + dependencies { + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1") + } + } + } +} + +dokkatoo { + // Create a custom source set not known to the Kotlin Gradle Plugin + dokkatooSourceSets.register("customSourceSet") { + jdkVersion.set(9) + displayName.set("custom") + sourceRoots.from("src/customJdk9/kotlin") + } +} + + +//region DON'T COPY - this is only needed for internal Dokkatoo integration tests +dokkatoo { + sourceSetScopeDefault.set(":dokkaHtml") +} +//endregion diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..014a7584 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "dokka-multiplatform-example" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt new file mode 100644 index 00000000..30bea657 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonCoroutineExtensions.kt @@ -0,0 +1,15 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Deferred + +/** + * Common `expect` declaration + */ +expect fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> + +/** + * Common coroutine extension + */ +fun CoroutineDispatcher.name(): String = TODO("Not implemented") diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt new file mode 100644 index 00000000..b241f5ea --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/CommonDateUtils.kt @@ -0,0 +1,14 @@ +package org.kotlintestmpp.date + +/** + * Common `expect` declaration + */ +expect fun getCurrentDate(): String + +/** + * Common date util function + */ +fun getDate(): String { + return "Today's Date is ${getCurrentDate()}" +} + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt new file mode 100644 index 00000000..96c825c5 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/commonMain/kotlin/org/kotlintestmpp/common/Foo.kt @@ -0,0 +1,7 @@ +package org.kotlintestmpp.common + +/** + * Common Foo class + */ +class Foo {} + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt new file mode 100644 index 00000000..d77b959b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/customJdk9/kotlin/org/kotlintest/jdk9/CustomSourceSetFile.kt @@ -0,0 +1,11 @@ +package org.kotlintest.jdk9 + +/** + * This class demonstrates custom dokka source sets + */ +class CustomSourceSetFile { + /** + * This function will not do anything + */ + fun thisIsAFunction() {} +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt new file mode 100644 index 00000000..85d6beb0 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * JS actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt new file mode 100644 index 00000000..2f4f3c45 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * JS actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + return "test" +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt new file mode 100644 index 00000000..76757359 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jsMain/kotlin/org/kotlintestmpp/JsFunctions.kt @@ -0,0 +1,18 @@ +package org.kotlintestmpp + +/** + * Function declares in JS source set + */ +fun js() {} + +/** + * Function declared in JS source set. + * + * Function with the same name exists in another source set as well. + */ +fun shared() {} + +/** + * Extension declared in JS source set + */ +fun String.myExtension() = println("test") diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java new file mode 100644 index 00000000..8b11ca09 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JavaAnnotation.java @@ -0,0 +1,19 @@ +package org.kotlintestmpp; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * This is a Java annotation + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +public @interface JavaAnnotation { + String usage(); + + String[] aliases(); + + String description(); +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt new file mode 100644 index 00000000..8f7fda49 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * JVM actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt new file mode 100644 index 00000000..db7f2d74 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * JVM actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + return "test" +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt new file mode 100644 index 00000000..0ef8a99d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/jvmMain/kotlin/org/kotlintestmpp/JvmFunctions.kt @@ -0,0 +1,35 @@ +package org.kotlintestmpp + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.launch +import org.kotlintestmpp.common.Foo + +/** + * Function declared in JVM source set + * + * also see the [Foo] class + * @see org.kotlintestmpp.common.Foo + */ +fun jvm() {} + +/** + * Function declared in JVM source set + * + * Function with the same name exists in another source set as well. + */ +fun shared() {} + +/** + * Extension declared in JVM source set + */ +fun CoroutineScope.startConnectionPipeline( + input: String +): Job = launch { TODO() } + +/** + * Extension declared in JVM source set + */ +fun String.myExtension() = println("test2") + + diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt new file mode 100644 index 00000000..5c84780b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/CInterop.kt @@ -0,0 +1,15 @@ +@file:Suppress("unused") + +package org.kotlintestmpp + +import kotlinx.cinterop.CPointed +import kotlinx.cinterop.CPointer +import kotlinx.cinterop.ExperimentalForeignApi + +/** + * Low-level Linux function + */ +@OptIn(ExperimentalForeignApi::class) +fun <T : CPointed> printPointerRawValue(pointer: CPointer<T>) { + println(pointer.rawValue) +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt new file mode 100644 index 00000000..b561272d --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * Linux actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt new file mode 100644 index 00000000..8900f87b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/linuxMain/kotlin/org/kotlintestmpp/LinuxDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * Linux actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt new file mode 100644 index 00000000..8576982c --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsCoroutineExtensions.kt @@ -0,0 +1,11 @@ +package org.kotlintestmpp.coroutines + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred + +/** + * MacOS actual implementation for `asyncWithDelay` + */ +actual fun <T> CoroutineScope.asyncWithDealy(delay: Long, block: suspend () -> T): Deferred<T> { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt new file mode 100644 index 00000000..accf98a9 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/multiplatform-example/dokkatoo/src/macosMain/kotlin/org/kotlintestmpp/MacOsDateUtils.kt @@ -0,0 +1,8 @@ +package org.kotlintestmpp.date + +/** + * MacOS actual implementation for `getCurrentDate` + */ +actual fun getCurrentDate(): String { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/README.md b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/README.md new file mode 100644 index 00000000..dd012003 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/README.md @@ -0,0 +1,25 @@ +# Dokka Versioning MultiModule example + +This example demonstrates configuration of Dokka's [versioning plugin](../../../plugins/versioning), which +allows readers to navigate through different versions of your API reference documentation. + +The example contains some code that exists only in the current documentation version `1.0`. You will not see +this code in the previous version `0.9`, which is located in the [previousDocVersions](previousDocVersions) directory. + +___ + +You can see up-to-date documentation generated for this example on +[GitHub Pages](https://kotlin.github.io/dokka/examples/dokka-versioning-multimodule-example/htmlMultiModule/index.html). + +![screenshot demonstration of output](demo.png) + +### Running + +Run `dokkaHtmlMultiModule` task to generate documentation for this example: + +```bash +./gradlew dokkaHtmlMultiModule +``` + +It will generate complete documentation for the root project and its subprojects, with the version navigation +dropdown menu. diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/build.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/build.gradle.kts new file mode 100644 index 00000000..14aea2b4 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/build.gradle.kts @@ -0,0 +1,19 @@ +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka") version "1.9.0" apply false +} + +// The versioning plugin must be applied in all submodules +subprojects { + repositories { + mavenCentral() + } + apply { + plugin("org.jetbrains.kotlin.jvm") + plugin("org.jetbrains.dokka") + } + val dokkaPlugin by configurations + dependencies { + dokkaPlugin("org.jetbrains.dokka:versioning-plugin:1.9.0") + } +} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/demo.png b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/demo.png Binary files differnew file mode 100644 index 00000000..9ac1d382 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/demo.png diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/build.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/build.gradle.kts new file mode 100644 index 00000000..6d830079 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/build.gradle.kts @@ -0,0 +1,27 @@ +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask +import org.jetbrains.dokka.versioning.VersioningPlugin +import org.jetbrains.dokka.versioning.VersioningConfiguration + +buildscript { + dependencies { + classpath("org.jetbrains.dokka:versioning-plugin:1.9.0") + } + + repositories { + mavenCentral() + } +} + +val currentVersion = "1.0" +val previousVersionsDirectory = project.rootProject.projectDir.resolve("previousDocVersions").invariantSeparatorsPath + +// Main configuration for the versioning plugin. It will generate documentation for +// the current version of the application, and look for previous versions of docs +// in the directory defined in previousVersionsDirectory, allowing it to create +// the version navigation dropdown menu. +tasks.dokkaHtmlMultiModule { + pluginConfiguration<VersioningPlugin, VersioningConfiguration> { + version = currentVersion + olderVersionsDir = file(previousVersionsDirectory) + } +} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts new file mode 100644 index 00000000..bf1513f8 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/build.gradle.kts @@ -0,0 +1 @@ +// intentionally empty - build config is set in the root build.gradle.kts diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt new file mode 100644 index 00000000..e0f9e86a --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/ChildProjectAClass.kt @@ -0,0 +1,18 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child project A + * + * @since 0.9 + */ +class ChildProjectAClass { + + /** + * Function that extends [ChildProjectAClass] + * + * @since 1.0 + */ + fun extend() {} +} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/FancyAPI.kt b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/FancyAPI.kt new file mode 100644 index 00000000..e691be03 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectA/src/main/kotlin/demo/FancyAPI.kt @@ -0,0 +1,10 @@ +package demo + +/** + * New shiny fancy API + * + * @since 1.0 + */ +class FancyAPI { + fun doSomething() {} +} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts new file mode 100644 index 00000000..bf1513f8 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/build.gradle.kts @@ -0,0 +1 @@ +// intentionally empty - build config is set in the root build.gradle.kts diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt new file mode 100644 index 00000000..6978a176 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/ChildProjectBClass.kt @@ -0,0 +1,10 @@ +@file:Suppress("unused") + +package demo + +/** + * Class defined in child module B + * + * @since 0.9 + */ +class ChildProjectBClass diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/Functions.kt b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/Functions.kt new file mode 100644 index 00000000..35a9275b --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/parentProject/childProjectB/src/main/kotlin/demo/Functions.kt @@ -0,0 +1,8 @@ +package demo + +/** + * New super function that does everything + * + * @since 1.0 + */ +fun superFunction42() {} diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/settings.gradle.kts new file mode 100644 index 00000000..ec6614f0 --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +include(":parentProject") +include(":parentProject:childProjectA") +include(":parentProject:childProjectB") + +rootProject.name = "dokka-versioning-multimodule-example"
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..850a82bf --- /dev/null +++ b/dokka-runners/dokkatoo/examples/versioning-multimodule-example/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "versioning-multimodule-example" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/gradle.properties b/dokka-runners/dokkatoo/gradle.properties new file mode 100644 index 00000000..cb5238c6 --- /dev/null +++ b/dokka-runners/dokkatoo/gradle.properties @@ -0,0 +1,12 @@ +# memory is set quite high because the tests launch a lot of processes +org.gradle.jvmargs=-Dfile.encoding=UTF-8 -Xmx4g -XX:MaxMetaspaceSize=2g -XX:+HeapDumpOnOutOfMemoryError -XX:+AlwaysPreTouch + +org.gradle.caching=true + +org.gradle.unsafe.configuration-cache=true +org.gradle.unsafe.configuration-cache-problems=warn + +org.gradle.parallel=true +org.gradle.welcome=never + +kotlin.mpp.import.enableKgpDependencyResolution=true diff --git a/dokka-runners/dokkatoo/gradle/libs.versions.toml b/dokka-runners/dokkatoo/gradle/libs.versions.toml new file mode 100644 index 00000000..4a6fa4ff --- /dev/null +++ b/dokka-runners/dokkatoo/gradle/libs.versions.toml @@ -0,0 +1,48 @@ +[versions] + +kotlin = "1.9.0" # should match Gradle's embedded Kotlin version https://docs.gradle.org/current/userguide/compatibility.html#kotlin +kotlin-dokka = "1.9.0" +kotlinx-serialization = "1.6.0" + +kotest = "5.6.2" + +gradlePlugin-android = "8.0.2" +gradlePlugin-dokkatoo = "1.6.0" +gradlePlugin-gradlePublishPlugin = "1.2.1" +gradlePlugin-bcvMu = "0.0.4" + + +[libraries] + +## Dokka +kotlin-dokkaCore = { module = "org.jetbrains.dokka:dokka-core", version.ref = "kotlin-dokka" } +kotlin-dokkaPlugin-allModulesPage = { module = "org.jetbrains.dokka:all-modules-page-plugin", version.ref = "kotlin-dokka" } +kotlin-dokkaPlugin-templating = { module = "org.jetbrains.dokka:templating-plugin", version.ref = "kotlin-dokka" } + +## Kotlinx Serialization +kotlinxSerialization-bom = { module = "org.jetbrains.kotlinx:kotlinx-serialization-bom", version.ref = "kotlinx-serialization" } +kotlinxSerialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json" } +#kotlinxSerialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinx-serialization" } + + +### Test libraries ### + +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } + +kotest-bom = { module = "io.kotest:kotest-bom", version.ref = "kotest" } +kotest-datatest = { module = "io.kotest:kotest-framework-datatest" } +kotest-junit5Runner = { module = "io.kotest:kotest-runner-junit5" } +kotest-assertionsCore = { module = "io.kotest:kotest-assertions-core" } +kotest-assertionsJson = { module = "io.kotest:kotest-assertions-json" } + +### Gradle plugins ### + +gradlePlugin-android = { module = "com.android.tools.build:gradle", version.ref = "gradlePlugin-android" } +gradlePlugin-androidApi = { module = "com.android.tools.build:gradle-api", version.ref = "gradlePlugin-android" } +gradlePlugin-dokkatoo = { module = "dev.adamko.dokkatoo:dokkatoo-plugin", version.ref = "gradlePlugin-dokkatoo" } +gradlePlugin-bcvMu = { module = "dev.adamko.kotlin.binary_compatibility_validator:bcv-gradle-plugin", version.ref = "gradlePlugin-bcvMu" } +gradlePlugin-gradlePublishPlugin = { module = "com.gradle.publish:plugin-publish-plugin", version.ref = "gradlePlugin-gradlePublishPlugin" } +gradlePlugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +gradlePlugin-kotlin-klibCommonizerApi = { module = "org.jetbrains.kotlin:kotlin-klib-commonizer-api", version.ref = "kotlin" } + +[plugins] diff --git a/dokka-runners/dokkatoo/modules/docs/build.gradle.kts b/dokka-runners/dokkatoo/modules/docs/build.gradle.kts new file mode 100644 index 00000000..a27b177c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/build.gradle.kts @@ -0,0 +1,58 @@ +import dev.adamko.dokkatoo.dokka.plugins.DokkaHtmlPluginParameters + +plugins { + buildsrc.conventions.base + dev.adamko.`dokkatoo-html` +} + +dependencies { + dokkatoo(projects.modules.dokkatooPlugin) + dokkatooPluginHtml(libs.kotlin.dokkaPlugin.allModulesPage) + dokkatooPluginHtml(libs.kotlin.dokkaPlugin.templating) +} + +dokkatoo { + moduleName.set("Dokkatoo Gradle Plugin") + + pluginsConfiguration.named<DokkaHtmlPluginParameters>("html") { + customStyleSheets.from( + "./style/logo-styles.css", + ) + customAssets.from( + "./images/logo-icon.svg", + ) + } +} + +tasks.dokkatooGeneratePublicationHtml { + doLast { + outputDirectory.get().asFile.walk() + .filter { it.isFile && it.extension == "html" } + .forEach { file -> + file.writeText( + file.readText() + .replace( + """<html>""", + """<html lang="en">""", + ) + .replace( + """ + <button id="theme-toggle-button"> + """.trimIndent(), + """ + <div id="github-link"><a href="https://github.com/adamko-dev/dokkatoo/"></a></div> + <button id="theme-toggle-button"> + """.trimIndent(), + ) + .replace( + """ + href="https://github.com/Kotlin/dokka"><span>dokka</span> + """.trimIndent(), + """ + href="https://github.com/adamko-dev/dokkatoo/"><span>Dokkatoo</span> + """.trimIndent(), + ) + ) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/docs/images/banner.svg b/dokka-runners/dokkatoo/modules/docs/images/banner.svg new file mode 100644 index 00000000..ee9c57e6 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/images/banner.svg @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + viewBox="0 0 128 32" + width="1920" + height="480" + xmlns="http://www.w3.org/2000/svg" + style="background-color: black;" +> + <defs> + <style> + /* + Specifically only embed the font required for 'Dokkatoo' + // NOTE replace & with plain ampersand in URL, because XML can't handle it even though it's in a comment + https://fonts.googleapis.com/icon?family=JetBrains+Mono&text=Dokkatoo + https://amio.github.io/embedded-google-fonts/ + */ + @font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 400; + src: url(data:application/font-woff2;charset=utf-8;base64,d09GMgABAAAAAAP0AA8AAAAAB3QAAAObAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGhwbHhwoBmA/U1RBVF4AXBEICoM0gn8LEAABNgIkAxAEIAWFAAcgGywGyAQeKK/6V5U0JrWweuRkIPSAPu3uB3ECeFTjw871+62KWCXStRIqNcGi7y+i1sSSaUziSTQxRM5L6vcAzzDqRKNLEcAyDCJUdBzUN3C9Btu4UaSm55biRv/HadtC+EEOMEosXtPTONLHCq1Fsl2X1eJCHCDPyko05JykRD2hIjkuIYS2cyPSXsWf3wBueJ1sC3iG5f/b2r//+0D7VxzbhbjhgyDIA/JtMO5qKuXOQ8A6NNq+2Hh7x0jdnuwcaewRKxEcYANONARSrGo4CBul0MWFIJ+4NT3yRL1dPCdiSygYxYmvk4krKtzvZBepNNJNPyOdbX3ca39uP2nfZF9pX2ZflLElKBYI22blBV2fYOCIInJaMz4oQh1zGhLZdCjgz3VNW2ZsV8vXrFirHMv1puWOlu5lyqHO8fuRCz6fUa7OCQptMWAYZWcDYjGolFh83Tjr22YEg+ocP3yOIYyNC77yc4JCGGdfZeE15VBSVajKKqPcoPFsICDEoqzghwWfZlkM4D/EBwv+5WcDQjOEEgqFIReUovrtW9+u8xyLS7KPxh6j9u0nM9oiohtT9qZEN7VFZDQVNFvZWdZeMzvLauafkIrmyty85oqyu6PCXor2mIesanf6cEmFt7exdKqyc+X38bVpkenJRUmh+Xce9xw98Snet6uv6Lrh4/ZHHI/v7Lzh1+7fOOqyvJ7wevNiK7zO67Esrye8zrzEDK/3esyCONNMvjjZNONiLTP54mTLpCMjPyct5Zria66pvubd4ndTkuIvLwbg3zE5JiN1c+UTM96V8b+5Ne3Lj/F3Xr/96tO2t1/9+a+Y/336xdq/hM7ZoJo96jk5h37PXzH/XaBfPLYXZCXV1GLipYyoGTebSzxCHw/glKJWpG8Zl88xSl8PwSfEiZtIsE2sEUlWCF2kEc8rIp1jXCtysIUlkZMjVApXEMkeSlRj3IBDenFlYiXLMJCs0g5BMf300kgfWYxosj100kwFrfl2OhiJ60cYYJhYTmqHhmlmiE4GGGGYE+jtpIff0c8Q7ZykgHRyyaaVESyGaKSTPobJox8U+MhximmlnVF6aGSIclp1KAB+ZXcRygkmHq1DdvMuXd6upgwkTkfr0JfK2zx0XWFxCv0MMNl19cBAU/FUvJxdlNJBK7uavK+QIfrpopXmrH6T0QGGoQPR+EOD7W+n82fxKE2coHmyJp2sYNqT3cXDe5zSTNqthC7U1gAAAA==) format('woff2'); + } + </style> + + <linearGradient + id="upper-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="0" stop-color="#2d2d2d"/> + <stop offset="1" stop-color="#3d3d3d"/> + </linearGradient> + <linearGradient + id="lower-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="1" stop-color="#1d1d1d"/> + <stop offset="0" stop-color="#2d2d2d"/> + </linearGradient> + <linearGradient + id="blue-purple-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.3" stop-color="#0095D5"/> + <stop offset="0.5" stop-color="#3C83DC"/> + <stop offset="0.6" stop-color="#6D74E1"/> + <stop offset="0.7" stop-color="#806EE3"/> + </linearGradient> + <linearGradient + id="purple-orange-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.5" stop-color="#C757BC"/> + <stop offset="0.65" stop-color="#D0609A"/> + <stop offset="0.9" stop-color="#F88909"/> + </linearGradient> + </defs> + + <rect id="black-background" width="100%" height="100%"/> + + <g id="bird"> + <path id="blue-feathers" fill="url(#blue-purple-gradient)" + d=" + M5,19 v-10 l7,-7 h10 + M20,16 l8,-8 v10 l-3,3 + "/> + <path id="orange-feather" fill="url(#purple-orange-gradient)" + d="M11,13 l11,-11 h6 v6 l-8,8 l-3,-3"/> + <path id="white-head" fill="#fff" + d="M5,19 l6,-6 h6 l8,8 v3 l2,6 h-18 l2,-4"/> + <g id="beak" stroke-width="0.2" stroke="#666" > + <path id="lower-beak" fill="url(#lower-beak-gradient)" + d="M5,26 l4,3 l4,-3 Z"/> + <path id="upper-beak" fill="url(#upper-beak-gradient)" + d="M4,26 l5,-4 l4,4 Z"/> + </g> + <g id="eye"> + <circle id="eye-base" fill="#000" cx="15" cy="21" r="1.5"/> + <path id="eye-twinkle" fill="none" + stroke="#f5f5f5" + stroke-linecap="round" + stroke-width=".2" + d="M15.1,20.1 q0.9,-0.1 1,0.9"/> + </g> + </g> + + <text x="36" y="28" font-family="JetBrains Mono" font-size="18" fill="#fff">Dokkatoo</text> +</svg> diff --git a/dokka-runners/dokkatoo/modules/docs/images/logo-icon.svg b/dokka-runners/dokkatoo/modules/docs/images/logo-icon.svg new file mode 100644 index 00000000..5b343c11 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/images/logo-icon.svg @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + viewBox="0 0 32 32" + height="480" + width="480" + style="background-color: black;" + xml:space="preserve" + xmlns="http://www.w3.org/2000/svg"> + <defs> + + <linearGradient + id="upper-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="0" stop-color="#2d2d2d"/> + <stop offset="1" stop-color="#3d3d3d"/> + </linearGradient> + <linearGradient + id="lower-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="1" stop-color="#1d1d1d"/> + <stop offset="0" stop-color="#2d2d2d"/> + </linearGradient> + <linearGradient + id="blue-purple-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.3" stop-color="#0095D5"/> + <stop offset="0.5" stop-color="#3C83DC"/> + <stop offset="0.6" stop-color="#6D74E1"/> + <stop offset="0.7" stop-color="#806EE3"/> + </linearGradient> + <linearGradient + id="purple-orange-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.5" stop-color="#C757BC"/> + <stop offset="0.65" stop-color="#D0609A"/> + <stop offset="0.9" stop-color="#F88909"/> + </linearGradient> +</defs> + + <rect id="black-background" width="100%" height="100%"/> + + <g id="bird"> + <path id="blue-feathers" fill="url(#blue-purple-gradient)" + d=" + M5,19 v-10 l7,-7 h10 + M20,16 l8,-8 v10 l-3,3 + "/> + <path id="orange-feather" fill="url(#purple-orange-gradient)" + d="M11,13 l11,-11 h6 v6 l-8,8 l-3,-3"/> + <path id="white-head" fill="#fff" + d="M5,19 l6,-6 h6 l8,8 v3 l2,6 h-18 l2,-4"/> + <g id="beak" stroke-width="0.2" stroke="#666" > + <path id="lower-beak" fill="url(#lower-beak-gradient)" + d="M5,26 l4,3 l4,-3 Z"/> + <path id="upper-beak" fill="url(#upper-beak-gradient)" + d="M4,26 l5,-4 l4,4 Z"/> + </g> + <g id="eye"> + <circle id="eye-base" fill="#000" cx="15" cy="21" r="1.5"/> + <path id="eye-twinkle" fill="none" + stroke="#f5f5f5" + stroke-linecap="round" + stroke-width=".2" + d="M15.1,20.1 q0.9,-0.1 1,0.9"/> + </g> + </g> +</svg> diff --git a/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.png b/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.png Binary files differnew file mode 100644 index 00000000..37d731bd --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.png diff --git a/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.svg b/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.svg new file mode 100644 index 00000000..46878cce --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/images/social_preview_banner.svg @@ -0,0 +1,106 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<svg + version="1.1" + viewBox="0 0 128 64" + width="1280" + height="640" + xmlns="http://www.w3.org/2000/svg" + style="background-color: black;" +> + <defs> + <style> + /* + Specifically only embed the font required for 'Dokkatoo' + // NOTE replace & with plain ampersand in URL, because XML can't handle it even though it's in a comment + https://fonts.googleapis.com/icon?family=JetBrains+Mono&text=Dokkatoo https://github.com/adamko-dev/dokkatoo Generate Kotlin documentation + https://amio.github.io/embedded-google-fonts/ + */ + @font-face { + font-family: 'JetBrains Mono'; + font-style: normal; + font-weight: 400; + src: url(data:font/woff2;base64,d09GMgABAAAAAA9AAA8AAAAAHpAAAA7kAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGkAbHhyPaAZgP1NUQVReAIECEQgKnnSYJQt8AAE2AiQDgXQEIAWFAAcgG8AZI1K0ekimjtSFP3/+/fw+3Kpf1dVZ6E6AMc+aOjNrYrDnzJ66Z+DEhTeZLzf7CfVM66mYpBpKN8wkpCZACB1KsBorYsf2ZnRVleN/vf49fROD6LQJ3rqkOhVQXGibKp8iTaEHhORB5anm8n9zpZ0JvDe5ArFkPa/y1FaSEjs/OcjO/t0DzjEmx5ArkAJ2fVWnC6iuVQSKQFbYalQVTlfJWgynOYhWjNaXGmNfIqO/jIo3BwFwkdoUybIJAJH0Wwyy0c9kpUE1qjcbM25KEhFIHsLQGmjWDMBRcPtWG5ft3IwYyuFJzprswxipHE8BcXdesWzjThRv3LJiI2IaOtKuwD9boQWicJFAEUpQhkrUoA4t0QYd0Ald0B290Rf9MRCDMRTDMapFxPnP+cE56Gx2fnB+cJLOOP2T/kS/pZfrpH5Lv6XLtZG8fCJvyQNyk+yV9ZKXPtJO8uKof9RH6g11WG1Vy/k3/kItV8v5BX6Ir+CzeAT3kjw9RfdIXvJ0ER2j7bSSJtAg8qkQ/+A7vIGncAMuwokg1N8bAcEBM5ZpsdlCHUYMTi5RwXAyuJU53wiT3Z3zUG0vF3gyuGGxyUJ7EHAa+CG79kEhLdTb8YMCXRT3ZP2d/g6kmhN+QGAAN5v0EwKf63D9YuNj9gXAw+D6LFFnge+ssRhTMABtkIAKuqiDYHVQHTSTvegkmabWqhIAPWs5a4QeoSexB616BJQaMTR9ySHVrUtYJH6ULIubKoieglHoBR+xOE4RhLpJJu9N45/CXfXd+AMwf8AfWM/kV2T/zkV8DxIMGHKWft34zkzxBZeyrJf1CddxH6gPDKPaJVRLtUp7Jm3SyCgBpd1CSUhiGv7S2jJ8JgCxKVLxRpfyuDdwIe5Dq9xpb0CUuXScI4NQWjN1yTQc1VoxbBUIb0UmlqI/dsMElDGVnIZBnH8xfFSZ0C56IXBxIqGSvTk+KlIYV6aNFZjznE+jhBLSro5zFH81LAPhPyv+qtLyPkApXa1tSfwACK8ouIaBzZnX8DXpeIDKl590CUpqa/B+/TR/qUngTlRWMc/iWYm+mi+Wk5yMz51qCMrdSynnsrGLcoBCYfYE9FJZwNQAK/hF8w7uawyvLcFreE2JJ9M9Ah9SEp6wiukKuiLe39GjZZ8FJSG2ml6mupTHp7onrkbr3OnnIuJrOEYGodgQOYe9oBvp84A+ogkFOJFQ4RtYeHbFNQvDR9NI+MiZfAePFRrMCRAojoGL8AkFxkIswok4Ub9FF3qBvGBoQZ7YtQwHiMCNqMPFV9ogAidgAE5S4AVCGCj0D7jDg5eMmJvJaiA8n86ABjjDcxGePHPRYvSiEYd2EMWgyxXgMIF/vmdKYOakUej4BLzmUOWbA7kwT138rpd/nAO4Hb93HktdQhCSPRpq424D3rNm7c5KNlm3c9lGikO2O+OgbgVMiScPMOzKJHQGwI84mZBntDiVnjLSGSxb540j2HymQYM9mgMf3TEIYzAek7EK67AVu59uCnx03bXpMqzBBmy/lwXCT8JHw4fDB8Krw6tUnMUhetW1qECNayVhiHh95VA93U8mMZmUrG8H3DXk1p2onh4kzzSGZyZQcbG1+0CUv5ihwkEqZBKFDmwg16ZAQ0jvDqKUa9p4NbYsVlsWqzR1sTLjxTuUFsbKY6Vdiwo913VLYkNk8G24brV4XiQi8agTj7siTiwq1RJ1CnzXak+We3rlBtdq29jUBE4FgWmwjTlS6awxyzLZLKl0LrfnDiadNZkAnGra24EMWaSCQL4C2wV27jwz33B9JkvpnLWUfstkgmoTdAUbMmWpoIHAMm/c981lmSx1NDDaImk7pA0QWqsEArNkjfLrzVXb3LYx19oGwc/DRPqOO9kGsmLIdEao6eEafmq8XhdZ8I2NvHqyjY9PMMe0TwXmgMntbHhxKjDz03qrLVDbPDbqeNzFkvUwgKYiNBAcC0UpsdAggjsvTUUEbr4Nd1cDmLTlIy23GhbN4QDkjP8UTJP+9tUPg7lxvM2f3PTLBNCAvHwxLQOl8unQbID0LrC70YcMuP01bP6W2nKn3vKlCR91/HM2veMGTY/dApuv18mfP4PNn+iIsV+UDKnUDWjNZgjBH5EeAL0C5n/zE1Rh/+7zyTsvZtrggGvsDrXlLhy/DTbfGsl4a4pC0cDjhDQLKf1kAOa92XD8dPTwtGJRdt69m5hw5MqTAosiH75+PT5xx4ED1L/wje3blxQ/c/NmoSKbDz26lMdM376dLJstO86cSaG3nTqVJK+Dq21UBFAYPaNrKRxWdWpWKzQFxRRongJoRisfyAYVWqE8R68m+q4UuCkXWOSmPMB56BIzfTnf5QyBmigQqREk8gWC3crJk4m+EyUWKEeBSPNaG4FyDIg2h0dP0G91Exs0T00xMSB6ejpeXvt1wTqw9s84doyZPl4su6koEEHFgdWEgsDKjmlSHQHC5XV7R1L10MJvuuJAbH84OXcdWVlae3KIHO48D662jL24c+dGs8O/BHyu5Pnt29dPlAmjQPipT/OO039jiP+Q386W6fT/lzaWgQH/7ZbhOs4l5oqce7iuxd3jEdrbhFzc3iZ4QGWY4GRrVdwu1NqdrCA4qmvsmPBI0OkEzHfwgqjTIZEnU8Yfp1CN6qgGA36inb+fc19S76wdOd7nNYK/wxSPtbPLo8gnGmqecCwuEkho8zKT4ljgMq+1eqN+NA1I9c2oT1/dfaycLa2YBR1hhr5WPRPrJX6yj1rPMLyxTTR5Fjj2q54Fi7u0WEQ+hEVtA5J4H48kUBkmuNk6FfuEOrubFRzLhjQKyUMcp5MEfoaXRB2HtLlEoywbAjDMOLhA3e/wLDAZ+tp4hqHW7yN+4o1l9H2t+V2cXuJ9vF7itFhEPoRFUBqGnTW1qt6Hau3OGoydNbV25LLUYWyYU/vLIAk1LO8tFPIxkvg5WMLpQWDfRePZkqGiZsoTG9nUI4mya6G6XHUtkMUeqSkyxtvf2E7kvrw8Ti/xXl4vcVpeRF7Ei2DATy554/JwV7Nkcg1Zl5JRHY/qOSQhFcjGh1tPGAM/XTcCh5/MzN3708i1oJuZ3r2/zPsVrKpqyS+UKn++bozT51iyzhqBUNAjxH6tCCqpYEX+cElhRX5JLdeFDpgO5jxOS53NOdR6MHc2Le1SziEwUEC8cXm4q1kyuYasS8mo7kc9h0SkgtIQwcHWqnhCqLU7WEFwsLV2PIlrVQeLe3QYowmEsa5RwGgCCRjYLvqJN5bRG1sNJvd8x359ma2X5URTG88w1Pp9q3/XIhH5EBK1DVjifTyWwKICqzc+DfV3kk5leL49myRj3tD4oUiFVlEEql6Enm6WYixQ+niFRrHM29tJjUiT9ERdciEp4jNMSUuNHQVPPvgVTw9bm9TI0ISkJKYW55agKiE9RatepF+0yLjoof6hOj5mrh5eDUw9upuLjEVx8ZrwCIUmrshYZO7VHZ/3cQUDeiyXByjk0/v2yN/HwbsBf4Fzv8Tzl9y5C/BewN2l6bscb6VpK7eKu0vTdzneStNWXL+4O0iRrIuOTtYph+QM0VWERJhUkf7G7NCe1THXqBYU1YJydnpKUS0p7C5MCAxNOxmieMC7/Wioy3Nx0ue56KwL36tV23u3MxlM7Xpv16v/T7TqDC5OeoNLWMMfbuHvFeyOsLSEwLC0HV18M+tl+nY5geQubYM3m9oGb8YkN6Ig8HEjy8h8soygqRs5QkwAOUJMZD+B9xb2IMceYtlDLHeQ8WYPcexBlj3IcodCyUjWkqM0M5YSZVfWomMty1mLjrXkKM2MpUTZlbXoWMty1hJQfzljWY5sRV7oPI/GPUbI64fON265XQaxYsvtsi2380LneTTuMUJeP3Q+DNlq4+BVZFU/bMlWGwefJWf71Vcb1fhOH/nEL1JrjiYesNVYhDPkjFVAUHPGxtncayzCN/LNKsyxcTb3mtf8Gx4c/Zp/w1cf+cRUMoPIwOCBIQdsCH/KvObf8NVHPjGVTJErUoqKoVmce8CGwmLmNf+Grz7yia8JCZ6adMAGgbiR6eQYMXZPaT2xf03/1hP9HlAtKKoF5ez0lKJaUigsZl4X8OM6L1STqnNC8HEHCSYVIprlIzWTrk2X88LelIOXJXO/vMo77p073Lt4dfdoXbbltjF0bv1DZvOh+hdfyT7/WV+n9IHn4InRnWUu5a4xOTE+vELL+HlNnujdmXLNc67Tklxz1bLwHu+W6eSsd3PTOztloi7/ofHiuohutvJI3dHB/i+Z0mG78X8v7NA2KIsdyv4HktLPof5/mUXZ17vqgXIUI5kjE5oe02GbiKEubyS333dsFzMnFPNw2kOZ42T/7jj9t9pSnYrrwml8PNNHCCHiWRTCfb4SHuVnDivWkJxysbekwew2wg3HL5E/FtdOC4jMEK6t9BcH1J5jeMKEOUtEfuL7JO0WdrtjpriJJE6IC5jopFHbiszZQZgAX9EwcWU3x3uHY/i0Q19e1iYJkzZNMrQ2NYv5WV8ieRvU4MH1tQtxt/tXv/2NFG5KZ0pqXYiIX4Ss1yuXdPvfSPu0D037waAqVmyAMaMFaJjpSn8ODmTYpCbq7FftBond7PnDgfcUHUcZmkImMyEk+f9DkBtG7TR6jZ4Lz4Fht5MLTIhjta2OAspmwrrk2tXCbizIBPZ/V7L9O8KL8ml/1cKE7YXqFgBe0U0GrixWtB9dsRt1+ZAS3pzGmPuxDF5f8w6L/FbrRJCLWq4To6lIuk6CGFjXSeGDxevqgMLIdXXhBYPTFGGfvARConKwLXxSe/DIY31EEtJuHk8G6NENXWBGVyShd+ztjGqUIu/FtKYSVeitOvZGd/RCFPzjml4oRc/X1qre6AW/7uusRmf4oRt6ohL+yIAOqUhGOXpDhZ4woxpd0Qtp6IauwYP4Qo9yVKIPOsOMnshFeQIWQFfliiD4STswHoIedU125tqPYEcnUjUZldIHyQ27RK8a3dAdA1y3mIG6ygB1fVyRjSqUw7XOdyPoiW6oQTlKqY4SfRgG7sBo/x5sO5Wo1nr7oAR+KG2sA/mXcLD+7krPTxIiy3SUf7AaOAAAAA==) format('woff2'); + } + </style> + + <linearGradient + id="upper-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="0" stop-color="#2d2d2d"/> + <stop offset="1" stop-color="#3d3d3d"/> + </linearGradient> + <linearGradient + id="lower-beak-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="16" + y2="31"> + <stop offset="1" stop-color="#1d1d1d"/> + <stop offset="0" stop-color="#2d2d2d"/> + </linearGradient> + <linearGradient + id="blue-purple-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.3" stop-color="#0095D5"/> + <stop offset="0.5" stop-color="#3C83DC"/> + <stop offset="0.6" stop-color="#6D74E1"/> + <stop offset="0.7" stop-color="#806EE3"/> + </linearGradient> + <linearGradient + id="purple-orange-gradient" + gradientUnits="userSpaceOnUse" + x1="0" + y1="31" + x2="31" + y2="0"> + <stop offset="0.5" stop-color="#C757BC"/> + <stop offset="0.65" stop-color="#D0609A"/> + <stop offset="0.9" stop-color="#F88909"/> + </linearGradient> + </defs> + + <rect id="black-background" width="100%" height="100%"/> + + <g transform="translate(2,16)" id="bird"> + <path id="blue-feathers" fill="url(#blue-purple-gradient)" + d=" + M5,19 v-10 l7,-7 h10 + M20,16 l8,-8 v10 l-3,3 + "/> + <path id="orange-feather" fill="url(#purple-orange-gradient)" + d="M11,13 l11,-11 h6 v6 l-8,8 l-3,-3"/> + <path id="white-head" fill="#fff" + d="M5,19 l6,-6 h6 l8,8 v3 l2,6 h-18 l2,-4"/> + <g id="beak" stroke-width="0.2" stroke="#666" > + <path id="lower-beak" fill="url(#lower-beak-gradient)" + d="M5,26 l4,3 l4,-3 Z"/> + <path id="upper-beak" fill="url(#upper-beak-gradient)" + d="M4,26 l5,-4 l4,4 Z"/> + </g> + <g id="eye"> + <circle id="eye-base" fill="#000" cx="15" cy="21" r="1.5"/> + <path id="eye-twinkle" fill="none" + stroke="#f5f5f5" + stroke-linecap="round" + stroke-width=".2" + d="M15.1,20.1 q0.9,-0.1 1,0.9"/> + </g> + </g> + + <text x="33" y="32" font-family="JetBrains Mono" font-size="18" fill="#fff">Dokkatoo</text> + <text x="34.2" y="39" font-family="JetBrains Mono" font-size="5" fill="#ddd"> + Generate Kotlin documentation + </text> + <text x="34.3" y="45" font-family="JetBrains Mono" font-size="3" fill="#999"> + https://github.com/adamko-dev/dokkatoo + </text> +</svg> diff --git a/dokka-runners/dokkatoo/modules/docs/style/logo-styles.css b/dokka-runners/dokkatoo/modules/docs/style/logo-styles.css new file mode 100644 index 00000000..73c59666 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/docs/style/logo-styles.css @@ -0,0 +1,44 @@ +.library-name a { + position: center; + display: flex; + margin-left: 0.2em; + margin-right: 0.2em; + align-items: center; + justify-content: center; +} + +.library-name a::before { + content: ""; + background-image: url('../images/logo-icon.svg'); + background-repeat: no-repeat; + background-size: 100% 100%; + position: center; + display: flex; + width: 3.5em; + height: 3.5em; + margin-right: 1em; +} + +.navigation-wrapper { + padding-top: 0.5em; + padding-bottom: 0.5em; +} + +div#github-link { + width: 36px; + height: 36px; + display: inline-flex; + border-radius: 24px; + align-items: center; + justify-content: center; + border: none; +} + +div#github-link a { + background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' fill='white' class='bi bi-github' viewBox='0 0 16 16'><path d='M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.012 8.012 0 0 0 16 8c0-4.42-3.58-8-8-8z'/></svg>"); + width: 24px; + height: 24px; + border-radius: 24px; + background-size: 100% 100%; + margin-left: 16px; +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/build.gradle.kts new file mode 100644 index 00000000..3af8ad71 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/build.gradle.kts @@ -0,0 +1,281 @@ +@file:Suppress("UnstableApiUsage") // jvm test suites & test report aggregation are incubating + +import buildsrc.tasks.SetupDokkaProjects +import buildsrc.utils.buildDir_ +import buildsrc.utils.skipTestFixturesPublications +import org.gradle.api.tasks.testing.logging.TestLogEvent + +plugins { + kotlin("jvm") + kotlin("plugin.serialization") + `java-test-fixtures` + + `jvm-test-suite` + `test-report-aggregation` + + buildsrc.conventions.`java-base` + buildsrc.conventions.`maven-publish-test` + buildsrc.conventions.`dokkatoo-example-projects` + buildsrc.conventions.`android-setup` +} + +description = """ + Integration tests for Dokkatoo Gradle Plugin. + The tests use Gradle TestKit to run the template projects that are committed in the repo. + """.trimIndent() + +dependencies { + testMavenPublication(projects.modules.dokkatooPlugin) + exampleProjects(projects.examples) + + testFixturesApi(testFixtures(projects.modules.dokkatooPlugin)) + + testFixturesImplementation(gradleTestKit()) + + testFixturesImplementation(platform(libs.kotlinxSerialization.bom)) + testFixturesImplementation(libs.kotlinxSerialization.json) + + testFixturesCompileOnly(libs.kotlin.dokkaCore) + + // don't define test dependencies here, instead define them in the testing.suites {} configuration below +} + +kotlin { + target { + compilations.configureEach { + // TODO Dokkatoo uses Gradle 8, while Dokka uses Gradle 7, which has an older version of Kotlin that + // doesn't include these options - so update them or update Gradle. +// compilerOptions.configure { +// freeCompilerArgs.addAll( +// "-opt-in=org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi", +// ) +// } + } + } +} + +//region Test suites and task configuration +testing.suites { + withType<JvmTestSuite>().configureEach { + useJUnitJupiter() + + dependencies { + implementation(project.dependencies.gradleTestKit()) + implementation(project.dependencies.testFixtures(project())) + + compileOnly(libs.kotlin.dokkaCore) + + implementation(project.dependencies.platform(libs.kotlinxSerialization.bom)) + implementation(libs.kotlinxSerialization.json) + } + + targets.configureEach { + testTask.configure { + javaLauncher.set(javaToolchains.launcherFor { + // Android test project requires Java 17 + languageVersion.set(JavaLanguageVersion.of(17)) + }) + + val projectTestTempDirPath = "$buildDir_/test-temp-dir" + inputs.property("projectTestTempDir", projectTestTempDirPath) + systemProperty("projectTestTempDir", projectTestTempDirPath) + + // depend on the test-publication configuration, but not the test-maven repo dir + // (otherwise this task will never be up-to-date) + dependsOn(configurations.testMavenPublication) + + // depend on example & integration-test projects setup + dependsOn(configurations.exampleProjects) + dependsOn(tasks.updateDokkatooExamples) + + val dokkatooExamplesDir = configurations.exampleProjects.map { + it.incoming.files.singleFile.absolutePath + } + + systemProperty("integrationTestProjectsDir", "$projectDir/projects") + systemProperty("testMavenRepoDir", file(mavenPublishTest.testMavenRepo).canonicalPath) + doFirst { + // workaround for lazy-properties not working https://github.com/gradle/gradle/issues/12247 + systemProperty("exampleProjectsDir", dokkatooExamplesDir.get()) + } + } + } + } + + /** Examples tests suite */ + val testExamples by registering(JvmTestSuite::class) { + description = "Test the example projects, from the 'examples' directory in the project root" + } + + /** Integration tests suite */ + val testIntegration by registering(JvmTestSuite::class) { + description = + "Test the integration template projects, in the dokkatoo-plugin-integration-tests/projects directory" + } + + tasks.check { dependsOn(testExamples, testIntegration) } +} + + +tasks.withType<Test>().configureEach { + // this seems to help OOM errors in the Worker Daemons + //setForkEvery(1) + jvmArgs( + "-Xmx1g", + "-XX:MaxMetaspaceSize=512m", + ) + + mustRunAfter(tasks.withType<AbstractPublishToMaven>()) + + testLogging { + events = setOf( + TestLogEvent.STARTED, + TestLogEvent.PASSED, + TestLogEvent.SKIPPED, + TestLogEvent.FAILED, + TestLogEvent.STANDARD_OUT, + TestLogEvent.STANDARD_ERROR, + ) + showStandardStreams = true + showExceptions = true + showCauses = true + showStackTraces = true + } +} +//endregion + +//region Example & Template projects setup +dokkatooExampleProjects { + exampleProjects { + projectsItAndroid0Dokkatoo { + gradlePropertiesContent.add("android.useAndroidX=true") + } + } +} + +dokkaTemplateProjects { + + val androidLocalPropertiesFile = tasks.createAndroidLocalPropertiesFile.map { + it.outputs.files + } + + register( + source = "integration-tests/gradle/projects/it-android-0", + destination = "projects/it-android-0/dokka", + ) { + additionalFiles.from(androidLocalPropertiesFile) + } + register( + source = "integration-tests/gradle/projects/it-basic", + destination = "projects/it-basic/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-basic-groovy", + destination = "projects/it-basic-groovy/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-collector-0", + destination = "projects/it-collector-0/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-js-ir-0", + destination = "projects/it-js-ir-0/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-multimodule-0", + destination = "projects/it-multimodule-0/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-multimodule-1", + destination = "projects/it-multimodule-1/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-multimodule-versioning-0", + destination = "projects/it-multimodule-versioning-0/dokka", + ) + register( + source = "integration-tests/gradle/projects/it-multiplatform-0", + destination = "projects/it-multiplatform-0/dokka", + ) + +// register("projects/coroutines/dokka") { } +// register("projects/serialization/dokka") { } +// register("projects/stdlib/dokka") { } + + configureEach { + additionalPaths.addAll( + "integration-tests/gradle/projects/template.root.gradle.kts", + "integration-tests/gradle/projects/template.settings.gradle.kts", + ) + } +} + +tasks.setupDokkaTemplateProjects.configure { + + val kotlinDokkaVersion = libs.versions.kotlin.dokka + inputs.property("kotlinDokkaVersion", kotlinDokkaVersion) + + doLast { + outputs.files.asFileTree.files.forEach { file -> + when (file.name) { + "build.gradle.kts" -> { + file.writeText( + file.readText() + .replace( + """../template.root.gradle.kts""", + """./template.root.gradle.kts""", + ).replace( + """${'$'}{System.getenv("DOKKA_VERSION")}""", + kotlinDokkaVersion.get(), + ) + ) + } + + "settings.gradle.kts" -> { + file.writeText( + file.readText() + .replace( + """../template.settings.gradle.kts""", + """./template.settings.gradle.kts""", + ) + ) + } + + "template.settings.gradle.kts" -> { + file.writeText( + file.readText() + .replace( + """for-integration-tests-SNAPSHOT""", + kotlinDokkaVersion.get(), + ) + ) + } + } + } + } +} + +tasks.withType<Test>().configureEach { + // this seems to help OOM errors in the Worker Daemons + //setForkEvery(1) + jvmArgs( + "-Xmx1g", + "-XX:MaxMetaspaceSize=512m", + "-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298 + ) +} + +dokkaSourceDownload { + dokkaVersion.set(libs.versions.kotlin.dokka) +} + +tasks.updateAndroidLocalProperties { + mustRunAfter(tasks.withType<SetupDokkaProjects>()) +} + +tasks.updateDokkatooExamples { + dependsOn(tasks.updateAndroidLocalProperties) +} +//endregion + +skipTestFixturesPublications() diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/.gitignore b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/.gitignore new file mode 100644 index 00000000..c0a92be3 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/.gitignore @@ -0,0 +1,15 @@ +# These projects are run using Gradle TestKit, which does not require Gradle or IDE files. +# However, it is convenient to open up the project in an IDEs, which will automatically +# download Gradle files - but since they're not nessessary, don't commit them. +.idea +**/gradle/wrapper/** +gradlew.bat +gradlew + +# gradle.properties will be auto-updated by Gradle tasks +gradle.properties + +ANDROID_SDK/* +**/local.properties +# Don't ignore licenses - they're required by Android to auto-download the SDK +!ANDROID_SDK/licenses/ diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-googletv-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-googletv-license new file mode 100644 index 00000000..c34f9a12 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-googletv-license @@ -0,0 +1,2 @@ + +601085b94cd77f0b54ff86406957099ebe79c4d6 diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-arm-dbt-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-arm-dbt-license new file mode 100644 index 00000000..5cbbb66e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-arm-dbt-license @@ -0,0 +1,2 @@ + +859f317696f67ef3d7f30a50a5560e7834b43903 diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-license new file mode 100644 index 00000000..ee55294f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-license @@ -0,0 +1,2 @@ + +24333f8a63b6825ea9c5514f83c2829b004d1fee diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-preview-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-preview-license new file mode 100644 index 00000000..74069f8f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/android-sdk-preview-license @@ -0,0 +1,2 @@ + +84831b9409646a918e30573bab4c9c91346d8abd diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/google-gdk-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/google-gdk-license new file mode 100644 index 00000000..5c5a0a6f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/google-gdk-license @@ -0,0 +1,2 @@ + +33b6a2b64607f11b759f320ef9dff4ae5c47d97a diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/intel-android-extra-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/intel-android-extra-license new file mode 100644 index 00000000..3ba49310 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/intel-android-extra-license @@ -0,0 +1,2 @@ + +d975f751698a77b662f1254ddbeed3901e976f5a diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/mips-android-sysimage-license b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/mips-android-sysimage-license new file mode 100644 index 00000000..5547e8de --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/ANDROID_SDK/licenses/mips-android-sysimage-license @@ -0,0 +1,2 @@ + +e9acab5b5fbb560a72cfaecce8946896ff6aab9d diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/build.gradle.kts new file mode 100644 index 00000000..860666eb --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/build.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("com.android.library") + id("org.jetbrains.dokka") + kotlin("android") +} + +apply(from = "./template.root.gradle.kts") + +android { + defaultConfig { + minSdkVersion(21) + setCompileSdkVersion(29) + } +} + +dependencies { + implementation("androidx.appcompat:appcompat:1.1.0") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..74641446 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-android-0" + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/AndroidManifest.xml b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a35f86be --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="org.jetbrains.dokka.it.android"/> diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/AndroidSpecificClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/AndroidSpecificClass.kt new file mode 100644 index 00000000..cb9046b1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/AndroidSpecificClass.kt @@ -0,0 +1,16 @@ +@file:Suppress("unused") + +package it.android + +import android.content.Context +import android.util.SparseIntArray +import android.view.View + +/** + * This class is specific to android and uses android classes like: + * [Context], [SparseIntArray] or [View] + */ +class AndroidSpecificClass { + fun sparseIntArray() = SparseIntArray() + fun createView(context: Context): View = View(context) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/IntegrationTestActivity.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/IntegrationTestActivity.kt new file mode 100644 index 00000000..1792818b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/src/main/java/it/android/IntegrationTestActivity.kt @@ -0,0 +1,22 @@ +package it.android + +import android.annotation.SuppressLint +import android.os.Bundle +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity + +/** + * Some Activity implementing [AppCompatActivity] from android x + */ +class IntegrationTestActivity : AppCompatActivity() { + /** + * Will show a small happy text + */ + @SuppressLint("SetTextI18n") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val textView = TextView(this) + textView.text = "I am so happy :)" + setContentView(textView) + } +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..ebcfc7a4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/build.gradle.kts @@ -0,0 +1,32 @@ +plugins { + id("com.android.library") version "8.0.2" + kotlin("android") version "1.9.0" + id("org.jetbrains.dokka.dokkatoo") version "2.1.0-SNAPSHOT" +} + +android { + namespace = "org.jetbrains.dokka.it.android" + defaultConfig { + minSdkVersion(21) + setCompileSdkVersion(29) + } +} + +dependencies { + implementation("androidx.appcompat:appcompat:1.1.0") +} + +tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + // Dokka and Dokkatoo fetch Source Set information in different ways. This results in the + // 'SourceSetId' for each DokkatooSourceSet being slightly different. + // The SourceSetId is not visible, and just because it's different in Dokka vs Dokkatoo, it + // doesn't have any impact. But for the purposes of automated testing, they need to be the same. + // So, forcibly rename the SourceSetId. + + generator.dokkaSourceSets.configureEach { + // sourceSetScope renaming is fine, I'm not worried about it. The default comes from the + // Gradle Task name, so a name difference doesn't matter. + // We can just manually force the Dokkatoo name to match Dokka. + sourceSetScope.set(":dokkaHtml") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..5bea52c9 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,19 @@ +rootProject.name = "it-android-0" + +pluginManagement { + repositories { + maven(providers.gradleProperty("testMavenRepo")) + mavenCentral() + google() + gradlePluginPortal() + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + maven(providers.gradleProperty("testMavenRepo")) + mavenCentral() + google() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/AndroidManifest.xml b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/AndroidManifest.xml new file mode 100644 index 00000000..a35f86be --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/AndroidManifest.xml @@ -0,0 +1 @@ +<manifest package="org.jetbrains.dokka.it.android"/> diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/AndroidSpecificClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/AndroidSpecificClass.kt new file mode 100644 index 00000000..cb9046b1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/AndroidSpecificClass.kt @@ -0,0 +1,16 @@ +@file:Suppress("unused") + +package it.android + +import android.content.Context +import android.util.SparseIntArray +import android.view.View + +/** + * This class is specific to android and uses android classes like: + * [Context], [SparseIntArray] or [View] + */ +class AndroidSpecificClass { + fun sparseIntArray() = SparseIntArray() + fun createView(context: Context): View = View(context) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/IntegrationTestActivity.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/IntegrationTestActivity.kt new file mode 100644 index 00000000..1792818b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo/src/main/java/it/android/IntegrationTestActivity.kt @@ -0,0 +1,22 @@ +package it.android + +import android.annotation.SuppressLint +import android.os.Bundle +import android.widget.TextView +import androidx.appcompat.app.AppCompatActivity + +/** + * Some Activity implementing [AppCompatActivity] from android x + */ +class IntegrationTestActivity : AppCompatActivity() { + /** + * Will show a small happy text + */ + @SuppressLint("SetTextI18n") + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + val textView = TextView(this) + textView.text = "I am so happy :)" + setContentView(textView) + } +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/build.gradle new file mode 100644 index 00000000..f368ed10 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/build.gradle @@ -0,0 +1,54 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id("org.jetbrains.dokka") +} + +apply from: '../template.root.gradle.kts' + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib" +} + +dokkaHtml { + outputDirectory = new File(buildDir, "/dokka/customHtml") + failOnWarning = false + dokkaSourceSets { + customSourceSet { + sourceRoot(file("src/main/java")) + sourceRoot(file("src/main/kotlin")) + displayName.set("custom") + reportUndocumented.set(true) + } + + configureEach { + perPackageOption { // testing closures + matchingRegex.set(".*internal.*") + suppress.set(true) + } + + sourceLink { // testing closures + localDirectory.set(file("src/main")) + remoteUrl.set( + new URL( + "https://github.com/Kotlin/dokka/tree/master/" + + "integration-tests/gradle/projects/it-basic-groovy/src/main" + ) + ) + } + } + } + +} + +dokkaJavadoc { + outputDirectory = new File(buildDir, "dokka/customJavadoc") +} + +dokkaGfm { + outputDirectory = new File(buildDir, "dokka/customGfm") +} + +dokkaJekyll { + outputDirectory = new File(buildDir, "dokka/customJekyll") +} + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/settings.gradle.kts new file mode 100644 index 00000000..87cf173c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-basic-groovy" + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/java/it/basic/java/SampleJavaClass.java b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/kotlin/it/basic/PublicClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..71bc7e63 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,48 @@ +@file:Suppress("unused") + +package it.basic + +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokkatoo/settings.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokkatoo/settings.gradle new file mode 100644 index 00000000..fd5763e1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokkatoo/settings.gradle @@ -0,0 +1,16 @@ +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven { url = providers.gradleProperty("testMavenRepo") } + } +} + +rootProject.name = "it-basic-groovy" + +dependencyResolutionManagement { + repositories { + mavenCentral() + maven { url = providers.gradleProperty("testMavenRepo") } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/build.gradle.kts new file mode 100644 index 00000000..e81e35fd --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/build.gradle.kts @@ -0,0 +1,63 @@ +import org.jetbrains.dokka.gradle.DokkaTask +import org.jetbrains.dokka.gradle.kotlinSourceSet +import org.jetbrains.dokka.base.DokkaBase +import org.jetbrains.dokka.base.DokkaBaseConfiguration +import org.jetbrains.dokka.DokkaConfiguration +import java.net.URL + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +buildscript { + dependencies { + classpath("org.jetbrains.dokka:dokka-base:1.9.0") + } +} + +version = "1.9.0-SNAPSHOT" + +apply(from = "./template.root.gradle.kts") + +dependencies { + testImplementation(kotlin("test-junit")) +} + +tasks.withType<DokkaTask> { + moduleName.set("Basic Project") + dokkaSourceSets { + configureEach { + documentedVisibilities.set( + setOf(DokkaConfiguration.Visibility.PUBLIC, DokkaConfiguration.Visibility.PROTECTED) + ) + suppressedFiles.from(file("src/main/kotlin/it/suppressedByPath")) + perPackageOption { + matchingRegex.set("it.suppressedByPackage.*") + suppress.set(true) + } + perPackageOption { + matchingRegex.set("it.overriddenVisibility.*") + documentedVisibilities.set( + setOf(DokkaConfiguration.Visibility.PRIVATE) + ) + } + sourceLink { + localDirectory.set(file("src/main")) + remoteUrl.set( + URL( + "https://github.com/Kotlin/dokka/tree/master/" + + "integration-tests/gradle/projects/it-basic/src/main" + ) + ) + } + } + + register("myTest") { + kotlinSourceSet(kotlin.sourceSets["test"]) + } + } + suppressObviousFunctions.set(false) + + pluginsMapConfiguration.set(mapOf(DokkaBase::class.qualifiedName to """{ "customStyleSheets": ["${file("../customResources/logo-styles.css").invariantSeparatorsPath}", "${file("../customResources/custom-style-to-add.css").invariantSeparatorsPath}"], "customAssets" : ["${file("../customResources/custom-resource.svg").invariantSeparatorsPath}"] }""")) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-resource.svg b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-resource.svg new file mode 100644 index 00000000..1865f739 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-resource.svg @@ -0,0 +1,3 @@ +<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/> +</svg>
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-style-to-add.css b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-style-to-add.css new file mode 100644 index 00000000..408c210e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/custom-style-to-add.css @@ -0,0 +1 @@ +/* custom stylesheet */
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/logo-styles.css b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/logo-styles.css new file mode 100644 index 00000000..2ac57218 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/customResources/logo-styles.css @@ -0,0 +1,3 @@ +#logo { + background-image: url('https://upload.wikimedia.org/wikipedia/commons/9/9d/Ubuntu_logo.svg'); +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/settings.gradle.kts new file mode 100644 index 00000000..d563ebcf --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-basic" + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/java/it/basic/java/SampleJavaClass.java b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/RootPackageClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/RootPackageClass.kt new file mode 100644 index 00000000..8ff6c750 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/RootPackageClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +/** + * A class that lives inside the root package + */ +class RootPackageClass { + val description = "I do live in the root package!" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/basic/PublicClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..2958948c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,69 @@ +@file:Suppress("unused") + +package it.basic + +import RootPackageClass + +/** + * This class, unlike [RootPackageClass] is located in a sub-package + * + * §PUBLIC§ (marker for asserts) + */ +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is protected and documented + */ + protected fun protectedDocumentedFunction(): String = "" + + protected fun protectedUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property is protected and documented + */ + val protectedDocumentedProperty: Int = 0 + + val protectedUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/internal/InternalClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000..6173d239 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ (marker for asserts) + * This class is internal and should not be rendered + */ +internal class InternalClass diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt new file mode 100644 index 00000000..230f5e0b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt @@ -0,0 +1,12 @@ +package it.overriddenVisibility + +/** + * Private classes and methods generally should not be visible, but [documentedVisibilities] + * are overriden for this specific package to include private code + * + * §PRIVATE§ (marker for asserts) + */ +private class VisiblePrivateClass { + private val privateVal: Int = 0 + private fun privateMethod() {} +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/protected/ProtectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/protected/ProtectedClass.kt new file mode 100644 index 00000000..ad19f1a1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/protected/ProtectedClass.kt @@ -0,0 +1,10 @@ +package it.protected + +/** + * Protected class should be visible because it's included in documentedVisibilities + * + * §PROTECTED§ (marker for asserts) + */ +protected class ProtectedClass { + protected fun protectedFun(): String = "protected" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt new file mode 100644 index 00000000..d8dc9cff --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt @@ -0,0 +1,7 @@ +package it.suppressedByPackage + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPackage diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt new file mode 100644 index 00000000..4dda9da4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt @@ -0,0 +1,7 @@ +package it.suppressedByPath + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPath diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/test/kotlin/it/basic/TestClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/test/kotlin/it/basic/TestClass.kt new file mode 100644 index 00000000..3584bdef --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/src/test/kotlin/it/basic/TestClass.kt @@ -0,0 +1,17 @@ +package it.basic + +import kotlin.test.Test +import kotlin.test.assertTrue + +annotation class OurAnnotation + +class TestClass { + /** + * Asserts something. [PublicClass] + */ + @Test + @OurAnnotation + fun test() { + assertTrue(1 == 1) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/build.gradle.kts new file mode 100644 index 00000000..a6b70f6c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/build.gradle.kts @@ -0,0 +1,64 @@ +import org.jetbrains.dokka.dokkatoo.dokka.parameters.VisibilityModifier +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaHtmlPluginParameters + +plugins { + kotlin("jvm") version "1.9.0" + id("org.jetbrains.dokka.dokkatoo") version "2.1.0-SNAPSHOT" +} + +version = "1.9.0-SNAPSHOT" + +dependencies { + testImplementation(kotlin("test-junit")) +} + +kotlin { + jvmToolchain(8) +} + +dokkatoo { + moduleName.set("Basic Project") + dokkatooSourceSets.configureEach { + documentedVisibilities( + VisibilityModifier.PUBLIC, + VisibilityModifier.PROTECTED, + ) + suppressedFiles.from(file("src/main/kotlin/it/suppressedByPath")) + perPackageOption { + matchingRegex.set("it.suppressedByPackage.*") + suppress.set(true) + } + perPackageOption { + matchingRegex.set("it.overriddenVisibility.*") + documentedVisibilities( + VisibilityModifier.PRIVATE, + ) + } + sourceLink { + localDirectory.set(file("src/main")) + remoteUrl( + "https://github.com/Kotlin/dokka/tree/master/integration-tests/gradle/projects/it-basic/src/main" + ) + } + } + + pluginsConfiguration.named<DokkaHtmlPluginParameters>("html") { + customStyleSheets.from( + "./customResources/logo-styles.css", + "./customResources/custom-style-to-add.css", + ) + customAssets.from( + "./customResources/custom-resource.svg", + ) + } + + dokkatooPublications.configureEach { + suppressObviousFunctions.set(false) + } +} + +tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + generator.dokkaSourceSets.configureEach { + sourceSetScope.set(":dokkaHtml") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-resource.svg b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-resource.svg new file mode 100644 index 00000000..1865f739 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-resource.svg @@ -0,0 +1,3 @@ +<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg"> + <path d="M18 9C18 14 14 18 9 18C4 18 0 14 0 9C0 4 4 0 9 0C14 0 18 4 18 9ZM14.2 6.2L12.8 4.8L7.5 10.1L5.3 7.8L3.8 9.2L7.5 13L14.2 6.2Z" fill="#4DBB5F"/> +</svg>
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-style-to-add.css b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-style-to-add.css new file mode 100644 index 00000000..408c210e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/custom-style-to-add.css @@ -0,0 +1 @@ +/* custom stylesheet */
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/logo-styles.css b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/logo-styles.css new file mode 100644 index 00000000..2ac57218 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/customResources/logo-styles.css @@ -0,0 +1,3 @@ +#logo { + background-image: url('https://upload.wikimedia.org/wikipedia/commons/9/9d/Ubuntu_logo.svg'); +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..a2872f47 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-basic" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/java/it/basic/java/SampleJavaClass.java b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/java/it/basic/java/SampleJavaClass.java new file mode 100644 index 00000000..23b0202c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/java/it/basic/java/SampleJavaClass.java @@ -0,0 +1,17 @@ +package it.basic.java; + +import it.basic.PublicClass; + +/** + * This class is, unlike {@link PublicClass}, written in Java + */ +@SuppressWarnings("unused") +public class SampleJavaClass { + + /** + * @return Empty instance of {@link PublicClass} + */ + public PublicClass publicDocumentedFunction() { + return new PublicClass(); + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/RootPackageClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/RootPackageClass.kt new file mode 100644 index 00000000..8ff6c750 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/RootPackageClass.kt @@ -0,0 +1,8 @@ +@file:Suppress("unused") + +/** + * A class that lives inside the root package + */ +class RootPackageClass { + val description = "I do live in the root package!" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/basic/PublicClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..2958948c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,69 @@ +@file:Suppress("unused") + +package it.basic + +import RootPackageClass + +/** + * This class, unlike [RootPackageClass] is located in a sub-package + * + * §PUBLIC§ (marker for asserts) + */ +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is protected and documented + */ + protected fun protectedDocumentedFunction(): String = "" + + protected fun protectedUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property is protected and documented + */ + val protectedDocumentedProperty: Int = 0 + + val protectedUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/internal/InternalClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000..6173d239 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ (marker for asserts) + * This class is internal and should not be rendered + */ +internal class InternalClass diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt new file mode 100644 index 00000000..230f5e0b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt @@ -0,0 +1,12 @@ +package it.overriddenVisibility + +/** + * Private classes and methods generally should not be visible, but [documentedVisibilities] + * are overriden for this specific package to include private code + * + * §PRIVATE§ (marker for asserts) + */ +private class VisiblePrivateClass { + private val privateVal: Int = 0 + private fun privateMethod() {} +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/protected/ProtectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/protected/ProtectedClass.kt new file mode 100644 index 00000000..ad19f1a1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/protected/ProtectedClass.kt @@ -0,0 +1,10 @@ +package it.protected + +/** + * Protected class should be visible because it's included in documentedVisibilities + * + * §PROTECTED§ (marker for asserts) + */ +protected class ProtectedClass { + protected fun protectedFun(): String = "protected" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt new file mode 100644 index 00000000..d8dc9cff --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt @@ -0,0 +1,7 @@ +package it.suppressedByPackage + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPackage diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt new file mode 100644 index 00000000..4dda9da4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt @@ -0,0 +1,7 @@ +package it.suppressedByPath + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPath diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/test/kotlin/it/basic/TestClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/test/kotlin/it/basic/TestClass.kt new file mode 100644 index 00000000..3584bdef --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo/src/test/kotlin/it/basic/TestClass.kt @@ -0,0 +1,17 @@ +package it.basic + +import kotlin.test.Test +import kotlin.test.assertTrue + +annotation class OurAnnotation + +class TestClass { + /** + * Asserts something. [PublicClass] + */ + @Test + @OurAnnotation + fun test() { + assertTrue(1 == 1) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/build.gradle.kts new file mode 100644 index 00000000..062f1eb7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/build.gradle.kts @@ -0,0 +1 @@ +apply(from = "./template.root.gradle.kts") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/build.gradle.kts new file mode 100644 index 00000000..ab86c333 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + // TODO: File bug report for gradle: :moduleA:moduleB:dokkaHtml is missing kotlin gradle plugin from + // the runtime classpath during execution without this plugin in the parent project + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/README.md b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/README.md new file mode 100644 index 00000000..f8c52880 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/README.md @@ -0,0 +1,2 @@ +# Module moduleB +Here is some description for module B diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/build.gradle.kts new file mode 100644 index 00000000..9f7e98de --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt new file mode 100644 index 00000000..430e2234 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleB + +@Suppress("unused") +class ModuleB { + fun undocumentedPublicFunction() {} +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/README.md b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/README.md new file mode 100644 index 00000000..4ead5671 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/README.md @@ -0,0 +1,2 @@ +# Module moduleC +Here is some description for module C diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/build.gradle.kts new file mode 100644 index 00000000..9f7e98de --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/build.gradle.kts @@ -0,0 +1,4 @@ +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt new file mode 100644 index 00000000..e14d68e0 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleC + +@Suppress("unused") +class ModuleC { + fun undocumentedPublicFunction() {} +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..a1b4baa6 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-multimodule-0" +include(":moduleA") +include(":moduleA:moduleB") +include(":moduleA:moduleC") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..13811d9a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-collector-0" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/build.gradle.kts new file mode 100644 index 00000000..fc353172 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/build.gradle.kts @@ -0,0 +1,21 @@ +plugins { + id("org.jetbrains.dokka") + kotlin("js") +} + +apply(from = "./template.root.gradle.kts") + +kotlin { + js(IR) { + browser() + nodejs() + } +} + +dependencies { + implementation(npm("is-sorted", "1.0.5")) + + val reactVersion = properties["react_version"] + implementation("org.jetbrains.kotlin-wrappers:kotlin-react:$reactVersion") + implementation("org.jetbrains.kotlin-wrappers:kotlin-react-dom:$reactVersion") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..431c0715 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/settings.gradle.kts @@ -0,0 +1,5 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-js-ir-0" + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/RootPackageClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/RootPackageClass.kt new file mode 100644 index 00000000..cbe6240e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/RootPackageClass.kt @@ -0,0 +1,26 @@ +@file:Suppress("unused") + +import org.w3c.dom.url.URLSearchParams +import org.w3c.dom.HTMLAnchorElement +import react.dom.html.AnchorHTMLAttributes +import react.Props +import react.State + +/** + * A class that lives inside the root package + */ +class RootPackageClass { + val description = "I do live in the root package!" +} + +// sample method that uses classes from dom and react, should compile +fun URLSearchParams.react(props: Props, state: State) {} + +fun test(list: MutableList<Int>) = "list" + +@JsModule("is-sorted") +@JsNonModule +external fun <T> sorted(a: Array<T>): Boolean + +// this declaration can be used to check deserialization of dynamic type +external interface TextLinkProps: AnchorHTMLAttributes<HTMLAnchorElement>
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/basic/PublicClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/basic/PublicClass.kt new file mode 100644 index 00000000..fc4b36bd --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/basic/PublicClass.kt @@ -0,0 +1,53 @@ +@file:Suppress("unused") + +package it.basic + +import RootPackageClass + +/** + * This class, unlike [RootPackageClass] is located in a sub-package + */ +class PublicClass { + /** + * This function is public and documented + */ + fun publicDocumentedFunction(): String = "" + + fun publicUndocumentedFunction(): String = "" + + /** + * This function is internal and documented + */ + internal fun internalDocumentedFunction(): String = "" + + internal fun internalUndocumentedFunction(): String = "" + + /** + * This function is private and documented + */ + private fun privateDocumentedFunction(): String = "" + + private fun privateUndocumentedFunction(): String = "" + + + /** + * This property is public and documented + */ + val publicDocumentedProperty: Int = 0 + + val publicUndocumentedProperty: Int = 0 + + /** + * This property internal and documented + */ + val internalDocumentedProperty: Int = 0 + + val internalUndocumentedProperty: Int = 0 + + /** + * This property private and documented + */ + private val privateDocumentedProperty: Int = 0 + + private val privateUndocumentedProperty: Int = 0 +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/internal/InternalClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000..7d42b978 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ + * This class is internal and should not be rendered + */ +internal class InternalClass diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt new file mode 100644 index 00000000..d8dc9cff --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPackage/SuppressedByPackage.kt @@ -0,0 +1,7 @@ +package it.suppressedByPackage + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPackage diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt new file mode 100644 index 00000000..4dda9da4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/src/main/kotlin/it/suppressedByPath/SuppressedByPath.kt @@ -0,0 +1,7 @@ +package it.suppressedByPath + +/** + * §SUPPRESSED§ + * This should not be rendered. + */ +class SuppressedByPath diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..85b71839 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-js-ir-0" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/build.gradle.kts new file mode 100644 index 00000000..062f1eb7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/build.gradle.kts @@ -0,0 +1 @@ +apply(from = "./template.root.gradle.kts") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/build.gradle.kts new file mode 100644 index 00000000..ab86c333 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/build.gradle.kts @@ -0,0 +1,6 @@ +plugins { + // TODO: File bug report for gradle: :moduleA:moduleB:dokkaHtml is missing kotlin gradle plugin from + // the runtime classpath during execution without this plugin in the parent project + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/Module.md b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/Module.md new file mode 100644 index 00000000..0570f467 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/Module.md @@ -0,0 +1,6 @@ +# Module !Module B! +Here is some description for Module B + +Module B: Second paragraph +# Module moduleB +§IGNORED$This documentation shall be ignored, because wrong module name§IGNORED$ diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/build.gradle.kts new file mode 100644 index 00000000..1981701f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/build.gradle.kts @@ -0,0 +1,13 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +tasks.withType<DokkaTask>().configureEach { + moduleName.set("!Module B!") + dokkaSourceSets.configureEach { + includes.from("Module.md") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt new file mode 100644 index 00000000..430e2234 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleB/src/main/kotlin/org/jetbrains/dokka/it/moduleB/ModuleB.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleB + +@Suppress("unused") +class ModuleB { + fun undocumentedPublicFunction() {} +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/Module.md b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/Module.md new file mode 100644 index 00000000..4ead5671 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/Module.md @@ -0,0 +1,2 @@ +# Module moduleC +Here is some description for module C diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/build.gradle.kts new file mode 100644 index 00000000..728e764d --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/build.gradle.kts @@ -0,0 +1,12 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} + +tasks.withType<DokkaTask>().configureEach { + dokkaSourceSets.configureEach { + includes.from("Module.md") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt new file mode 100644 index 00000000..e14d68e0 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleC/src/main/kotlin/org/jetbrains/dokka/it/moduleC/ModuleC.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleC + +@Suppress("unused") +class ModuleC { + fun undocumentedPublicFunction() {} +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/build.gradle.kts new file mode 100644 index 00000000..e5edf940 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/build.gradle.kts @@ -0,0 +1,6 @@ +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + kotlin("jvm") + id("org.jetbrains.dokka") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/src/main/kotlin/org/jetbrains/dokka/it/moduleD/ModuleC.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/src/main/kotlin/org/jetbrains/dokka/it/moduleD/ModuleC.kt new file mode 100644 index 00000000..88174d53 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/moduleA/moduleD/src/main/kotlin/org/jetbrains/dokka/it/moduleD/ModuleC.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.it.moduleD + +@Suppress("unused") +class ModuleD { + fun undocumentedPublicFunction() {} +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..20cde260 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/settings.gradle.kts @@ -0,0 +1,6 @@ +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-multimodule-0" +include(":moduleA") +include(":moduleA:moduleB") +include(":moduleA:moduleC") +include(":moduleA:moduleD") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..97712ae9 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-multimodule-0" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/build.gradle new file mode 100644 index 00000000..57d22b79 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/build.gradle @@ -0,0 +1,18 @@ +plugins { + id 'org.jetbrains.kotlin.jvm' + id("org.jetbrains.dokka") +} + +apply from: '../template.root.gradle.kts' + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib" + implementation project(':first') + implementation project(':second') +} + + +subprojects { + apply plugin: 'org.jetbrains.kotlin.jvm' + apply plugin: 'org.jetbrains.dokka' +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/build.gradle new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/build.gradle diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstClass.kt new file mode 100644 index 00000000..93f73bf4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstClass.kt @@ -0,0 +1,11 @@ +package foo + +/** + * First class description + */ +open class FirstClass{ + /** + * PropertyOne description + */ + val propertyOne: Int = 5 +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstSubclass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstSubclass.kt new file mode 100644 index 00000000..0deb65c0 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/FirstSubclass.kt @@ -0,0 +1,12 @@ +package foo + +/** + * Subclass description + * @property surname Surname description + */ +class FirstSubclass(var surname: String) : FirstClass() { + /** + * printNewLine description + */ + fun printNewline() = print("\r\n") +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/Main.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/Main.kt new file mode 100644 index 00000000..8c7f58a7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/foo/Main.kt @@ -0,0 +1,8 @@ +package foo + +/** + * Main function + */ +fun main(args : Array<String>) { + println("Hello, world!") +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/noPackage.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/noPackage.kt new file mode 100644 index 00000000..c41e218f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/first/src/main/kotlin/noPackage.kt @@ -0,0 +1,3 @@ +fun noPackage(): String = "Hello there" + +open class NoPackage diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/build.gradle new file mode 100644 index 00000000..2b62f963 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/build.gradle @@ -0,0 +1,14 @@ +dependencies { + implementation project(":first") +} +dokkaHtml { + dependsOn(":first:dokkaHtml") + dokkaSourceSets { + "main" { + externalDocumentationLink { + url.set(new URL("file://" + rootProject.rootDir.toPath().toAbsolutePath().resolve("first/build/dokka/html/"))) + packageListUrl.set(new URL("file://" + rootProject.rootDir.toPath().toAbsolutePath().resolve("first/build/dokka/html/first/package-list"))) + } + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/NoPackageClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/NoPackageClass.kt new file mode 100644 index 00000000..d2f30ef7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/NoPackageClass.kt @@ -0,0 +1 @@ +class NoPackageClass : NoPackage() diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/bar/SecondClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/bar/SecondClass.kt new file mode 100644 index 00000000..6a0c935e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/bar/SecondClass.kt @@ -0,0 +1,21 @@ +package bar +/** + * Second class in second module description [foo.FirstClass] + * @author John Doe + * @version 0.1.3 + * @param name Name description text text text. +*/ +class SecondClass(val name: String) { + val firstProperty = "propertystring" + /** + * Second property in second module description [foo.FirstSubclass] + */ + var secondProperty: String = "" + set(value) { + println("Second property not set") + } + + init { + println("InitializerBlock") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/foo/ThirdClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/foo/ThirdClass.kt new file mode 100644 index 00000000..cc24a6b7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/second/src/main/kotlin/foo/ThirdClass.kt @@ -0,0 +1,11 @@ +package foo + +/** + * Third class description + */ +open class ThirdClass{ + /** + * PropertyOne description + */ + val propertyOne: Int = 5 +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/settings.gradle.kts new file mode 100644 index 00000000..753b6ec2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/settings.gradle.kts @@ -0,0 +1,4 @@ +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-multimodule-1" +include(":first") +include(":second") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..6ce1808e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-multimodule-1" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/build.gradle new file mode 100644 index 00000000..e04e412b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/build.gradle @@ -0,0 +1,43 @@ +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask + +plugins { + id 'org.jetbrains.kotlin.jvm' + id("org.jetbrains.dokka") +} + +apply from: '../template.root.gradle.kts' + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib" + dokkaPlugin "org.jetbrains.dokka:versioning-plugin:${System.getenv("DOKKA_VERSION")}" +} + + +subprojects { + apply plugin: 'org.jetbrains.kotlin.jvm' + apply plugin: 'org.jetbrains.dokka' +} + +dokkaHtmlMultiModule { + pluginsMapConfiguration.set(["org.jetbrains.dokka.versioning.VersioningPlugin": """{ "version": "1.2", "olderVersionsDir": "$projectDir/dokkas" }"""]) +} + +tasks.register('dokkaHtmlMultiModuleBaseVersion', DokkaMultiModuleTask){ + dependencies { + dokkaPlugin("org.jetbrains.dokka:all-modules-page-plugin:${System.getenv("DOKKA_VERSION")}") + dokkaPlugin("org.jetbrains.dokka:versioning-plugin:${System.getenv("DOKKA_VERSION")}") + } + outputDirectory.set(file(projectDir.toPath().resolve("dokkas").resolve("1.0"))) + pluginsMapConfiguration.set(["org.jetbrains.dokka.versioning.VersioningPlugin": """{ "version": "1.0" }"""]) + addChildTasks([project(":first"), project(":second")], "dokkaHtmlPartial") +} + +tasks.register('dokkaHtmlMultiModuleNextVersion', DokkaMultiModuleTask){ + dependencies { + dokkaPlugin("org.jetbrains.dokka:all-modules-page-plugin:${System.getenv("DOKKA_VERSION")}") + dokkaPlugin("org.jetbrains.dokka:versioning-plugin:${System.getenv("DOKKA_VERSION")}") + } + outputDirectory.set(file(projectDir.toPath().resolve("dokkas").resolve("1.1"))) + pluginsMapConfiguration.set(["org.jetbrains.dokka.versioning.VersioningPlugin": """{ "version": "1.1", "olderVersionsDir": "$projectDir/dokkas" }"""]) + addChildTasks([project(":first"), project(":second")], "dokkaHtmlPartial") +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/build.gradle new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/build.gradle diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/src/main/kotlin/foo/FirstClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/src/main/kotlin/foo/FirstClass.kt new file mode 100644 index 00000000..93f73bf4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/first/src/main/kotlin/foo/FirstClass.kt @@ -0,0 +1,11 @@ +package foo + +/** + * First class description + */ +open class FirstClass{ + /** + * PropertyOne description + */ + val propertyOne: Int = 5 +}
\ No newline at end of file diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/build.gradle b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/build.gradle new file mode 100644 index 00000000..cf1d1f21 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/build.gradle @@ -0,0 +1,3 @@ +dependencies { + implementation project(":first") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/src/main/kotlin/bar/SecondClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/src/main/kotlin/bar/SecondClass.kt new file mode 100644 index 00000000..6a0c935e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/second/src/main/kotlin/bar/SecondClass.kt @@ -0,0 +1,21 @@ +package bar +/** + * Second class in second module description [foo.FirstClass] + * @author John Doe + * @version 0.1.3 + * @param name Name description text text text. +*/ +class SecondClass(val name: String) { + val firstProperty = "propertystring" + /** + * Second property in second module description [foo.FirstSubclass] + */ + var secondProperty: String = "" + set(value) { + println("Second property not set") + } + + init { + println("InitializerBlock") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..da027565 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/settings.gradle.kts @@ -0,0 +1,4 @@ +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-multimodule-versioning-0" +include(":first") +include(":second") diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..dbd0ee03 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-multimodule-versioning-0" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/build.gradle.kts new file mode 100644 index 00000000..d7d47bd5 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/build.gradle.kts @@ -0,0 +1,48 @@ +import org.jetbrains.dokka.gradle.DokkaTask +import java.net.URL + +plugins { + kotlin("multiplatform") + id("org.jetbrains.dokka") +} + +apply(from = "./template.root.gradle.kts") + +kotlin { + jvm() + linuxX64("linux") + macosX64("macos") + js(BOTH) + //TODO Add wasm when kx.coroutines will be supported and published into the main repo + sourceSets { + val commonMain by sourceSets.getting + val linuxMain by sourceSets.getting + val macosMain by sourceSets.getting + val desktopMain by sourceSets.creating { + dependsOn(commonMain) + linuxMain.dependsOn(this) + macosMain.dependsOn(this) + } + named("commonMain") { + dependencies { + if (properties["dokka_it_kotlin_version"] in listOf("1.4.32", "1.5.31")) + // otherwise for a modern versin of coroutines: + // Failed to resolve Kotlin library: project/build/kotlinSourceSetMetadata/commonMain/org.jetbrains.kotlinx-kotlinx-coroutines-core/org.jetbrains.kotlinx-kotlinx-coroutines-core-commonMain.klib + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9") + else + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + } + } + } +} + +tasks.withType<DokkaTask>().configureEach { + dokkaSourceSets { + configureEach { + externalDocumentationLink { + url.set(URL("https://kotlinlang.org/api/kotlinx.coroutines/")) + //packageListUrl.set(URL("https://kotlinlang.org/api/kotlinx.coroutines/package-list")) + } + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/settings.gradle.kts new file mode 100644 index 00000000..1d4f1681 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/settings.gradle.kts @@ -0,0 +1,2 @@ +apply(from = "./template.settings.gradle.kts") +rootProject.name = "it-multiplatform-0" diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt new file mode 100644 index 00000000..499a4f1e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/CommonMainClass.kt @@ -0,0 +1,8 @@ +package it.mpp0 + +/** + * This class is defined in commonMain + */ +class CommonMainClass { + fun publicFunction(): String = "public" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..e610b09a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +expect class ExpectedClass { + val platform: String +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/coroutines.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/coroutines.kt new file mode 100644 index 00000000..8eee326f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/commonMain/kotlin/it/mpp0/coroutines.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +import kotlinx.coroutines.CoroutineScope + +expect fun <T> CoroutineScope.runBlocking(block: suspend () -> T) : T diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/CPointerExtension.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/CPointerExtension.kt new file mode 100644 index 00000000..342a749e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/CPointerExtension.kt @@ -0,0 +1,11 @@ +package it.mpp0 + +import kotlinx.cinterop.CPointed +import kotlinx.cinterop.CPointer + +/** + * Will print the raw value + */ +fun CPointer<CPointed>.customExtension() { + println(this.rawValue) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..19070a96 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/desktopMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "linux" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..1e4a6d22 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "js" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/runBlocking.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/runBlocking.kt new file mode 100644 index 00000000..03b3b0ea --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jsMain/kotlin/it/mpp0/runBlocking.kt @@ -0,0 +1,7 @@ +package it.mpp0 + +import kotlinx.coroutines.CoroutineScope + +actual fun <T> CoroutineScope.runBlocking(block: suspend () -> T): T { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..6de30de6 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,11 @@ +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/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/JvmOnlyClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/JvmOnlyClass.kt new file mode 100644 index 00000000..21101a89 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/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/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/runBlocking.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/runBlocking.kt new file mode 100644 index 00000000..03b3b0ea --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/jvmMain/kotlin/it/mpp0/runBlocking.kt @@ -0,0 +1,7 @@ +package it.mpp0 + +import kotlinx.coroutines.CoroutineScope + +actual fun <T> CoroutineScope.runBlocking(block: suspend () -> T): T { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/CPointerExtension.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/CPointerExtension.kt new file mode 100644 index 00000000..342a749e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/CPointerExtension.kt @@ -0,0 +1,11 @@ +package it.mpp0 + +import kotlinx.cinterop.CPointed +import kotlinx.cinterop.CPointer + +/** + * Will print the raw value + */ +fun CPointer<CPointed>.customExtension() { + println(this.rawValue) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..19070a96 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "linux" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/runBlocking.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/runBlocking.kt new file mode 100644 index 00000000..b56fb80a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/linuxMain/kotlin/it/mpp0/runBlocking.kt @@ -0,0 +1,13 @@ +package it.mpp0 + +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Deferred +import kotlinx.coroutines.async + +actual fun <T> CoroutineScope.runBlocking(block: suspend () -> T): T { + TODO("Not yet implemented") +} + +fun <T> CoroutineScope.customAsync(block: suspend () -> T): Deferred<T> { + return async { block() } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt new file mode 100644 index 00000000..7a4a8f75 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/ExpectedClass.kt @@ -0,0 +1,5 @@ +package it.mpp0 + +actual class ExpectedClass { + actual val platform: String = "macos" +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/runBlocking.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/runBlocking.kt new file mode 100644 index 00000000..03b3b0ea --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/src/macosMain/kotlin/it/mpp0/runBlocking.kt @@ -0,0 +1,7 @@ +package it.mpp0 + +import kotlinx.coroutines.CoroutineScope + +actual fun <T> CoroutineScope.runBlocking(block: suspend () -> T): T { + TODO("Not yet implemented") +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.root.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.root.gradle.kts new file mode 100644 index 00000000..f63c28e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.root.gradle.kts @@ -0,0 +1,23 @@ +allprojects { + repositories { + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenLocal() + mavenCentral() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + maven("https://maven.pkg.jetbrains.space/public/p/kotlinx-html/maven") { + content { + includeGroup("org.jetbrains.kotlinx") + } + } + } +} + +afterEvaluate { + logger.quiet("Gradle version: ${gradle.gradleVersion}") + logger.quiet("Kotlin version: ${properties["dokka_it_kotlin_version"]}") + properties["dokka_it_android_gradle_plugin_version"]?.let { androidVersion -> + logger.quiet("Android version: $androidVersion") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.settings.gradle.kts new file mode 100644 index 00000000..b7e3195e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokka/template.settings.gradle.kts @@ -0,0 +1,38 @@ +@file:Suppress("LocalVariableName", "UnstableApiUsage") + +pluginManagement { + val dokka_it_kotlin_version: String by settings + val dokka_it_android_gradle_plugin_version: String? by settings + + plugins { + id("org.jetbrains.kotlin.js") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.jvm") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.android") version dokka_it_kotlin_version + id("org.jetbrains.kotlin.multiplatform") version dokka_it_kotlin_version + } + + resolutionStrategy { + eachPlugin { + if (requested.id.id == "org.jetbrains.dokka") { + useModule("org.jetbrains.dokka:dokka-gradle-plugin:1.9.0") + } + + if (requested.id.id == "com.android.library") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + + if (requested.id.id == "com.android.application") { + useModule("com.android.tools.build:gradle:$dokka_it_android_gradle_plugin_version") + } + } + } + repositories { + mavenLocal() + maven("https://cache-redirector.jetbrains.com/jcenter.bintray.com") + mavenCentral() + gradlePluginPortal() + google() + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-eap") + maven("https://cache-redirector.jetbrains.com/dl.bintray.com/kotlin/kotlin-dev") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..3e4d074a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokkatoo/settings.gradle.kts @@ -0,0 +1,17 @@ +rootProject.name = "it-multiplatform-0" + +pluginManagement { + repositories { + gradlePluginPortal() + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + repositories { + mavenCentral() + maven(providers.gradleProperty("testMavenRepo")) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/CustomFormatExampleTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/CustomFormatExampleTest.kt new file mode 100644 index 00000000..b2dbdb1e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/CustomFormatExampleTest.kt @@ -0,0 +1,197 @@ +package org.jetbrains.dokka.dokkatoo.tests.examples + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKA_VERSION +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File + +class CustomFormatExampleTest : FunSpec({ + + val dokkaProject = initDokkaProject( + GradleProjectTest.projectTestTempDir.resolve("it/examples/custom-format-dokka").toFile() + ) + + val dokkatooProject = initDokkatooProject( + GradleProjectTest.projectTestTempDir.resolve("it/examples/custom-format-dokkatoo").toFile() + ) + + context("compare dokka and dokkatoo HTML generators") { + test("expect dokka can generate HTML") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Generation completed successfully" + } + } + + test("expect dokkatoo can generate HTML") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldNotBeNull().shouldBeAFile() + dokkaWorkerLog.readText() shouldContain "Generation completed successfully" + } + } + } + + context("expect dokka and dokkatoo HTML is the same") { + val dokkaHtmlDir = dokkaProject.projectDir.resolve("build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("build/dokka/html") + + test("expect file trees are the same") { + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + // drop the first line from each, since the directory name is different + expectedFileTree.substringAfter("\n") shouldBe actualFileTree.substringAfter("\n") + } + + test("expect directories are the same") { + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + } + } + + + context("Gradle caching") { + test("expect Dokkatoo is compatible with Gradle Build Cache") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldNotBeNull().shouldBeAFile() + dokkaWorkerLog.readText() shouldContain "Generation completed successfully" + } + } + + dokkatooProject.runner + .addArguments( + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--build-cache", + "--info", + ) + .forwardOutput() + .build { + output shouldContainAll listOf( + "> Task :dokkatooGeneratePublicationHtml UP-TO-DATE", + "BUILD SUCCESSFUL", + "1 actionable task: 1 up-to-date", + ) + withClue("Dokka Generator should not be triggered, so check it doesn't log anything") { + output shouldNotContain "Generation completed successfully" + } + } + } + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("custom-format-example/dokka") + + buildGradleKts = buildGradleKts + .replace( + Regex("""id\("org\.jetbrains\.dokka"\) version \("[\d.]+"\)"""), + Regex.escapeReplacement("""id("org.jetbrains.dokka") version "$DOKKA_VERSION""""), + ) + .replace( + "org.jetbrains.dokka:dokka-base:1.7.10", + "org.jetbrains.dokka:dokka-base:1.7.20", + ) + + settingsGradleKts = settingsGradleKts + .replace( + """rootProject.name = "dokka-customFormat-example"""", + """rootProject.name = "customFormat-example"""", + ) + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("custom-format-example/dokkatoo") + + buildGradleKts += """ + | + |tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + | generator.dokkaSourceSets.configureEach { + | sourceSetScope.set(":dokkaHtml") // only necessary for testing + | } + |} + | + """.trimMargin() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/GradleExampleTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/GradleExampleTest.kt new file mode 100644 index 00000000..371ac938 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/GradleExampleTest.kt @@ -0,0 +1,190 @@ +package org.jetbrains.dokka.dokkatoo.tests.examples + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKA_VERSION +import org.jetbrains.dokka.dokkatoo.utils.* +import org.jetbrains.dokka.dokkatoo.utils.GradleProjectTest.Companion.projectTestTempDir +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.nulls.shouldNotBeNull +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File +import kotlin.text.Regex.Companion.escapeReplacement + +class GradleExampleTest : FunSpec({ + + val dokkaProject = initDokkaProject( + projectTestTempDir.resolve("it/examples/gradle-example/dokka").toFile() + ) + + val dokkatooProject = initDokkatooProject( + projectTestTempDir.resolve("it/examples/gradle-example/dokkatoo").toFile() + ) + + context("compare dokka and dokkatoo HTML generators") { + test("expect dokka can generate HTML") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Generation completed successfully" + } + } + + test("expect dokkatoo can generate HTML") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldNotBeNull().shouldBeAFile() + dokkaWorkerLog.readText() shouldContain "Generation completed successfully" + } + } + } + + context("expect dokka and dokkatoo HTML is the same") { + val dokkaHtmlDir = dokkaProject.projectDir.resolve("build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("build/dokka/html") + + test("expect file trees are the same") { + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + expectedFileTree shouldBe actualFileTree + } + + test("expect directories are the same") { + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + } + } + + + context("Gradle caching") { + test("expect Dokkatoo is compatible with Gradle Build Cache") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + + + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldNotBeNull().shouldBeAFile() + dokkaWorkerLog.readText() shouldContain "Generation completed successfully" + } + } + + dokkatooProject.runner + .addArguments( + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + "--build-cache", + ) + .forwardOutput() + .build { + output shouldContainAll listOf( + "> Task :dokkatooGeneratePublicationHtml UP-TO-DATE", + "BUILD SUCCESSFUL", + "1 actionable task: 1 up-to-date", + ) + withClue("Dokka Generator should not be triggered, so check it doesn't log anything") { + output shouldNotContain "Generation completed successfully" + } + } + } + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("gradle-example/dokka") + + buildGradleKts = buildGradleKts + .replace( + Regex("""id\("org\.jetbrains\.dokka"\) version \("[\d.]+"\)"""), + escapeReplacement("""id("org.jetbrains.dokka") version "$DOKKA_VERSION""""), + ) + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("gradle-example/dokkatoo") + + buildGradleKts += """ + | + |tasks.withType<org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask>().configureEach { + | generator.dokkaSourceSets.configureEach { + | sourceSetScope.set(":dokkaHtml") // only necessary for testing + | } + |} + | + """.trimMargin() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/KotlinMultiplatformExampleTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/KotlinMultiplatformExampleTest.kt new file mode 100644 index 00000000..4a9efaac --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/KotlinMultiplatformExampleTest.kt @@ -0,0 +1,226 @@ +package org.jetbrains.dokka.dokkatoo.tests.examples + +import org.jetbrains.dokka.dokkatoo.utils.* +import org.jetbrains.dokka.dokkatoo.utils.GradleProjectTest.Companion.projectTestTempDir +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File + +class KotlinMultiplatformExampleTest : FunSpec({ + + val dokkaProject = initDokkaProject( + projectTestTempDir.resolve("it/examples/multiplatform-example/dokka").toFile() + ) + + val dokkatooProject = initDokkatooProject( + projectTestTempDir.resolve("it/examples/multiplatform-example/dokkatoo").toFile() + ) + + context("compare dokka and dokkatoo HTML generators") { + test("expect dokka can generate HTML") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Generation completed successfully" + } + } + + context("when Dokkatoo generates HTML") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + } + + context("expect dokka and dokkatoo HTML is the same") { + val dokkaHtmlDir = + dokkaProject.projectDir.resolve("build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("build/dokka/html") + + test("expect file trees are the same") { + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + expectedFileTree shouldBe actualFileTree + } + + test("expect directories are the same") { + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + } + } + + + context("Gradle caching") { + + context("expect Dokkatoo is compatible with Gradle Build Cache") { + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + + test("expect tasks are UP-TO-DATE") { + dokkatooProject.runner + .addArguments( + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + "--build-cache", + ) + .forwardOutput() + .build { + + output shouldContainAll listOf( + "> Task :dokkatooGeneratePublicationHtml UP-TO-DATE", + "BUILD SUCCESSFUL", + "2 actionable tasks: 2 up-to-date", + ) + withClue("Dokka Generator should not be triggered, so check it doesn't log anything") { + output shouldNotContain "Generation completed successfully" + } + } + } + } + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("multiplatform-example/dokka") + + settingsGradleKts = settingsGradleKts + .replace( + """pluginManagement {""", + """ + | + |pluginManagement { + | repositories { + | gradlePluginPortal() + | mavenCentral() + | mavenLocal() + | } + | + """.trimMargin() + ) + """ + | + |dependencyResolutionManagement { + | repositories { + | mavenCentral() + | mavenLocal() + | } + |} + | + """.trimMargin() + + buildGradleKts += """ + | + |val hackDokkaHtmlDir by tasks.registering(Sync::class) { + | // sync directories so the dirs in both dokka and dokkatoo are the same + | from(layout.buildDirectory.dir("dokka/htmlMultiModule")) + | into(layout.buildDirectory.dir("dokka/html")) + |} + | + |tasks.matching { "dokka" in it.name.toLowerCase() && it.name != hackDokkaHtmlDir.name }.configureEach { + | finalizedBy(hackDokkaHtmlDir) + |} + | + """.trimMargin() + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("multiplatform-example/dokkatoo") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/MultimoduleExampleTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/MultimoduleExampleTest.kt new file mode 100644 index 00000000..8e2215c2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testExamples/kotlin/MultimoduleExampleTest.kt @@ -0,0 +1,244 @@ +package org.jetbrains.dokka.dokkatoo.tests.examples + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKA_VERSION +import org.jetbrains.dokka.dokkatoo.utils.* +import org.jetbrains.dokka.dokkatoo.utils.GradleProjectTest.Companion.projectTestTempDir +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.collections.shouldHaveSize +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File +import org.gradle.testkit.runner.TaskOutcome.UP_TO_DATE + +class MultimoduleExampleTest : FunSpec({ + + val dokkaProject = initDokkaProject( + projectTestTempDir.resolve("it/examples/multimodule-example/dokka").toFile() + ) + + val dokkatooProject = initDokkatooProject( + projectTestTempDir.resolve("it/examples/multimodule-example/dokkatoo").toFile() + ) + + context("compare dokka and dokkatoo HTML generators") { + test("expect dokka can generate HTML") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtmlMultiModule", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Generation completed successfully" + } + } + + context("when Dokkatoo generates HTML") { + dokkatooProject.runner + .addArguments( + "clean", + ":parentProject:dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldForAll { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + } + + context("expect dokka and dokkatoo HTML is the same") { + val dokkaHtmlDir = + dokkaProject.projectDir.resolve("parentProject/build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("parentProject/build/dokka/html") + + test("expect file trees are the same") { + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + expectedFileTree shouldBe actualFileTree + } + + test("expect directories are the same") { + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + } + } + + + context("Gradle caching") { + + context("expect Dokkatoo is compatible with Gradle Build Cache") { + dokkatooProject.runner + .addArguments( + "clean", + ":parentProject:dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldForAll { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + + dokkatooProject.runner + .addArguments( + ":parentProject:dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + "--build-cache", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all tasks are UP-TO-DATE") { + val nonLoggingTasks = + tasks.filter { it.name != "logLinkDokkatooGeneratePublicationHtml" } + nonLoggingTasks.shouldForAll { + it shouldHaveOutcome UP_TO_DATE + } + tasks.shouldHaveSize(6) + } + + test("expect Dokka Generator is not triggered") { + // Dokka Generator shouldn't run, so check it doesn't log anything + output shouldNotContain "Generation completed successfully" + } + } + } + + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + ":parentProject:dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("multimodule-example/dokka") + + gradleProperties = gradleProperties.lines().joinToString("\n") { line -> + when { + line.startsWith("dokkaVersion=") -> "dokkaVersion=$DOKKA_VERSION" + else -> line + } + } + + settingsGradleKts = settingsGradleKts + .replace( + """pluginManagement {""", + """ + | + |pluginManagement { + | repositories { + | mavenCentral() + | gradlePluginPortal() + | } + | + """.trimMargin() + ) + """ + | + |dependencyResolutionManagement { + | repositories { + | mavenCentral() + | } + |} + | + """.trimMargin() + + dir("parentProject") { + + buildGradleKts += """ + | + |val hackDokkaHtmlDir by tasks.registering(Sync::class) { + | // sync directories so the dirs in both dokka and dokkatoo are the same + | from(layout.buildDirectory.dir("dokka/htmlMultiModule")) + | into(layout.buildDirectory.dir("dokka/html")) + |} + | + |tasks.matching { it.name.contains("dokka", ignoreCase = true) && it.name != hackDokkaHtmlDir.name }.configureEach { + | finalizedBy(hackDokkaHtmlDir) + |} + | + """.trimMargin() + } + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyExampleProject("multimodule-example/dokkatoo") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testFixtures/kotlin/templateProjectUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testFixtures/kotlin/templateProjectUtils.kt new file mode 100644 index 00000000..93a52564 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testFixtures/kotlin/templateProjectUtils.kt @@ -0,0 +1,17 @@ +package org.jetbrains.dokka.dokkatoo.utils + + +fun GradleProjectTest.copyExampleProject(path: String) { + GradleProjectTest.exampleProjectsDir + .resolve(path) + .toFile() + .copyRecursively(projectDir.toFile(), overwrite = true) { _, _ -> OnErrorAction.SKIP } +} + + +fun GradleProjectTest.copyIntegrationTestProject(path: String) { + GradleProjectTest.integrationTestProjectsDir + .resolve(path) + .toFile() + .copyRecursively(projectDir.toFile(), overwrite = true) { _, _ -> OnErrorAction.SKIP } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/AndroidProjectIntegrationTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/AndroidProjectIntegrationTest.kt new file mode 100644 index 00000000..9389a798 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/AndroidProjectIntegrationTest.kt @@ -0,0 +1,166 @@ +package org.jetbrains.dokka.dokkatoo.tests.integration + +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File +import kotlin.io.path.deleteIfExists + +/** + * Integration test for the `it-android-0` project in Dokka + * + * Runs Dokka & Dokkatoo, and compares the resulting HTML site. + */ +class AndroidProjectIntegrationTest : FunSpec({ + + val tempDir = GradleProjectTest.projectTestTempDir.resolve("it/it-android-0").toFile() + + val dokkatooProject = initDokkatooProject(tempDir.resolve("dokkatoo")) + val dokkaProject = initDokkaProject(tempDir.resolve("dokka")) + + context("when generating HTML") { + context("with Dokka") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect project builds successfully") { + output shouldContain "BUILD SUCCESSFUL" + } + } + } + + context("with Dokkatoo") { + dokkatooProject.runner + .addArguments( + "clean", + "dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect project builds successfully") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + } + + test("expect the same HTML is generated") { + + val dokkaHtmlDir = dokkaProject.projectDir.resolve("build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("build/dokka/html") + + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + expectedFileTree shouldBe actualFileTree + + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + + test("Dokkatoo tasks should be cacheable") { + dokkatooProject.runner + .addArguments( + "dokkatooGeneratePublicationHtml", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + output shouldContainAll listOf( + "Task :dokkatooGeneratePublicationHtml UP-TO-DATE", + ) + } + } + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + "dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyIntegrationTestProject("it-android-0/dokka") + + gradleProperties = gradleProperties + .replace( + "dokka_it_android_gradle_plugin_version=4.2.2", + "dokka_it_android_gradle_plugin_version=8.0.2", + ) + + file("src/main/AndroidManifest.xml").deleteIfExists() + + buildGradleKts += """ + + android { + namespace = "org.jetbrains.dokka.it.android" + } + + java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } + } + """.trimIndent() + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyIntegrationTestProject("it-android-0/dokkatoo") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/BasicProjectIntegrationTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/BasicProjectIntegrationTest.kt new file mode 100644 index 00000000..265e7c18 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/kotlin/BasicProjectIntegrationTest.kt @@ -0,0 +1,153 @@ +package org.jetbrains.dokka.dokkatoo.tests.integration + +import org.jetbrains.dokka.dokkatoo.utils.* +import org.jetbrains.dokka.dokkatoo.utils.GradleProjectTest.Companion.projectTestTempDir +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.file.shouldHaveSameStructureAndContentAs +import io.kotest.matchers.file.shouldHaveSameStructureAs +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain +import java.io.File + +/** + * Integration test for the `it-basic` project in Dokka + * + * Runs Dokka & Dokkatoo, and compares the resulting HTML site. + */ +class BasicProjectIntegrationTest : FunSpec({ + + val tempDir = projectTestTempDir.resolve("it/it-basic").toFile() + + val dokkatooProject = initDokkatooProject(tempDir.resolve("dokkatoo")) + val dokkaProject = initDokkaProject(tempDir.resolve("dokka")) + + context("when generating HTML") { + dokkaProject.runner + .addArguments( + "clean", + "dokkaHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + context("with Dokka") { + test("expect project builds successfully") { + output shouldContain "BUILD SUCCESSFUL" + } + } + } + + dokkatooProject.runner + .addArguments( + "clean", + "dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + context("with Dokkatoo") { + test("expect project builds successfully") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + dokkatooProject + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + } + + test("expect the same HTML is generated") { + + val dokkaHtmlDir = dokkaProject.projectDir.resolve("build/dokka/html") + val dokkatooHtmlDir = dokkatooProject.projectDir.resolve("build/dokka/html") + + val expectedFileTree = dokkaHtmlDir.toTreeString() + val actualFileTree = dokkatooHtmlDir.toTreeString() + println((actualFileTree to expectedFileTree).sideBySide()) + expectedFileTree shouldBe actualFileTree + + dokkatooHtmlDir.toFile().shouldHaveSameStructureAs(dokkaHtmlDir.toFile()) + dokkatooHtmlDir.toFile().shouldHaveSameStructureAndContentAs(dokkaHtmlDir.toFile()) + } + + test("Dokkatoo tasks should be cacheable") { + dokkatooProject.runner + .addArguments( + "dokkatooGeneratePublicationHtml", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + output shouldContainAll listOf( + "Task :dokkatooGeneratePublicationHtml UP-TO-DATE", + ) + } + } + + context("expect Dokkatoo is compatible with Gradle Configuration Cache") { + dokkatooProject.file(".gradle/configuration-cache").toFile().deleteRecursively() + dokkatooProject.file("build/reports/configuration-cache").toFile().deleteRecursively() + + val configCacheRunner = + dokkatooProject.runner + .addArguments( + "clean", + "dokkatooGeneratePublicationHtml", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + + test("first build should store the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry stored" + output shouldNotContain "problems were found storing the configuration cache" + } + } + + test("second build should reuse the configuration cache") { + configCacheRunner.build { + output shouldContain "BUILD SUCCESSFUL" + output shouldContain "Configuration cache entry reused" + } + } + } + } +}) + + +private fun initDokkaProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyIntegrationTestProject("it-basic/dokka") + + buildGradleKts = buildGradleKts + .replace( + // no idea why this needs to be changed + """file("../customResources/""", + """file("./customResources/""", + ) + } +} + +private fun initDokkatooProject( + destinationDir: File, +): GradleProjectTest { + return GradleProjectTest(destinationDir.toPath()).apply { + copyIntegrationTestProject("it-basic/dokkatoo") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectA.json b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectA.json new file mode 100644 index 00000000..21b8498f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectA.json @@ -0,0 +1,83 @@ +{ + "moduleName": "childProjectA", + "moduleVersion": "unspecified", + "outputDir": ".../dokka-multimodule-example/parentProject/childProjectA/build/dokka/htmlPartial", + "cacheRoot": null, + "offlineMode": false, + "sourceSets": [ + { + "displayName": "jvm", + "sourceSetID": { + "scopeId": ":parentProject:childProjectA:dokkaHtmlPartial", + "sourceSetName": "main" + }, + "classpath": [ + ".../kotlin-stdlib-1.7.20.jar", + ".../kotlin-stdlib-common-1.7.20.jar", + ".../annotations-13.0.jar" + ], + "sourceRoots": [ + ".../dokka-multimodule-example/parentProject/childProjectA/src/main/kotlin" + ], + "dependentSourceSets": [], + "samples": [], + "includes": [ + ".../dokka-multimodule-example/parentProject/childProjectA/Module.md" + ], + "includeNonPublic": false, + "reportUndocumented": false, + "skipEmptyPackages": true, + "skipDeprecated": false, + "jdkVersion": 8, + "sourceLinks": [], + "perPackageOptions": [], + "externalDocumentationLinks": [ + { + "url": "https://kotlinlang.org/api/latest/jvm/stdlib/", + "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" + }, + { + "url": "https://docs.oracle.com/javase/8/docs/api/", + "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" + } + ], + "languageVersion": null, + "apiVersion": null, + "noStdlibLink": false, + "noJdkLink": false, + "suppressedFiles": [], + "analysisPlatform": "jvm", + "documentedVisibilities": [ + "PUBLIC" + ] + } + ], + "pluginsClasspath": [ + ".../dokka-analysis-for-integration-tests-SNAPSHOT.jar", + ".../dokka-base-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-intellij-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-compiler-for-integration-tests-SNAPSHOT.jar", + ".../kotlinx-html-jvm-0.7.5.jar", + ".../kotlinx-coroutines-core-jvm-1.6.3.jar", + ".../kotlin-stdlib-jdk8-1.7.20.jar", + ".../jackson-databind-2.12.7.jar", + ".../jackson-annotations-2.12.7.jar", + ".../jackson-core-2.12.7.jar", + ".../jackson-module-kotlin-2.12.7.jar", + ".../kotlin-reflect-1.7.20.jar", + ".../kotlin-stdlib-jdk7-1.7.20.jar", + ".../kotlin-stdlib-1.7.20.jar", + ".../jsoup-1.14.3.jar", + ".../freemarker-2.3.31.jar", + ".../kotlin-stdlib-common-1.7.20.jar", + ".../annotations-13.0.jar" + ], + "pluginsConfiguration": [], + "modules": [], + "failOnWarning": false, + "delayTemplateSubstitution": true, + "suppressObviousFunctions": true, + "includes": [], + "suppressInheritedMembers": false, + "finalizeCoroutines": true +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectB.json b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectB.json new file mode 100644 index 00000000..98d218d3 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/childProjectB.json @@ -0,0 +1,83 @@ +{ + "moduleName": "childProjectB", + "moduleVersion": "unspecified", + "outputDir": ".../dokka-multimodule-example/parentProject/childProjectB/build/dokka/htmlPartial", + "cacheRoot": null, + "offlineMode": false, + "sourceSets": [ + { + "displayName": "jvm", + "sourceSetID": { + "scopeId": ":parentProject:childProjectB:dokkaHtmlPartial", + "sourceSetName": "main" + }, + "classpath": [ + ".../kotlin-stdlib-1.7.20.jar", + ".../kotlin-stdlib-common-1.7.20.jar", + ".../annotations-13.0.jar" + ], + "sourceRoots": [ + ".../dokka-multimodule-example/parentProject/childProjectB/src/main/kotlin" + ], + "dependentSourceSets": [], + "samples": [], + "includes": [ + ".../dokka-multimodule-example/parentProject/childProjectB/Module.md" + ], + "includeNonPublic": false, + "reportUndocumented": false, + "skipEmptyPackages": true, + "skipDeprecated": false, + "jdkVersion": 8, + "sourceLinks": [], + "perPackageOptions": [], + "externalDocumentationLinks": [ + { + "url": "https://kotlinlang.org/api/latest/jvm/stdlib/", + "packageListUrl": "https://kotlinlang.org/api/latest/jvm/stdlib/package-list" + }, + { + "url": "https://docs.oracle.com/javase/8/docs/api/", + "packageListUrl": "https://docs.oracle.com/javase/8/docs/api/package-list" + } + ], + "languageVersion": null, + "apiVersion": null, + "noStdlibLink": false, + "noJdkLink": false, + "suppressedFiles": [], + "analysisPlatform": "jvm", + "documentedVisibilities": [ + "PUBLIC" + ] + } + ], + "pluginsClasspath": [ + ".../dokka-analysis-for-integration-tests-SNAPSHOT.jar", + ".../dokka-base-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-intellij-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-compiler-for-integration-tests-SNAPSHOT.jar", + ".../kotlinx-html-jvm-0.7.5.jar", + ".../kotlinx-coroutines-core-jvm-1.6.3.jar", + ".../kotlin-stdlib-jdk8-1.7.20.jar", + ".../jackson-databind-2.12.7.jar", + ".../jackson-annotations-2.12.7.jar", + ".../jackson-core-2.12.7.jar", + ".../jackson-module-kotlin-2.12.7.jar", + ".../kotlin-reflect-1.7.20.jar", + ".../kotlin-stdlib-jdk7-1.7.20.jar", + ".../kotlin-stdlib-1.7.20.jar", + ".../jsoup-1.14.3.jar", + ".../freemarker-2.3.31.jar", + ".../kotlin-stdlib-common-1.7.20.jar", + ".../annotations-13.0.jar" + ], + "pluginsConfiguration": [], + "modules": [], + "failOnWarning": false, + "delayTemplateSubstitution": true, + "suppressObviousFunctions": true, + "includes": [], + "suppressInheritedMembers": false, + "finalizeCoroutines": true +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/parentProject.json b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/parentProject.json new file mode 100644 index 00000000..fb300e37 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/parentProject.json @@ -0,0 +1,55 @@ +{ + "moduleName": "parentProject", + "moduleVersion": null, + "outputDir": ".../dokka-multimodule-example/parentProject/build/dokka/htmlMultiModule", + "cacheRoot": null, + "offlineMode": false, + "sourceSets": [], + "pluginsClasspath": [ + ".../dokka-analysis-for-integration-tests-SNAPSHOT.jar", + ".../all-modules-page-plugin-for-integration-tests-SNAPSHOT.jar", + ".../templating-plugin-for-integration-tests-SNAPSHOT.jar", + ".../dokka-base-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-intellij-for-integration-tests-SNAPSHOT.jar", + ".../kotlin-analysis-compiler-for-integration-tests-SNAPSHOT.jar", + "../kotlinx-html-jvm-0.7.5.jar", + "../kotlinx-coroutines-core-jvm-1.6.3.jar", + "../kotlin-stdlib-jdk8-1.7.20.jar", + "../jackson-databind-2.12.7.jar", + "../jackson-annotations-2.12.7.jar", + "../jackson-core-2.12.7.jar", + "../jackson-module-kotlin-2.12.7.jar", + "../kotlin-reflect-1.7.20.jar", + "../kotlin-stdlib-jdk7-1.7.20.jar", + "../kotlin-stdlib-1.7.20.jar", + "../jsoup-1.14.3.jar", + "../freemarker-2.3.31.jar", + "../kotlin-stdlib-common-1.7.20.jar", + "../annotations-13.0.jar" + ], + "pluginsConfiguration": [], + "modules": [ + { + "name": "childProjectA", + "relativePathToOutputDirectory": "childProjectA", + "includes": [ + ".../dokka-multimodule-example/parentProject/childProjectA/Module.md" + ], + "sourceOutputDirectory": ".../dokka-multimodule-example/parentProject/childProjectA/build/dokka/htmlPartial" + }, + { + "name": "childProjectB", + "relativePathToOutputDirectory": "childProjectB", + "includes": [ + ".../dokka-multimodule-example/parentProject/childProjectB/Module.md" + ], + "sourceOutputDirectory": ".../dokka-multimodule-example/parentProject/childProjectB/build/dokka/htmlPartial" + } + ], + "failOnWarning": false, + "delayTemplateSubstitution": false, + "suppressObviousFunctions": true, + "includes": [], + "suppressInheritedMembers": false, + "finalizeCoroutines": true +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/readme.md b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/readme.md new file mode 100644 index 00000000..cffaf26b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin-integration-tests/src/testIntegration/resources/it/example/dokka-multi-module/readme.md @@ -0,0 +1,5 @@ +This directory contains example JSON that the current Dokka plugin generates +for the dokka-multi-module example. + +It is committed here for development so that I can manually compare and contrast. +In time, these files can be removed. diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/api/dokkatoo-plugin.api b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/api/dokkatoo-plugin.api new file mode 100644 index 00000000..d767d2ec --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/api/dokkatoo-plugin.api @@ -0,0 +1,397 @@ +public abstract class dev/adamko/dokkatoo/DokkatooBasePlugin : org/gradle/api/Plugin { + public static final field Companion Ldev/adamko/dokkatoo/DokkatooBasePlugin$Companion; + public static final field EXTENSION_NAME Ljava/lang/String; + public static final field TASK_GROUP Ljava/lang/String; + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public final class dev/adamko/dokkatoo/DokkatooBasePlugin$Companion { + public final fun getDependencyContainerNames ()Ldev/adamko/dokkatoo/DokkatooBasePlugin$DependencyContainerNames; + public final fun getTaskNames ()Ldev/adamko/dokkatoo/DokkatooBasePlugin$TaskNames; +} + +public final class dev/adamko/dokkatoo/DokkatooBasePlugin$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun <init> (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public abstract class dev/adamko/dokkatoo/DokkatooExtension : java/io/Serializable, org/gradle/api/plugins/ExtensionAware { + public abstract fun getDokkatooCacheDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getDokkatooConfigurationsDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getDokkatooModuleDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getDokkatooPublicationDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public final fun getDokkatooPublications ()Lorg/gradle/api/NamedDomainObjectContainer; + public final fun getDokkatooSourceSets ()Lorg/gradle/api/NamedDomainObjectContainer; + public abstract fun getModuleName ()Lorg/gradle/api/provider/Property; + public abstract fun getModulePath ()Lorg/gradle/api/provider/Property; + public abstract fun getModuleVersion ()Lorg/gradle/api/provider/Property; + public final fun getPluginsConfiguration ()Lorg/gradle/api/ExtensiblePolymorphicDomainObjectContainer; + public abstract fun getSourceSetScopeDefault ()Lorg/gradle/api/provider/Property; + public final fun getVersions ()Ldev/adamko/dokkatoo/DokkatooExtension$Versions; +} + +public abstract interface class dev/adamko/dokkatoo/DokkatooExtension$Versions : org/gradle/api/plugins/ExtensionAware { + public static final field Companion Ldev/adamko/dokkatoo/DokkatooExtension$Versions$Companion; + public abstract fun getFreemarker ()Lorg/gradle/api/provider/Property; + public abstract fun getJetbrainsDokka ()Lorg/gradle/api/provider/Property; + public abstract fun getJetbrainsMarkdown ()Lorg/gradle/api/provider/Property; + public abstract fun getKotlinxCoroutines ()Lorg/gradle/api/provider/Property; + public abstract fun getKotlinxHtml ()Lorg/gradle/api/provider/Property; +} + +public final class dev/adamko/dokkatoo/DokkatooExtension$Versions$Companion { +} + +public abstract class dev/adamko/dokkatoo/DokkatooPlugin : org/gradle/api/Plugin { + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V +} + +public abstract class dev/adamko/dokkatoo/dokka/DokkaPublication : java/io/Serializable, org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware { + public abstract fun getCacheRoot ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getDelayTemplateSubstitution ()Lorg/gradle/api/provider/Property; + public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; + public abstract fun getFailOnWarning ()Lorg/gradle/api/provider/Property; + public abstract fun getFinalizeCoroutines ()Lorg/gradle/api/provider/Property; + public final fun getFormatName ()Ljava/lang/String; + public abstract fun getIncludes ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getModuleName ()Lorg/gradle/api/provider/Property; + public abstract fun getModuleVersion ()Lorg/gradle/api/provider/Property; + public fun getName ()Ljava/lang/String; + public abstract fun getOfflineMode ()Lorg/gradle/api/provider/Property; + public abstract fun getOutputDir ()Lorg/gradle/api/file/DirectoryProperty; + public final fun getPluginsConfiguration ()Lorg/gradle/api/ExtensiblePolymorphicDomainObjectContainer; + public abstract fun getSuppressInheritedMembers ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppressObviousFunctions ()Lorg/gradle/api/provider/Property; +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpec : java/io/Serializable, org/gradle/api/Named { + public abstract fun getEnabled ()Lorg/gradle/api/provider/Property; + public fun getName ()Ljava/lang/String; + public abstract fun getPackageListUrl ()Lorg/gradle/api/provider/Property; + public abstract fun getUrl ()Lorg/gradle/api/provider/Property; + public final fun packageListUrl (Ljava/lang/String;)V + public final fun packageListUrl (Lorg/gradle/api/provider/Provider;)V + public final fun url (Ljava/lang/String;)V + public final fun url (Lorg/gradle/api/provider/Provider;)V +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec : org/gradle/api/plugins/ExtensionAware { + public abstract fun getDokkaModuleFiles ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getDokkaSourceSets ()Lorg/gradle/api/NamedDomainObjectContainer; + public abstract fun getFailOnWarning ()Lorg/gradle/api/provider/Property; + public abstract fun getFinalizeCoroutines ()Lorg/gradle/api/provider/Property; + public abstract fun getIncludes ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getModuleName ()Lorg/gradle/api/provider/Property; + public abstract fun getModuleVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getOfflineMode ()Lorg/gradle/api/provider/Property; + public abstract fun getPluginsClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getPluginsConfiguration ()Lorg/gradle/api/ExtensiblePolymorphicDomainObjectContainer; + public abstract fun getSuppressInheritedMembers ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppressObviousFunctions ()Lorg/gradle/api/provider/Property; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/DokkaModuleDescriptionKxs$$serializer : kotlinx/serialization/internal/GeneratedSerializer { + public static final field INSTANCE Ldev/adamko/dokkatoo/dokka/parameters/DokkaModuleDescriptionKxs$$serializer; + public fun childSerializers ()[Lkotlinx/serialization/KSerializer; + public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ldev/adamko/dokkatoo/dokka/parameters/DokkaModuleDescriptionKxs; + public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object; + public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor; + public fun serialize (Lkotlinx/serialization/encoding/Encoder;Ldev/adamko/dokkatoo/dokka/parameters/DokkaModuleDescriptionKxs;)V + public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V + public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/DokkaModuleDescriptionKxs$Companion { + public final fun serializer ()Lkotlinx/serialization/KSerializer; +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaPackageOptionsSpec : dev/adamko/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers, java/io/Serializable { + public abstract fun getDocumentedVisibilities ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getMatchingRegex ()Lorg/gradle/api/provider/Property; + public abstract fun getReportUndocumented ()Lorg/gradle/api/provider/Property; + public abstract fun getSkipDeprecated ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppress ()Lorg/gradle/api/provider/Property; +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaSourceLinkSpec : java/io/Serializable { + public abstract fun getLocalDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getRemoteLineSuffix ()Lorg/gradle/api/provider/Property; + public abstract fun getRemoteUrl ()Lorg/gradle/api/provider/Property; + public final fun remoteUrl (Ljava/lang/String;)V + public final fun remoteUrl (Lorg/gradle/api/provider/Provider;)V +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec : java/io/Serializable, org/gradle/api/Named { + public static final field Companion Ldev/adamko/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec$Companion; + public fun equals (Ljava/lang/Object;)Z + public fun getName ()Ljava/lang/String; + public final fun getScopeId ()Ljava/lang/String; + public final fun getSourceSetName ()Ljava/lang/String; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec$Companion { +} + +public abstract class dev/adamko/dokkatoo/dokka/parameters/DokkaSourceSetSpec : dev/adamko/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers, java/io/Serializable, org/gradle/api/Named, org/gradle/api/plugins/ExtensionAware { + public abstract fun getAnalysisPlatform ()Lorg/gradle/api/provider/Property; + public abstract fun getApiVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getDependentSourceSets ()Lorg/gradle/api/NamedDomainObjectContainer; + public abstract fun getDisplayName ()Lorg/gradle/api/provider/Property; + public abstract fun getDocumentedVisibilities ()Lorg/gradle/api/provider/SetProperty; + public abstract fun getEnableAndroidDocumentationLink ()Lorg/gradle/api/provider/Property; + public abstract fun getEnableJdkDocumentationLink ()Lorg/gradle/api/provider/Property; + public abstract fun getEnableKotlinStdLibDocumentationLink ()Lorg/gradle/api/provider/Property; + public final fun getExternalDocumentationLinks ()Lorg/gradle/api/NamedDomainObjectContainer; + public abstract fun getIncludes ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getJdkVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getLanguageVersion ()Lorg/gradle/api/provider/Property; + public fun getName ()Ljava/lang/String; + public abstract fun getPerPackageOptions ()Lorg/gradle/api/DomainObjectSet; + public abstract fun getReportUndocumented ()Lorg/gradle/api/provider/Property; + public abstract fun getSamples ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getSkipDeprecated ()Lorg/gradle/api/provider/Property; + public abstract fun getSkipEmptyPackages ()Lorg/gradle/api/provider/Property; + public abstract fun getSourceLinks ()Lorg/gradle/api/DomainObjectSet; + public abstract fun getSourceRoots ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun getSourceSetId ()Lorg/gradle/api/provider/Provider; + public abstract fun getSourceSetScope ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppress ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppressGeneratedFiles ()Lorg/gradle/api/provider/Property; + public abstract fun getSuppressedFiles ()Lorg/gradle/api/file/ConfigurableFileCollection; + public final fun perPackageOption (Lorg/gradle/api/Action;)V + public final fun sourceLink (Lorg/gradle/api/Action;)V +} + +public final class dev/adamko/dokkatoo/dokka/parameters/KotlinPlatform : java/lang/Enum { + public static final field AndroidJVM Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static final field Common Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static final field Companion Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform$Companion; + public static final field JS Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static final field JVM Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static final field Native Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static final field WASM Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static fun valueOf (Ljava/lang/String;)Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public static fun values ()[Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/KotlinPlatform$Companion { + public final fun fromString (Ljava/lang/String;)Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; + public final fun getDEFAULT ()Ldev/adamko/dokkatoo/dokka/parameters/KotlinPlatform; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/VisibilityModifier : java/lang/Enum { + public static final field Companion Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier$Companion; + public static final field INTERNAL Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static final field PACKAGE Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static final field PRIVATE Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static final field PROTECTED Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static final field PUBLIC Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static fun valueOf (Ljava/lang/String;)Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; + public static fun values ()[Ldev/adamko/dokkatoo/dokka/parameters/VisibilityModifier; +} + +public final class dev/adamko/dokkatoo/dokka/parameters/VisibilityModifier$Companion { +} + +public abstract class dev/adamko/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters : dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec { + public static final field Companion Ldev/adamko/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters$Companion; + public static final field DOKKA_HTML_PARAMETERS_NAME Ljava/lang/String; + public static final field DOKKA_HTML_PLUGIN_FQN Ljava/lang/String; + public abstract fun getCustomAssets ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getCustomStyleSheets ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getFooterMessage ()Lorg/gradle/api/provider/Property; + public abstract fun getMergeImplicitExpectActualDeclarations ()Lorg/gradle/api/provider/Property; + public abstract fun getSeparateInheritedMembers ()Lorg/gradle/api/provider/Property; + public abstract fun getTemplatesDir ()Lorg/gradle/api/file/DirectoryProperty; + public fun jsonEncode ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters$Companion { +} + +public abstract class dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec : java/io/Serializable, org/gradle/api/Named { + public fun getName ()Ljava/lang/String; + public fun getPluginFqn ()Ljava/lang/String; + public abstract fun jsonEncode ()Ljava/lang/String; +} + +public abstract class dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder : dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec { + public static final field Companion Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder$Companion; + public fun getPluginFqn ()Ljava/lang/String; + public fun jsonEncode ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder$Companion { +} + +public final class dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilderKt { + public static final fun PluginConfigBooleanValue (Lorg/gradle/api/provider/Provider;)Lorg/gradle/api/provider/Provider; + public static final fun PluginConfigNumberValue (Lorg/gradle/api/provider/Provider;)Lorg/gradle/api/provider/Provider; + public static final fun PluginConfigStringValue (Lorg/gradle/api/provider/Provider;)Lorg/gradle/api/provider/Provider; + public static final fun PluginConfigValue (Ljava/lang/Number;)Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$NumberValue; + public static final fun PluginConfigValue (Ljava/lang/String;)Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$StringValue; + public static final fun PluginConfigValue (Z)Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$BooleanValue; + public static final fun add (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Ljava/lang/Number;)V + public static final fun add (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Ljava/lang/String;)V + public static final fun add (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Z)V + public static final fun addBoolean (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Lorg/gradle/api/provider/Provider;)V + public static final fun addNumber (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Lorg/gradle/api/provider/Provider;)V + public static final fun addString (Ldev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values;Lorg/gradle/api/provider/Provider;)V + public static final fun booleanProperty (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Lorg/gradle/api/provider/Provider;)V + public static final fun files (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public static final fun numberProperty (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Lorg/gradle/api/provider/Provider;)V + public static final fun pluginParameters (Lorg/gradle/api/ExtensiblePolymorphicDomainObjectContainer;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public static final fun properties (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Lkotlin/jvm/functions/Function1;)V + public static final fun property (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Ljava/lang/Number;)V + public static final fun property (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Ljava/lang/String;)V + public static final fun property (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Z)V + public static final fun stringProperty (Ldev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder;Ljava/lang/String;Lorg/gradle/api/provider/Provider;)V +} + +public abstract class dev/adamko/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters : dev/adamko/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec { + public static final field Companion Ldev/adamko/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters$Companion; + public static final field DOKKA_VERSIONING_PLUGIN_FQN Ljava/lang/String; + public static final field DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME Ljava/lang/String; + public abstract fun getOlderVersions ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getOlderVersionsDir ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getRenderVersionsNavigationOnAllPages ()Lorg/gradle/api/provider/Property; + public abstract fun getVersion ()Lorg/gradle/api/provider/Property; + public abstract fun getVersionsOrdering ()Lorg/gradle/api/provider/ListProperty; + public fun jsonEncode ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters$Companion { +} + +public abstract interface class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$BooleanValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Primitive { + public fun <init> (Z)V + public final fun getBoolean ()Z +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$DirectoryValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { + public fun <init> (Lorg/gradle/api/file/DirectoryProperty;)V + public final fun getDirectory ()Lorg/gradle/api/file/DirectoryProperty; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$FileValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { + public fun <init> (Lorg/gradle/api/file/RegularFileProperty;)V + public final fun getFile ()Lorg/gradle/api/file/RegularFileProperty; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$FilesValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { + public fun <init> (Lorg/gradle/api/file/ConfigurableFileCollection;)V + public final fun getFiles ()Lorg/gradle/api/file/ConfigurableFileCollection; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$NumberValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Primitive { + public fun <init> (Ljava/lang/Number;)V + public final fun getNumber ()Ljava/lang/Number; +} + +public abstract interface class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Primitive : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Properties : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { + public fun <init> (Lorg/gradle/api/provider/MapProperty;)V + public final fun getValues ()Lorg/gradle/api/provider/MapProperty; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$StringValue : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Primitive { + public fun <init> (Ljava/lang/String;)V + public final fun getString ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue$Values : dev/adamko/dokkatoo/dokka/plugins/PluginConfigValue { + public fun <init> (Lorg/gradle/api/provider/ListProperty;)V + public final fun getValues ()Lorg/gradle/api/provider/ListProperty; +} + +public abstract class dev/adamko/dokkatoo/formats/DokkatooFormatPlugin : org/gradle/api/Plugin { + public fun <init> (Ljava/lang/String;)V + public synthetic fun apply (Ljava/lang/Object;)V + public fun apply (Lorg/gradle/api/Project;)V + public fun configure (Ldev/adamko/dokkatoo/formats/DokkatooFormatPlugin$DokkatooFormatPluginContext;)V + public final fun getFormatName ()Ljava/lang/String; +} + +public final class dev/adamko/dokkatoo/formats/DokkatooFormatTasks$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun <init> (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public abstract class dev/adamko/dokkatoo/formats/DokkatooGfmPlugin : dev/adamko/dokkatoo/formats/DokkatooFormatPlugin { + public fun configure (Ldev/adamko/dokkatoo/formats/DokkatooFormatPlugin$DokkatooFormatPluginContext;)V +} + +public abstract class dev/adamko/dokkatoo/formats/DokkatooHtmlPlugin : dev/adamko/dokkatoo/formats/DokkatooFormatPlugin { + public fun configure (Ldev/adamko/dokkatoo/formats/DokkatooFormatPlugin$DokkatooFormatPluginContext;)V +} + +public final class dev/adamko/dokkatoo/formats/DokkatooHtmlPlugin$inlined$sam$i$org_gradle_api_Action$0 : org/gradle/api/Action { + public fun <init> (Lkotlin/jvm/functions/Function1;)V + public final synthetic fun execute (Ljava/lang/Object;)V +} + +public abstract class dev/adamko/dokkatoo/formats/DokkatooJavadocPlugin : dev/adamko/dokkatoo/formats/DokkatooFormatPlugin { + public fun configure (Ldev/adamko/dokkatoo/formats/DokkatooFormatPlugin$DokkatooFormatPluginContext;)V +} + +public abstract class dev/adamko/dokkatoo/formats/DokkatooJekyllPlugin : dev/adamko/dokkatoo/formats/DokkatooFormatPlugin { + public fun configure (Ldev/adamko/dokkatoo/formats/DokkatooFormatPlugin$DokkatooFormatPluginContext;)V +} + +public abstract interface annotation class dev/adamko/dokkatoo/internal/DokkatooInternalApi : java/lang/annotation/Annotation { +} + +public abstract class dev/adamko/dokkatoo/tasks/DokkatooGenerateTask : dev/adamko/dokkatoo/tasks/DokkatooTask { + public abstract fun getCacheDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getGenerationType ()Lorg/gradle/api/provider/Property; + public final fun getGenerator ()Ldev/adamko/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec; + public abstract fun getOutputDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getPublicationEnabled ()Lorg/gradle/api/provider/Property; + public abstract fun getRuntimeClasspath ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getWorkerDebugEnabled ()Lorg/gradle/api/provider/Property; + public abstract fun getWorkerJvmArgs ()Lorg/gradle/api/provider/ListProperty; + public abstract fun getWorkerLogFile ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getWorkerMaxHeapSize ()Lorg/gradle/api/provider/Property; + public abstract fun getWorkerMinHeapSize ()Lorg/gradle/api/provider/Property; +} + +public final class dev/adamko/dokkatoo/tasks/DokkatooGenerateTask$GenerationType : java/lang/Enum { + public static final field MODULE Ldev/adamko/dokkatoo/tasks/DokkatooGenerateTask$GenerationType; + public static final field PUBLICATION Ldev/adamko/dokkatoo/tasks/DokkatooGenerateTask$GenerationType; + public static fun valueOf (Ljava/lang/String;)Ldev/adamko/dokkatoo/tasks/DokkatooGenerateTask$GenerationType; + public static fun values ()[Ldev/adamko/dokkatoo/tasks/DokkatooGenerateTask$GenerationType; +} + +public abstract class dev/adamko/dokkatoo/tasks/DokkatooPrepareModuleDescriptorTask : dev/adamko/dokkatoo/tasks/DokkatooTask { + public abstract fun getDokkaModuleDescriptorJson ()Lorg/gradle/api/file/RegularFileProperty; + public abstract fun getIncludes ()Lorg/gradle/api/file/ConfigurableFileCollection; + public abstract fun getModuleDirectory ()Lorg/gradle/api/file/DirectoryProperty; + public abstract fun getModuleName ()Lorg/gradle/api/provider/Property; + public abstract fun getModulePath ()Lorg/gradle/api/provider/Property; +} + +public abstract class dev/adamko/dokkatoo/tasks/DokkatooTask : org/gradle/api/DefaultTask { + public abstract fun getObjects ()Lorg/gradle/api/model/ObjectFactory; +} + +public abstract class dev/adamko/dokkatoo/tasks/LogHtmlPublicationLinkTask : dev/adamko/dokkatoo/tasks/DokkatooTask { + public static final field Companion Ldev/adamko/dokkatoo/tasks/LogHtmlPublicationLinkTask$Companion; + public static final field ENABLE_TASK_PROPERTY_NAME Ljava/lang/String; + public final fun exec ()V + public abstract fun getIndexHtmlPath ()Lorg/gradle/api/provider/Property; + public abstract fun getServerUri ()Lorg/gradle/api/provider/Property; +} + +public final class dev/adamko/dokkatoo/tasks/LogHtmlPublicationLinkTask$Companion { +} + diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/build.gradle.kts b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/build.gradle.kts new file mode 100644 index 00000000..8bb60f57 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/build.gradle.kts @@ -0,0 +1,254 @@ +@file:Suppress("UnstableApiUsage") // jvm test suites & test report aggregation are incubating + +import buildsrc.utils.buildDir_ +import buildsrc.utils.skipTestFixturesPublications + +plugins { + buildsrc.conventions.`kotlin-gradle-plugin` + kotlin("plugin.serialization") + + dev.adamko.kotlin.`binary-compatibility-validator` + + dev.adamko.`dokkatoo-html` + buildsrc.conventions.`maven-publishing` + + `java-test-fixtures` + `jvm-test-suite` + `test-report-aggregation` + buildsrc.conventions.`maven-publish-test` +} + +description = "Generates documentation for Kotlin projects (using Dokka)" + +dependencies { + // ideally there should be a 'dokka-core-api' dependency (that is very thin and doesn't drag in loads of unnecessary code) + // that would be used as an implementation dependency, while dokka-core would be used as a compileOnly dependency + // https://github.com/Kotlin/dokka/issues/2933 + implementation(libs.kotlin.dokkaCore) + + compileOnly(libs.gradlePlugin.kotlin) + compileOnly(libs.gradlePlugin.kotlin.klibCommonizerApi) + compileOnly(libs.gradlePlugin.android) + compileOnly(libs.gradlePlugin.androidApi) + + implementation(platform(libs.kotlinxSerialization.bom)) + implementation(libs.kotlinxSerialization.json) + + testFixturesImplementation(gradleApi()) + testFixturesImplementation(gradleTestKit()) + + testFixturesCompileOnly(libs.kotlin.dokkaCore) + testFixturesImplementation(platform(libs.kotlinxSerialization.bom)) + testFixturesImplementation(libs.kotlinxSerialization.json) + + testFixturesCompileOnly(libs.kotlin.dokkaCore) + + testFixturesApi(platform(libs.kotest.bom)) + testFixturesApi(libs.kotest.junit5Runner) + testFixturesApi(libs.kotest.assertionsCore) + testFixturesApi(libs.kotest.assertionsJson) + testFixturesApi(libs.kotest.datatest) + + // don't define test dependencies here, instead define them in the testing.suites {} configuration below +} + +gradlePlugin { + isAutomatedPublishing = true + + plugins.register("dokkatoo") { + id = "org.jetbrains.dokka.dokkatoo" + displayName = "Dokkatoo" + description = "Generates documentation for Kotlin projects (using Dokka)" + implementationClass = "org.jetbrains.dokka.dokkatoo.DokkatooPlugin" + } + + fun registerDokkaPlugin( + pluginClass: String, + shortName: String, + longName: String = shortName, + ) { + plugins.register(pluginClass) { + id = "org.jetbrains.dokka.dokkatoo-${shortName.toLowerCase()}" + displayName = "Dokkatoo $shortName" + description = "Generates $longName documentation for Kotlin projects (using Dokka)" + implementationClass = "org.jetbrains.dokka.dokkatoo.formats.$pluginClass" + } + } + registerDokkaPlugin("DokkatooGfmPlugin", "GFM", longName = "GFM (GitHub Flavoured Markdown)") + registerDokkaPlugin("DokkatooHtmlPlugin", "HTML") + registerDokkaPlugin("DokkatooJavadocPlugin", "Javadoc") + registerDokkaPlugin("DokkatooJekyllPlugin", "Jekyll") + + plugins.configureEach { + website.set("https://github.com/adamko-dev/dokkatoo/") + vcsUrl.set("https://github.com/adamko-dev/dokkatoo.git") + tags.addAll( + "dokka", + "dokkatoo", + "kotlin", + "kdoc", + "android", + "documentation", + "javadoc", + "html", + "markdown", + "gfm", + "website", + ) + } +} + +kotlin { + target { + compilations.configureEach { + // TODO Dokkatoo uses Gradle 8, while Dokka uses Gradle 7, which has an older version of Kotlin that + // doesn't include these options - so update them or update Gradle. +// compilerOptions.configure { +// freeCompilerArgs.addAll( +// "-opt-in=org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi", +// ) +// } + } + } +} + +testing.suites { + withType<JvmTestSuite>().configureEach { + useJUnitJupiter() + + dependencies { + implementation(project.dependencies.gradleTestKit()) + + implementation(project.dependencies.testFixtures(project())) + + implementation(project.dependencies.platform(libs.kotlinxSerialization.bom)) + implementation(libs.kotlinxSerialization.json) + } + + targets.configureEach { + testTask.configure { + val projectTestTempDirPath = "$buildDir_/test-temp-dir" + inputs.property("projectTestTempDir", projectTestTempDirPath) + systemProperty("projectTestTempDir", projectTestTempDirPath) + + when (testType.get()) { + TestSuiteType.FUNCTIONAL_TEST, + TestSuiteType.INTEGRATION_TEST -> { + dependsOn(tasks.matching { it.name == "publishAllPublicationsToTestRepository" }) + + systemProperties( + "testMavenRepoDir" to file(mavenPublishTest.testMavenRepo).canonicalPath, + ) + + // depend on the test-publication task, but not the test-maven repo + // (otherwise this task will never be up-to-date) + dependsOn(tasks.publishToTestMavenRepo) + } + } + } + } + } + + + /** Unit tests suite */ + val test by getting(JvmTestSuite::class) { + description = "Standard unit tests" + } + + + /** Functional tests suite */ + val testFunctional by registering(JvmTestSuite::class) { + description = "Tests that use Gradle TestKit to test functionality" + testType.set(TestSuiteType.FUNCTIONAL_TEST) + + targets.all { + testTask.configure { + shouldRunAfter(test) + } + } + } + + tasks.check { dependsOn(test, testFunctional) } +} + +skipTestFixturesPublications() + +val aggregateTestReports by tasks.registering(TestReport::class) { + group = LifecycleBasePlugin.VERIFICATION_GROUP + destinationDirectory.set(layout.buildDirectory.dir("reports/tests/aggregated")) + + dependsOn(tasks.withType<AbstractTestTask>()) + + // hardcoded dirs is a bit of a hack, but a fileTree just didn't work + testResults.from("$buildDir_/test-results/test/binary") + testResults.from("$buildDir_/test-results/testFunctional/binary") + testResults.from("$buildDir_/test-results/testIntegration/binary") + + doLast { + logger.lifecycle("Aggregated test report: file://${destinationDirectory.asFile.get()}/index.html") + } +} + +binaryCompatibilityValidator { + ignoredMarkers.add("org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi") +} + +val dokkatooVersion = provider { project.version.toString() } + +val dokkatooConstantsProperties = objects.mapProperty<String, String>().apply { + put("DOKKATOO_VERSION", dokkatooVersion) + put("DOKKA_VERSION", libs.versions.kotlin.dokka) +} + +val buildConfigFileContents: Provider<TextResource> = + dokkatooConstantsProperties.map { constants -> + + val vals = constants.entries + .sortedBy { it.key } + .joinToString("\n") { (k, v) -> + """const val $k = "$v"""" + }.prependIndent(" ") + + resources.text.fromString( + """ + |package org.jetbrains.dokka.dokkatoo.internal + | + |@DokkatooInternalApi + |object DokkatooConstants { + |$vals + |} + | + """.trimMargin() + ) + } + +val generateDokkatooConstants by tasks.registering(Sync::class) { + group = project.name + + val buildConfigFileContents = buildConfigFileContents + + from(buildConfigFileContents) { + rename { "DokkatooConstants.kt" } + into("dev/adamko/dokkatoo/internal/") + } + + into(layout.buildDirectory.dir("generated-source/main/kotlin/")) +} + +kotlin.sourceSets.main { + kotlin.srcDir(generateDokkatooConstants.map { it.destinationDir }) +} + +dokkatoo { + dokkatooSourceSets.configureEach { + externalDocumentationLinks.register("gradle") { + // https://docs.gradle.org/current/javadoc/index.html + url("https://docs.gradle.org/${gradle.gradleVersion}/javadoc/") + } + sourceLink { + localDirectory.set(file("src/main/kotlin")) + val relativeProjectPath = projectDir.relativeToOrNull(rootDir)?.invariantSeparatorsPath ?: "" + remoteUrl("https://github.com/adamko-dev/dokkatoo/tree/main/$relativeProjectPath/src/main/kotlin") + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooBasePlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooBasePlugin.kt new file mode 100644 index 00000000..9d67471a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooBasePlugin.kt @@ -0,0 +1,355 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKATOO_BASE_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKATOO_CATEGORY_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKA_FORMAT_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform +import org.jetbrains.dokka.dokkatoo.dokka.parameters.VisibilityModifier +import org.jetbrains.dokka.dokkatoo.internal.* +import org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask +import org.jetbrains.dokka.dokkatoo.tasks.DokkatooPrepareModuleDescriptorTask +import org.jetbrains.dokka.dokkatoo.tasks.DokkatooTask +import java.io.File +import javax.inject.Inject +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.file.ProjectLayout +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.tasks.TaskContainer +import org.gradle.kotlin.dsl.* +import org.gradle.language.base.plugins.LifecycleBasePlugin + +/** + * The base plugin for Dokkatoo. Sets up Dokkatoo and configures default values, but does not + * add any specific config (specifically, it does not create Dokka Publications). + */ +abstract class DokkatooBasePlugin +@DokkatooInternalApi +@Inject +constructor( + private val providers: ProviderFactory, + private val layout: ProjectLayout, + private val objects: ObjectFactory, +) : Plugin<Project> { + + override fun apply(target: Project) { + // apply the lifecycle-base plugin so the clean task is available + target.pluginManager.apply(LifecycleBasePlugin::class) + + val dokkatooExtension = createExtension(target) + + target.tasks.createDokkaLifecycleTasks() + + val configurationAttributes = objects.newInstance<DokkatooConfigurationAttributes>() + + target.dependencies.attributesSchema { + attribute(DOKKATOO_BASE_ATTRIBUTE) + attribute(DOKKATOO_CATEGORY_ATTRIBUTE) + attribute(DOKKA_FORMAT_ATTRIBUTE) + } + + target.configurations.register(dependencyContainerNames.dokkatoo) { + description = "Fetch all Dokkatoo files from all configurations in other subprojects" + asConsumer() + isVisible = false + attributes { + attribute(DOKKATOO_BASE_ATTRIBUTE, configurationAttributes.dokkatooBaseUsage) + } + } + + configureDokkaPublicationsDefaults(dokkatooExtension) + dokkatooExtension.dokkatooSourceSets.configureDefaults( + sourceSetScopeConvention = dokkatooExtension.sourceSetScopeDefault + ) + + target.tasks.withType<DokkatooGenerateTask>().configureEach { + cacheDirectory.convention(dokkatooExtension.dokkatooCacheDirectory) + workerDebugEnabled.convention(false) + workerLogFile.convention(temporaryDir.resolve("dokka-worker.log")) + workerJvmArgs.set( + listOf( + //"-XX:MaxMetaspaceSize=512m", + "-XX:+HeapDumpOnOutOfMemoryError", + "-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298 + //"-XX:StartFlightRecording=disk=true,name={path.drop(1).map { if (it.isLetterOrDigit()) it else '-' }.joinToString("")},dumponexit=true,duration=30s", + //"-XX:FlightRecorderOptions=repository=$baseDir/jfr,stackdepth=512", + ) + ) + dokkaConfigurationJsonFile.convention(temporaryDir.resolve("dokka-configuration.json")) + } + + target.tasks.withType<DokkatooPrepareModuleDescriptorTask>().configureEach { + moduleName.convention(dokkatooExtension.moduleName) + includes.from(providers.provider { dokkatooExtension.dokkatooSourceSets.flatMap { it.includes } }) + modulePath.convention(dokkatooExtension.modulePath) + } + + target.tasks.withType<DokkatooGenerateTask>().configureEach { + + publicationEnabled.convention(true) + onlyIf("publication must be enabled") { publicationEnabled.getOrElse(true) } + + generator.dokkaSourceSets.addAllLater( + providers.provider { + // exclude suppressed source sets as early as possible, to avoid unnecessary dependency resolution + dokkatooExtension.dokkatooSourceSets.filterNot { it.suppress.get() } + } + ) + + generator.dokkaSourceSets.configureDefaults( + sourceSetScopeConvention = dokkatooExtension.sourceSetScopeDefault + ) + } + + dokkatooExtension.dokkatooSourceSets.configureDefaults( + sourceSetScopeConvention = dokkatooExtension.sourceSetScopeDefault + ) + } + + private fun createExtension(project: Project): DokkatooExtension { + val dokkatooExtension = project.extensions.create<DokkatooExtension>(EXTENSION_NAME).apply { + moduleName.convention(providers.provider { project.name }) + moduleVersion.convention(providers.provider { project.version.toString() }) + modulePath.convention(project.pathAsFilePath()) + konanHome.convention( + providers + .provider { + // konanHome is set into in extraProperties: + // https://github.com/JetBrains/kotlin/blob/v1.9.0/libraries/tools/kotlin-gradle-plugin/src/common/kotlin/org/jetbrains/kotlin/gradle/targets/native/KotlinNativeTargetPreset.kt#L35-L38 + project.extensions.extraProperties.get("konanHome") as? String? + } + .map { File(it) } + ) + + sourceSetScopeDefault.convention(project.path) + dokkatooPublicationDirectory.convention(layout.buildDirectory.dir("dokka")) + dokkatooModuleDirectory.convention(layout.buildDirectory.dir("dokka-module")) + dokkatooConfigurationsDirectory.convention(layout.buildDirectory.dir("dokka-config")) + } + + dokkatooExtension.versions { + jetbrainsDokka.convention(DokkatooConstants.DOKKA_VERSION) + jetbrainsMarkdown.convention("0.3.1") + freemarker.convention("2.3.31") + kotlinxHtml.convention("0.8.0") + kotlinxCoroutines.convention("1.6.4") + } + + return dokkatooExtension + } + + /** Set defaults in all [DokkatooExtension.dokkatooPublications]s */ + private fun configureDokkaPublicationsDefaults( + dokkatooExtension: DokkatooExtension, + ) { + dokkatooExtension.dokkatooPublications.all { + enabled.convention(true) + cacheRoot.convention(dokkatooExtension.dokkatooCacheDirectory) + delayTemplateSubstitution.convention(false) + failOnWarning.convention(false) + finalizeCoroutines.convention(false) + moduleName.convention(dokkatooExtension.moduleName) + moduleVersion.convention(dokkatooExtension.moduleVersion) + offlineMode.convention(false) + outputDir.convention(dokkatooExtension.dokkatooPublicationDirectory) + suppressInheritedMembers.convention(false) + suppressObviousFunctions.convention(true) + } + } + + /** Set conventions for all [DokkaSourceSetSpec] properties */ + private fun NamedDomainObjectContainer<DokkaSourceSetSpec>.configureDefaults( + sourceSetScopeConvention: Property<String>, + ) { + configureEach dss@{ + analysisPlatform.convention(KotlinPlatform.DEFAULT) + displayName.convention( + analysisPlatform.map { platform -> + // Match existing Dokka naming conventions. (This should probably be simplified!) + when { + // Multiplatform source sets (e.g. commonMain, jvmMain, macosMain) + name.endsWith("Main") -> name.substringBeforeLast("Main") + + // indeterminate source sets should be named by the Kotlin platform + else -> platform.displayName + } + } + ) + documentedVisibilities.convention(setOf(VisibilityModifier.PUBLIC)) + jdkVersion.convention(8) + + enableKotlinStdLibDocumentationLink.convention(true) + enableJdkDocumentationLink.convention(true) + enableAndroidDocumentationLink.convention( + analysisPlatform.map { it == KotlinPlatform.AndroidJVM } + ) + + reportUndocumented.convention(false) + skipDeprecated.convention(false) + skipEmptyPackages.convention(true) + sourceSetScope.convention(sourceSetScopeConvention) + + // Manually added sourceSets should not be suppressed by default. dokkatooSourceSets that are + // automatically added by DokkatooKotlinAdapter will have a sensible value for suppress. + suppress.convention(false) + + suppressGeneratedFiles.convention(true) + + sourceLinks.configureEach { + localDirectory.convention(layout.projectDirectory) + remoteLineSuffix.convention("#L") + } + + perPackageOptions.configureEach { + matchingRegex.convention(".*") + suppress.convention(false) + skipDeprecated.convention(false) + reportUndocumented.convention(false) + } + + externalDocumentationLinks { + configureEach { + enabled.convention(true) + packageListUrl.convention(url.map { it.appendPath("package-list") }) + } + + maybeCreate("jdk") { + enabled.convention(this@dss.enableJdkDocumentationLink) + url(this@dss.jdkVersion.map { jdkVersion -> + when { + jdkVersion < 11 -> "https://docs.oracle.com/javase/${jdkVersion}/docs/api/" + else -> "https://docs.oracle.com/en/java/javase/${jdkVersion}/docs/api/" + } + }) + packageListUrl(this@dss.jdkVersion.map { jdkVersion -> + when { + 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" + } + }) + } + + maybeCreate("kotlinStdlib") { + enabled.convention(this@dss.enableKotlinStdLibDocumentationLink) + url("https://kotlinlang.org/api/latest/jvm/stdlib/") + } + + maybeCreate("androidSdk") { + enabled.convention(this@dss.enableAndroidDocumentationLink) + url("https://developer.android.com/reference/kotlin/") + } + + maybeCreate("androidX") { + enabled.convention(this@dss.enableAndroidDocumentationLink) + url("https://developer.android.com/reference/kotlin/") + packageListUrl("https://developer.android.com/reference/kotlin/androidx/package-list") + } + } + } + } + + private fun TaskContainer.createDokkaLifecycleTasks() { + register<DokkatooTask>(taskNames.generate) { + description = "Generates Dokkatoo publications for all formats" + dependsOn(withType<DokkatooGenerateTask>()) + } + } + + // workaround for https://github.com/gradle/gradle/issues/23708 + private fun RegularFileProperty.convention(file: File): RegularFileProperty = + convention(objects.fileProperty().fileValue(file)) + + // workaround for https://github.com/gradle/gradle/issues/23708 + private fun RegularFileProperty.convention(file: Provider<File>): RegularFileProperty = + convention(objects.fileProperty().fileProvider(file)) + + companion object { + + const val EXTENSION_NAME = "dokkatoo" + + /** + * The group of all Dokkatoo [Gradle tasks][org.gradle.api.Task]. + * + * @see org.gradle.api.Task.getGroup + */ + const val TASK_GROUP = "dokkatoo" + + /** The names of [Gradle tasks][org.gradle.api.Task] created by Dokkatoo */ + val taskNames = TaskNames(null) + + /** The names of [Configuration]s created by Dokkatoo */ + val dependencyContainerNames = DependencyContainerNames(null) + + internal val jsonMapper = Json { + prettyPrint = true + @OptIn(ExperimentalSerializationApi::class) + prettyPrintIndent = " " + } + } + + @DokkatooInternalApi + abstract class HasFormatName { + abstract val formatName: String? + + /** Appends [formatName] to the end of the string, camelcase style, if [formatName] is not null */ + protected fun String.appendFormat(): String = + when (val name = formatName) { + null -> this + else -> this + name.uppercaseFirstChar() + } + } + + /** + * Names of the Gradle [Configuration]s used by the [Dokkatoo Plugin][DokkatooBasePlugin]. + * + * Beware the confusing terminology: + * - [Gradle Configurations][org.gradle.api.artifacts.Configuration] - share files between subprojects. Each has a name. + * - [DokkaConfiguration][org.jetbrains.dokka.DokkaConfiguration] - parameters for executing the Dokka Generator + */ + @DokkatooInternalApi + class DependencyContainerNames(override val formatName: String?) : HasFormatName() { + + val dokkatoo = "dokkatoo".appendFormat() + + /** Name of the [Configuration] that _consumes_ all [org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription] files */ + val dokkatooModuleFilesConsumer = "dokkatooModule".appendFormat() + + /** Name of the [Configuration] that _provides_ all [org.jetbrains.dokka.DokkaConfiguration.DokkaModuleDescription] files to other projects */ + val dokkatooModuleFilesProvider = "dokkatooModuleElements".appendFormat() + + /** + * Classpath used to execute the Dokka Generator. + * + * Extends [dokkaPluginsClasspath], so Dokka plugins and their dependencies are included. + */ + val dokkaGeneratorClasspath = "dokkatooGeneratorClasspath".appendFormat() + + /** Dokka Plugins (including transitive dependencies, so this can be passed to the Dokka Generator Worker classpath) */ + val dokkaPluginsClasspath = "dokkatooPlugin".appendFormat() + + /** + * Dokka Plugins (excluding transitive dependencies) will be used to create Dokka Generator Parameters + * + * Generally, this configuration should not be invoked manually. Instead, use [dokkaPluginsClasspath]. + */ + val dokkaPluginsIntransitiveClasspath = "dokkatooPluginIntransitive".appendFormat() + } + + @DokkatooInternalApi + class TaskNames(override val formatName: String?) : HasFormatName() { + val generate = "dokkatooGenerate".appendFormat() + val generatePublication = "dokkatooGeneratePublication".appendFormat() + val generateModule = "dokkatooGenerateModule".appendFormat() + val prepareModuleDescriptor = "prepareDokkatooModuleDescriptor".appendFormat() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooExtension.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooExtension.kt new file mode 100644 index 00000000..d7b91541 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooExtension.kt @@ -0,0 +1,130 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec +import org.jetbrains.dokka.dokkatoo.internal.* +import java.io.Serializable +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.* + +/** + * Configure the behaviour of the [DokkatooBasePlugin]. + */ +abstract class DokkatooExtension +@DokkatooInternalApi +constructor( + objects: ObjectFactory, +) : ExtensionAware, Serializable { + + /** Directory into which [DokkaPublication]s will be produced */ + abstract val dokkatooPublicationDirectory: DirectoryProperty + + /** Directory into which Dokka Modules will be produced */ + abstract val dokkatooModuleDirectory: DirectoryProperty + + abstract val dokkatooConfigurationsDirectory: DirectoryProperty + + /** Default Dokkatoo cache directory */ + abstract val dokkatooCacheDirectory: DirectoryProperty + + abstract val moduleName: Property<String> + abstract val moduleVersion: Property<String> + abstract val modulePath: Property<String> + + /** + * An arbitrary string used to group source sets that originate from different Gradle subprojects. + * + * This is primarily used by Kotlin Multiplatform projects, which can have multiple source sets + * per subproject. + * + * Defaults to [the path of the subproject][org.gradle.api.Project.getPath]. + */ + abstract val sourceSetScopeDefault: Property<String> + + /** + * The Konan home directory, which contains libraries for Kotlin/Native development. + * + * This is only required as a workaround to fetch the compile-time dependencies in Kotlin/Native + * projects with a version below 2.0. + */ + // This property should be removed when Dokkatoo only supports KGP 2 or higher. + @DokkatooInternalApi + abstract val konanHome: RegularFileProperty + + /** + * Configuration for creating Dokka Publications. + * + * Each publication will generate one Dokka site based on the included Dokka Source Sets. + * + * The type of site is determined by the Dokka Plugins. By default, an HTML site will be generated. + */ + val dokkatooPublications: NamedDomainObjectContainer<DokkaPublication> = + extensions.adding( + "dokkatooPublications", + objects.domainObjectContainer { named -> objects.newInstance(named, pluginsConfiguration) } + ) + + /** + * Dokka Source Sets describe the source code that should be included in a Dokka Publication. + * + * Dokka will not generate documentation unless there is at least there is at least one Dokka Source Set. + * + * TODO make sure dokkatooSourceSets doc is up to date... + * + * Only source sets that are contained within _this project_ should be included here. + * To merge source sets from other projects, use the Gradle dependencies block. + * + * ```kotlin + * dependencies { + * // merge :other-project into this project's Dokka Configuration + * dokka(project(":other-project")) + * } + * ``` + * + * Or, to include other Dokka Publications as a Dokka Module use + * + * ```kotlin + * dependencies { + * // include :other-project as a module in this project's Dokka Configuration + * dokkaModule(project(":other-project")) + * } + * ``` + * + * Dokka will merge Dokka Source Sets from other subprojects if... + */ + val dokkatooSourceSets: NamedDomainObjectContainer<DokkaSourceSetSpec> = + extensions.adding("dokkatooSourceSets", objects.domainObjectContainer()) + + /** + * Dokka Plugin are used to configure the way Dokka generates a format. + * Some plugins can be configured via parameters, and those parameters are stored in this + * container. + */ + val pluginsConfiguration: DokkaPluginParametersContainer = + extensions.adding("pluginsConfiguration", objects.dokkaPluginParametersContainer()) + + /** + * Versions of dependencies that Dokkatoo will use to run Dokka Generator. + * + * These versions can be set to change the versions of dependencies that Dokkatoo uses defaults, + * or can be read to align versions. + */ + val versions: Versions = extensions.adding("versions", objects.newInstance()) + + interface Versions : ExtensionAware { + + /** Default version used for Dokka dependencies */ + val jetbrainsDokka: Property<String> + val jetbrainsMarkdown: Property<String> + val freemarker: Property<String> + val kotlinxHtml: Property<String> + val kotlinxCoroutines: Property<String> + + companion object + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooPlugin.kt new file mode 100644 index 00000000..0ace2ca6 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/DokkatooPlugin.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.formats.DokkatooGfmPlugin +import org.jetbrains.dokka.dokkatoo.formats.DokkatooHtmlPlugin +import org.jetbrains.dokka.dokkatoo.formats.DokkatooJavadocPlugin +import org.jetbrains.dokka.dokkatoo.formats.DokkatooJekyllPlugin +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.* + +/** + * Dokkatoo Gradle Plugin. + * + * Creates all necessary defaults to generate documentation for HTML, Jekyll, Markdown, and Javadoc formats. + */ +abstract class DokkatooPlugin +@DokkatooInternalApi +constructor() : Plugin<Project> { + + override fun apply(target: Project) { + with(target.pluginManager) { + apply(type = DokkatooBasePlugin::class) + + // auto-apply the custom format plugins + apply(type = DokkatooGfmPlugin::class) + apply(type = DokkatooHtmlPlugin::class) + apply(type = DokkatooJavadocPlugin::class) + apply(type = DokkatooJekyllPlugin::class) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt new file mode 100644 index 00000000..f5261bb4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt @@ -0,0 +1,214 @@ +package org.jetbrains.dokka.dokkatoo.adapters + +import com.android.build.api.dsl.CommonExtension +import com.android.build.gradle.AppExtension +import com.android.build.gradle.BaseExtension +import com.android.build.gradle.LibraryExtension +import com.android.build.gradle.TestExtension +import com.android.build.gradle.api.BaseVariant +import com.android.build.gradle.internal.dependency.VariantDependencies +import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.CLASSES_JAR +import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType.PROCESSED_JAR +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.collectIncomingFiles +import javax.inject.Inject +import org.gradle.api.DomainObjectSet +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logging +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.* + +@DokkatooInternalApi +abstract class DokkatooAndroidAdapter @Inject constructor( + private val objects: ObjectFactory, +) : Plugin<Project> { + + override fun apply(project: Project) { + logger.info("applied DokkatooAndroidAdapter to ${project.path}") + + project.plugins.withType<DokkatooBasePlugin>().configureEach { + project.pluginManager.apply { + withPlugin("com.android.base") { configure(project) } + withPlugin("com.android.application") { configure(project) } + withPlugin("com.android.library") { configure(project) } + } + } + } + + protected fun configure(project: Project) { + val dokkatooExtension = project.extensions.getByType<DokkatooExtension>() + + val androidExt = AndroidExtensionWrapper(project) + + if (androidExt == null) { + logger.warn("DokkatooAndroidAdapter could not get Android Extension for project ${project.path}") + return + } + + dokkatooExtension.dokkatooSourceSets.configureEach { + + classpath.from( + analysisPlatform.map { analysisPlatform -> + when (analysisPlatform) { + KotlinPlatform.AndroidJVM -> + AndroidClasspathCollector( + androidExt = androidExt, + configurations = project.configurations, + objects = objects, + ) + + else -> + objects.fileCollection() + } + } + ) + } + } + + @DokkatooInternalApi + companion object { + private val logger = Logging.getLogger(DokkatooAndroidAdapter::class.java) + } +} + +private fun AndroidExtensionWrapper( + project: Project +): AndroidExtensionWrapper? { + +// fetching _all_ configuration names is very brute force and should probably be refined to +// only fetch those that match a specific DokkaSourceSetSpec + + return runCatching { + val androidExt = project.extensions.getByType<BaseExtension>() + AndroidExtensionWrapper.forBaseExtension( + androidExt = androidExt, + providers = project.providers, + objects = project.objects + ) + }.recoverCatching { + val androidExt = project.extensions.getByType(CommonExtension::class) + AndroidExtensionWrapper.forCommonExtension(androidExt) + }.getOrNull() +} + +/** + * Android Gradle Plugin is having a refactor. Try to wrap the Android extension so that Dokkatoo + * can still access the configuration names without caring about which AGP version is in use. + */ +private interface AndroidExtensionWrapper { + fun variantConfigurationNames(): Set<String> + + companion object { + + @Suppress("DEPRECATION") + fun forBaseExtension( + androidExt: BaseExtension, + providers: ProviderFactory, + objects: ObjectFactory, + ): AndroidExtensionWrapper { + return object : AndroidExtensionWrapper { + /** Fetch all configuration names used by all variants. */ + override fun variantConfigurationNames(): Set<String> { + val collector = objects.domainObjectSet(BaseVariant::class) + + val variants: DomainObjectSet<BaseVariant> = + collector.apply { + addAllLater(providers.provider { + when (androidExt) { + is LibraryExtension -> androidExt.libraryVariants + is AppExtension -> androidExt.applicationVariants + is TestExtension -> androidExt.applicationVariants + else -> emptyList() + } + }) + } + + return buildSet { + variants.forEach { + add(it.compileConfiguration.name) + add(it.runtimeConfiguration.name) + add(it.annotationProcessorConfiguration.name) + } + } + } + } + } + + fun forCommonExtension( + androidExt: CommonExtension<*, *, *, *> + ): AndroidExtensionWrapper { + return object : AndroidExtensionWrapper { + /** Fetch all configuration names used by all variants. */ + override fun variantConfigurationNames(): Set<String> { + return buildSet { + @Suppress("UnstableApiUsage") + androidExt.sourceSets.forEach { + add(it.apiConfigurationName) + add(it.compileOnlyConfigurationName) + add(it.implementationConfigurationName) + add(it.runtimeOnlyConfigurationName) + add(it.wearAppConfigurationName) + add(it.annotationProcessorConfigurationName) + } + } + } + } + } + } +} + + +/** + * A utility for determining the classpath of an Android compilation. + * + * It's important that this class is separate from [DokkatooAndroidAdapter]. It must be separate + * because it uses Android Gradle Plugin classes (like [BaseExtension]). Were it not separate, and + * these classes were present in the function signatures of [DokkatooAndroidAdapter], then when + * Gradle tries to create a decorated instance of [DokkatooAndroidAdapter] it will if the project + * does not have the Android Gradle Plugin applied, because the classes will be missing. + */ +private object AndroidClasspathCollector { + + operator fun invoke( + androidExt: AndroidExtensionWrapper, + configurations: ConfigurationContainer, + objects: ObjectFactory, + ): FileCollection { + val compilationClasspath = objects.fileCollection() + + fun collectConfiguration(named: String) { + listOf( + // need to fetch multiple different types of files, because AGP is weird and doesn't seem + // to have a 'just give me normal JVM classes' option + ARTIFACT_TYPE_ATTRIBUTE to PROCESSED_JAR.type, + ARTIFACT_TYPE_ATTRIBUTE to CLASSES_JAR.type, + ).forEach { (attribute, attributeValue) -> + configurations.collectIncomingFiles(named, collector = compilationClasspath) { + attributes { + attribute(attribute, attributeValue) + } + lenient(true) + } + } + } + + // fetch android.jar + collectConfiguration(named = VariantDependencies.CONFIG_NAME_ANDROID_APIS) + + val variantConfigurations = androidExt.variantConfigurationNames() + + for (variantConfig in variantConfigurations) { + collectConfiguration(named = variantConfig) + } + + return compilationClasspath + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt new file mode 100644 index 00000000..0f834363 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka.dokkatoo.adapters + +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.logging.Logging +import org.gradle.api.plugins.JavaBasePlugin +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.kotlin.dsl.* + +/** + * Apply Java specific configuration to the Dokkatoo plugin. + * + * **Must be applied *after* [org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin]** + */ +@DokkatooInternalApi +abstract class DokkatooJavaAdapter @Inject constructor() : Plugin<Project> { + + private val logger = Logging.getLogger(this::class.java) + + override fun apply(project: Project) { + logger.info("applied DokkatooJavaAdapter to ${project.path}") + + // wait for the Java plugin to be applied + project.plugins.withType<JavaBasePlugin>().configureEach { + + // fetch the toolchain, and use the language version as Dokka's jdkVersion + val toolchainLanguageVersion = project.extensions.getByType<JavaPluginExtension>() + .toolchain + .languageVersion + + val dokka = project.extensions.getByType<DokkatooExtension>() + dokka.dokkatooSourceSets.configureEach { + jdkVersion.set(toolchainLanguageVersion.map { it.asInt() }.orElse(8)) + } + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt new file mode 100644 index 00000000..82df651d --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt @@ -0,0 +1,459 @@ +package org.jetbrains.dokka.dokkatoo.adapters + +import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.api.LibraryVariant +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.adapters.DokkatooKotlinAdapter.Companion.currentKotlinToolingVersion +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec.Companion.dokkaSourceSetIdSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.not +import java.io.File +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.FileCollection +import org.gradle.api.logging.Logging +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.provider.SetProperty +import org.gradle.kotlin.dsl.* +import org.jetbrains.kotlin.commonizer.KonanDistribution +import org.jetbrains.kotlin.commonizer.platformLibsDir +import org.jetbrains.kotlin.commonizer.stdlib +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.KotlinCompilation.Companion.MAIN_COMPILATION_NAME +import org.jetbrains.kotlin.gradle.plugin.KotlinSourceSet +import org.jetbrains.kotlin.gradle.plugin.getKotlinPluginVersion +import org.jetbrains.kotlin.gradle.plugin.mpp.AbstractKotlinNativeCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinJvmAndroidCompilation +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinMetadataCompilation +import org.jetbrains.kotlin.konan.target.KonanTarget +import org.jetbrains.kotlin.tooling.core.KotlinToolingVersion + +/** + * The [DokkatooKotlinAdapter] plugin will automatically register Kotlin source sets as Dokka source sets. + * + * This is not a standalone plugin, it requires [org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin] is also applied. + */ +@DokkatooInternalApi +abstract class DokkatooKotlinAdapter @Inject constructor( + private val objects: ObjectFactory, + private val providers: ProviderFactory, +) : Plugin<Project> { + + override fun apply(project: Project) { + logger.info("applied DokkatooKotlinAdapter to ${project.path}") + + project.plugins.withType<DokkatooBasePlugin>().configureEach { + project.pluginManager.apply { + withPlugin("org.jetbrains.kotlin.android") { exec(project) } + withPlugin("org.jetbrains.kotlin.js") { exec(project) } + withPlugin("org.jetbrains.kotlin.jvm") { exec(project) } + withPlugin("org.jetbrains.kotlin.multiplatform") { exec(project) } + } + } + } + + private fun exec(project: Project) { + val kotlinExtension = project.extensions.findKotlinExtension() ?: run { + logger.info("could not find Kotlin Extension") + return + } + logger.info("Configuring Dokkatoo in Gradle Kotlin Project ${project.path}") + + val dokkatooExtension = project.extensions.getByType<DokkatooExtension>() + + // first fetch the relevant properties of all KotlinCompilations + val compilationDetailsBuilder = KotlinCompilationDetailsBuilder( + providers = providers, + objects = objects, + konanHome = dokkatooExtension.konanHome.asFile, + ) + val allKotlinCompilationDetails: ListProperty<KotlinCompilationDetails> = + compilationDetailsBuilder.createCompilationDetails( + kotlinProjectExtension = kotlinExtension, + ) + + // second, fetch the relevant properties of the Kotlin source sets + val sourceSetDetailsBuilder = KotlinSourceSetDetailsBuilder( + providers = providers, + objects = objects, + sourceSetScopeDefault = dokkatooExtension.sourceSetScopeDefault, + projectPath = project.path, + ) + val sourceSetDetails: NamedDomainObjectContainer<KotlinSourceSetDetails> = + sourceSetDetailsBuilder.createSourceSetDetails( + kotlinSourceSets = kotlinExtension.sourceSets, + allKotlinCompilationDetails = allKotlinCompilationDetails, + ) + + // for each Kotlin source set, register a Dokkatoo source set + registerDokkatooSourceSets( + dokkatooExtension = dokkatooExtension, + sourceSetDetails = sourceSetDetails, + ) + } + + /** Register a [DokkaSourceSetSpec] for each element in [sourceSetDetails] */ + private fun registerDokkatooSourceSets( + dokkatooExtension: DokkatooExtension, + sourceSetDetails: NamedDomainObjectContainer<KotlinSourceSetDetails>, + ) { + // proactively use 'all' so source sets will be available in users' build files if they use `named("...")` + sourceSetDetails.all details@{ + dokkatooExtension.dokkatooSourceSets.register(details = this@details) + } + } + + /** Register a single [DokkaSourceSetSpec] for [details] */ + private fun NamedDomainObjectContainer<DokkaSourceSetSpec>.register( + details: KotlinSourceSetDetails + ) { + val kssPlatform = details.compilations.map { values: List<KotlinCompilationDetails> -> + values.map { it.kotlinPlatform } + .distinct() + .singleOrNull() ?: KotlinPlatform.Common + } + + val kssClasspath = determineClasspath(details) + + register(details.name) dss@{ + suppress.set(!details.isPublishedSourceSet()) + sourceRoots.from(details.sourceDirectories) + classpath.from(kssClasspath) + analysisPlatform.set(kssPlatform) + dependentSourceSets.addAllLater(details.dependentSourceSetIds) + } + } + + private fun determineClasspath( + details: KotlinSourceSetDetails + ): Provider<FileCollection> { + return details.compilations.map { compilations: List<KotlinCompilationDetails> -> + val classpath = objects.fileCollection() + + if (compilations.isNotEmpty()) { + compilations.fold(classpath) { acc, compilation -> + acc.from(compilation.compilationClasspath) + // can't use compileDependencyFiles, it causes weird dependency resolution errors in Android projects + //acc.from(providers.provider { compilation.compileDependencyFiles }) + } + } else { + classpath + .from(details.sourceDirectories) + .from(details.sourceDirectoriesOfDependents) + } + } + } + + @DokkatooInternalApi + companion object { + private val logger = Logging.getLogger(DokkatooKotlinAdapter::class.java) + + /** Try and get [KotlinProjectExtension], or `null` if it's not present */ + private fun ExtensionContainer.findKotlinExtension(): KotlinProjectExtension? = + try { + findByType() + // fallback to trying to get the JVM extension + // (not sure why I did this... maybe to be compatible with really old versions?) + ?: findByType<org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension>() + } catch (e: Throwable) { + when (e) { + is TypeNotPresentException, + is ClassNotFoundException, + is NoClassDefFoundError -> null + + else -> throw e + } + } + + /** Get the version of the Kotlin Gradle Plugin currently used to compile the project */ + // Must be lazy, else tests fail (because the KGP plugin isn't accessible) + internal val currentKotlinToolingVersion: KotlinToolingVersion by lazy { + val kgpVersion = getKotlinPluginVersion(logger) + KotlinToolingVersion(kgpVersion) + } + } +} + + +/** + * Store the details of all [KotlinCompilation]s in a configuration cache compatible way. + * + * The compilation details may come from a multiplatform project ([KotlinMultiplatformExtension]) + * or a single-platform project ([KotlinSingleTargetExtension]). + */ +@DokkatooInternalApi +private data class KotlinCompilationDetails( + val target: String, + val kotlinPlatform: KotlinPlatform, + val allKotlinSourceSetsNames: Set<String>, + val publishedCompilation: Boolean, + val dependentSourceSetNames: Set<String>, + val compilationClasspath: FileCollection, + val defaultSourceSetName: String, +) + +/** Utility class, encapsulating logic for building [KotlinCompilationDetails] */ +private class KotlinCompilationDetailsBuilder( + private val objects: ObjectFactory, + private val providers: ProviderFactory, + private val konanHome: Provider<File>, +) { + + fun createCompilationDetails( + kotlinProjectExtension: KotlinProjectExtension, + ): ListProperty<KotlinCompilationDetails> { + + val details = objects.listProperty<KotlinCompilationDetails>() + + details.addAll( + providers.provider { + kotlinProjectExtension + .allKotlinCompilations() + .map { compilation -> + createCompilationDetails(compilation = compilation) + } + }) + + return details + } + + /** Create a single [KotlinCompilationDetails] for [compilation] */ + private fun createCompilationDetails( + compilation: KotlinCompilation<*>, + ): KotlinCompilationDetails { + val allKotlinSourceSetsNames = + compilation.allKotlinSourceSets.map { it.name } + compilation.defaultSourceSet.name + + val dependentSourceSetNames = + compilation.defaultSourceSet.dependsOn.map { it.name } + + val compilationClasspath: FileCollection = + collectKotlinCompilationClasspath(compilation = compilation) + + return KotlinCompilationDetails( + target = compilation.target.name, + kotlinPlatform = KotlinPlatform.fromString(compilation.platformType.name), + allKotlinSourceSetsNames = allKotlinSourceSetsNames.toSet(), + publishedCompilation = compilation.isPublished(), + dependentSourceSetNames = dependentSourceSetNames.toSet(), + compilationClasspath = compilationClasspath, + defaultSourceSetName = compilation.defaultSourceSet.name + ) + } + + private fun KotlinProjectExtension.allKotlinCompilations(): Collection<KotlinCompilation<*>> = + when (this) { + is KotlinMultiplatformExtension -> targets.flatMap { it.compilations } + is KotlinSingleTargetExtension<*> -> target.compilations + else -> emptyList() // shouldn't happen? + } + + /** + * Get the [Configuration][org.gradle.api.artifacts.Configuration] names of all configurations + * used to build this [KotlinCompilation] and + * [its source sets][KotlinCompilation.kotlinSourceSets]. + */ + private fun collectKotlinCompilationClasspath( + compilation: KotlinCompilation<*>, + ): FileCollection { + val compilationClasspath = objects.fileCollection() + + // collect dependency files from 'regular' Kotlin compilations + compilationClasspath.from(providers.provider { compilation.compileDependencyFiles }) + + // apply workaround for Kotlin/Native, which will be fixed in Kotlin 2.0 + // (see KT-61559: K/N dependencies will be part of `compilation.compileDependencyFiles`) + if ( + currentKotlinToolingVersion < KotlinToolingVersion("2.0.0") + && + compilation is AbstractKotlinNativeCompilation + ) { + compilationClasspath.from( + konanHome.map { konanHome -> + kotlinNativeDependencies(konanHome, compilation.konanTarget) + } + ) + } + + return compilationClasspath + } + + private fun kotlinNativeDependencies(konanHome: File, target: KonanTarget): FileCollection { + val konanDistribution = KonanDistribution(konanHome) + + val dependencies = objects.fileCollection() + + dependencies.from(konanDistribution.stdlib) + + // Konan library files for a specific target + dependencies.from( + konanDistribution.platformLibsDir + .resolve(target.name) + .listFiles() + .orEmpty() + .filter { it.isDirectory || it.extension == "klib" } + ) + + return dependencies + } + + companion object { + + /** + * Determine if a [KotlinCompilation] is 'publishable', and so should be enabled by default + * when creating a Dokka publication. + * + * Typically, 'main' compilations are publishable and 'test' compilations should be suppressed. + * This can be overridden manually, though. + * + * @see DokkaSourceSetSpec.suppress + */ + private fun KotlinCompilation<*>.isPublished(): Boolean { + return when (this) { + is KotlinMetadataCompilation<*> -> true + + is KotlinJvmAndroidCompilation -> + androidVariant is LibraryVariant || androidVariant is ApplicationVariant + + else -> + name == MAIN_COMPILATION_NAME + } + } + } +} + + +/** + * Store the details of all [KotlinSourceSet]s in a configuration cache compatible way. + * + * @param[named] Should be [KotlinSourceSet.getName] + */ +@DokkatooInternalApi +private abstract class KotlinSourceSetDetails @Inject constructor( + private val named: String, +) : Named { + + /** Direct source sets that this source set depends on */ + abstract val dependentSourceSetIds: SetProperty<DokkaSourceSetIdSpec> + abstract val sourceDirectories: ConfigurableFileCollection + /** _All_ source directories from any (recursively) dependant source set */ + abstract val sourceDirectoriesOfDependents: ConfigurableFileCollection + /** The specific compilations used to build this source set */ + abstract val compilations: ListProperty<KotlinCompilationDetails> + + /** Estimate if this Kotlin source set contains 'published' sources */ + fun isPublishedSourceSet(): Provider<Boolean> = + compilations.map { values -> + values.any { it.publishedCompilation } + } + + override fun getName(): String = named +} + +/** Utility class, encapsulating logic for building [KotlinCompilationDetails] */ +private class KotlinSourceSetDetailsBuilder( + private val sourceSetScopeDefault: Provider<String>, + private val objects: ObjectFactory, + private val providers: ProviderFactory, + /** Used for logging */ + private val projectPath: String, +) { + + private val logger = Logging.getLogger(KotlinSourceSetDetails::class.java) + + fun createSourceSetDetails( + kotlinSourceSets: NamedDomainObjectContainer<KotlinSourceSet>, + allKotlinCompilationDetails: ListProperty<KotlinCompilationDetails>, + ): NamedDomainObjectContainer<KotlinSourceSetDetails> { + + val sourceSetDetails = objects.domainObjectContainer(KotlinSourceSetDetails::class) + + kotlinSourceSets.configureEach kss@{ + sourceSetDetails.register( + kotlinSourceSet = this, + allKotlinCompilationDetails = allKotlinCompilationDetails, + ) + } + + return sourceSetDetails + } + + private fun NamedDomainObjectContainer<KotlinSourceSetDetails>.register( + kotlinSourceSet: KotlinSourceSet, + allKotlinCompilationDetails: ListProperty<KotlinCompilationDetails>, + ) { + + // TODO: Needs to respect filters. + // We probably need to change from "sourceRoots" to support "sourceFiles" + // https://github.com/Kotlin/dokka/issues/1215 + val extantSourceDirectories = providers.provider { + kotlinSourceSet.kotlin.sourceDirectories.filter { it.exists() } + } + + val compilations = allKotlinCompilationDetails.map { allCompilations -> + allCompilations.filter { compilation -> + kotlinSourceSet.name in compilation.allKotlinSourceSetsNames + } + } + + // determine the source sets IDs of _other_ source sets that _this_ source depends on. + val dependentSourceSets = providers.provider { kotlinSourceSet.dependsOn } + val dependentSourceSetIds = + providers.zip( + dependentSourceSets, + sourceSetScopeDefault, + ) { sourceSets, sourceSetScope -> + logger.info("[$projectPath] source set ${kotlinSourceSet.name} has ${sourceSets.size} dependents ${sourceSets.joinToString { it.name }}") + sourceSets.map { dependedKss -> + objects.dokkaSourceSetIdSpec(sourceSetScope, dependedKss.name) + } + } + + val sourceDirectoriesOfDependents = providers.provider { + kotlinSourceSet + .allDependentSourceSets() + .fold(objects.fileCollection()) { acc, sourceSet -> + acc.from(sourceSet.kotlin.sourceDirectories) + } + } + + register(kotlinSourceSet.name) { + this.dependentSourceSetIds.addAll(dependentSourceSetIds) + this.sourceDirectories.from(extantSourceDirectories) + this.sourceDirectoriesOfDependents.from(sourceDirectoriesOfDependents) + this.compilations.addAll(compilations) + } + } + + /** + * Return a list containing _all_ source sets that this source set depends on, + * searching recursively. + * + * @see KotlinSourceSet.dependsOn + */ + private tailrec fun KotlinSourceSet.allDependentSourceSets( + queue: Set<KotlinSourceSet> = dependsOn.toSet(), + allDependents: List<KotlinSourceSet> = emptyList(), + ): List<KotlinSourceSet> { + val next = queue.firstOrNull() ?: return allDependents + return next.allDependentSourceSets( + queue = (queue - next) union next.dependsOn, + allDependents = allDependents + next, + ) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt new file mode 100644 index 00000000..57ca5ef9 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt @@ -0,0 +1,59 @@ +package org.jetbrains.dokka.dokkatoo.distributions + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.Attribute +import org.gradle.api.attributes.Usage +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.* + +/** + * Gradle Configuration Attributes for sharing Dokkatoo files across subprojects. + * + * These attributes are used to tag [Configuration]s, so files can be shared between subprojects. + */ +@DokkatooInternalApi +abstract class DokkatooConfigurationAttributes +@Inject +constructor( + objects: ObjectFactory, +) { + + /** A general attribute for all [Configuration]s that are used by the Dokka Gradle plugin */ + val dokkatooBaseUsage: DokkatooBaseAttribute = objects.named("dokkatoo") + + /** for [Configuration]s that provide or consume Dokka parameter files */ + val dokkaParameters: DokkatooCategoryAttribute = objects.named("generator-parameters") + + /** for [Configuration]s that provide or consume Dokka Module files */ + val dokkaModuleFiles: DokkatooCategoryAttribute = objects.named("module-files") +// val dokkaModuleSource: DokkatooCategoryAttribute = objects.named("module-source") + + val dokkaGeneratorClasspath: DokkatooCategoryAttribute = objects.named("generator-classpath") + + val dokkaPluginsClasspath: DokkatooCategoryAttribute = objects.named("plugins-classpath") + + @DokkatooInternalApi + interface DokkatooBaseAttribute : Usage + + @DokkatooInternalApi + interface DokkatooCategoryAttribute : Named + + @DokkatooInternalApi + interface DokkaFormatAttribute : Named + + @DokkatooInternalApi + companion object { + val DOKKATOO_BASE_ATTRIBUTE = + Attribute<DokkatooBaseAttribute>("org.jetbrains.dokka.dokkatoo.base") + val DOKKATOO_CATEGORY_ATTRIBUTE = + Attribute<DokkatooCategoryAttribute>("org.jetbrains.dokka.dokkatoo.category") + val DOKKA_FORMAT_ATTRIBUTE = + Attribute<DokkaFormatAttribute>("org.jetbrains.dokka.dokkatoo.format") + + private inline fun <reified T> Attribute(name: String): Attribute<T> = + Attribute.of(name, T::class.java) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/DokkaPublication.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/DokkaPublication.kt new file mode 100644 index 00000000..50c26415 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/DokkaPublication.kt @@ -0,0 +1,122 @@ +package org.jetbrains.dokka.dokkatoo.dokka + +import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.adding +import java.io.Serializable +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.kotlin.dsl.* + +/** + * A [DokkaPublication] describes a single Dokka output. + * + * Each Publication has its own set of Gradle tasks and [org.gradle.api.artifacts.Configuration]s. + * + * The type of site is determined by the Dokka Plugins. By default, an HTML site will be generated. + * By default, Dokka will create publications for HTML, Jekyll, and GitHub Flavoured Markdown. + */ +abstract class DokkaPublication +@DokkatooInternalApi +@Inject +constructor( + @get:Internal + val formatName: String, + + /** + * Configurations for Dokka Generator Plugins. Must be provided from + * [org.jetbrains.dokka.dokkatoo.DokkatooExtension.pluginsConfiguration]. + */ + pluginsConfiguration: DokkaPluginParametersContainer, +) : Named, Serializable, ExtensionAware { + + /** Configurations for Dokka Generator Plugins. */ + @get:Nested + val pluginsConfiguration: DokkaPluginParametersContainer = + extensions.adding("pluginsConfiguration", pluginsConfiguration) + + @Internal + override fun getName(): String = formatName + + @get:Input + abstract val enabled: Property<Boolean> + + @get:Input + abstract val moduleName: Property<String> + + @get:Input + @get:Optional + abstract val moduleVersion: Property<String> + + @get:Internal + // marked as Internal because this task does not use the directory contents, only the location + abstract val outputDir: DirectoryProperty + + /** + * Because [outputDir] must be [Internal] (so Gradle doesn't check the directory contents), + * [outputDirPath] is required so Gradle can determine if the task is up-to-date. + */ + @get:Input + // marked as an Input because a DokkaPublication is used to configure the appropriate + // DokkatooTasks, which will then + @DokkatooInternalApi + protected val outputDirPath: Provider<String> + get() = outputDir.map { it.asFile.invariantSeparatorsPath } + + @get:Internal + // Marked as Internal because this task does not use the directory contents, only the location. + // Note that `cacheRoot` is not used by Dokka, and will probably be deprecated. + abstract val cacheRoot: DirectoryProperty + + /** + * Because [cacheRoot] must be [Internal] (so Gradle doesn't check the directory contents), + * [cacheRootPath] is required so Gradle can determine if the task is up-to-date. + */ + @get:Input + @get:Optional + @DokkatooInternalApi + protected val cacheRootPath: Provider<String> + get() = cacheRoot.map { it.asFile.invariantSeparatorsPath } + + @get:Input + abstract val offlineMode: Property<Boolean> + +// /** Dokka Configuration files from other subprojects that will be merged into this Dokka Configuration */ +// @get:InputFiles +// @get:NormalizeLineEndings +// @get:PathSensitive(PathSensitivity.NAME_ONLY) +// abstract val dokkaSubprojectConfigurations: ConfigurableFileCollection + +// /** Dokka Module Configuration from other subprojects. */ +// @get:InputFiles +// @get:NormalizeLineEndings +// @get:PathSensitive(PathSensitivity.NAME_ONLY) +// abstract val dokkaModuleDescriptorFiles: ConfigurableFileCollection + + @get:Input + abstract val failOnWarning: Property<Boolean> + + @get:Input + abstract val delayTemplateSubstitution: Property<Boolean> + + @get:Input + abstract val suppressObviousFunctions: Property<Boolean> + + @get:InputFiles + @get:PathSensitive(RELATIVE) + abstract val includes: ConfigurableFileCollection + + @get:Input + abstract val suppressInheritedMembers: Property<Boolean> + + @get:Input + // TODO probably not needed any more, since Dokka Generator now runs in an isolated JVM process + abstract val finalizeCoroutines: Property<Boolean> +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt new file mode 100644 index 00000000..e91721aa --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt @@ -0,0 +1,120 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import java.net.URI +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.intellij.lang.annotations.Language + +/** + * Configuration builder that allows creating links leading to externally hosted + * documentation of your dependencies. + * + * For instance, if you are using types from `kotlinx.serialization`, by default + * they will be unclickable in your documentation, as if unresolved. However, + * since API reference for `kotlinx.serialization` is also built by Dokka and is + * [published on kotlinlang.org](https://kotlinlang.org/api/kotlinx.serialization/), + * you can configure external documentation links for it, allowing Dokka to generate + * documentation links for used types, making them clickable and appear resolved. + * + * Example in Gradle Kotlin DSL: + * + * ```kotlin + * externalDocumentationLink { + * url.set(URI("https://kotlinlang.org/api/kotlinx.serialization/")) + * packageListUrl.set( + * rootProject.projectDir.resolve("serialization.package.list").toURI() + * ) + * } + * ``` + */ +abstract class DokkaExternalDocumentationLinkSpec +@DokkatooInternalApi +@Inject +constructor( + private val name: String +) : Serializable, Named { + + /** + * Root URL of documentation to link with. + * + * Dokka will do its best to automatically find `package-list` for the given URL, and link + * declarations together. + * + * It automatic resolution fails or if you want to use locally cached files instead, + * consider providing [packageListUrl]. + * + * Example: + * + * ```kotlin + * java.net.URI("https://kotlinlang.org/api/kotlinx.serialization/") + * ``` + */ + @get:Input + abstract val url: Property<URI> + + /** + * Set the value of [url]. + * + * @param[value] will be converted to a [URI] + */ + fun url(@Language("http-url-reference") value: String): Unit = + url.set(URI(value)) + + /** + * Set the value of [url]. + * + * @param[value] will be converted to a [URI] + */ + fun url(value: Provider<String>): Unit = + url.set(value.map(::URI)) + + /** + * Specifies the exact location of a `package-list` instead of relying on Dokka + * automatically resolving it. Can also be a locally cached file to avoid network calls. + * + * Example: + * + * ```kotlin + * rootProject.projectDir.resolve("serialization.package.list").toURL() + * ``` + */ + @get:Input + abstract val packageListUrl: Property<URI> + + /** + * Set the value of [packageListUrl]. + * + * @param[value] will be converted to a [URI] + */ + fun packageListUrl(@Language("http-url-reference") value: String): Unit = + packageListUrl.set(URI(value)) + + /** + * Set the value of [packageListUrl]. + * + * @param[value] will be converted to a [URI] + */ + fun packageListUrl(value: Provider<String>): Unit = + packageListUrl.set(value.map(::URI)) + + /** + * If enabled this link will be passed to the Dokka Generator. + * + * Defaults to `true`. + * + * @see org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec.enableKotlinStdLibDocumentationLink + * @see org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec.enableJdkDocumentationLink + * @see org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec.enableAndroidDocumentationLink + */ + @get:Input + abstract val enabled: Property<Boolean> + + @Internal + override fun getName(): String = name +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt new file mode 100644 index 00000000..41090e65 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt @@ -0,0 +1,93 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.adding +import org.jetbrains.dokka.dokkatoo.internal.domainObjectContainer +import javax.inject.Inject +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.work.NormalizeLineEndings + +/** + * Parameters used to run Dokka Generator to produce either a Publication or a Module. + * + * + */ +abstract class DokkaGeneratorParametersSpec +@DokkatooInternalApi +@Inject +constructor( + objects: ObjectFactory, + /** + * Configurations for Dokka Generator Plugins. Must be provided from + * [org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.pluginsConfiguration]. + */ + @get:Nested + val pluginsConfiguration: DokkaPluginParametersContainer, +) : ExtensionAware { + +// /** Dokka Configuration files from other subprojects that will be merged into this Dokka Configuration */ +// @get:InputFiles +// //@get:NormalizeLineEndings +// @get:PathSensitive(PathSensitivity.RELATIVE) +// @get:Optional +// abstract val dokkaSubprojectParameters: ConfigurableFileCollection + + @get:Input + abstract val failOnWarning: Property<Boolean> + + @get:Input + abstract val finalizeCoroutines: Property<Boolean> + + @get:Input + abstract val moduleName: Property<String> + + @get:Input + @get:Optional + abstract val moduleVersion: Property<String> + + @get:Input + abstract val offlineMode: Property<Boolean> + + @get:Input + abstract val suppressObviousFunctions: Property<Boolean> + + @get:Input + abstract val suppressInheritedMembers: Property<Boolean> + + @get:InputFiles + @get:PathSensitive(RELATIVE) + abstract val includes: ConfigurableFileCollection + + /** + * Classpath that contains the Dokka Generator Plugins used to modify this publication. + * + * The plugins should be configured in [org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.pluginsConfiguration]. + */ + @get:InputFiles + @get:Classpath + abstract val pluginsClasspath: ConfigurableFileCollection + + /** + * Source sets used to generate a Dokka Module. + * + * The values are not used directly in this task, but they are required to be registered as a + * task input for up-to-date checks + */ + @get:Nested + val dokkaSourceSets: NamedDomainObjectContainer<DokkaSourceSetSpec> = + extensions.adding("dokkaSourceSets", objects.domainObjectContainer()) + + /** Dokka Module files from other subprojects. */ + @get:InputFiles + @get:NormalizeLineEndings + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val dokkaModuleFiles: ConfigurableFileCollection +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt new file mode 100644 index 00000000..af3e13b0 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt @@ -0,0 +1,49 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.DokkaConfiguration + +/** + * Properties that describe a Dokka Module. + * + * These values are passed into Dokka Generator, which will aggregate all provided Modules into a + * single publication. + */ +@DokkatooInternalApi +abstract class DokkaModuleDescriptionSpec +@DokkatooInternalApi +@Inject constructor( + @get:Input + val moduleName: String, +) : Named { + + /** + * @see DokkaConfiguration.DokkaModuleDescription.sourceOutputDirectory + */ + @get:Input + abstract val sourceOutputDirectory: RegularFileProperty + + /** + * @see DokkaConfiguration.DokkaModuleDescription.includes + */ + @get:Input + abstract val includes: ConfigurableFileCollection + + /** + * File path of the subproject that determines where the Dokka Module will be placed within an + * assembled Dokka Publication. + * + * This must be a relative path, and will be appended to the root Dokka Publication directory. + * + * The Gradle project path will also be accepted ([org.gradle.api.Project.getPath]), and the + * colons `:` will be replaced with file separators `/`. + */ + @get:Input + abstract val projectPath: Property<String> +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt new file mode 100644 index 00000000..44e55a74 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt @@ -0,0 +1,84 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input + +/** + * Configuration builder that allows setting some options for specific packages + * matched by [matchingRegex]. + * + * Example in Gradle Kotlin DSL: + * + * ```kotlin + * tasks.dokkaHtml { + * dokkaSourceSets.configureEach { + * perPackageOption { + * matchingRegex.set(".*internal.*") + * suppress.set(true) + * } + * } + * } + * ``` + */ +abstract class DokkaPackageOptionsSpec +@DokkatooInternalApi +constructor() : + HasConfigurableVisibilityModifiers, + Serializable { + + /** + * Regular expression that is used to match the package. + * + * Default is any string: `.*`. + */ + @get:Input + abstract val matchingRegex: Property<String> + + /** + * Whether this package should be skipped when generating documentation. + * + * Default is `false`. + */ + @get:Input + abstract val suppress: Property<Boolean> + + /** + * Set of visibility modifiers that should be documented. + * + * This can be used if you want to document protected/internal/private declarations within a + * specific package, as well as if you want to exclude public declarations and only document internal API. + * + * Can be configured for a whole source set, see [DokkaSourceSetSpec.documentedVisibilities]. + * + * Default is [VisibilityModifier.PUBLIC]. + */ + @get:Input + abstract override val documentedVisibilities: SetProperty<VisibilityModifier> + + /** + * Whether to document declarations annotated with [Deprecated]. + * + * Can be overridden on source set level by setting [DokkaSourceSetSpec.skipDeprecated]. + * + * Default is `false`. + */ + @get:Input + abstract val skipDeprecated: Property<Boolean> + + /** + * Whether to emit warnings about visible undocumented declarations, that is declarations from + * this package and without KDocs, after they have been filtered by [documentedVisibilities]. + * + * + * Can be overridden on source set level by setting [DokkaSourceSetSpec.reportUndocumented]. + * + * Default is `false`. + */ + @get:Input + abstract val reportUndocumented: Property<Boolean> +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt new file mode 100644 index 00000000..df790bcb --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt @@ -0,0 +1,78 @@ +@file:UseSerializers( + FileAsPathStringSerializer::class, +) + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.File +import java.nio.file.Paths +import kotlinx.serialization.KSerializer +import kotlinx.serialization.Serializable +import kotlinx.serialization.UseSerializers +import kotlinx.serialization.descriptors.PrimitiveKind +import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor +import kotlinx.serialization.descriptors.SerialDescriptor +import kotlinx.serialization.encoding.Decoder +import kotlinx.serialization.encoding.Encoder +import org.gradle.kotlin.dsl.* +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaModuleDescriptionImpl + + +// Implementations of DokkaConfiguration interfaces that can be serialized to files. +// Serialization is required because Gradle tasks can only pass data to one-another via files. + + +/** + * Any subproject can be merged into a single Dokka Publication. To do this, first it must create + * a Dokka Module. A [DokkaModuleDescriptionKxs] describes a config file for the Dokka Module that + * describes its content. This config file will be used by any aggregating project to produce + * a Dokka Publication with multiple modules. + * + * Note: this class implements [java.io.Serializable] because it is used as a + * [Gradle Property][org.gradle.api.provider.Property], and Gradle must be able to fingerprint + * property values classes using Java Serialization. + * + * All other configuration data classes also implement [java.io.Serializable] via their parent interfaces. + */ +@Serializable +@DokkatooInternalApi +data class DokkaModuleDescriptionKxs( + /** @see DokkaConfiguration.DokkaModuleDescription.name */ + val name: String, + /** + * Location of the Dokka Module directory for a subproject. + * + * @see DokkaConfiguration.DokkaModuleDescription.sourceOutputDirectory + */ + val sourceOutputDirectory: File, + /** @see DokkaConfiguration.DokkaModuleDescription.includes */ + val includes: Set<File>, + /** @see [org.gradle.api.Project.getPath] */ + val modulePath: String, +) { + internal fun convert() = + DokkaModuleDescriptionImpl( + name = name, + relativePathToOutputDirectory = File(modulePath.removePrefix(":").replace(':', '/')), + includes = includes, + sourceOutputDirectory = sourceOutputDirectory, + ) +} + + +/** + * Serialize a [File] as an absolute, canonical file path, with + * [invariant path separators][invariantSeparatorsPath] + */ +private object FileAsPathStringSerializer : KSerializer<File> { + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("java.io.File", PrimitiveKind.STRING) + + override fun deserialize(decoder: Decoder): File = + Paths.get(decoder.decodeString()).toFile() + + override fun serialize(encoder: Encoder, value: File): Unit = + encoder.encodeString(value.invariantSeparatorsPath) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt new file mode 100644 index 00000000..c89b8b24 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt @@ -0,0 +1,106 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import java.net.URI +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.api.tasks.Optional +import org.intellij.lang.annotations.Language + +/** + * Configuration builder that allows adding a `source` link to each signature + * which leads to [remoteUrl] with a specific line number (configurable by setting [remoteLineSuffix]), + * letting documentation readers find source code for each declaration. + * + * Example in Gradle Kotlin DSL: + * + * ```kotlin + * sourceLink { + * localDirectory.set(projectDir.resolve("src")) + * remoteUrl.set(URI("https://github.com/kotlin/dokka/tree/master/src")) + * remoteLineSuffix.set("#L") + * } + * ``` + */ +abstract class DokkaSourceLinkSpec +@DokkatooInternalApi +constructor() : Serializable { + + /** + * Path to the local source directory. The path must be relative to the root of current project. + * + * This path is used to find relative paths of the source files from which the documentation is built. + * These relative paths are then combined with the base url of a source code hosting service specified with + * the [remoteUrl] property to create source links for each declaration. + * + * Example: + * + * ```kotlin + * projectDir.resolve("src") + * ``` + */ + @get:Internal // changing contents of the directory should not invalidate the task + abstract val localDirectory: DirectoryProperty + + /** + * The relative path to [localDirectory] from the project directory. Declared as an input to invalidate the task if that path changes. + * Should not be used anywhere directly. + */ + @get:Input + @DokkatooInternalApi + protected val localDirectoryPath: Provider<String> + get() = localDirectory.map { it.asFile.invariantSeparatorsPath } + + /** + * URL of source code hosting service that can be accessed by documentation readers, + * like GitHub, GitLab, Bitbucket, etc. This URL will be used to generate + * source code links of declarations. + * + * Example: + * + * ```kotlin + * java.net.URI("https://github.com/username/projectname/tree/master/src")) + * ``` + */ + @get:Input + abstract val remoteUrl: Property<URI> + + /** + * Set the value of [remoteUrl]. + * + * @param[value] will be converted to a [URI] + */ + fun remoteUrl(@Language("http-url-reference") value: String): Unit = + remoteUrl.set(URI(value)) + + /** + * Set the value of [remoteUrl]. + * + * @param[value] will be converted to a [URI] + */ + fun remoteUrl(value: Provider<String>): Unit = + remoteUrl.set(value.map(::URI)) + + /** + * Suffix used to append source code line number to the URL. This will help readers navigate + * not only to the file, but to the specific line number of the declaration. + * + * The number itself will be appended to the specified suffix. For instance, + * if this property is set to `#L` and the line number is 10, resulting URL suffix + * will be `#L10` + * + * Suffixes used by popular services: + * - GitHub: `#L` + * - GitLab: `#L` + * - Bitbucket: `#lines-` + * + * Default is `#L`. + */ + @get:Optional + @get:Input + abstract val remoteLineSuffix: Property<String> +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt new file mode 100644 index 00000000..0248e387 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt @@ -0,0 +1,61 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.Internal +import org.gradle.kotlin.dsl.* + +abstract class DokkaSourceSetIdSpec +@DokkatooInternalApi +@Inject +constructor( + /** + * Unique identifier of the scope that this source set is placed in. + * Each scope provide only unique source set names. + * + * TODO update this doc - DokkaTask doesn't represent one source set scope anymore + * + * E.g. One DokkaTask inside the Gradle plugin represents one source set scope, since there cannot be multiple + * source sets with the same name. However, a Gradle project will not be a proper scope, since there can be + * multiple DokkaTasks that contain source sets with the same name (but different configuration) + */ + @get:Input + val scopeId: String, + + @get:Input + val sourceSetName: String, +) : Named, Serializable { + + @Internal + override fun getName(): String = "$scopeId/$sourceSetName" + + override fun toString(): String = "DokkaSourceSetIdSpec($scopeId/$sourceSetName)" + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other !is DokkaSourceSetIdSpec) return false + + if (scopeId != other.scopeId) return false + return sourceSetName == other.sourceSetName + } + + override fun hashCode(): Int { + var result = scopeId.hashCode() + result = 31 * result + sourceSetName.hashCode() + return result + } + + companion object { + + /** Utility for creating a new [DokkaSourceSetIdSpec] instance using [ObjectFactory.newInstance] */ + @DokkatooInternalApi + fun ObjectFactory.dokkaSourceSetIdSpec( + scopeId: String, + sourceSetName: String, + ): DokkaSourceSetIdSpec = newInstance<DokkaSourceSetIdSpec>(scopeId, sourceSetName) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt new file mode 100644 index 00000000..9481885b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt @@ -0,0 +1,366 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec.Companion.dokkaSourceSetIdSpec +import org.jetbrains.dokka.dokkatoo.internal.* +import java.io.Serializable +import javax.inject.Inject +import org.gradle.api.* +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.provider.* +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.* + +/** + * [Source set](https://kotlinlang.org/docs/multiplatform-discover-project.html#source-sets) level configuration. + * + * Can be configured in the following way with Gradle Kotlin DSL: + * + * ```kotlin + * // build.gradle.kts + * + * dokkatoo { + * dokkatooSourceSets { + * // configure individual source set by name + * named("customSourceSet") { + * suppress.set(true) + * } + * + * // configure all source sets at once + * configureEach { + * reportUndocumented.set(true) + * } + * } + * } + * ``` + */ +abstract class DokkaSourceSetSpec +@DokkatooInternalApi +@Inject +constructor( + private val name: String, + private val objects: ObjectFactory, +) : + HasConfigurableVisibilityModifiers, + Named, + Serializable, + ExtensionAware { + + @Internal // will be tracked by sourceSetId + override fun getName(): String = name + + /** + * An arbitrary string used to group source sets that originate from different Gradle subprojects. + * This is primarily used by Kotlin Multiplatform projects, which can have multiple source sets + * per subproject. + * + * The default is set from [DokkatooExtension.sourceSetScopeDefault][org.jetbrains.dokka.dokkatoo.DokkatooExtension.sourceSetScopeDefault] + * + * It's unlikely that this value needs to be changed. + */ + @get:Internal // will be tracked by sourceSetId + abstract val sourceSetScope: Property<String> + + /** + * The identifier for this source set, across all Gradle subprojects. + * + * @see sourceSetScope + * @see getName + */ + @get:Input + val sourceSetId: Provider<DokkaSourceSetIdSpec> + get() = sourceSetScope.map { scope -> objects.dokkaSourceSetIdSpec(scope, getName()) } + + /** + * Whether this source set should be skipped when generating documentation. + * + * Default is `false`. + */ + @get:Input + abstract val suppress: Property<Boolean> + + /** + * Display name used to refer to the source set. + * + * The name will be used both externally (for example, source set name visible to documentation readers) and + * internally (for example, for logging messages of [reportUndocumented]). + * + * By default, the value is deduced from information provided by the Kotlin Gradle plugin. + */ + @get:Input + abstract val displayName: Property<String> + + /** + * List of Markdown files that contain + * [module and package documentation](https://kotlinlang.org/docs/reference/dokka-module-and-package-docs.html). + * + * Contents of specified files will be parsed and embedded into documentation as module and package descriptions. + * + * Example of such a file: + * + * ```markdown + * # Module kotlin-demo + * + * The module shows the Dokka usage. + * + * # Package org.jetbrains.kotlin.demo + * + * Contains assorted useful stuff. + * + * ## Level 2 heading + * + * Text after this heading is also part of documentation for `org.jetbrains.kotlin.demo` + * + * # Package org.jetbrains.kotlin.demo2 + * + * Useful stuff in another package. + * ``` + */ + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val includes: ConfigurableFileCollection + + /** + * Set of visibility modifiers that should be documented. + * + * This can be used if you want to document protected/internal/private declarations, + * as well as if you want to exclude public declarations and only document internal API. + * + * Can be configured on per-package basis, see [DokkaPackageOptionsSpec.documentedVisibilities]. + * + * Default is [VisibilityModifier.PUBLIC]. + */ + @get:Input + abstract override val documentedVisibilities: SetProperty<VisibilityModifier> + + /** + * Specifies source sets that current source set depends on. + * + * Among other things, this information is needed to resolve + * [expect/actual](https://kotlinlang.org/docs/multiplatform-connect-to-apis.html) declarations. + * + * By default, the values are deduced from information provided by the Kotlin Gradle plugin. + */ + @get:Nested + val dependentSourceSets: NamedDomainObjectContainer<DokkaSourceSetIdSpec> = + extensions.adding("dependentSourceSets", objects.domainObjectContainer()) + + /** + * Classpath for analysis and interactive samples. + * + * Useful if some types that come from dependencies are not resolved/picked up automatically. + * Property accepts both `.jar` and `.klib` files. + * + * By default, classpath is deduced from information provided by the Kotlin Gradle plugin. + */ + @get:Classpath + @get:Optional + abstract val classpath: ConfigurableFileCollection + + /** + * Source code roots to be analyzed and documented. + * Accepts directories and individual `.kt` / `.java` files. + * + * By default, source roots are deduced from information provided by the Kotlin Gradle plugin. + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val sourceRoots: ConfigurableFileCollection + + /** + * List of directories or files that contain sample functions which are referenced via + * [`@sample`](https://kotlinlang.org/docs/kotlin-doc.html#sample-identifier) KDoc tag. + */ + @get:InputFiles + @get:Optional + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val samples: ConfigurableFileCollection + + /** + * Whether to emit warnings about visible undocumented declarations, that is declarations without KDocs + * after they have been filtered by [documentedVisibilities]. + * + * Can be overridden for a specific package by setting [DokkaPackageOptionsSpec.reportUndocumented]. + * + * Default is `false`. + */ + @get:Input + abstract val reportUndocumented: Property<Boolean> + + /** + * Specifies the location of the project source code on the Web. If provided, Dokka generates + * "source" links for each declaration. See [DokkaSourceLinkSpec] for more details. + * + * Prefer using [sourceLink] action/closure for adding source links. + * + * @see sourceLink + */ + @get:Nested + abstract val sourceLinks: DomainObjectSet<DokkaSourceLinkSpec> + + /** + * Allows to customize documentation generation options on a per-package basis. + * + * @see DokkaPackageOptionsSpec for details + */ + @get:Nested + abstract val perPackageOptions: DomainObjectSet<DokkaPackageOptionsSpec> + + /** + * Allows linking to Dokka/Javadoc documentation of the project's dependencies. + */ + @get:Nested + val externalDocumentationLinks: NamedDomainObjectContainer<DokkaExternalDocumentationLinkSpec> = + extensions.adding("externalDocumentationLinks", objects.domainObjectContainer()) + + /** + * Platform to be used for setting up code analysis and samples. + * + * The default value is deduced from information provided by the Kotlin Gradle plugin. + */ + @get:Input + abstract val analysisPlatform: Property<KotlinPlatform> + + /** + * Whether to skip packages that contain no visible declarations after + * various filters have been applied. + * + * For instance, if [skipDeprecated] is set to `true` and your package contains only + * deprecated declarations, it will be considered to be empty. + * + * Default is `true`. + */ + @get:Input + abstract val skipEmptyPackages: Property<Boolean> + + /** + * Whether to document declarations annotated with [Deprecated]. + * + * Can be overridden on package level by setting [DokkaPackageOptionsSpec.skipDeprecated]. + * + * Default is `false`. + */ + @get:Input + abstract val skipDeprecated: Property<Boolean> + + /** + * Directories or individual files that should be suppressed, meaning declarations from them + * will be not documented. + * + * Will be concatenated with generated files if [suppressGeneratedFiles] is set to `false`. + */ + @get:InputFiles + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val suppressedFiles: ConfigurableFileCollection + + /** + * Whether to document/analyze generated files. + * + * Generated files are expected to be present under `{project}/{buildDir}/generated` directory. + * If set to `true`, it effectively adds all files from that directory to [suppressedFiles], so + * you can configure it manually. + * + * Default is `true`. + */ + @get:Input + abstract val suppressGeneratedFiles: Property<Boolean> + + /** + * Whether to generate external documentation links that lead to API reference documentation for + * Kotlin's standard library when declarations from it are used. + * + * Default is `true`, meaning links will be generated. + * + * @see externalDocumentationLinks + */ + @get:Input + abstract val enableKotlinStdLibDocumentationLink: Property<Boolean> + + /** + * Whether to generate external documentation links to JDK's Javadocs when declarations from it + * are used. + * + * The version of JDK Javadocs is determined by [jdkVersion] property. + * + * Default is `true`, meaning links will be generated. + * + * @see externalDocumentationLinks + */ + @get:Input + abstract val enableJdkDocumentationLink: Property<Boolean> + + /** + * Whether to generate external documentation links for Android SDK API reference when + * declarations from it are used. + * + * Only relevant in Android projects, ignored otherwise. + * + * Default is `false`, meaning links will not be generated. + * + * @see externalDocumentationLinks + */ + @get:Input + abstract val enableAndroidDocumentationLink: Property<Boolean> + + /** + * [Kotlin language version](https://kotlinlang.org/docs/compatibility-modes.html) + * used for setting up analysis and [`@sample`](https://kotlinlang.org/docs/kotlin-doc.html#sample-identifier) + * environment. + * + * By default, the latest language version available to Dokka's embedded compiler will be used. + */ + @get:Input + @get:Optional + abstract val languageVersion: Property<String?> + + /** + * [Kotlin API version](https://kotlinlang.org/docs/compatibility-modes.html) + * used for setting up analysis and [`@sample`](https://kotlinlang.org/docs/kotlin-doc.html#sample-identifier) + * environment. + * + * By default, it will be deduced from [languageVersion]. + */ + @get:Input + @get:Optional + abstract val apiVersion: Property<String?> + + /** + * JDK version to use when generating external documentation links for Java types. + * + * For instance, if you use [java.util.UUID] from JDK in some public declaration signature, + * and this property is set to `8`, Dokka will generate an external documentation link + * to [JDK 8 Javadocs](https://docs.oracle.com/javase/8/docs/api/java/util/UUID.html) for it. + * + * Default is JDK 8. + */ + @get:Input + abstract val jdkVersion: Property<Int> + + /** + * Configure and add a new source link to [sourceLinks]. + * + * @see DokkaSourceLinkSpec + */ + fun sourceLink(action: Action<in DokkaSourceLinkSpec>) { + sourceLinks.add( + objects.newInstance(DokkaSourceLinkSpec::class).also { + action.execute(it) + } + ) + } + + /** + * Action for configuring package options, appending to [perPackageOptions]. + * + * @see DokkaPackageOptionsSpec + */ + fun perPackageOption(action: Action<in DokkaPackageOptionsSpec>) { + perPackageOptions.add( + objects.newInstance(DokkaPackageOptionsSpec::class).also { + action.execute(it) + } + ) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt new file mode 100644 index 00000000..2ed5ddd9 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input + +internal interface HasConfigurableVisibilityModifiers { + + @get:Input + val documentedVisibilities: SetProperty<VisibilityModifier> + + /** Sets [documentedVisibilities] (overrides any previously set values). */ + fun documentedVisibilities(vararg visibilities: VisibilityModifier): Unit = + documentedVisibilities.set(visibilities.asList()) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt new file mode 100644 index 00000000..c950fbbe --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt @@ -0,0 +1,54 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.Platform + + +/** + * The Kotlin + * + * @see org.jetbrains.dokka.Platform + * @param[displayName] The display name, eventually used in the rendered Dokka publication. + */ +enum class KotlinPlatform( + internal val displayName: String +) { + AndroidJVM("androidJvm"), + Common("common"), + JS("js"), + JVM("jvm"), + Native("native"), + WASM("wasm"), + ; + + companion object { + internal val values: Set<KotlinPlatform> = values().toSet() + + val DEFAULT: KotlinPlatform = JVM + + fun fromString(key: String): KotlinPlatform { + val keyMatch = values.firstOrNull { + it.name.equals(key, ignoreCase = true) || it.displayName.equals(key, ignoreCase = true) + } + if (keyMatch != null) { + return keyMatch + } + + return when (key.lowercase()) { + "android" -> AndroidJVM + "metadata" -> Common + else -> error("Unrecognized platform: $key") + } + } + + // Not defined as a property to try and minimize the dependency on Dokka Core types + internal val KotlinPlatform.dokkaType: Platform + get() = + when (this) { + AndroidJVM, JVM -> Platform.jvm + JS -> Platform.js + WASM -> Platform.wasm + Native -> Platform.native + Common -> Platform.common + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt new file mode 100644 index 00000000..de61f97b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt @@ -0,0 +1,42 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.DokkaConfiguration + +/** + * Denotes the + * [visibility modifier](https://kotlinlang.org/docs/visibility-modifiers.html) + * of a source code elements. + * + * @see org.jetbrains.dokka.DokkaConfiguration.Visibility + */ +enum class VisibilityModifier { + /** `public` modifier for Java, default visibility for Kotlin */ + PUBLIC, + + /** `private` modifier for both Kotlin and Java */ + PRIVATE, + + /** `protected` modifier for both Kotlin and Java */ + PROTECTED, + + /** Kotlin-specific `internal` modifier */ + INTERNAL, + + /** Java-specific package-private visibility (no modifier) */ + PACKAGE, + ; + + companion object { + internal val entries: Set<VisibilityModifier> = values().toSet() + + // Not defined as a property to try and minimize the dependency on Dokka Core types + internal val VisibilityModifier.dokkaType: DokkaConfiguration.Visibility + get() = when (this) { + PUBLIC -> DokkaConfiguration.Visibility.PUBLIC + PRIVATE -> DokkaConfiguration.Visibility.PRIVATE + PROTECTED -> DokkaConfiguration.Visibility.PROTECTED + INTERNAL -> DokkaConfiguration.Visibility.INTERNAL + PACKAGE -> DokkaConfiguration.Visibility.PACKAGE + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt new file mode 100644 index 00000000..c6ff8891 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt @@ -0,0 +1,33 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionSpec +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.File +import org.jetbrains.dokka.DokkaModuleDescriptionImpl +import org.jetbrains.dokka.DokkaSourceSetImpl + +/** + * Convert the Gradle-focused [DokkaModuleDescriptionSpec] into a [DokkaSourceSetImpl] instance, + * which will be passed to Dokka Generator. + * + * The conversion is defined in a separate class to try and prevent classes from Dokka Generator + * leaking into the public API. + */ +// to be used to fix https://github.com/adamko-dev/dokkatoo/issues/67 +@DokkatooInternalApi +internal object DokkaModuleDescriptionBuilder { + + fun build( + spec: DokkaModuleDescriptionSpec, + includes: Set<File>, + sourceOutputDirectory: File, + ): DokkaModuleDescriptionImpl = + DokkaModuleDescriptionImpl( + name = spec.name, + relativePathToOutputDirectory = File( + spec.projectPath.get().removePrefix(":").replace(':', '/') + ), + includes = includes, + sourceOutputDirectory = sourceOutputDirectory, + ) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt new file mode 100644 index 00000000..d39969a2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt @@ -0,0 +1,77 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaGeneratorParametersSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.mapNotNullToSet +import java.io.File +import org.gradle.api.logging.Logging +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.DokkaSourceSetImpl +import org.jetbrains.dokka.PluginConfigurationImpl + +/** + * Convert the Gradle-focused [DokkaGeneratorParametersSpec] into a [DokkaSourceSetImpl] instance, + * which will be passed to Dokka Generator. + * + * The conversion is defined in a separate class to try and prevent classes from Dokka Generator + * leaking into the public API. + */ +@DokkatooInternalApi +internal object DokkaParametersBuilder { + + fun build( + spec: DokkaGeneratorParametersSpec, + delayTemplateSubstitution: Boolean, + modules: List<DokkaModuleDescriptionKxs>, + outputDirectory: File, + cacheDirectory: File? = null, + ): DokkaConfiguration { + val moduleName = spec.moduleName.get() + val moduleVersion = spec.moduleVersion.orNull?.takeIf { it != "unspecified" } + val offlineMode = spec.offlineMode.get() + val sourceSets = DokkaSourceSetBuilder.buildAll(spec.dokkaSourceSets) + val failOnWarning = spec.failOnWarning.get() + val suppressObviousFunctions = spec.suppressObviousFunctions.get() + val suppressInheritedMembers = spec.suppressInheritedMembers.get() + val finalizeCoroutines = spec.finalizeCoroutines.get() + val pluginsConfiguration = spec.pluginsConfiguration.toSet() + + val pluginsClasspath = spec.pluginsClasspath.files.toList() + val includes = spec.includes.files + + return DokkaConfigurationImpl( + moduleName = moduleName, + moduleVersion = moduleVersion, + outputDir = outputDirectory, + cacheRoot = cacheDirectory, + offlineMode = offlineMode, + sourceSets = sourceSets, + pluginsClasspath = pluginsClasspath, + pluginsConfiguration = pluginsConfiguration.map(::build), + modules = modules.map(DokkaModuleDescriptionKxs::convert), +// modules = modules.map { +// it.convert( +// moduleDescriptionFiles.get(it.name) +// ?: error("missing module description files for ${it.name}") +// ) +// }, + failOnWarning = failOnWarning, + delayTemplateSubstitution = delayTemplateSubstitution, + suppressObviousFunctions = suppressObviousFunctions, + includes = includes, + suppressInheritedMembers = suppressInheritedMembers, + finalizeCoroutines = finalizeCoroutines, + ) + } + + private fun build(spec: DokkaPluginParametersBaseSpec): PluginConfigurationImpl { + return PluginConfigurationImpl( + fqPluginName = spec.pluginFqn, + serializationFormat = DokkaConfiguration.SerializationFormat.JSON, + values = spec.jsonEncode(), + ) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt new file mode 100644 index 00000000..77935d8c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt @@ -0,0 +1,112 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.* +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform.Companion.dokkaType +import org.jetbrains.dokka.dokkatoo.dokka.parameters.VisibilityModifier.Companion.dokkaType +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.mapNotNullToSet +import org.jetbrains.dokka.dokkatoo.internal.mapToSet +import org.gradle.api.logging.Logging +import org.jetbrains.dokka.* + + +/** + * Convert the Gradle-focused [DokkaSourceSetSpec] into a [DokkaSourceSetImpl] instance, which + * will be passed to Dokka Generator. + * + * The conversion is defined in a separate class to try and prevent classes from Dokka Generator + * leaking into the public API. + */ +@DokkatooInternalApi +internal object DokkaSourceSetBuilder { + + private val logger = Logging.getLogger(DokkaParametersBuilder::class.java) + + fun buildAll(sourceSets: Set<DokkaSourceSetSpec>): List<DokkaSourceSetImpl> { + + val suppressedSourceSetIds = sourceSets.mapNotNullToSet { + val suppressed = it.suppress.get() + val sourceSetId = it.sourceSetId.get() + if (suppressed) { + logger.info("Dokka source set $sourceSetId is suppressed") + sourceSetId + } else { + logger.info("Dokka source set $sourceSetId isn't suppressed") + null + } + } + + val enabledSourceSets = sourceSets.filter { it.sourceSetId.get() !in suppressedSourceSetIds } + + return enabledSourceSets.map { build(it, suppressedSourceSetIds) } + } + + private fun build( + spec: DokkaSourceSetSpec, + suppressedSourceSetIds: Set<DokkaSourceSetIdSpec>, + ): DokkaSourceSetImpl { + + val dependentSourceSets = + (spec.dependentSourceSets subtract suppressedSourceSetIds).mapToSet(::build) + + return DokkaSourceSetImpl( + // properties + analysisPlatform = spec.analysisPlatform.get().dokkaType, + apiVersion = spec.apiVersion.orNull, + dependentSourceSets = dependentSourceSets, + displayName = spec.displayName.get(), + documentedVisibilities = spec.documentedVisibilities.get().mapToSet { it.dokkaType }, + externalDocumentationLinks = spec.externalDocumentationLinks.mapNotNullToSet(::build), + jdkVersion = spec.jdkVersion.get(), + languageVersion = spec.languageVersion.orNull, + noJdkLink = !spec.enableJdkDocumentationLink.get(), + noStdlibLink = !spec.enableKotlinStdLibDocumentationLink.get(), + perPackageOptions = spec.perPackageOptions.map(::build), + reportUndocumented = spec.reportUndocumented.get(), + skipDeprecated = spec.skipDeprecated.get(), + skipEmptyPackages = spec.skipEmptyPackages.get(), + sourceLinks = spec.sourceLinks.mapToSet { build(it) }, + sourceSetID = build(spec.sourceSetId.get()), + + // files + classpath = spec.classpath.files.toList(), + includes = spec.includes.files, + samples = spec.samples.files, + sourceRoots = spec.sourceRoots.files, + suppressedFiles = spec.suppressedFiles.files, + ) + } + + private fun build(spec: DokkaExternalDocumentationLinkSpec): ExternalDocumentationLinkImpl? { + if (!spec.enabled.getOrElse(true)) return null + + return ExternalDocumentationLinkImpl( + url = spec.url.get().toURL(), + packageListUrl = spec.packageListUrl.get().toURL(), + ) + } + + private fun build(spec: DokkaPackageOptionsSpec): PackageOptionsImpl = + PackageOptionsImpl( + matchingRegex = spec.matchingRegex.get(), + documentedVisibilities = spec.documentedVisibilities.get().mapToSet { it.dokkaType }, + reportUndocumented = spec.reportUndocumented.get(), + skipDeprecated = spec.skipDeprecated.get(), + suppress = spec.suppress.get(), + includeNonPublic = DokkaDefaults.includeNonPublic, + ) + + private fun build(spec: DokkaSourceSetIdSpec): DokkaSourceSetID = + DokkaSourceSetID( + scopeId = spec.scopeId, + sourceSetName = spec.sourceSetName + ) + + private fun build(spec: DokkaSourceLinkSpec): SourceLinkDefinitionImpl = + SourceLinkDefinitionImpl( + localDirectory = spec.localDirectory.asFile.get().invariantSeparatorsPath, + remoteUrl = spec.remoteUrl.get().toURL(), + remoteLineSuffix = spec.remoteLineSuffix.orNull, + ) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt new file mode 100644 index 00000000..a3252b51 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt @@ -0,0 +1,129 @@ +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.addAll +import org.jetbrains.dokka.dokkatoo.internal.putIfNotNull +import javax.inject.Inject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.putJsonArray +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE + + +/** + * Configuration for Dokka's base HTML format + * + * [More information is available in the Dokka docs.](https://kotlinlang.org/docs/dokka-html.html#configuration) + */ +abstract class DokkaHtmlPluginParameters +@DokkatooInternalApi +@Inject +constructor( + name: String +) : DokkaPluginParametersBaseSpec( + name, + DOKKA_HTML_PLUGIN_FQN, +) { + + /** + * List of paths for image assets to be bundled with documentation. + * The image assets can have any file extension. + * + * For more information, see + * [Customizing assets](https://kotlinlang.org/docs/dokka-html.html#customize-assets). + * + * Be aware that files will be copied as-is to a specific directory inside the assembled Dokka + * publication. This means that any relative paths must be written in such a way that they will + * work _after_ the files are moved into the publication. + * + * It's best to try and mirror Dokka's directory structure in the source files, which can help + * IDE inspections. + */ + @get:InputFiles + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val customAssets: ConfigurableFileCollection + + /** + * List of paths for `.css` stylesheets to be bundled with documentation and used for rendering. + * + * For more information, see + * [Customizing assets](https://kotlinlang.org/docs/dokka-html.html#customize-assets). + * + * Be aware that files will be copied as-is to a specific directory inside the assembled Dokka + * publication. This means that any relative paths must be written in such a way that they will + * work _after_ the files are moved into the publication. + * + * It's best to try and mirror Dokka's directory structure in the source files, which can help + * IDE inspections. + */ + @get:InputFiles + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val customStyleSheets: ConfigurableFileCollection + + /** + * This is a boolean option. If set to `true`, Dokka renders properties/functions and inherited + * properties/inherited functions separately. + * + * This is disabled by default. + */ + @get:Input + @get:Optional + abstract val separateInheritedMembers: Property<Boolean> + + /** + * This is a boolean option. If set to `true`, Dokka merges declarations that are not declared as + * [expect/actual](https://kotlinlang.org/docs/multiplatform-connect-to-apis.html), but have the + * same fully qualified name. This can be useful for legacy codebases. + * + * This is disabled by default. + */ + @get:Input + @get:Optional + abstract val mergeImplicitExpectActualDeclarations: Property<Boolean> + + /** The text displayed in the footer. */ + @get:Input + @get:Optional + abstract val footerMessage: Property<String> + + /** + * Path to the directory containing custom HTML templates. + * + * For more information, see [Templates](https://kotlinlang.org/docs/dokka-html.html#templates). + */ + @get:InputDirectory + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val templatesDir: DirectoryProperty + + override fun jsonEncode(): String = + buildJsonObject { + putJsonArray("customAssets") { + addAll(customAssets.files) + } + putJsonArray("customStyleSheets") { + addAll(customStyleSheets.files) + } + putIfNotNull("separateInheritedMembers", separateInheritedMembers.orNull) + putIfNotNull( + "mergeImplicitExpectActualDeclarations", + mergeImplicitExpectActualDeclarations.orNull + ) + putIfNotNull("footerMessage", footerMessage.orNull) + putIfNotNull("footerMessage", footerMessage.orNull) + putIfNotNull( + "templatesDir", + templatesDir.orNull?.asFile?.canonicalFile?.invariantSeparatorsPath + ) + }.toString() + + companion object { + const val DOKKA_HTML_PARAMETERS_NAME = "html" + const val DOKKA_HTML_PLUGIN_FQN = "org.jetbrains.dokka.base.DokkaBase" + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt new file mode 100644 index 00000000..486bb80e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt @@ -0,0 +1,32 @@ +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import javax.inject.Inject +import org.gradle.api.Named +import org.gradle.api.tasks.Input + +/** + * Base class for defining Dokka Plugin configuration. + * + * This class should not be instantiated directly. Instead, use a subclass, or create plugin + * parameters dynamically using [DokkaPluginParametersBuilder]. + * + * [More information about Dokka Plugins is available in the Dokka docs.](https://kotlinlang.org/docs/dokka-plugins.html) + * + * @param[pluginFqn] Fully qualified classname of the Dokka Plugin + */ +abstract class DokkaPluginParametersBaseSpec +@DokkatooInternalApi +@Inject +constructor( + private val name: String, + @get:Input + open val pluginFqn: String, +) : Serializable, Named { + + abstract fun jsonEncode(): String // to be implemented by subclasses + + @Input + override fun getName(): String = name +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt new file mode 100644 index 00000000..a29b94c2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt @@ -0,0 +1,232 @@ +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.File +import javax.inject.Inject +import kotlinx.serialization.json.* +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.MapProperty +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE +import org.gradle.kotlin.dsl.* + + +/** + * Dynamically create some configuration to control the behaviour of a Dokka Plugin. + * + * @param[pluginFqn] The fully-qualified name of a Dokka Plugin. For example, the FQN of the + * [Dokka Base plugin](https://github.com/Kotlin/dokka/tree/master/plugins/base#readme) + * is `org.jetbrains.dokka.base.DokkaBase` + */ +fun DokkaPluginParametersContainer.pluginParameters( + pluginFqn: String, + configure: DokkaPluginParametersBuilder.() -> Unit +) { + containerWithType(DokkaPluginParametersBuilder::class) + .maybeCreate(pluginFqn) + .configure() +} + + +/** + * Dynamically create some configuration to control the behaviour of a Dokka Plugin. + * + * This type of builder is necessary to respect + * [Gradle incremental build annotations](https://docs.gradle.org/current/userguide/incremental_build.html#sec:task_input_output_annotations). + * + * @param[pluginFqn] The fully-qualified name of a Dokka Plugin. For example, the Dokka Base plugin's FQN is `org.jetbrains.dokka.base.DokkaBase` + */ +abstract class DokkaPluginParametersBuilder +@Inject +@DokkatooInternalApi +constructor( + name: String, + @get:Input + override val pluginFqn: String, + + @Internal + internal val objects: ObjectFactory, +) : DokkaPluginParametersBaseSpec(name, pluginFqn) { + + @get:Nested + internal val properties = PluginConfigValue.Properties(objects.mapProperty()) + + @Internal + override fun jsonEncode(): String = properties.convertToJson().toString() + + companion object { + private fun PluginConfigValue.convertToJson(): JsonElement = + when (this) { + is PluginConfigValue.DirectoryValue -> directory.asFile.orNull.convertToJson() + is PluginConfigValue.FileValue -> file.asFile.orNull.convertToJson() + is PluginConfigValue.FilesValue -> JsonArray(files.files.map { it.convertToJson() }) + + is PluginConfigValue.BooleanValue -> JsonPrimitive(boolean) + is PluginConfigValue.NumberValue -> JsonPrimitive(number) + is PluginConfigValue.StringValue -> JsonPrimitive(string) + + is PluginConfigValue.Properties -> + JsonObject(values.get().mapValues { (_, value) -> value.convertToJson() }) + + is PluginConfigValue.Values -> + JsonArray(values.get().map { it.convertToJson() }) + } + + /** Creates a [JsonPrimitive] from the given [File]. */ + private fun File?.convertToJson(): JsonPrimitive = + JsonPrimitive(this?.canonicalFile?.invariantSeparatorsPath) + } +} + + +fun DokkaPluginParametersBuilder.files( + propertyName: String, + filesConfig: ConfigurableFileCollection.() -> Unit +) { + val files = objects.fileCollection() + files.filesConfig() + properties.values.put(propertyName, PluginConfigValue.FilesValue(files)) +} + +//region Primitive Properties +fun DokkaPluginParametersBuilder.property(propertyName: String, value: String) { + properties.values.put(propertyName, PluginConfigValue(value)) +} + +fun DokkaPluginParametersBuilder.property(propertyName: String, value: Number) { + properties.values.put(propertyName, PluginConfigValue(value)) +} + +fun DokkaPluginParametersBuilder.property(propertyName: String, value: Boolean) { + properties.values.put(propertyName, PluginConfigValue(value)) +} + +@JvmName("stringProperty") +fun DokkaPluginParametersBuilder.property(propertyName: String, provider: Provider<String>) { + properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) +} + +@JvmName("numberProperty") +fun DokkaPluginParametersBuilder.property(propertyName: String, provider: Provider<Number>) { + properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) +} + +@JvmName("booleanProperty") +fun DokkaPluginParametersBuilder.property( + propertyName: String, + provider: Provider<Boolean> +) { + properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) +} +//endregion + + +//region List Properties +fun DokkaPluginParametersBuilder.properties( + propertyName: String, + build: PluginConfigValue.Values.() -> Unit +) { + val values = PluginConfigValue.Values(objects.listProperty()) + values.build() + properties.values.put(propertyName, values) +} + +fun PluginConfigValue.Values.add(value: String) = + values.add(PluginConfigValue(value)) + +fun PluginConfigValue.Values.add(value: Number) = + values.add(PluginConfigValue(value)) + +fun PluginConfigValue.Values.add(value: Boolean) = + values.add(PluginConfigValue(value)) + +@JvmName("addString") +fun PluginConfigValue.Values.add(value: Provider<String>) = + values.add(PluginConfigValue(value)) + +@JvmName("addNumber") +fun PluginConfigValue.Values.add(value: Provider<Number>) = + values.add(PluginConfigValue(value)) + +@JvmName("addBoolean") +fun PluginConfigValue.Values.add(value: Provider<Boolean>) = + values.add(PluginConfigValue(value)) +//endregion + + +sealed interface PluginConfigValue { + + /** An input file */ + class FileValue( + @InputFile + @PathSensitive(RELATIVE) + val file: RegularFileProperty, + ) : PluginConfigValue + + /** Input files and directories */ + class FilesValue( + @InputFiles + @PathSensitive(RELATIVE) + val files: ConfigurableFileCollection, + ) : PluginConfigValue + + /** An input directory */ + class DirectoryValue( + @InputDirectory + @PathSensitive(RELATIVE) + val directory: DirectoryProperty, + ) : PluginConfigValue + + /** Key-value properties. Analogous to a [JsonObject]. */ + class Properties( + @Nested + val values: MapProperty<String, PluginConfigValue> + ) : PluginConfigValue + + /** Multiple values. Analogous to a [JsonArray]. */ + class Values( + @Nested + val values: ListProperty<PluginConfigValue> + ) : PluginConfigValue + + sealed interface Primitive : PluginConfigValue + + /** A basic [String] value */ + class StringValue(@Input val string: String) : Primitive + + /** A basic [Number] value */ + class NumberValue(@Input val number: Number) : Primitive + + /** A basic [Boolean] value */ + class BooleanValue(@Input val boolean: Boolean) : Primitive +} + +fun PluginConfigValue(value: String) = + PluginConfigValue.StringValue(value) + +fun PluginConfigValue(value: Number) = + PluginConfigValue.NumberValue(value) + +fun PluginConfigValue(value: Boolean) = + PluginConfigValue.BooleanValue(value) + +@Suppress("FunctionName") +@JvmName("PluginConfigStringValue") +fun PluginConfigValue(value: Provider<String>): Provider<PluginConfigValue.StringValue> = + value.map { PluginConfigValue(it) } + +@Suppress("FunctionName") +@JvmName("PluginConfigNumberValue") +fun PluginConfigValue(value: Provider<Number>): Provider<PluginConfigValue.NumberValue> = + value.map { PluginConfigValue(it) } + +@Suppress("FunctionName") +@JvmName("PluginConfigBooleanValue") +fun PluginConfigValue(value: Provider<Boolean>): Provider<PluginConfigValue.BooleanValue> = + value.map { PluginConfigValue(it) } diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt new file mode 100644 index 00000000..1a4d75f2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt @@ -0,0 +1,101 @@ +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.addAll +import org.jetbrains.dokka.dokkatoo.internal.addAllIfNotNull +import org.jetbrains.dokka.dokkatoo.internal.putIfNotNull +import javax.inject.Inject +import kotlinx.serialization.json.buildJsonObject +import kotlinx.serialization.json.putJsonArray +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE + + +/** + * Configuration for + * [Dokka's Versioning plugin](https://github.com/Kotlin/dokka/tree/master/plugins/versioning#readme). + * + * The versioning plugin provides the ability to host documentation for multiple versions of your + * library/application with seamless switching between them. This, in turn, provides a better + * experience for your users. + * + * Note: The versioning plugin only works with Dokka's HTML format. + */ +abstract class DokkaVersioningPluginParameters +@DokkatooInternalApi +@Inject +constructor( + name: String, +) : DokkaPluginParametersBaseSpec( + name, + DOKKA_VERSIONING_PLUGIN_FQN, +) { + + /** + * The version of your application/library that documentation is going to be generated for. + * This will be the version shown in the dropdown menu. + */ + @get:Input + @get:Optional + abstract val version: Property<String> + + /** + * An optional list of strings that represents the order that versions should appear in the + * dropdown menu. + * + * Must match [version] string exactly. The first item in the list is at the top of the dropdown. + */ + @get:Input + @get:Optional + abstract val versionsOrdering: ListProperty<String> + + /** + * An optional path to a parent folder that contains other documentation versions. + * It requires a specific directory structure. + * + * For more information, see + * [Directory structure](https://github.com/Kotlin/dokka/blob/master/plugins/versioning/README.md#directory-structure). + */ + @get:InputDirectory + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val olderVersionsDir: DirectoryProperty + + /** + * An optional list of paths to other documentation versions. It must point to Dokka's outputs + * directly. This is useful if different versions can't all be in the same directory. + */ + @get:InputFiles + @get:PathSensitive(RELATIVE) + @get:Optional + abstract val olderVersions: ConfigurableFileCollection + + /** + * An optional boolean value indicating whether to render the navigation dropdown on all pages. + * + * Set to `true` by default. + */ + @get:Input + @get:Optional + abstract val renderVersionsNavigationOnAllPages: Property<Boolean> + + override fun jsonEncode(): String = + buildJsonObject { + putIfNotNull("version", version.orNull) + putJsonArray("versionsOrdering") { addAllIfNotNull(versionsOrdering.orNull) } + putIfNotNull("olderVersionsDir", olderVersionsDir.orNull?.asFile) + putJsonArray("olderVersions") { + addAll(olderVersions.files) + } + putIfNotNull("renderVersionsNavigationOnAllPages", renderVersionsNavigationOnAllPages.orNull) + }.toString() + + companion object { + const val DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME = "versioning" + const val DOKKA_VERSIONING_PLUGIN_FQN = "org.jetbrains.dokka.versioning.VersioningPlugin" + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt new file mode 100644 index 00000000..08eece77 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt @@ -0,0 +1,152 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKATOO_BASE_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKATOO_CATEGORY_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.distributions.DokkatooConfigurationAttributes.Companion.DOKKA_FORMAT_ATTRIBUTE +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.asConsumer +import org.jetbrains.dokka.dokkatoo.internal.asProvider +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.attributes.AttributeContainer +import org.gradle.api.attributes.Bundling.BUNDLING_ATTRIBUTE +import org.gradle.api.attributes.Bundling.EXTERNAL +import org.gradle.api.attributes.Category.CATEGORY_ATTRIBUTE +import org.gradle.api.attributes.Category.LIBRARY +import org.gradle.api.attributes.LibraryElements.JAR +import org.gradle.api.attributes.LibraryElements.LIBRARY_ELEMENTS_ATTRIBUTE +import org.gradle.api.attributes.Usage.JAVA_RUNTIME +import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE +import org.gradle.api.attributes.java.TargetJvmEnvironment.STANDARD_JVM +import org.gradle.api.attributes.java.TargetJvmEnvironment.TARGET_JVM_ENVIRONMENT_ATTRIBUTE +import org.gradle.api.model.ObjectFactory +import org.gradle.kotlin.dsl.* + +/** + * The Dokka-specific Gradle [Configuration]s used to produce and consume files from external sources + * (example: Maven Central), or between subprojects. + * + * (Be careful of the confusing names: Gradle [Configuration]s are used to transfer files, + * [DokkaConfiguration][org.jetbrains.dokka.DokkaConfiguration] + * is used to configure Dokka behaviour.) + */ +@DokkatooInternalApi +class DokkatooFormatDependencyContainers( + private val formatName: String, + dokkatooConsumer: NamedDomainObjectProvider<Configuration>, + project: Project, +) { + + private val objects: ObjectFactory = project.objects + + private val dependencyContainerNames = DokkatooBasePlugin.DependencyContainerNames(formatName) + + private val dokkatooAttributes: DokkatooConfigurationAttributes = objects.newInstance() + + private fun AttributeContainer.dokkaCategory(category: DokkatooConfigurationAttributes.DokkatooCategoryAttribute) { + attribute(DOKKATOO_BASE_ATTRIBUTE, dokkatooAttributes.dokkatooBaseUsage) + attribute(DOKKA_FORMAT_ATTRIBUTE, objects.named(formatName)) + attribute(DOKKATOO_CATEGORY_ATTRIBUTE, category) + } + + private fun AttributeContainer.jvmJar() { + attribute(USAGE_ATTRIBUTE, objects.named(JAVA_RUNTIME)) + attribute(CATEGORY_ATTRIBUTE, objects.named(LIBRARY)) + attribute(BUNDLING_ATTRIBUTE, objects.named(EXTERNAL)) + attribute(TARGET_JVM_ENVIRONMENT_ATTRIBUTE, objects.named(STANDARD_JVM)) + attribute(LIBRARY_ELEMENTS_ATTRIBUTE, objects.named(JAR)) + } + + //<editor-fold desc="Dokka Module files"> + /** Fetch Dokka Module files from other subprojects */ + val dokkaModuleConsumer: NamedDomainObjectProvider<Configuration> = + project.configurations.register(dependencyContainerNames.dokkatooModuleFilesConsumer) { + description = "Fetch Dokka Module files for $formatName from other subprojects" + asConsumer() + extendsFrom(dokkatooConsumer.get()) + attributes { + dokkaCategory(dokkatooAttributes.dokkaModuleFiles) + } + } + /** Provide Dokka Module files to other subprojects */ + val dokkaModuleOutgoing: NamedDomainObjectProvider<Configuration> = + project.configurations.register(dependencyContainerNames.dokkatooModuleFilesProvider) { + description = "Provide Dokka Module files for $formatName to other subprojects" + asProvider() + // extend from dokkaConfigurationsConsumer, so Dokka Module Configs propagate api() style + extendsFrom(dokkaModuleConsumer.get()) + attributes { + dokkaCategory(dokkatooAttributes.dokkaModuleFiles) + } + } + //</editor-fold> + + //<editor-fold desc="Dokka Generator Plugins"> + /** + * Dokka plugins. + * + * Users can add plugins to this dependency. + * + * Should not contain runtime dependencies. + */ + val dokkaPluginsClasspath: NamedDomainObjectProvider<Configuration> = + project.configurations.register(dependencyContainerNames.dokkaPluginsClasspath) { + description = "Dokka Plugins classpath for $formatName" + asConsumer() + attributes { + jvmJar() + dokkaCategory(dokkatooAttributes.dokkaPluginsClasspath) + } + } + + /** + * Dokka Plugins, without transitive dependencies. + * + * It extends [dokkaPluginsClasspath], so do not add dependencies to this configuration - + * the dependencies are computed automatically. + */ + val dokkaPluginsIntransitiveClasspath: NamedDomainObjectProvider<Configuration> = + project.configurations.register(dependencyContainerNames.dokkaPluginsIntransitiveClasspath) { + description = + "Dokka Plugins classpath for $formatName - for internal use. Fetch only the plugins (no transitive dependencies) for use in the Dokka JSON Configuration." + asConsumer() + extendsFrom(dokkaPluginsClasspath.get()) + isTransitive = false + attributes { + jvmJar() + dokkaCategory(dokkatooAttributes.dokkaPluginsClasspath) + } + } + //</editor-fold> + + //<editor-fold desc="Dokka Generator Classpath"> + /** + * Runtime classpath used to execute Dokka Worker. + * + * This configuration is not exposed to other subprojects. + * + * Extends [dokkaPluginsClasspath]. + * + * @see org.jetbrains.dokka.dokkatoo.workers.DokkaGeneratorWorker + * @see org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask + */ + val dokkaGeneratorClasspath: NamedDomainObjectProvider<Configuration> = + project.configurations.register(dependencyContainerNames.dokkaGeneratorClasspath) { + description = + "Dokka Generator runtime classpath for $formatName - will be used in Dokka Worker. Should contain all transitive dependencies, plugins (and their transitive dependencies), so Dokka Worker can run." + asConsumer() + + // extend from plugins classpath, so Dokka Worker can run the plugins + extendsFrom(dokkaPluginsClasspath.get()) + + isTransitive = true + attributes { + jvmJar() + dokkaCategory(dokkatooAttributes.dokkaGeneratorClasspath) + } + } + //</editor-fold> +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt new file mode 100644 index 00000000..c8f601a6 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt @@ -0,0 +1,174 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.adapters.DokkatooAndroidAdapter +import org.jetbrains.dokka.dokkatoo.adapters.DokkatooJavaAdapter +import org.jetbrains.dokka.dokkatoo.adapters.DokkatooKotlinAdapter +import org.jetbrains.dokka.dokkatoo.internal.* +import javax.inject.Inject +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.api.artifacts.Dependency +import org.gradle.api.artifacts.dsl.DependencyHandler +import org.gradle.api.file.FileSystemOperations +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.* + +/** + * Base Gradle Plugin for setting up a Dokka Publication for a specific format. + * + * [DokkatooBasePlugin] must be applied for this plugin (or any subclass) to have an effect. + * + * Anyone can use this class as a basis for a generating a Dokka Publication in a custom format. + */ +abstract class DokkatooFormatPlugin( + val formatName: String, +) : Plugin<Project> { + + @get:Inject + @DokkatooInternalApi + protected abstract val objects: ObjectFactory + @get:Inject + @DokkatooInternalApi + protected abstract val providers: ProviderFactory + @get:Inject + @DokkatooInternalApi + protected abstract val files: FileSystemOperations + + + override fun apply(target: Project) { + + // apply DokkatooBasePlugin + target.pluginManager.apply(DokkatooBasePlugin::class) + + // apply the plugin that will autoconfigure Dokkatoo to use the sources of a Kotlin project + target.pluginManager.apply(type = DokkatooKotlinAdapter::class) + target.pluginManager.apply(type = DokkatooJavaAdapter::class) + target.pluginManager.apply(type = DokkatooAndroidAdapter::class) + + target.plugins.withType<DokkatooBasePlugin>().configureEach { + val dokkatooExtension = target.extensions.getByType(DokkatooExtension::class) + + val publication = dokkatooExtension.dokkatooPublications.create(formatName) + + val dokkatooConsumer = + target.configurations.named(DokkatooBasePlugin.dependencyContainerNames.dokkatoo) + + val dependencyContainers = DokkatooFormatDependencyContainers( + formatName = formatName, + dokkatooConsumer = dokkatooConsumer, + project = target, + ) + + val dokkatooTasks = DokkatooFormatTasks( + project = target, + publication = publication, + dokkatooExtension = dokkatooExtension, + dependencyContainers = dependencyContainers, + providers = providers, + ) + + dependencyContainers.dokkaModuleOutgoing.configure { + outgoing { + artifact(dokkatooTasks.prepareModuleDescriptor.flatMap { it.dokkaModuleDescriptorJson }) + } + outgoing { + artifact(dokkatooTasks.generateModule.flatMap { it.outputDirectory }) { + type = "directory" + } + } + } + + // TODO DokkaCollect replacement - share raw files without first generating a Dokka Module + //dependencyCollections.dokkaParametersOutgoing.configure { + // outgoing { + // artifact(dokkatooTasks.prepareParametersTask.flatMap { it.dokkaConfigurationJson }) + // } + //} + + val context = DokkatooFormatPluginContext( + project = target, + dokkatooExtension = dokkatooExtension, + dokkatooTasks = dokkatooTasks, + formatName = formatName, + ) + + context.configure() + + if (context.addDefaultDokkaDependencies) { + with(context) { + addDefaultDokkaDependencies() + } + } + } + } + + + /** Format specific configuration - to be implemented by subclasses */ + open fun DokkatooFormatPluginContext.configure() {} + + + @DokkatooInternalApi + class DokkatooFormatPluginContext( + val project: Project, + val dokkatooExtension: DokkatooExtension, + val dokkatooTasks: DokkatooFormatTasks, + formatName: String, + ) { + private val dependencyContainerNames = DokkatooBasePlugin.DependencyContainerNames(formatName) + + var addDefaultDokkaDependencies = true + + /** Create a [Dependency] for a Dokka module */ + fun DependencyHandler.dokka(module: String): Provider<Dependency> = + dokkatooExtension.versions.jetbrainsDokka.map { version -> create("org.jetbrains.dokka:$module:$version") } + + /** Add a dependency to the Dokka plugins classpath */ + fun DependencyHandler.dokkaPlugin(dependency: Provider<Dependency>): Unit = + addProvider(dependencyContainerNames.dokkaPluginsClasspath, dependency) + + /** Add a dependency to the Dokka plugins classpath */ + fun DependencyHandler.dokkaPlugin(dependency: String) { + add(dependencyContainerNames.dokkaPluginsClasspath, dependency) + } + + /** Add a dependency to the Dokka Generator classpath */ + fun DependencyHandler.dokkaGenerator(dependency: Provider<Dependency>) { + addProvider(dependencyContainerNames.dokkaGeneratorClasspath, dependency) + } + + /** Add a dependency to the Dokka Generator classpath */ + fun DependencyHandler.dokkaGenerator(dependency: String) { + add(dependencyContainerNames.dokkaGeneratorClasspath, dependency) + } + } + + + private fun DokkatooFormatPluginContext.addDefaultDokkaDependencies() { + project.dependencies { + /** lazily create a [Dependency] with the provided [version] */ + infix fun String.version(version: Property<String>): Provider<Dependency> = + version.map { v -> create("$this:$v") } + + with(dokkatooExtension.versions) { + dokkaPlugin(dokka("analysis-kotlin-descriptors")) + dokkaPlugin(dokka("templating-plugin")) + dokkaPlugin(dokka("dokka-base")) +// dokkaPlugin(dokka("all-modules-page-plugin")) + + dokkaPlugin("org.jetbrains.kotlinx:kotlinx-html" version kotlinxHtml) + dokkaPlugin("org.freemarker:freemarker" version freemarker) + + dokkaGenerator(dokka("dokka-core")) + // TODO why does org.jetbrains:markdown need a -jvm suffix? + dokkaGenerator("org.jetbrains:markdown-jvm" version jetbrainsMarkdown) + dokkaGenerator("org.jetbrains.kotlinx:kotlinx-coroutines-core" version kotlinxCoroutines) + } + } + } + +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt new file mode 100644 index 00000000..ab3639bc --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt @@ -0,0 +1,105 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.LocalProjectOnlyFilter +import org.jetbrains.dokka.dokkatoo.internal.configuring +import org.jetbrains.dokka.dokkatoo.tasks.DokkatooGenerateTask +import org.jetbrains.dokka.dokkatoo.tasks.DokkatooPrepareModuleDescriptorTask +import org.gradle.api.Project +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.* + +/** Tasks for generating a Dokkatoo Publication in a specific format. */ +@DokkatooInternalApi +class DokkatooFormatTasks( + project: Project, + private val publication: DokkaPublication, + private val dokkatooExtension: DokkatooExtension, + private val dependencyContainers: DokkatooFormatDependencyContainers, + + private val providers: ProviderFactory, +) { + private val formatName: String get() = publication.formatName + + private val taskNames = DokkatooBasePlugin.TaskNames(formatName) + + private fun DokkatooGenerateTask.applyFormatSpecificConfiguration() { + runtimeClasspath.from( + dependencyContainers.dokkaGeneratorClasspath.map { classpath -> + classpath.incoming.artifacts.artifactFiles + } + ) + generator.apply { + publicationEnabled.convention(publication.enabled) + + failOnWarning.convention(publication.failOnWarning) + finalizeCoroutines.convention(publication.finalizeCoroutines) + includes.from(publication.includes) + moduleName.convention(publication.moduleName) + moduleVersion.convention(publication.moduleVersion) + offlineMode.convention(publication.offlineMode) + pluginsConfiguration.addAllLater(providers.provider { publication.pluginsConfiguration }) + pluginsClasspath.from( + dependencyContainers.dokkaPluginsIntransitiveClasspath.map { classpath -> + classpath.incoming.artifacts.artifactFiles + } + ) + suppressInheritedMembers.convention(publication.suppressInheritedMembers) + suppressObviousFunctions.convention(publication.suppressObviousFunctions) + } + } + + val generatePublication = project.tasks.register<DokkatooGenerateTask>( + taskNames.generatePublication, + publication.pluginsConfiguration, + ).configuring task@{ + description = "Executes the Dokka Generator, generating the $formatName publication" + generationType.set(DokkatooGenerateTask.GenerationType.PUBLICATION) + + outputDirectory.convention(dokkatooExtension.dokkatooPublicationDirectory.dir(formatName)) + + generator.apply { + // depend on Dokka Module Descriptors from other subprojects + dokkaModuleFiles.from( + dependencyContainers.dokkaModuleConsumer.map { modules -> + modules.incoming + .artifactView { componentFilter(LocalProjectOnlyFilter) } + .artifacts.artifactFiles + } + ) + } + + applyFormatSpecificConfiguration() + } + + val generateModule = project.tasks.register<DokkatooGenerateTask>( + taskNames.generateModule, + publication.pluginsConfiguration, + ).configuring task@{ + description = "Executes the Dokka Generator, generating a $formatName module" + generationType.set(DokkatooGenerateTask.GenerationType.MODULE) + + outputDirectory.convention(dokkatooExtension.dokkatooModuleDirectory.dir(formatName)) + + applyFormatSpecificConfiguration() + } + + val prepareModuleDescriptor = project.tasks.register<DokkatooPrepareModuleDescriptorTask>( + taskNames.prepareModuleDescriptor + ) task@{ + description = "Prepares the Dokka Module Descriptor for $formatName" + includes.from(publication.includes) + dokkaModuleDescriptorJson.convention( + dokkatooExtension.dokkatooConfigurationsDirectory.file("$formatName/module_descriptor.json") + ) + moduleDirectory.set(generateModule.flatMap { it.outputDirectory }) + +// dokkaSourceSets.addAllLater(providers.provider { dokkatooExtension.dokkatooSourceSets }) +// dokkaSourceSets.configureEach { +// sourceSetScope.convention(this@task.path) +// } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt new file mode 100644 index 00000000..79df47df --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.gradle.kotlin.dsl.* + +abstract class DokkatooGfmPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "gfm") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("gfm-plugin")) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt new file mode 100644 index 00000000..5748f7d1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt @@ -0,0 +1,72 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaHtmlPluginParameters +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaHtmlPluginParameters.Companion.DOKKA_HTML_PARAMETERS_NAME +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaVersioningPluginParameters +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaVersioningPluginParameters.Companion.DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.uppercaseFirstChar +import org.jetbrains.dokka.dokkatoo.tasks.LogHtmlPublicationLinkTask +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.* + +abstract class DokkatooHtmlPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "html") { + + override fun DokkatooFormatPluginContext.configure() { + registerDokkaBasePluginConfiguration() + registerDokkaVersioningPlugin() + + val logHtmlUrlTask = registerLogHtmlUrlTask() + + dokkatooTasks.generatePublication.configure { + finalizedBy(logHtmlUrlTask) + } + } + + private fun DokkatooFormatPluginContext.registerDokkaBasePluginConfiguration() { + with(dokkatooExtension.pluginsConfiguration) { + registerBinding(DokkaHtmlPluginParameters::class, DokkaHtmlPluginParameters::class) + register<DokkaHtmlPluginParameters>(DOKKA_HTML_PARAMETERS_NAME) + withType<DokkaHtmlPluginParameters>().configureEach { + separateInheritedMembers.convention(false) + mergeImplicitExpectActualDeclarations.convention(false) + } + } + } + + private fun DokkatooFormatPluginContext.registerDokkaVersioningPlugin() { + // register and configure Dokka Versioning Plugin + with(dokkatooExtension.pluginsConfiguration) { + registerBinding( + DokkaVersioningPluginParameters::class, + DokkaVersioningPluginParameters::class, + ) + register<DokkaVersioningPluginParameters>(DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME) + withType<DokkaVersioningPluginParameters>().configureEach { + renderVersionsNavigationOnAllPages.convention(true) + } + } + } + + private fun DokkatooFormatPluginContext.registerLogHtmlUrlTask(): + TaskProvider<LogHtmlPublicationLinkTask> { + + val indexHtmlFile = dokkatooTasks.generatePublication + .flatMap { it.outputDirectory.file("index.html") } + + val indexHtmlPath = indexHtmlFile.map { indexHtml -> + indexHtml.asFile + .relativeTo(project.rootDir.parentFile) + .invariantSeparatorsPath + } + + return project.tasks.register<LogHtmlPublicationLinkTask>( + "logLink" + dokkatooTasks.generatePublication.name.uppercaseFirstChar() + ) { + serverUri.convention("http://localhost:63342") + this.indexHtmlPath.convention(indexHtmlPath) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt new file mode 100644 index 00000000..90f024df --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.gradle.kotlin.dsl.* + +abstract class DokkatooJavadocPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "javadoc") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("javadoc-plugin")) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt new file mode 100644 index 00000000..d8434732 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt @@ -0,0 +1,14 @@ +package org.jetbrains.dokka.dokkatoo.formats + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.gradle.kotlin.dsl.* + +abstract class DokkatooJekyllPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "jekyll") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("jekyll-plugin")) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt new file mode 100644 index 00000000..e3e63753 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt @@ -0,0 +1,37 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import kotlin.RequiresOptIn.Level.WARNING +import kotlin.annotation.AnnotationRetention.BINARY +import kotlin.annotation.AnnotationTarget.* + + +/** + * Functionality that is annotated with this API is intended only for use by Dokkatoo internal code, + * but it has been given + * [`public` visibility](https://kotlinlang.org/docs/visibility-modifiers.html) + * for technical reasons. + * + * Any code that is annotated with this may be used + * + * Anyone is welcome to + * [opt in](https://kotlinlang.org/docs/opt-in-requirements.html#opt-in-to-using-api) + * to use this API, but be aware that it might change unexpectedly and without warning or migration + * hints. + * + * If you find yourself needing to opt in, then please report your use-case on + * [the Dokkatoo issue tracker](https://github.com/adamko-dev/dokkatoo/issues). + */ +@RequiresOptIn( + "Internal API - may change at any time without notice", + level = WARNING +) +@Retention(BINARY) +@Target( + CLASS, + FUNCTION, + CONSTRUCTOR, + PROPERTY, + PROPERTY_GETTER, +) +@MustBeDocumented +annotation class DokkatooInternalApi diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/LoggerAdapter.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/LoggerAdapter.kt new file mode 100644 index 00000000..0a1b94fc --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/LoggerAdapter.kt @@ -0,0 +1,65 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import java.io.File +import java.io.Writer +import java.util.concurrent.atomic.AtomicInteger +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.dokka.utilities.LoggingLevel + +/** + * Logs all Dokka messages to a file. + * + * @see org.jetbrains.dokka.DokkaGenerator + */ +// Gradle causes OOM errors when there is a lot of console output. Logging to file is a workaround. +// https://github.com/gradle/gradle/issues/23965 +// https://github.com/gradle/gradle/issues/15621 +internal class LoggerAdapter( + outputFile: File +) : DokkaLogger, AutoCloseable { + + private val logWriter: Writer + + init { + if (!outputFile.exists()) { + outputFile.parentFile.mkdirs() + outputFile.createNewFile() + } + + logWriter = outputFile.bufferedWriter() + } + + private val warningsCounter = AtomicInteger() + private val errorsCounter = AtomicInteger() + + override var warningsCount: Int + get() = warningsCounter.get() + set(value) = warningsCounter.set(value) + + override var errorsCount: Int + get() = errorsCounter.get() + set(value) = errorsCounter.set(value) + + override fun debug(message: String) = log(LoggingLevel.DEBUG, message) + override fun progress(message: String) = log(LoggingLevel.PROGRESS, message) + override fun info(message: String) = log(LoggingLevel.INFO, message) + + override fun warn(message: String) { + warningsCount++ + log(LoggingLevel.WARN, message) + } + + override fun error(message: String) { + errorsCount++ + log(LoggingLevel.ERROR, message) + } + + @Synchronized + private fun log(level: LoggingLevel, message: String) { + logWriter.appendLine("[${level.name}] $message") + } + + override fun close() { + logWriter.close() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/collectionsUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/collectionsUtils.kt new file mode 100644 index 00000000..80b66f4b --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/collectionsUtils.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.dokkatoo.internal + +internal fun <T, R> Set<T>.mapToSet(transform: (T) -> R): Set<R> = + mapTo(mutableSetOf(), transform) + +internal fun <T, R : Any> Set<T>.mapNotNullToSet(transform: (T) -> R?): Set<R> = + mapNotNullTo(mutableSetOf(), transform) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt new file mode 100644 index 00000000..85208897 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import org.jetbrains.dokka.dokkatoo.DokkatooExtension + +// When Dokkatoo is applied to a build script Gradle will auto-generate these accessors + +internal fun DokkatooExtension.versions(configure: DokkatooExtension.Versions.() -> Unit) { + versions.apply(configure) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleTypealiases.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleTypealiases.kt new file mode 100644 index 00000000..7f59db86 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleTypealiases.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec +import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer + +/** Container for all [Dokka Plugin parameters][DokkaPluginParametersBaseSpec]. */ +typealias DokkaPluginParametersContainer = + ExtensiblePolymorphicDomainObjectContainer<DokkaPluginParametersBaseSpec> + + +/** + * The path of a Gradle [Project][org.gradle.api.Project]. This is unique per subproject. + * This is _not_ the file path, which + * [can be configured to be different to the project path](https://docs.gradle.org/current/userguide/fine_tuning_project_layout.html#sub:modifying_element_of_the_project_tree). + * + * Example: `:modules:tests:alpha-project`. + * + * @see org.gradle.api.Project.getPath + */ +internal typealias GradleProjectPath = org.gradle.util.Path diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleUtils.kt new file mode 100644 index 00000000..53ba49b9 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/gradleUtils.kt @@ -0,0 +1,187 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec +import org.gradle.api.* +import org.gradle.api.artifacts.ArtifactView +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.ConfigurationContainer +import org.gradle.api.artifacts.component.ComponentIdentifier +import org.gradle.api.artifacts.component.ProjectComponentIdentifier +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.model.ObjectFactory +import org.gradle.api.plugins.ExtensionAware +import org.gradle.api.plugins.ExtensionContainer +import org.gradle.api.provider.Provider +import org.gradle.api.specs.Spec +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.* + + +/** + * Mark this [Configuration] as one that will be consumed by other subprojects. + * + * ``` + * isCanBeResolved = false + * isCanBeConsumed = true + * ``` + */ +internal fun Configuration.asProvider( + visible: Boolean = true, +) { + isCanBeResolved = false + isCanBeConsumed = true + isVisible = visible +} + +/** + * Mark this [Configuration] as one that will consume artifacts from other subprojects (also known as 'resolving') + * + * ``` + * isCanBeResolved = true + * isCanBeConsumed = false + * ``` + * */ +internal fun Configuration.asConsumer( + visible: Boolean = false, +) { + isCanBeResolved = true + isCanBeConsumed = false + isVisible = visible +} + + +/** Invert a boolean [Provider] */ +internal operator fun Provider<Boolean>.not(): Provider<Boolean> = map { !it } + + +/** Only matches components that come from subprojects */ +internal object LocalProjectOnlyFilter : Spec<ComponentIdentifier> { + override fun isSatisfiedBy(element: ComponentIdentifier?): Boolean = + element is ProjectComponentIdentifier +} + + +/** Invert the result of a [Spec] predicate */ +internal operator fun <T> Spec<T>.not(): Spec<T> = Spec<T> { !this@not.isSatisfiedBy(it) } + + +internal fun Project.pathAsFilePath() = path + .removePrefix(GradleProjectPath.SEPARATOR) + .replace(GradleProjectPath.SEPARATOR, "/") + + +/** + * Apply some configuration to a [Task] using + * [configure][org.gradle.api.tasks.TaskContainer.configure], + * and return the same [TaskProvider]. + */ +internal fun <T : Task> TaskProvider<T>.configuring( + block: Action<T> +): TaskProvider<T> = apply { configure(block) } + + +internal fun <T> NamedDomainObjectContainer<T>.maybeCreate( + name: String, + configure: T.() -> Unit, +): T = maybeCreate(name).apply(configure) + + +/** + * Aggregate the incoming files from a [Configuration] (with name [named]) into [collector]. + * + * Configurations that do not exist or cannot be + * [resolved][org.gradle.api.artifacts.Configuration.isCanBeResolved] + * will be ignored. + * + * @param[builtBy] An optional [TaskProvider], used to set [ConfigurableFileCollection.builtBy]. + * This should not typically be used, and is only necessary in rare cases where a Gradle Plugin is + * misconfigured. + */ +internal fun ConfigurationContainer.collectIncomingFiles( + named: String, + collector: ConfigurableFileCollection, + builtBy: TaskProvider<*>? = null, + artifactViewConfiguration: ArtifactView.ViewConfiguration.() -> Unit = { + // ignore failures: it's usually okay if fetching files is best-effort because + // maybe Dokka doesn't need _all_ dependencies + lenient(true) + }, +) { + val conf = findByName(named) + if (conf != null && conf.isCanBeResolved) { + val incomingFiles = conf.incoming + .artifactView(artifactViewConfiguration) + .artifacts + .resolvedArtifacts // using 'resolved' might help with triggering artifact transforms? + .map { artifacts -> artifacts.map { it.file } } + + collector.from(incomingFiles) + + if (builtBy != null) { + collector.builtBy(builtBy) + } + } +} + + +/** + * Create a new [NamedDomainObjectContainer], using + * [org.gradle.kotlin.dsl.domainObjectContainer] + * (but [T] is `reified`). + * + * @param[factory] an optional factory for creating elements + * @see org.gradle.kotlin.dsl.domainObjectContainer + */ +internal inline fun <reified T : Any> ObjectFactory.domainObjectContainer( + factory: NamedDomainObjectFactory<T>? = null +): NamedDomainObjectContainer<T> = + if (factory == null) { + domainObjectContainer(T::class) + } else { + domainObjectContainer(T::class, factory) + } + + +/** + * Create a new [ExtensiblePolymorphicDomainObjectContainer], using + * [org.gradle.kotlin.dsl.polymorphicDomainObjectContainer] + * (but [T] is `reified`). + * + * @see org.gradle.kotlin.dsl.polymorphicDomainObjectContainer + */ +internal inline fun <reified T : Any> ObjectFactory.polymorphicDomainObjectContainer() + : ExtensiblePolymorphicDomainObjectContainer<T> = + polymorphicDomainObjectContainer(T::class) + + +/** + * Add an extension to the [ExtensionContainer], and return the value. + * + * Adding an extension is especially useful for improving the DSL in build scripts when [T] is a + * [NamedDomainObjectContainer]. + * Using an extension will allow Gradle to generate + * [type-safe model accessors](https://docs.gradle.org/current/userguide/kotlin_dsl.html#kotdsl:accessor_applicability) + * for added types. + * + * ([name] should match the property name. This has to be done manually. I tried using a + * delegated-property provider but then Gradle can't introspect the types properly, so it fails to + * create accessors). + */ +internal inline fun <reified T : Any> ExtensionContainer.adding( + name: String, + value: T, +): T { + add<T>(name, value) + return value +} + + +/** Create a new [DokkaPluginParametersContainer] instance. */ +internal fun ObjectFactory.dokkaPluginParametersContainer(): DokkaPluginParametersContainer { + val container = polymorphicDomainObjectContainer<DokkaPluginParametersBaseSpec>() + container.whenObjectAdded { + // workaround for https://github.com/gradle/gradle/issues/24972 + (container as ExtensionAware).extensions.add(name, this) + } + return container +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt new file mode 100644 index 00000000..d4f98004 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt @@ -0,0 +1,36 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import java.io.File +import kotlinx.serialization.json.JsonArrayBuilder +import kotlinx.serialization.json.JsonObjectBuilder +import kotlinx.serialization.json.JsonPrimitive +import kotlinx.serialization.json.add + + +@JvmName("addAllFiles") +internal fun JsonArrayBuilder.addAll(files: Iterable<File>) { + files + .map { it.canonicalFile.invariantSeparatorsPath } + .forEach { path -> add(path) } +} + +@JvmName("addAllStrings") +internal fun JsonArrayBuilder.addAll(values: Iterable<String>) { + values.forEach { add(it) } +} + +internal fun JsonArrayBuilder.addAllIfNotNull(values: Iterable<String>?) { + if (values != null) addAll(values) +} + +internal fun JsonObjectBuilder.putIfNotNull(key: String, value: Boolean?) { + if (value != null) put(key, JsonPrimitive(value)) +} + +internal fun JsonObjectBuilder.putIfNotNull(key: String, value: String?) { + if (value != null) put(key, JsonPrimitive(value)) +} + +internal fun JsonObjectBuilder.putIfNotNull(key: String, value: File?) { + if (value != null) put(key, JsonPrimitive(value.canonicalFile.invariantSeparatorsPath)) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/stringUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/stringUtils.kt new file mode 100644 index 00000000..75b3b8ec --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/stringUtils.kt @@ -0,0 +1,11 @@ +package org.jetbrains.dokka.dokkatoo.internal + + +/** + * Title case the first char of a string. + * + * (Custom implementation because [uppercase] is deprecated, and Dokkatoo should try and be as + * stable as possible.) + */ +internal fun String.uppercaseFirstChar(): String = + if (isNotEmpty()) Character.toTitleCase(this[0]) + substring(1) else this diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/uriUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/uriUtils.kt new file mode 100644 index 00000000..942551c4 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/internal/uriUtils.kt @@ -0,0 +1,9 @@ +package org.jetbrains.dokka.dokkatoo.internal + +import java.net.URI + +internal fun URI.appendPath(addition: String): URI { + val currentPath = path.removeSuffix("/") + val newPath = "$currentPath/$addition" + return resolve(newPath).normalize() +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt new file mode 100644 index 00000000..b27acbc5 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt @@ -0,0 +1,187 @@ +package org.jetbrains.dokka.dokkatoo.tasks + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin.Companion.jsonMapper +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaGeneratorParametersSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs +import org.jetbrains.dokka.dokkatoo.dokka.parameters.builders.DokkaParametersBuilder +import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.workers.DokkaGeneratorWorker +import java.io.IOException +import javax.inject.Inject +import kotlinx.serialization.json.JsonElement +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.model.ObjectFactory +import org.gradle.api.model.ReplacedBy +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.* +import org.gradle.process.JavaForkOptions +import org.gradle.workers.WorkerExecutor +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.toPrettyJsonString + +/** + * Executes the Dokka Generator, and produces documentation. + * + * The type of documentation generated is determined by the supplied Dokka Plugins in [generator]. + */ +@CacheableTask +abstract class DokkatooGenerateTask +@DokkatooInternalApi +@Inject +constructor( + objects: ObjectFactory, + private val workers: WorkerExecutor, + + /** + * Configurations for Dokka Generator Plugins. Must be provided from + * [org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.pluginsConfiguration]. + */ + pluginsConfiguration: DokkaPluginParametersContainer, +) : DokkatooTask() { + + @get:OutputDirectory + abstract val outputDirectory: DirectoryProperty + + /** + * Classpath required to run Dokka Generator. + * + * Contains the Dokka Generator, Dokka plugins, and any transitive dependencies. + */ + @get:Classpath + abstract val runtimeClasspath: ConfigurableFileCollection + + @get:LocalState + abstract val cacheDirectory: DirectoryProperty + + /** + * Generating a Dokka Module? Set this to [GenerationType.MODULE]. + * + * Generating a Dokka Publication? [GenerationType.PUBLICATION]. + */ + @get:Input + abstract val generationType: Property<GenerationType> + + /** @see org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.enabled */ + @get:Input + abstract val publicationEnabled: Property<Boolean> + + @get:Nested + val generator: DokkaGeneratorParametersSpec = objects.newInstance(pluginsConfiguration) + + /** @see JavaForkOptions.getDebug */ + @get:Input + abstract val workerDebugEnabled: Property<Boolean> + /** @see JavaForkOptions.getMinHeapSize */ + @get:Input + @get:Optional + abstract val workerMinHeapSize: Property<String> + /** @see JavaForkOptions.getMaxHeapSize */ + @get:Input + @get:Optional + abstract val workerMaxHeapSize: Property<String> + /** @see JavaForkOptions.jvmArgs */ + @get:Input + abstract val workerJvmArgs: ListProperty<String> + @get:Internal + abstract val workerLogFile: RegularFileProperty + + /** + * The [DokkaConfiguration] by Dokka Generator can be saved to a file for debugging purposes. + * To disable this behaviour set this property to `null`. + */ + @DokkatooInternalApi + @get:Internal + abstract val dokkaConfigurationJsonFile: RegularFileProperty + + enum class GenerationType { + MODULE, + PUBLICATION, + } + + @TaskAction + internal fun generateDocumentation() { + val dokkaConfiguration = createDokkaConfiguration() + logger.info("dokkaConfiguration: $dokkaConfiguration") + dumpDokkaConfigurationJson(dokkaConfiguration) + + logger.info("DokkaGeneratorWorker runtimeClasspath: ${runtimeClasspath.asPath}") + + val workQueue = workers.processIsolation { + classpath.from(runtimeClasspath) + forkOptions { + defaultCharacterEncoding = "UTF-8" + minHeapSize = workerMinHeapSize.orNull + maxHeapSize = workerMaxHeapSize.orNull + enableAssertions = true + debug = workerDebugEnabled.get() + jvmArgs = workerJvmArgs.get() + } + } + + workQueue.submit(DokkaGeneratorWorker::class) { + this.dokkaParameters.set(dokkaConfiguration) + this.logFile.set(workerLogFile) + } + } + + /** + * Dump the [DokkaConfiguration] JSON to a file ([dokkaConfigurationJsonFile]) for debugging + * purposes. + */ + private fun dumpDokkaConfigurationJson( + dokkaConfiguration: DokkaConfiguration, + ) { + val destFile = dokkaConfigurationJsonFile.asFile.orNull ?: return + destFile.parentFile.mkdirs() + destFile.createNewFile() + + val compactJson = dokkaConfiguration.toPrettyJsonString() + val json = jsonMapper.decodeFromString(JsonElement.serializer(), compactJson) + val prettyJson = jsonMapper.encodeToString(JsonElement.serializer(), json) + + destFile.writeText(prettyJson) + + logger.info("[$path] Dokka Generator configuration JSON: ${destFile.toURI()}") + } + + private fun createDokkaConfiguration(): DokkaConfiguration { + val outputDirectory = outputDirectory.get().asFile + + val delayTemplateSubstitution = when (generationType.orNull) { + GenerationType.MODULE -> true + GenerationType.PUBLICATION -> false + null -> error("missing GenerationType") + } + + val dokkaModuleDescriptors = dokkaModuleDescriptors() + + return DokkaParametersBuilder.build( + spec = generator, + delayTemplateSubstitution = delayTemplateSubstitution, + outputDirectory = outputDirectory, + modules = dokkaModuleDescriptors, + cacheDirectory = cacheDirectory.asFile.orNull, + ) + } + + private fun dokkaModuleDescriptors(): List<DokkaModuleDescriptionKxs> { + return generator.dokkaModuleFiles.asFileTree + .matching { include("**/module_descriptor.json") } + .files.map { file -> + try { + val fileContent = file.readText() + jsonMapper.decodeFromString( + DokkaModuleDescriptionKxs.serializer(), + fileContent, + ) + } catch (ex: Exception) { + throw IOException("Could not parse DokkaModuleDescriptionKxs from $file", ex) + } + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt new file mode 100644 index 00000000..1247ebc7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt @@ -0,0 +1,62 @@ +package org.jetbrains.dokka.dokkatoo.tasks + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin.Companion.jsonMapper +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject +import kotlinx.serialization.encodeToString +import org.gradle.api.file.* +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE + +/** + * Produces a Dokka Configuration that describes a single module of a multimodule Dokka configuration. + * + * @see org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaModuleDescriptionKxs + */ +@CacheableTask +abstract class DokkatooPrepareModuleDescriptorTask +@DokkatooInternalApi +@Inject +constructor() : DokkatooTask() { + + @get:OutputFile + abstract val dokkaModuleDescriptorJson: RegularFileProperty + + @get:Input + abstract val moduleName: Property<String> + + @get:Input + abstract val modulePath: Property<String> + + @get:InputDirectory + @get:PathSensitive(RELATIVE) + abstract val moduleDirectory: DirectoryProperty + + @get:InputFiles + @get:Optional + @get:PathSensitive(RELATIVE) + abstract val includes: ConfigurableFileCollection + + @TaskAction + internal fun generateModuleConfiguration() { + val moduleName = moduleName.get() + val moduleDirectory = moduleDirectory.asFile.get() + val includes = includes.files + val modulePath = modulePath.get() + + val moduleDesc = DokkaModuleDescriptionKxs( + name = moduleName, + sourceOutputDirectory = moduleDirectory, + includes = includes, + modulePath = modulePath, + ) + + val encodedModuleDesc = jsonMapper.encodeToString(moduleDesc) + + logger.info("encodedModuleDesc: $encodedModuleDesc") + + dokkaModuleDescriptorJson.get().asFile.writeText(encodedModuleDesc) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt new file mode 100644 index 00000000..c125a64e --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/DokkatooTask.kt @@ -0,0 +1,22 @@ +package org.jetbrains.dokka.dokkatoo.tasks + +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject +import org.gradle.api.DefaultTask +import org.gradle.api.model.ObjectFactory +import org.gradle.api.tasks.CacheableTask + +/** Base Dokkatoo task */ +@CacheableTask +abstract class DokkatooTask +@DokkatooInternalApi +constructor() : DefaultTask() { + + @get:Inject + abstract val objects: ObjectFactory + + init { + group = DokkatooBasePlugin.TASK_GROUP + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt new file mode 100644 index 00000000..c281ce56 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt @@ -0,0 +1,156 @@ +package org.jetbrains.dokka.dokkatoo.tasks + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.appendPath +import org.jetbrains.dokka.dokkatoo.tasks.LogHtmlPublicationLinkTask.Companion.ENABLE_TASK_PROPERTY_NAME +import java.net.URI +import java.net.http.HttpClient +import java.net.http.HttpRequest +import java.net.http.HttpResponse +import java.time.Duration +import javax.inject.Inject +import org.gradle.api.provider.Property +import org.gradle.api.provider.ProviderFactory +import org.gradle.api.provider.ValueSource +import org.gradle.api.provider.ValueSourceParameters +import org.gradle.api.tasks.Console +import org.gradle.api.tasks.TaskAction +import org.gradle.kotlin.dsl.* +import org.gradle.work.DisableCachingByDefault + +/** + * Prints an HTTP link in the console when the HTML publication is generated. + * + * The HTML publication requires a web server, since it loads resources via javascript. + * + * By default, it uses + * [IntelliJ's built-in server](https://www.jetbrains.com/help/idea/php-built-in-web-server.html) + * to host the file. + * + * This task can be disabled using the [ENABLE_TASK_PROPERTY_NAME] project property. + */ +@DisableCachingByDefault(because = "logging-only task") +abstract class LogHtmlPublicationLinkTask +@Inject +@DokkatooInternalApi +constructor( + providers: ProviderFactory +) : DokkatooTask() { + + @get:Console + abstract val serverUri: Property<String> + + /** + * Path to the `index.html` of the publication. Will be appended to [serverUri]. + * + * The IntelliJ built-in server requires a relative path originating from the _parent_ directory + * of the IntelliJ project. + * + * For example, + * + * * given an IntelliJ project path of + * ``` + * /Users/rachel/projects/my-project/ + * ``` + * * and the publication is generated with an index file + * ``` + * /Users/rachel/projects/my-project/docs/build/dokka/html/index.html + * ```` + * * then IntelliJ requires the [indexHtmlPath] is + * ``` + * my-project/docs/build/dokka/html/index.html + * ``` + * * so that (assuming [serverUri] is `http://localhost:63342`) the logged URL is + * ``` + * http://localhost:63342/my-project/docs/build/dokka/html/index.html + * ``` + */ + @get:Console + abstract val indexHtmlPath: Property<String> + + init { + // don't assign a group. This task is a 'finalizer' util task, so it doesn't make sense + // to display this task prominently. + group = "other" + + val serverActive = providers.of(ServerActiveCheck::class) { + parameters.uri.convention(serverUri) + } + super.onlyIf("server URL is reachable") { serverActive.get() } + + val logHtmlPublicationLinkTaskEnabled = providers + .gradleProperty(ENABLE_TASK_PROPERTY_NAME) + .orElse("true") + .map(String::toBoolean) + super.onlyIf("task is enabled via property") { + logHtmlPublicationLinkTaskEnabled.get() + } + } + + @TaskAction + fun exec() { + val serverUri = serverUri.orNull + val filePath = indexHtmlPath.orNull + + if (serverUri != null && !filePath.isNullOrBlank()) { + val link = URI(serverUri).appendPath(filePath).toString() + + logger.lifecycle("Generated Dokka HTML publication: $link") + } + } + + /** + * Check if the server URI that can host the generated Dokka HTML publication is accessible. + * + * Use the [HttpClient] included with Java 11 to avoid bringing in a new dependency for such + * a small util. + * + * The check uses a [ValueSource] source to attempt to be compatible with Configuration Cache, but + * I'm not certain that this is necessary, or if a [ValueSource] is the best way to achieve it. + */ + internal abstract class ServerActiveCheck : ValueSource<Boolean, ServerActiveCheck.Parameters> { + + interface Parameters : ValueSourceParameters { + /** E.g. `http://localhost:63342` */ + val uri: Property<String> + } + + override fun obtain(): Boolean { + try { + val uri = URI.create(parameters.uri.get()) + val client = HttpClient.newHttpClient() + val request = HttpRequest + .newBuilder() + .uri(uri) + .timeout(Duration.ofSeconds(1)) + .GET() + .build() + val response = client.send(request, HttpResponse.BodyHandlers.ofString()) + + // don't care about the status - only if the server is available + return response.statusCode() > 0 + } catch (ex: Exception) { + return false + } + } + } + + companion object { + /** + * Control whether the [LogHtmlPublicationLinkTask] task is enabled. Useful for disabling the + * task locally, or in CI/CD, or for tests. + * + * ```properties + * #$GRADLE_USER_HOME/gradle.properties + * org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false + * ``` + * + * or via an environment variable + * + * ```env + * ORG_GRADLE_PROJECT_org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false + * ``` + */ + const val ENABLE_TASK_PROPERTY_NAME = "org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled" + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt new file mode 100644 index 00000000..4ac58d03 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt @@ -0,0 +1,77 @@ +package org.jetbrains.dokka.dokkatoo.workers + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.LoggerAdapter +import java.io.File +import java.time.Duration +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.workers.WorkAction +import org.gradle.workers.WorkParameters +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaGenerator + +/** + * Gradle Worker Daemon for running [DokkaGenerator]. + * + * The worker requires [DokkaGenerator] is present on its classpath, as well as any Dokka plugins + * that are used to generate the Dokka files. Transitive dependencies are also required. + */ +@DokkatooInternalApi +abstract class DokkaGeneratorWorker : WorkAction<DokkaGeneratorWorker.Parameters> { + + @DokkatooInternalApi + interface Parameters : WorkParameters { + val dokkaParameters: Property<DokkaConfiguration> + val logFile: RegularFileProperty + } + + override fun execute() { + val dokkaParameters = parameters.dokkaParameters.get() + + prepareOutputDir(dokkaParameters) + + executeDokkaGenerator( + parameters.logFile.get().asFile, + dokkaParameters, + ) + } + + private fun prepareOutputDir(dokkaParameters: DokkaConfiguration) { + // Dokka Generator doesn't clean up old files, so we need to manually clean the output directory + dokkaParameters.outputDir.deleteRecursively() + dokkaParameters.outputDir.mkdirs() + + // workaround until https://github.com/Kotlin/dokka/pull/2867 is released + dokkaParameters.modules.forEach { module -> + val moduleDir = dokkaParameters.outputDir.resolve(module.relativePathToOutputDirectory) + moduleDir.mkdirs() + } + } + + private fun executeDokkaGenerator( + logFile: File, + dokkaParameters: DokkaConfiguration + ) { + LoggerAdapter(logFile).use { logger -> + logger.progress("Executing DokkaGeneratorWorker with dokkaParameters: $dokkaParameters") + + val generator = DokkaGenerator(dokkaParameters, logger) + + val duration = measureTime { generator.generate() } + + logger.info("DokkaGeneratorWorker completed in $duration") + } + } + + @DokkatooInternalApi + companion object { + // can't use kotlin.Duration or kotlin.time.measureTime {} because + // the implementation isn't stable across Kotlin versions + private fun measureTime(block: () -> Unit): Duration = + System.nanoTime().let { startTime -> + block() + Duration.ofNanos(System.nanoTime() - startTime) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/DokkatooPluginTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/DokkatooPluginTest.kt new file mode 100644 index 00000000..843708a3 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/DokkatooPluginTest.kt @@ -0,0 +1,76 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.utils.create_ +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldEndWith +import org.gradle.kotlin.dsl.* +import org.gradle.testfixtures.ProjectBuilder + +class DokkatooPluginTest : FunSpec({ + + test("expect plugin id can be applied to project successfully") { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka.dokkatoo") + project.plugins.hasPlugin("org.jetbrains.dokka.dokkatoo") shouldBe true + project.plugins.hasPlugin(DokkatooPlugin::class) shouldBe true + } + + test("expect plugin class can be applied to project successfully") { + val project = ProjectBuilder.builder().build() + project.plugins.apply(type = DokkatooPlugin::class) + project.plugins.hasPlugin("org.jetbrains.dokka.dokkatoo") shouldBe true + project.plugins.hasPlugin(DokkatooPlugin::class) shouldBe true + } + + context("Dokkatoo property conventions") { + val project = ProjectBuilder.builder().build() + project.plugins.apply("org.jetbrains.dokka.dokkatoo") + + val extension = project.extensions.getByType<DokkatooExtension>() + + context("DokkatooSourceSets") { + val testSourceSet = extension.dokkatooSourceSets.create_("Test") { + externalDocumentationLinks.create_("gradle") { + url("https://docs.gradle.org/7.6.1/javadoc") + } + } + + context("JDK external documentation link") { + val jdkLink = testSourceSet.externalDocumentationLinks.getByName("jdk") + + test("when enableJdkDocumentationLink is false, expect jdk link is disabled") { + testSourceSet.enableJdkDocumentationLink.set(false) + jdkLink.enabled.get() shouldBe false + } + + test("when enableJdkDocumentationLink is true, expect jdk link is enabled") { + testSourceSet.enableJdkDocumentationLink.set(true) + jdkLink.enabled.get() shouldBe true + } + + (5..10).forEach { jdkVersion -> + test("when jdkVersion is $jdkVersion, expect packageListUrl uses package-list file") { + testSourceSet.jdkVersion.set(jdkVersion) + jdkLink.packageListUrl.get().toString() shouldEndWith "package-list" + } + } + + (11..22).forEach { jdkVersion -> + test("when jdkVersion is $jdkVersion, expect packageListUrl uses element-list file") { + testSourceSet.jdkVersion.set(jdkVersion) + jdkLink.packageListUrl.get().toString() shouldEndWith "element-list" + } + } + } + + context("external doc links") { + test("package-list url should be appended to Javadoc URL") { + val gradleDocLink = testSourceSet.externalDocumentationLinks.getByName("gradle") + gradleDocLink.packageListUrl.get() + .toString() shouldBe "https://docs.gradle.org/7.6.1/javadoc/package-list" + } + } + } + } +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt new file mode 100644 index 00000000..28fb2b83 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt @@ -0,0 +1,102 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.DokkatooPlugin +import org.jetbrains.dokka.dokkatoo.utils.create_ +import io.kotest.core.spec.style.FunSpec +import io.kotest.datatest.WithDataTestName +import io.kotest.datatest.withData +import io.kotest.matchers.shouldBe +import org.gradle.kotlin.dsl.* +import org.gradle.testfixtures.ProjectBuilder + + +class DokkaExternalDocumentationLinkSpecTest : FunSpec({ + + context("expect url can be set") { + test("using a string") { + val actual = createExternalDocLinkSpec { + url("https://github.com/adamko-dev/dokkatoo/") + } + + actual.url.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + + test("using a string-provider") { + val actual = createExternalDocLinkSpec { + url(project.provider { "https://github.com/adamko-dev/dokkatoo/" }) + } + + actual.url.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + } + + context("expect packageListUrl can be set") { + test("using a string") { + val actual = createExternalDocLinkSpec { + packageListUrl("https://github.com/adamko-dev/dokkatoo/") + } + + actual.packageListUrl.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + + test("using a string-provider") { + val actual = createExternalDocLinkSpec { + packageListUrl(project.provider { "https://github.com/adamko-dev/dokkatoo/" }) + } + + actual.packageListUrl.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + } + + context("expect packageList defaults to url+package-list") { + data class TestCase( + val actualUrl: String, + val expected: String, + val testName: String, + ) : WithDataTestName { + override fun dataTestName(): String = testName + } + + withData( + TestCase( + testName = "non-empty path, with trailing slash", + actualUrl = "https://github.com/adamko-dev/dokkatoo/", + expected = "https://github.com/adamko-dev/dokkatoo/package-list", + ), + TestCase( + testName = "non-empty path, without trailing slash", + actualUrl = "https://github.com/adamko-dev/dokkatoo", + expected = "https://github.com/adamko-dev/dokkatoo/package-list", + ), + TestCase( + testName = "empty path, with trailing slash", + actualUrl = "https://github.com/", + expected = "https://github.com/package-list", + ), + TestCase( + testName = "empty path, without trailing slash", + actualUrl = "https://github.com", + expected = "https://github.com/package-list", + ) + ) { (actualUrl, expected) -> + val actual = createExternalDocLinkSpec { url(actualUrl) } + actual.packageListUrl.get().toString() shouldBe expected + } + } +}) + +private val project = ProjectBuilder.builder().build().also { project -> + project.plugins.apply(type = DokkatooPlugin::class) +} + +private fun createExternalDocLinkSpec( + configure: DokkaExternalDocumentationLinkSpec.() -> Unit +): DokkaExternalDocumentationLinkSpec { + + val dssContainer = project.extensions.getByType<DokkatooExtension>().dokkatooSourceSets + + return dssContainer.create_("test" + dssContainer.size) + .externalDocumentationLinks + .create("testLink", configure) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt new file mode 100644 index 00000000..f3171a57 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt @@ -0,0 +1,58 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import io.kotest.core.spec.style.FunSpec +import io.kotest.engine.spec.tempdir +import io.kotest.matchers.shouldBe +import org.gradle.api.Project +import org.gradle.api.provider.Provider +import org.gradle.kotlin.dsl.* +import org.gradle.testfixtures.ProjectBuilder + +class DokkaSourceLinkSpecTest : FunSpec({ + val project = ProjectBuilder.builder().build() + + context("expect localDirectoryPath") { + test("is the invariantSeparatorsPath of localDirectory") { + val tempDir = tempdir() + + val actual = project.createDokkaSourceLinkSpec { + localDirectory.set(tempDir) + } + + actual.localDirectoryPath2.get() shouldBe tempDir.invariantSeparatorsPath + } + } + + + context("expect remoteUrl can be set") { + test("using a string") { + val actual = project.createDokkaSourceLinkSpec { + remoteUrl("https://github.com/adamko-dev/dokkatoo/") + } + + actual.remoteUrl.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + + test("using a string-provider") { + val actual = project.createDokkaSourceLinkSpec { + remoteUrl(project.provider { "https://github.com/adamko-dev/dokkatoo/" }) + } + + actual.remoteUrl.get().toString() shouldBe "https://github.com/adamko-dev/dokkatoo/" + } + } +}) { + + /** Re-implement [DokkaSourceLinkSpec] to make [localDirectoryPath] accessible in tests */ + abstract class DokkaSourceLinkSpec2 : DokkaSourceLinkSpec() { + val localDirectoryPath2: Provider<String> + get() = super.localDirectoryPath + } + + companion object { + private fun Project.createDokkaSourceLinkSpec( + configure: DokkaSourceLinkSpec.() -> Unit + ): DokkaSourceLinkSpec2 = + objects.newInstance(DokkaSourceLinkSpec2::class).apply(configure) + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt new file mode 100644 index 00000000..c921df9a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt @@ -0,0 +1,37 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform.Companion.dokkaType +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.collections.shouldBeIn +import io.kotest.matchers.shouldBe +import org.jetbrains.dokka.Platform + +class KotlinPlatformTest : FunSpec({ + + test("should have same default as Dokka type") { + KotlinPlatform.DEFAULT.dokkaType shouldBe Platform.DEFAULT + } + + test("Dokka platform should have equivalent KotlinPlatform") { + + Platform.values().shouldForAll { dokkaPlatform -> + dokkaPlatform shouldBeIn KotlinPlatform.values.map { it.dokkaType } + } + } + + test("platform strings should map to same KotlinPlatform and Platform") { + listOf( + "androidJvm", + "android", + "metadata", + "jvm", + "js", + "wasm", + "native", + "common", + ).shouldForAll { + Platform.fromString(it) shouldBe KotlinPlatform.fromString(it).dokkaType + } + } +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt new file mode 100644 index 00000000..ca5ad49a --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt @@ -0,0 +1,17 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.VisibilityModifier.Companion.dokkaType +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.inspectors.shouldForOne +import io.kotest.matchers.shouldBe +import org.jetbrains.dokka.DokkaConfiguration + +class VisibilityModifierTest : FunSpec({ + + test("DokkaConfiguration.Visibility should have equivalent VisibilityModifier") { + DokkaConfiguration.Visibility.values().shouldForAll { dokkaVisibility -> + VisibilityModifier.entries.map { it.dokkaType }.shouldForOne { it shouldBe dokkaVisibility } + } + } +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt new file mode 100644 index 00000000..ff442663 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import io.kotest.core.spec.style.FunSpec + +class DokkaModuleDescriptionBuilderTest : FunSpec({ + +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt new file mode 100644 index 00000000..66918194 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt @@ -0,0 +1,7 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import io.kotest.core.spec.style.FunSpec + +class DokkaParametersBuilderTest : FunSpec({ + +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt new file mode 100644 index 00000000..bb4bf8a7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt @@ -0,0 +1,198 @@ +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.DokkatooPlugin +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec +import org.jetbrains.dokka.dokkatoo.utils.all_ +import org.jetbrains.dokka.dokkatoo.utils.create_ +import org.jetbrains.dokka.dokkatoo.utils.shouldContainAll +import org.jetbrains.dokka.dokkatoo.utils.sourceLink_ +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.engine.spec.tempdir +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.collections.shouldBeSingleton +import io.kotest.matchers.equals.shouldNotBeEqual +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldContain +import java.io.File +import java.net.URI +import org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.internal.provider.MissingValueException +import org.gradle.kotlin.dsl.* +import org.gradle.testfixtures.ProjectBuilder + +class DokkaSourceSetBuilderTest : FunSpec({ + + context("when building a ExternalDocumentationLinkSpec") { + val project = createProject() + + test("expect url is required") { + val sourceSetSpec = project.createDokkaSourceSetSpec("test1") { + externalDocumentationLinks.create_("TestLink") { + url.set(null as URI?) + packageListUrl("https://github.com/adamko-dev/dokkatoo/") + } + } + + val caughtException = shouldThrow<MissingValueException> { + DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)) + } + + caughtException.message shouldContain "Cannot query the value of property 'url' because it has no value available" + } + + test("expect packageListUrl is required") { + val sourceSetSpec = project.createDokkaSourceSetSpec("test2") { + externalDocumentationLinks.create_("TestLink") { + url("https://github.com/adamko-dev/dokkatoo/") + packageListUrl.convention(null as URI?) + packageListUrl.set(null as URI?) + } + } + + val caughtException = shouldThrow<MissingValueException> { + DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)) + } + + caughtException.message shouldContain "Cannot query the value of property 'packageListUrl' because it has no value available" + } + + test("expect null when not enabled") { + val sourceSetSpec = project.createDokkaSourceSetSpec("test3") + val linkSpec = sourceSetSpec.externalDocumentationLinks.create_("TestLink") { + url("https://github.com/adamko-dev/dokkatoo/") + packageListUrl("https://github.com/adamko-dev/dokkatoo/") + enabled.set(false) + } + + DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)).shouldBeSingleton { sourceSet -> + sourceSet.externalDocumentationLinks.shouldForAll { link -> + link.url shouldNotBeEqual linkSpec.url.get().toURL() + link.packageListUrl shouldNotBeEqual linkSpec.packageListUrl.get().toURL() + } + } + } + } + + + context("when DokkaSourceLinkSpec is built") { + val project = createProject() + + test("expect built object contains all properties") { + val tempDir = tempdir() + + val sourceSetSpec = project.createDokkaSourceSetSpec("testAllProperties") { + sourceLink_ { + localDirectory.set(tempDir) + remoteUrl("https://github.com/adamko-dev/dokkatoo/") + remoteLineSuffix.set("%L") + } + } + + val sourceSet = DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)).single() + + sourceSet.sourceLinks.shouldBeSingleton { sourceLink -> + sourceLink.remoteUrl shouldBe URI("https://github.com/adamko-dev/dokkatoo/").toURL() + sourceLink.localDirectory shouldBe tempDir.invariantSeparatorsPath + sourceLink.remoteLineSuffix shouldBe "%L" + } + } + + test("expect localDirectory is required") { + val sourceSetSpec = project.createDokkaSourceSetSpec("testLocalDirRequired") { + sourceLink_ { + remoteUrl("https://github.com/adamko-dev/dokkatoo/") + remoteLineSuffix.set("%L") + } + } + + sourceSetSpec.sourceLinks.all_ { + localDirectory.convention(null as Directory?) + localDirectory.set(null as File?) + } + + val caughtException = shouldThrow<MissingValueException> { + DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)) + } + + caughtException.message.shouldContainAll( + "Cannot query the value of this provider because it has no value available", + "The value of this provider is derived from", + "property 'localDirectory'", + ) + } + + test("expect localDirectory is an invariantSeparatorsPath") { + val tempDir = tempdir() + + val sourceSetSpec = project.createDokkaSourceSetSpec("testLocalDirPath") { + sourceLink_ { + localDirectory.set(tempDir) + remoteUrl("https://github.com/adamko-dev/dokkatoo/") + remoteLineSuffix.set(null as String?) + } + } + + val link = DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)) + .single() + .sourceLinks + .single() + + link.localDirectory shouldBe tempDir.invariantSeparatorsPath + } + + test("expect remoteUrl is required") { + val sourceSetSpec = project.createDokkaSourceSetSpec("testRemoteUrlRequired") { + sourceLink_ { + localDirectory.set(tempdir()) + remoteUrl.set(project.providers.provider { null }) + remoteLineSuffix.set("%L") + } + } + + val caughtException = shouldThrow<MissingValueException> { + DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)) + } + + caughtException.message shouldContain "Cannot query the value of property 'remoteUrl' because it has no value available" + } + + test("expect remoteLineSuffix is optional") { + val tempDir = tempdir() + + val sourceSetSpec = project.createDokkaSourceSetSpec("testRemoteLineSuffixOptional") { + sourceLink_ { + localDirectory.set(tempDir) + remoteUrl("https://github.com/adamko-dev/dokkatoo/") + remoteLineSuffix.set(project.providers.provider { null }) + } + } + + val sourceSet = DokkaSourceSetBuilder.buildAll(setOf(sourceSetSpec)).single() + + sourceSet.sourceLinks.shouldBeSingleton { sourceLink -> + sourceLink.remoteUrl shouldBe URI("https://github.com/adamko-dev/dokkatoo/").toURL() + sourceLink.localDirectory shouldBe tempDir.invariantSeparatorsPath + sourceLink.remoteLineSuffix shouldBe null + } + } + } +}) + +private fun createProject(): Project { + val project = ProjectBuilder.builder().build() + project.plugins.apply(type = DokkatooPlugin::class) + return project +} + +private fun Project.createDokkaSourceSetSpec( + name: String, + configure: DokkaSourceSetSpec.() -> Unit = {} +): DokkaSourceSetSpec { + return extensions + .getByType<DokkatooExtension>() + .dokkatooSourceSets + .create_(name, configure) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt new file mode 100644 index 00000000..2f9e1b41 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt @@ -0,0 +1,274 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import kotlin.properties.PropertyDelegateProvider +import kotlin.properties.ReadWriteProperty +import kotlin.reflect.KProperty +import org.gradle.testkit.runner.GradleRunner +import org.intellij.lang.annotations.Language + + +// utils for testing using Gradle TestKit + + +class GradleProjectTest( + override val projectDir: Path, +) : ProjectDirectoryScope { + + constructor( + testProjectName: String, + baseDir: Path = funcTestTempDir, + ) : this(projectDir = baseDir.resolve(testProjectName)) + + val runner: GradleRunner + get() = GradleRunner.create() + .withProjectDir(projectDir.toFile()) + .withJvmArguments( + "-XX:MaxMetaspaceSize=512m", + "-XX:+AlwaysPreTouch", // https://github.com/gradle/gradle/issues/3093#issuecomment-387259298 + ).addArguments( + // disable the logging task so the tests work consistently on local machines and CI/CD + "-P" + "org.jetbrains.dokka.dokkatoo.tasks.logHtmlPublicationLinkEnabled=false" + ) + + val testMavenRepoRelativePath: String = + projectDir.relativize(testMavenRepoDir).toFile().invariantSeparatorsPath + + companion object { + + /** file-based Maven Repo that contains the Dokka dependencies */ + val testMavenRepoDir: Path by systemProperty(Paths::get) + + val projectTestTempDir: Path by systemProperty(Paths::get) + + /** Temporary directory for the functional tests */ + val funcTestTempDir: Path by lazy { + projectTestTempDir.resolve("functional-tests") + } + + /** Dokka Source directory that contains Gradle projects used for integration tests */ + val integrationTestProjectsDir: Path by systemProperty(Paths::get) + /** Dokka Source directory that contains example Gradle projects */ + val exampleProjectsDir: Path by systemProperty(Paths::get) + } +} + + +///** +// * Load a project from the [GradleProjectTest.dokkaSrcIntegrationTestProjectsDir] +// */ +//fun gradleKtsProjectIntegrationTest( +// testProjectName: String, +// build: GradleProjectTest.() -> Unit, +//): GradleProjectTest = +// GradleProjectTest( +// baseDir = GradleProjectTest.dokkaSrcIntegrationTestProjectsDir, +// testProjectName = testProjectName, +// ).apply(build) + + +/** + * Builder for testing a Gradle project that uses Kotlin script DSL and creates default + * `settings.gradle.kts` and `gradle.properties` files. + * + * @param[testProjectName] the path of the project directory, relative to [baseDir + */ +fun gradleKtsProjectTest( + testProjectName: String, + baseDir: Path = GradleProjectTest.funcTestTempDir, + build: GradleProjectTest.() -> Unit, +): GradleProjectTest { + return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { + + settingsGradleKts = """ + |rootProject.name = "test" + | + |@Suppress("UnstableApiUsage") + |dependencyResolutionManagement { + | repositories { + | mavenCentral() + | maven(file("$testMavenRepoRelativePath")) { + | mavenContent { + | includeGroup("org.jetbrains.dokka.dokkatoo") + | includeGroup("org.jetbrains.dokka.dokkatoo-html") + | } + | } + | } + |} + | + |pluginManagement { + | repositories { + | mavenCentral() + | gradlePluginPortal() + | maven(file("$testMavenRepoRelativePath")) { + | mavenContent { + | includeGroup("org.jetbrains.dokka.dokkatoo") + | includeGroup("org.jetbrains.dokka.dokkatoo-html") + | } + | } + | } + |} + | + """.trimMargin() + + gradleProperties = """ + |kotlin.mpp.stability.nowarn=true + |org.gradle.cache=true + """.trimMargin() + + build() + } +} + +/** + * Builder for testing a Gradle project that uses Groovy script and creates default, + * `settings.gradle`, and `gradle.properties` files. + * + * @param[testProjectName] the name of the test, which should be distinct across the project + */ +fun gradleGroovyProjectTest( + testProjectName: String, + baseDir: Path = GradleProjectTest.funcTestTempDir, + build: GradleProjectTest.() -> Unit, +): GradleProjectTest { + return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { + + settingsGradle = """ + |rootProject.name = "test" + | + |dependencyResolutionManagement { + | repositories { + | mavenCentral() + | maven { url = file("$testMavenRepoRelativePath") } + | } + |} + | + |pluginManagement { + | repositories { + | mavenCentral() + | gradlePluginPortal() + | maven { url = file("$testMavenRepoRelativePath") } + | } + |} + | + """.trimMargin() + + gradleProperties = """ + |kotlin.mpp.stability.nowarn=true + |org.gradle.cache=true + """.trimMargin() + + build() + } +} + + +fun GradleProjectTest.projectFile( + @Language("TEXT") + filePath: String +): PropertyDelegateProvider<Any?, ReadWriteProperty<Any?, String>> = + PropertyDelegateProvider { _, _ -> + TestProjectFileProvidedDelegate(this, filePath) + } + + +/** Delegate for reading and writing a [GradleProjectTest] file. */ +private class TestProjectFileProvidedDelegate( + private val project: GradleProjectTest, + private val filePath: String, +) : ReadWriteProperty<Any?, String> { + override fun getValue(thisRef: Any?, property: KProperty<*>): String = + project.projectDir.resolve(filePath).toFile().readText() + + override fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { + project.createFile(filePath, value) + } +} + +/** Delegate for reading and writing a [GradleProjectTest] file. */ +class TestProjectFileDelegate( + private val filePath: String, +) : ReadWriteProperty<ProjectDirectoryScope, String> { + override fun getValue(thisRef: ProjectDirectoryScope, property: KProperty<*>): String = + thisRef.projectDir.resolve(filePath).toFile().readText() + + override fun setValue(thisRef: ProjectDirectoryScope, property: KProperty<*>, value: String) { + thisRef.createFile(filePath, value) + } +} + + +@DslMarker +annotation class ProjectDirectoryDsl + +@ProjectDirectoryDsl +interface ProjectDirectoryScope { + val projectDir: Path +} + +private data class ProjectDirectoryScopeImpl( + override val projectDir: Path +) : ProjectDirectoryScope + + +fun ProjectDirectoryScope.createFile(filePath: String, contents: String): File = + projectDir.resolve(filePath).toFile().apply { + parentFile.mkdirs() + createNewFile() + writeText(contents) + } + + +@ProjectDirectoryDsl +fun ProjectDirectoryScope.dir( + path: String, + block: ProjectDirectoryScope.() -> Unit = {}, +): ProjectDirectoryScope = + ProjectDirectoryScopeImpl(projectDir.resolve(path)).apply(block) + + +@ProjectDirectoryDsl +fun ProjectDirectoryScope.file( + path: String +): Path = projectDir.resolve(path) + + +fun ProjectDirectoryScope.findFiles(matcher: (File) -> Boolean): Sequence<File> = + projectDir.toFile().walk().filter(matcher) + + +/** Set the content of `settings.gradle.kts` */ +@delegate:Language("kts") +var ProjectDirectoryScope.settingsGradleKts: String by TestProjectFileDelegate("settings.gradle.kts") + + +/** Set the content of `build.gradle.kts` */ +@delegate:Language("kts") +var ProjectDirectoryScope.buildGradleKts: String by TestProjectFileDelegate("build.gradle.kts") + + +/** Set the content of `settings.gradle` */ +@delegate:Language("groovy") +var ProjectDirectoryScope.settingsGradle: String by TestProjectFileDelegate("settings.gradle") + + +/** Set the content of `build.gradle` */ +@delegate:Language("groovy") +var ProjectDirectoryScope.buildGradle: String by TestProjectFileDelegate("build.gradle") + + +/** Set the content of `gradle.properties` */ +@delegate:Language("properties") +var ProjectDirectoryScope.gradleProperties: String by TestProjectFileDelegate( + /* language=text */ "gradle.properties" +) + + +fun ProjectDirectoryScope.createKotlinFile(filePath: String, @Language("kotlin") contents: String) = + createFile(filePath, contents) + + +fun ProjectDirectoryScope.createKtsFile(filePath: String, @Language("kts") contents: String) = + createFile(filePath, contents) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt new file mode 100644 index 00000000..d6eadba0 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import io.kotest.core.config.AbstractProjectConfig + +@Suppress("unused") // this class is automatically picked up by Kotest +object KotestProjectConfig : AbstractProjectConfig() { + init { + displayFullTestPath = true + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/fileTree.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/fileTree.kt new file mode 100644 index 00000000..4ba850d3 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/fileTree.kt @@ -0,0 +1,61 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import java.io.File +import java.nio.file.Path + +// based on https://gist.github.com/mfwgenerics/d1ec89eb80c95da9d542a03b49b5e15b +// context: https://kotlinlang.slack.com/archives/C0B8MA7FA/p1676106647658099 + +fun Path.toTreeString(): String = toFile().toTreeString() + +fun File.toTreeString(): String = when { + isDirectory -> name + "/\n" + buildTreeString(this) + else -> name +} + +private fun buildTreeString( + dir: File, + margin: String = "", +): String { + val entries = dir.listDirectoryEntries() + + return entries.joinToString("\n") { entry -> + val (currentPrefix, nextPrefix) = when (entry) { + entries.last() -> PrefixPair.LAST_ENTRY + else -> PrefixPair.INTERMEDIATE + } + + buildString { + append("$margin${currentPrefix}${entry.name}") + + if (entry.isDirectory) { + append("/") + if (entry.countDirectoryEntries() > 0) { + append("\n") + } + append(buildTreeString(entry, margin + nextPrefix)) + } + } + } +} + +private fun File.listDirectoryEntries(): Sequence<File> = + walkTopDown().maxDepth(1).filter { it != this@listDirectoryEntries } + + +private fun File.countDirectoryEntries(): Int = + listDirectoryEntries().count() + +private data class PrefixPair( + /** The current entry should be prefixed with this */ + val currentPrefix: String, + /** If the next item is a directory, it should be prefixed with this */ + val nextPrefix: String, +) { + companion object { + /** Prefix pair for a non-last directory entry */ + val INTERMEDIATE = PrefixPair("├── ", "│ ") + /** Prefix pair for the last directory entry */ + val LAST_ENTRY = PrefixPair("└── ", " ") + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/files.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/files.kt new file mode 100644 index 00000000..6a423b55 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/files.kt @@ -0,0 +1,6 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import java.io.File + +fun File.copyInto(directory: File, overwrite: Boolean = false) = + copyTo(directory.resolve(name), overwrite = overwrite) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt new file mode 100644 index 00000000..912d1df1 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt @@ -0,0 +1,47 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.BuildTask +import org.gradle.testkit.runner.GradleRunner +import org.gradle.testkit.runner.internal.DefaultGradleRunner + + +/** Edit environment variables in the Gradle Runner */ +@Deprecated("Windows does not support withEnvironment - https://github.com/gradle/gradle/issues/23959") +fun GradleRunner.withEnvironment(build: MutableMap<String, String?>.() -> Unit): GradleRunner { + val env = environment ?: mutableMapOf() + env.build() + return withEnvironment(env) +} + + +inline fun GradleRunner.build( + handleResult: BuildResult.() -> Unit +): Unit = build().let(handleResult) + + +inline fun GradleRunner.buildAndFail( + handleResult: BuildResult.() -> Unit +): Unit = buildAndFail().let(handleResult) + + +fun GradleRunner.withJvmArguments( + vararg jvmArguments: String +): GradleRunner = (this as DefaultGradleRunner).withJvmArguments(*jvmArguments) + + +/** + * Helper function to _append_ [arguments] to any existing + * [GradleRunner arguments][GradleRunner.getArguments]. + */ +fun GradleRunner.addArguments( + vararg arguments: String +): GradleRunner = + withArguments(this@addArguments.arguments + arguments) + + +/** + * Get the name of the task, without the leading [BuildTask.getPath]. + */ +val BuildTask.name: String + get() = path.substringAfterLast(':') diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt new file mode 100644 index 00000000..8c33e3eb --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt @@ -0,0 +1,20 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import io.kotest.matchers.collections.shouldBeSingleton +import io.kotest.matchers.maps.shouldContainAll +import io.kotest.matchers.maps.shouldContainExactly + +/** @see io.kotest.matchers.maps.shouldContainAll */ +fun <K, V> Map<K, V>.shouldContainAll( + vararg expected: Pair<K, V> +): Unit = shouldContainAll(expected.toMap()) + +/** @see io.kotest.matchers.maps.shouldContainExactly */ +fun <K, V> Map<K, V>.shouldContainExactly( + vararg expected: Pair<K, V> +): Unit = shouldContainExactly(expected.toMap()) + +/** Verify the sequence contains a single element, matching [match]. */ +fun <T> Sequence<T>.shouldBeSingleton(match: (T) -> Unit) { + toList().shouldBeSingleton(match) +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestConditions.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestConditions.kt new file mode 100644 index 00000000..7b692afb --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestConditions.kt @@ -0,0 +1,10 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import io.kotest.core.annotation.EnabledCondition +import io.kotest.core.spec.Spec +import kotlin.reflect.KClass + +class NotWindowsCondition : EnabledCondition { + override fun enabled(kclass: KClass<out Spec>): Boolean = + "win" !in System.getProperty("os.name").lowercase() +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt new file mode 100644 index 00000000..e1863c8f --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt @@ -0,0 +1,130 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import io.kotest.assertions.assertSoftly +import io.kotest.matchers.* +import org.gradle.api.NamedDomainObjectCollection +import org.gradle.testkit.runner.BuildResult +import org.gradle.testkit.runner.BuildTask +import org.gradle.testkit.runner.TaskOutcome + +infix fun <T : Any> NamedDomainObjectCollection<out T>?.shouldContainDomainObject( + name: String +): T { + this should containDomainObject(name) + return this?.getByName(name)!! +} + +infix fun <T : Any> NamedDomainObjectCollection<out T>?.shouldNotContainDomainObject( + name: String +): NamedDomainObjectCollection<out T>? { + this shouldNot containDomainObject(name) + return this +} + +private fun <T> containDomainObject(name: String): Matcher<NamedDomainObjectCollection<T>?> = + neverNullMatcher { value -> + MatcherResult( + name in value.names, + { "NamedDomainObjectCollection(${value.names}) should contain DomainObject named '$name'" }, + { "NamedDomainObjectCollection(${value.names}) should not contain DomainObject named '$name'" }) + } + +/** Assert that a task ran. */ +infix fun BuildResult?.shouldHaveRunTask(taskPath: String): BuildTask { + this should haveTask(taskPath) + return this?.task(taskPath)!! +} + +/** Assert that a task ran, with an [expected outcome][expectedOutcome]. */ +fun BuildResult?.shouldHaveRunTask( + taskPath: String, + expectedOutcome: TaskOutcome +): BuildTask { + this should haveTask(taskPath) + val task = this?.task(taskPath)!! + task should haveOutcome(expectedOutcome) + return task +} + +/** + * Assert that a task did not run. + * + * A task might not have run if one of its dependencies failed before it could be run. + */ +infix fun BuildResult?.shouldNotHaveRunTask(taskPath: String) { + this shouldNot haveTask(taskPath) +} + +private fun haveTask(taskPath: String): Matcher<BuildResult?> = + neverNullMatcher { value -> + MatcherResult( + value.task(taskPath) != null, + { "BuildResult should have run task $taskPath. All tasks: ${value.tasks.joinToString { it.path }}" }, + { "BuildResult should not have run task $taskPath. All tasks: ${value.tasks.joinToString { it.path }}" }, + ) + } + + +infix fun BuildTask?.shouldHaveOutcome(outcome: TaskOutcome) { + this should haveOutcome(outcome) +} + + +infix fun BuildTask?.shouldHaveAnyOutcome(outcomes: Collection<TaskOutcome>) { + this should haveAnyOutcome(outcomes) +} + + +infix fun BuildTask?.shouldNotHaveOutcome(outcome: TaskOutcome) { + this shouldNot haveOutcome(outcome) +} + + +private fun haveOutcome(outcome: TaskOutcome): Matcher<BuildTask?> = + haveAnyOutcome(listOf(outcome)) + + +private fun haveAnyOutcome(outcomes: Collection<TaskOutcome>): Matcher<BuildTask?> { + val shouldHaveOutcome = when (outcomes.size) { + 0 -> error("Must provide 1 or more expected task outcome, but received none") + 1 -> "should have outcome ${outcomes.first().name}" + else -> "should have any outcome of ${outcomes.joinToString()}" + } + + return neverNullMatcher { value -> + MatcherResult( + value.outcome in outcomes, + { "Task ${value.path} $shouldHaveOutcome, but was ${value.outcome}" }, + { "Task ${value.path} $shouldHaveOutcome, but was ${value.outcome}" }, + ) + } +} + +fun BuildResult.shouldHaveTaskWithOutcome(taskPath: String, outcome: TaskOutcome) { + this shouldHaveRunTask taskPath shouldHaveOutcome outcome +} + + +fun BuildResult.shouldHaveTaskWithAnyOutcome(taskPath: String, outcomes: Collection<TaskOutcome>) { + this shouldHaveRunTask taskPath shouldHaveAnyOutcome outcomes +} + +fun BuildResult.shouldHaveTasksWithOutcome( + vararg taskPathToExpectedOutcome: Pair<String, TaskOutcome> +) { + assertSoftly { + taskPathToExpectedOutcome.forEach { (taskPath, outcome) -> + shouldHaveTaskWithOutcome(taskPath, outcome) + } + } +} + +fun BuildResult.shouldHaveTasksWithAnyOutcome( + vararg taskPathToExpectedOutcome: Pair<String, Collection<TaskOutcome>> +) { + assertSoftly { + taskPathToExpectedOutcome.forEach { (taskPath, outcomes) -> + shouldHaveTaskWithAnyOutcome(taskPath, outcomes) + } + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt new file mode 100644 index 00000000..58bbe768 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt @@ -0,0 +1,65 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import io.kotest.assertions.print.print +import io.kotest.matchers.MatcherResult +import io.kotest.matchers.neverNullMatcher +import io.kotest.matchers.should +import io.kotest.matchers.shouldNot + + +infix fun String?.shouldContainAll(substrings: Iterable<String>): String? { + this should containAll(substrings) + return this +} + +infix fun String?.shouldNotContainAll(substrings: Iterable<String>): String? { + this shouldNot containAll(substrings) + return this +} + +fun String?.shouldContainAll(vararg substrings: String): String? { + this should containAll(substrings.asList()) + return this +} + +fun String?.shouldNotContainAll(vararg substrings: String): String? { + this shouldNot containAll(substrings.asList()) + return this +} + +private fun containAll(substrings: Iterable<String>) = + neverNullMatcher<String> { value -> + MatcherResult( + substrings.all { it in value }, + { "${value.print().value} should include substrings ${substrings.print().value}" }, + { "${value.print().value} should not include substrings ${substrings.print().value}" }) + } + + +infix fun String?.shouldContainAnyOf(substrings: Iterable<String>): String? { + this should containAnyOf(substrings) + return this +} + +infix fun String?.shouldNotContainAnyOf(substrings: Iterable<String>): String? { + this shouldNot containAnyOf(substrings) + return this +} + +fun String?.shouldContainAnyOf(vararg substrings: String): String? { + this should containAnyOf(substrings.asList()) + return this +} + +fun String?.shouldNotContainAnyOf(vararg substrings: String): String? { + this shouldNot containAnyOf(substrings.asList()) + return this +} + +private fun containAnyOf(substrings: Iterable<String>) = + neverNullMatcher<String> { value -> + MatcherResult( + substrings.any { it in value }, + { "${value.print().value} should include any of these substrings ${substrings.print().value}" }, + { "${value.print().value} should not include any of these substrings ${substrings.print().value}" }) + } diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt new file mode 100644 index 00000000..62cd5860 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt @@ -0,0 +1,77 @@ +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.dokkatoo.utils + +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaPackageOptionsSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceLinkSpec +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetSpec +import org.gradle.api.DomainObjectCollection +import org.gradle.api.NamedDomainObjectContainer +import org.gradle.api.NamedDomainObjectProvider +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.DependencySet + + +/** + * Workarounds because `SamWithReceiver` not working in test sources + * https://youtrack.jetbrains.com/issue/KTIJ-14684 + * + * The `SamWithReceiver` plugin is automatically applied by the `kotlin-dsl` plugin. + * It converts all [org.gradle.api.Action] so the parameter is the receiver: + * + * ``` + * // with SamWithReceiver ✅ + * tasks.configureEach { + * val task: Task = this + * } + * + * // without SamWithReceiver + * tasks.configureEach { it -> + * val task: Task = it + * } + * ``` + * + * This is nice because it means that the Dokka Gradle Plugin more closely matches `build.gradle.kts` files. + * + * However, [IntelliJ is bugged](https://youtrack.jetbrains.com/issue/KTIJ-14684) and doesn't + * acknowledge that `SamWithReceiver` has been applied in test sources. The code works and compiles, + * but IntelliJ shows red errors. + * + * These functions are workarounds, and should be removed ASAP. + */ +@Suppress("unused") +private object Explain + +fun Project.subprojects_(configure: Project.() -> Unit) = + subprojects(configure) + +@Suppress("SpellCheckingInspection") +fun Project.allprojects_(configure: Project.() -> Unit) = + allprojects(configure) + +fun <T> DomainObjectCollection<T>.configureEach_(configure: T.() -> Unit) = + configureEach(configure) + +fun <T> DomainObjectCollection<T>.all_(configure: T.() -> Unit) = + all(configure) + +fun Configuration.withDependencies_(action: DependencySet.() -> Unit): Configuration = + withDependencies(action) + +fun <T> NamedDomainObjectContainer<T>.create_(name: String, configure: T.() -> Unit = {}): T = + create(name, configure) + +fun <T> NamedDomainObjectContainer<T>.register_( + name: String, + configure: T.() -> Unit +): NamedDomainObjectProvider<T> = + register(name, configure) + +fun DokkaSourceSetSpec.sourceLink_( + action: DokkaSourceLinkSpec.() -> Unit +): Unit = sourceLink(action) + +fun DokkaSourceSetSpec.perPackageOption_( + action: DokkaPackageOptionsSpec.() -> Unit +): Unit = perPackageOption(action) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/stringUtils.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/stringUtils.kt new file mode 100644 index 00000000..eb8777e7 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/stringUtils.kt @@ -0,0 +1,21 @@ +package org.jetbrains.dokka.dokkatoo.utils + + +fun String.splitToPair(delimiter: String): Pair<String, String> = + substringBefore(delimiter) to substringAfter(delimiter) + + +/** Title case the first char of a string */ +fun String.uppercaseFirstChar(): String = mapFirstChar(Character::toTitleCase) + + +private inline fun String.mapFirstChar( + transform: (Char) -> Char +): String = if (isNotEmpty()) transform(this[0]) + substring(1) else this + + +/** Split a string into lines, sort the lines, and re-join them (using [separator]). */ +fun String.sortLines(separator: String = "\n") = + lines() + .sorted() + .joinToString(separator) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/systemVariableProviders.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/systemVariableProviders.kt new file mode 100644 index 00000000..b15b3edb --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/systemVariableProviders.kt @@ -0,0 +1,40 @@ +package org.jetbrains.dokka.dokkatoo.utils + +import kotlin.properties.ReadOnlyProperty + +// Utilities for fetching System Properties and Environment Variables via delegated properties + + +internal fun optionalSystemProperty() = optionalSystemProperty { it } + +internal fun <T : Any> optionalSystemProperty( + convert: (String) -> T? +): ReadOnlyProperty<Any, T?> = + ReadOnlyProperty { _, property -> + val value = System.getProperty(property.name) + if (value != null) convert(value) else null + } + + +internal fun systemProperty() = systemProperty { it } + +internal fun <T> systemProperty( + convert: (String) -> T +): ReadOnlyProperty<Any, T> = + ReadOnlyProperty { _, property -> + val value = requireNotNull(System.getProperty(property.name)) { + "system property ${property.name} is unavailable" + } + convert(value) + } + + +internal fun optionalEnvironmentVariable() = optionalEnvironmentVariable { it } + +internal fun <T : Any> optionalEnvironmentVariable( + convert: (String) -> T? +): ReadOnlyProperty<Any, T?> = + ReadOnlyProperty { _, property -> + val value = System.getenv(property.name) + if (value != null) convert(value) else null + } diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/text.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/text.kt new file mode 100644 index 00000000..ce0ebd9d --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFixtures/kotlin/text.kt @@ -0,0 +1,24 @@ +package org.jetbrains.dokka.dokkatoo.utils + +/** Replace all newlines with `\n`, so the String can be used in assertions cross-platform */ +fun String.invariantNewlines(): String = + lines().joinToString("\n") + +fun Pair<String, String>.sideBySide( + buffer: String = " ", +): String { + val (left, right) = this + + val leftLines = left.lines() + val rightLines = right.lines() + + val maxLeftWidth = leftLines.maxOf { it.length } + + return (0..maxOf(leftLines.size, rightLines.size)).joinToString("\n") { i -> + + val leftLine = (leftLines.getOrNull(i) ?: "").padEnd(maxLeftWidth, ' ') + val rightLine = rightLines.getOrNull(i) ?: "" + + leftLine + buffer + rightLine + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt new file mode 100644 index 00000000..90d587ce --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt @@ -0,0 +1,205 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKATOO_VERSION +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.assertions.asClue +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder +import io.kotest.matchers.string.shouldContain + +class DokkatooPluginFunctionalTest : FunSpec({ + val testProject = gradleKtsProjectTest("DokkatooPluginFunctionalTest") { + buildGradleKts = """ + |plugins { + | id("org.jetbrains.dokka.dokkatoo") version "$DOKKATOO_VERSION" + |} + | + """.trimMargin() + } + + test("expect Dokka Plugin creates Dokka tasks") { + testProject.runner + .addArguments("tasks", "--group=dokkatoo", "-q") + .build { + withClue(output) { + val dokkatooTasks = output + .substringAfter("Dokkatoo tasks") + .lines() + .filter { it.contains(" - ") } + .associate { it.splitToPair(" - ") } + + dokkatooTasks.shouldContainExactly( + //@formatter:off + "dokkatooGenerate" to "Generates Dokkatoo publications for all formats", + "dokkatooGenerateModuleGfm" to "Executes the Dokka Generator, generating a gfm module", + "dokkatooGenerateModuleHtml" to "Executes the Dokka Generator, generating a html module", + "dokkatooGenerateModuleJavadoc" to "Executes the Dokka Generator, generating a javadoc module", + "dokkatooGenerateModuleJekyll" to "Executes the Dokka Generator, generating a jekyll module", + "dokkatooGeneratePublicationGfm" to "Executes the Dokka Generator, generating the gfm publication", + "dokkatooGeneratePublicationHtml" to "Executes the Dokka Generator, generating the html publication", + "dokkatooGeneratePublicationJavadoc" to "Executes the Dokka Generator, generating the javadoc publication", + "dokkatooGeneratePublicationJekyll" to "Executes the Dokka Generator, generating the jekyll publication", + "prepareDokkatooModuleDescriptorGfm" to "Prepares the Dokka Module Descriptor for gfm", + "prepareDokkatooModuleDescriptorHtml" to "Prepares the Dokka Module Descriptor for html", + "prepareDokkatooModuleDescriptorJavadoc" to "Prepares the Dokka Module Descriptor for javadoc", + "prepareDokkatooModuleDescriptorJekyll" to "Prepares the Dokka Module Descriptor for jekyll", + //@formatter:on + ) + } + } + } + + test("expect Dokka Plugin creates Dokka outgoing variants") { + val build = testProject.runner + .addArguments("outgoingVariants", "-q") + .build { + val variants = output.invariantNewlines().replace('\\', '/') + + val dokkatooVariants = variants.lines() + .filter { it.contains("dokka", ignoreCase = true) } + .mapNotNull { it.substringAfter("Variant ", "").takeIf(String::isNotBlank) } + + + dokkatooVariants.shouldContainExactlyInAnyOrder( + "dokkatooModuleElementsGfm", + "dokkatooModuleElementsHtml", + "dokkatooModuleElementsJavadoc", + "dokkatooModuleElementsJekyll", + ) + + fun checkVariant(format: String) { + val formatCapitalized = format.uppercaseFirstChar() + + variants shouldContain /* language=text */ """ + |-------------------------------------------------- + |Variant dokkatooModuleElements$formatCapitalized + |-------------------------------------------------- + |Provide Dokka Module files for $format to other subprojects + | + |Capabilities + | - :test:unspecified (default capability) + |Attributes + | - org.jetbrains.dokka.dokkatoo.base = dokkatoo + | - org.jetbrains.dokka.dokkatoo.category = module-files + | - org.jetbrains.dokka.dokkatoo.format = $format + |Artifacts + | - build/dokka-config/$format/module_descriptor.json (artifactType = json) + | - build/dokka-module/$format (artifactType = directory) + | + """.trimMargin() + } + + checkVariant("gfm") + checkVariant("html") + checkVariant("javadoc") + checkVariant("jekyll") + } + } + + test("expect Dokka Plugin creates Dokka resolvable configurations") { + + val expectedFormats = listOf("Gfm", "Html", "Javadoc", "Jekyll") + + testProject.runner + .addArguments("resolvableConfigurations", "-q") + .build { + output.invariantNewlines().asClue { allConfigurations -> + + val dokkatooConfigurations = allConfigurations.lines() + .filter { it.contains("dokka", ignoreCase = true) } + .mapNotNull { it.substringAfter("Configuration ", "").takeIf(String::isNotBlank) } + + dokkatooConfigurations.shouldContainExactlyInAnyOrder( + buildList { + add("dokkatoo") + + addAll(expectedFormats.map { "dokkatooModule$it" }) + addAll(expectedFormats.map { "dokkatooGeneratorClasspath$it" }) + addAll(expectedFormats.map { "dokkatooPlugin$it" }) + addAll(expectedFormats.map { "dokkatooPluginIntransitive$it" }) + } + ) + + withClue("Configuration dokka") { + output.invariantNewlines() shouldContain /* language=text */ """ + |-------------------------------------------------- + |Configuration dokkatoo + |-------------------------------------------------- + |Fetch all Dokkatoo files from all configurations in other subprojects + | + |Attributes + | - org.jetbrains.dokka.dokkatoo.base = dokkatoo + | + """.trimMargin() + } + + fun checkConfigurations(format: String) { + val formatLowercase = format.lowercase() + + allConfigurations shouldContain /* language=text */ """ + |-------------------------------------------------- + |Configuration dokkatooGeneratorClasspath$format + |-------------------------------------------------- + |Dokka Generator runtime classpath for $formatLowercase - will be used in Dokka Worker. Should contain all transitive dependencies, plugins (and their transitive dependencies), so Dokka Worker can run. + | + |Attributes + | - org.jetbrains.dokka.dokkatoo.base = dokkatoo + | - org.jetbrains.dokka.dokkatoo.category = generator-classpath + | - org.jetbrains.dokka.dokkatoo.format = $formatLowercase + | - org.gradle.category = library + | - org.gradle.dependency.bundling = external + | - org.gradle.jvm.environment = standard-jvm + | - org.gradle.libraryelements = jar + | - org.gradle.usage = java-runtime + |Extended Configurations + | - dokkatooPlugin$format + | + """.trimMargin() + + allConfigurations shouldContain /* language=text */ """ + |-------------------------------------------------- + |Configuration dokkatooPlugin$format + |-------------------------------------------------- + |Dokka Plugins classpath for $formatLowercase + | + |Attributes + | - org.jetbrains.dokka.dokkatoo.base = dokkatoo + | - org.jetbrains.dokka.dokkatoo.category = plugins-classpath + | - org.jetbrains.dokka.dokkatoo.format = $formatLowercase + | - org.gradle.category = library + | - org.gradle.dependency.bundling = external + | - org.gradle.jvm.environment = standard-jvm + | - org.gradle.libraryelements = jar + | - org.gradle.usage = java-runtime + | + """.trimMargin() + + allConfigurations shouldContain /* language=text */ """ + |-------------------------------------------------- + |Configuration dokkatooPluginIntransitive$format + |-------------------------------------------------- + |Dokka Plugins classpath for $formatLowercase - for internal use. Fetch only the plugins (no transitive dependencies) for use in the Dokka JSON Configuration. + | + |Attributes + | - org.jetbrains.dokka.dokkatoo.base = dokkatoo + | - org.jetbrains.dokka.dokkatoo.category = plugins-classpath + | - org.jetbrains.dokka.dokkatoo.format = $formatLowercase + | - org.gradle.category = library + | - org.gradle.dependency.bundling = external + | - org.gradle.jvm.environment = standard-jvm + | - org.gradle.libraryelements = jar + | - org.gradle.usage = java-runtime + |Extended Configurations + | - dokkatooPlugin$format + | + """.trimMargin() + } + + expectedFormats.forEach { + checkConfigurations(it) + } + } + } + } +}) diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt new file mode 100644 index 00000000..d35150a2 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt @@ -0,0 +1,110 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.sequences.shouldNotBeEmpty +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain + +class GradlePluginProjectIntegrationTest : FunSpec({ + + context("given a gradle plugin project") { + val project = initGradlePluginProject() + + project.runner + .addArguments( + "clean", + "dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + + test("expect project builds successfully") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect no 'unknown class' message in HTML files") { + val htmlFiles = project.projectDir.toFile() + .resolve("build/dokka/html") + .walk() + .filter { it.isFile && it.extension == "html" } + + htmlFiles.shouldNotBeEmpty() + + htmlFiles.forEach { htmlFile -> + val relativePath = htmlFile.relativeTo(project.projectDir.toFile()) + withClue("$relativePath should not contain Error class: unknown class") { + htmlFile.useLines { lines -> + lines.shouldForAll { line -> line.shouldNotContain("Error class: unknown class") } + } + } + } + } + } + } +}) + +private fun initGradlePluginProject( + config: GradleProjectTest.() -> Unit = {}, +): GradleProjectTest { + return gradleKtsProjectTest("gradle-plugin-project") { + + settingsGradleKts += """ + | + """.trimMargin() + + buildGradleKts = """ + |plugins { + | `kotlin-dsl` + | id("org.jetbrains.dokka.dokkatoo") version "${DokkatooConstants.DOKKATOO_VERSION}" + |} + | + """.trimMargin() + + dir("src/main/kotlin") { + + createKotlinFile( + "MyCustomGradlePlugin.kt", + """ + |package com.project.gradle.plugin + | + |import javax.inject.Inject + |import org.gradle.api.Plugin + |import org.gradle.api.Project + |import org.gradle.api.model.ObjectFactory + |import org.gradle.kotlin.dsl.* + | + |abstract class MyCustomGradlePlugin @Inject constructor( + | private val objects: ObjectFactory + |) : Plugin<Project> { + | override fun apply(project: Project) { + | println(objects.property<String>().getOrElse("empty")) + | } + |} + + """.trimMargin() + ) + + createKotlinFile( + "MyCustomGradlePluginExtension.kt", + """ + |package com.project.gradle.plugin + | + |import org.gradle.api.provider.* + | + |interface MyCustomGradlePluginExtension { + | val versionProperty: Property<String> + | val versionProvider: Provider<String> + |} + | + """.trimMargin() + ) + } + + config() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt new file mode 100644 index 00000000..23a6744c --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt @@ -0,0 +1,247 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.assertions.withClue +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.paths.shouldBeAFile +import io.kotest.matchers.sequences.shouldNotBeEmpty +import io.kotest.matchers.string.shouldContain +import io.kotest.matchers.string.shouldNotContain + +class KotlinMultiplatformFunctionalTest : FunSpec({ + + context("when dokkatoo generates all formats") { + val project = initKotlinMultiplatformProject() + + project.runner + .addArguments( + "clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + } + + test("expect all dokka workers are successful") { + project + .findFiles { it.name == "dokka-worker.log" } + .shouldBeSingleton { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + + context("expect HTML site is generated") { + + test("with expected HTML files") { + project.projectDir.resolve("build/dokka/html/index.html").shouldBeAFile() + project.projectDir.resolve("build/dokka/html/com/project/hello/Hello.html") + .shouldBeAFile() + } + + test("and dokka_parameters.json is generated") { + project.projectDir.resolve("build/dokka/html/dokka_parameters.json") + .shouldBeAFile() + } + + test("with element-list") { + project.projectDir.resolve("build/dokka/html/test/package-list").shouldBeAFile() + project.projectDir.resolve("build/dokka/html/test/package-list").toFile().readText() + .sortLines() + .shouldContain( /* language=text */ """ + |${'$'}dokka.format:html-v1 + |${'$'}dokka.linkExtension:html + |${'$'}dokka.location:com.project////PointingToDeclaration/test/com.project/index.html + |${'$'}dokka.location:com.project//goodbye/#kotlinx.serialization.json.JsonObject/PointingToDeclaration/test/com.project/goodbye.html + |${'$'}dokka.location:com.project/Hello///PointingToDeclaration/test/com.project/-hello/index.html + |${'$'}dokka.location:com.project/Hello/Hello/#/PointingToDeclaration/test/com.project/-hello/-hello.html + |${'$'}dokka.location:com.project/Hello/sayHello/#kotlinx.serialization.json.JsonObject/PointingToDeclaration/test/com.project/-hello/say-hello.html + |com.project + """.trimMargin() + ) + } + + test("expect no 'unknown class' message in HTML files") { + val htmlFiles = project.projectDir.toFile() + .resolve("build/dokka/html") + .walk() + .filter { it.isFile && it.extension == "html" } + + htmlFiles.shouldNotBeEmpty() + + htmlFiles.forEach { htmlFile -> + val relativePath = htmlFile.relativeTo(project.projectDir.toFile()) + withClue("$relativePath should not contain Error class: unknown class") { + htmlFile.useLines { lines -> + lines.shouldForAll { line -> line.shouldNotContain("Error class: unknown class") } + } + } + } + } + } + } +}) + + +private fun initKotlinMultiplatformProject( + config: GradleProjectTest.() -> Unit = {}, +): GradleProjectTest { + return gradleKtsProjectTest("kotlin-multiplatform-project") { + + settingsGradleKts += """ + | + |dependencyResolutionManagement { + | + | repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + | + | repositories { + | mavenCentral() + | + | // Declare the Node.js & Yarn download repositories + | exclusiveContent { + | forRepository { + | ivy("https://nodejs.org/dist/") { + | name = "Node Distributions at ${'$'}url" + | patternLayout { artifact("v[revision]/[artifact](-v[revision]-[classifier]).[ext]") } + | metadataSources { artifact() } + | content { includeModule("org.nodejs", "node") } + | } + | } + | filter { includeGroup("org.nodejs") } + | } + | + | exclusiveContent { + | forRepository { + | ivy("https://github.com/yarnpkg/yarn/releases/download") { + | name = "Node Distributions at ${'$'}url" + | patternLayout { artifact("v[revision]/[artifact](-v[revision]).[ext]") } + | metadataSources { artifact() } + | content { includeModule("com.yarnpkg", "yarn") } + | } + | } + | filter { includeGroup("com.yarnpkg") } + | } + | } + |} + | + """.trimMargin() + + buildGradleKts = """ + |plugins { + | kotlin("multiplatform") version "1.8.22" + | id("org.jetbrains.dokka.dokkatoo") version "${DokkatooConstants.DOKKATOO_VERSION}" + |} + | + |kotlin { + | jvm() + | js(IR) { + | browser() + | } + | + | sourceSets { + | commonMain { + | dependencies { + | implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.0") + | } + | } + | commonTest { + | dependencies { + | implementation(kotlin("test")) + | } + | } + | } + |} + | + |dependencies { + | // must manually add this dependency for aggregation to work + | //dokkatooPluginHtml("org.jetbrains.dokka:all-modules-page-plugin:1.8.10") + |} + | + |dokkatoo { + | dokkatooSourceSets.configureEach { + | externalDocumentationLinks { + | create("kotlinxSerialization") { + | url("https://kotlinlang.org/api/kotlinx.serialization/") + | } + | } + | } + |} + | + | + """.trimMargin() + + dir("src/commonMain/kotlin/") { + + createKotlinFile( + "Hello.kt", + """ + |package com.project + | + |import kotlinx.serialization.json.JsonObject + | + |/** The Hello class */ + |class Hello { + | /** prints `Hello` and [json] to the console */ + | fun sayHello(json: JsonObject) = println("Hello ${'$'}json") + |} + | + """.trimMargin() + ) + + createKotlinFile( + "goodbye.kt", + """ + |package com.project + | + |import kotlinx.serialization.json.JsonObject + | + |/** Should print `goodbye` and [json] to the console */ + |expect fun goodbye(json: JsonObject) + | + """.trimMargin() + ) + } + + dir("src/jvmMain/kotlin/") { + createKotlinFile( + "goodbyeJvm.kt", + """ + |package com.project + | + |import kotlinx.serialization.json.JsonObject + | + |/** JVM implementation - prints `goodbye` and [json] to the console */ + |actual fun goodbye(json: JsonObject) = println("[JVM] goodbye ${'$'}json") + | + """.trimMargin() + ) + } + + dir("src/jsMain/kotlin/") { + createKotlinFile( + "goodbyeJs.kt", + """ + |package com.project + | + |import kotlinx.serialization.json.JsonObject + | + |/** JS implementation - prints `goodbye` and [json] to the console */ + |actual fun goodbye(json: JsonObject) = println("[JS] goodbye ${'$'}json") + | + """.trimMargin() + ) + } + + config() + } +} diff --git a/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt new file mode 100644 index 00000000..cac20f69 --- /dev/null +++ b/dokka-runners/dokkatoo/modules/dokkatoo-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt @@ -0,0 +1,468 @@ +package org.jetbrains.dokka.dokkatoo + +import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKATOO_VERSION +import org.jetbrains.dokka.dokkatoo.utils.* +import io.kotest.core.spec.style.FunSpec +import io.kotest.inspectors.shouldForAll +import io.kotest.matchers.collections.shouldBeIn +import io.kotest.matchers.collections.shouldContainAll +import io.kotest.matchers.file.shouldBeAFile +import io.kotest.matchers.paths.shouldBeAFile +import io.kotest.matchers.paths.shouldNotExist +import io.kotest.matchers.string.shouldBeEmpty +import io.kotest.matchers.string.shouldContain +import org.gradle.testkit.runner.TaskOutcome.* + +class MultiModuleFunctionalTest : FunSpec({ + + context("when dokkatoo generates all formats") { + val project = initDokkatooProject("all-formats") + + project.runner + .addArguments( + "clean", + ":dokkatooGenerate", + "--stacktrace", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + } + + test("expect all dokka workers are successful") { + project + .findFiles { it.name == "dokka-worker.log" } + .shouldForAll { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + + context("expect HTML site is generated") { + + test("with expected HTML files") { + project.file("subproject/build/dokka/html/index.html").shouldBeAFile() + project.file("subproject/build/dokka/html/com/project/hello/Hello.html") + .shouldBeAFile() + } + + test("and dokka_parameters.json is generated") { + project.file("subproject/build/dokka/html/dokka_parameters.json") + .shouldBeAFile() + } + + test("with element-list") { + project.file("build/dokka/html/package-list").shouldBeAFile() + project.file("build/dokka/html/package-list").toFile().readText() + .shouldContain( /* language=text */ """ + |${'$'}dokka.format:html-v1 + |${'$'}dokka.linkExtension:html + | + |module:subproject-hello + |com.project.hello + |module:subproject-goodbye + |com.project.goodbye + """.trimMargin() + ) + } + } + } + + context("Gradle caching") { + + context("expect Dokkatoo is compatible with Gradle Build Cache") { + val project = initDokkatooProject("build-cache") + + test("expect clean is successful") { + project.runner.addArguments("clean").build { + output shouldContain "BUILD SUCCESSFUL" + } + } + + project.runner + .addArguments( + //"clean", + ":dokkatooGenerate", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect all dokka workers are successful") { + project + .findFiles { it.name == "dokka-worker.log" } + .shouldForAll { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + + context("when build cache is enabled") { + project.runner + .addArguments( + ":dokkatooGenerate", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContainAll listOf( + "BUILD SUCCESSFUL", + "24 actionable tasks: 24 up-to-date", + ) + } + + test("expect all dokkatoo tasks are up-to-date") { + tasks + .filter { task -> + task.name.contains("dokkatoo", ignoreCase = true) + } + .shouldForAll { task -> + task.outcome.shouldBeIn(FROM_CACHE, UP_TO_DATE, SKIPPED) + } + } + } + } + } + + context("Gradle Configuration Cache") { + val project = initDokkatooProject("config-cache") + + test("expect clean is successful") { + project.runner.addArguments("clean").build { + output shouldContain "BUILD SUCCESSFUL" + } + } + + project.runner + .addArguments( + //"clean", + ":dokkatooGenerate", + "--stacktrace", + "--no-build-cache", + "--configuration-cache", + ) + .forwardOutput() + .build { + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + } + + test("expect all dokka workers are successful") { + project + .findFiles { it.name == "dokka-worker.log" } + .shouldForAll { dokkaWorkerLog -> + dokkaWorkerLog.shouldBeAFile() + dokkaWorkerLog.readText().shouldNotContainAnyOf( + "[ERROR]", + "[WARN]", + ) + } + } + } + + + context("expect updates in subprojects re-run tasks") { + + val project = initDokkatooProject("submodule-update") + + test("expect clean is successful") { + project.runner.addArguments("clean").build { + output shouldContain "BUILD SUCCESSFUL" + } + } + + test("expect first build is successful") { + project.runner + .addArguments( + //"clean", + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + output shouldContain "BUILD SUCCESSFUL" + } + } + + context("and when a file in a subproject changes") { + + val helloAgainIndexHtml = + @Suppress("KDocUnresolvedReference") + project.createKotlinFile( + "subproject-hello/src/main/kotlin/HelloAgain.kt", + """ + |package com.project.hello + | + |/** Like [Hello], but again */ + |class HelloAgain { + | /** prints `Hello Again` to the console */ + | fun sayHelloAgain() = println("Hello Again") + |} + | + """.trimMargin() + ).toPath() + + context("expect Dokka re-generates the publication") { + project.runner + .addArguments( + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--build-cache", + ) + .forwardOutput() + .build { + + test("expect HelloAgain HTML file exists") { + helloAgainIndexHtml.shouldBeAFile() + } + + test("expect :subproject-goodbye tasks are up-to-date, because no files changed") { + shouldHaveTasksWithOutcome( + ":subproject-goodbye:dokkatooGenerateModuleHtml" to UP_TO_DATE, + ":subproject-goodbye:prepareDokkatooModuleDescriptorHtml" to UP_TO_DATE, + ) + } + + val successfulOutcomes = listOf(SUCCESS, FROM_CACHE) + test("expect :subproject-hello tasks should be re-run, since a file changed") { + shouldHaveTasksWithAnyOutcome( + ":subproject-hello:dokkatooGenerateModuleHtml" to successfulOutcomes, + ":subproject-hello:prepareDokkatooModuleDescriptorHtml" to successfulOutcomes, + ) + } + + test("expect aggregating tasks should re-run because the :subproject-hello Dokka Module changed") { + shouldHaveTasksWithAnyOutcome( + ":dokkatooGeneratePublicationHtml" to successfulOutcomes, + ) + } + + test("expect build is successful") { + output shouldContain "BUILD SUCCESSFUL" + } + + test("expect 5 tasks are run") { + output shouldContain "5 actionable tasks" + } + } + + context("and when the class is deleted") { + project.dir("subproject-hello") { + require(file("src/main/kotlin/HelloAgain.kt").toFile().delete()) { + "failed to delete HelloAgain.kt" + } + } + + project.runner + .addArguments( + ":dokkatooGeneratePublicationHtml", + "--stacktrace", + "--info", + "--build-cache", + ) + .forwardOutput() + .build { + + test("expect HelloAgain HTML file is now deleted") { + helloAgainIndexHtml.shouldNotExist() + + project.dir("build/dokka/html/") { + projectDir.toTreeString().shouldNotContainAnyOf( + "hello-again", + "-hello-again/", + "-hello-again.html", + ) + } + } + } + } + } + } + } + } + + context("logging") { + val project = initDokkatooProject("logging") + + test("expect no logs when built using --quiet log level") { + + project.runner + .addArguments( + "clean", + ":dokkatooGenerate", + "--no-configuration-cache", + "--no-build-cache", + "--quiet", + ) + .forwardOutput() + .build { + output.shouldBeEmpty() + } + } + + test("expect no Dokkatoo logs when built using lifecycle log level") { + + project.runner + .addArguments( + "clean", + ":dokkatooGenerate", + "--no-configuration-cache", + "--no-build-cache", + "--no-parallel", + // no logging option => lifecycle log level + ) + .forwardOutput() + .build { + + // projects are only configured the first time TestKit runs, and annoyingly there's no + // easy way to force Gradle to re-configure the projects - so only check conditionally. + if ("Configure project" in output) { + output shouldContain /*language=text*/ """ + ¦> Configure project : + ¦> Configure project :subproject-goodbye + ¦> Configure project :subproject-hello + ¦> Task :clean + """.trimMargin("¦") + } + + output.lines() + .filter { it.startsWith("> Task :") } + .shouldContainAll( + "> Task :clean", + "> Task :dokkatooGenerate", + "> Task :dokkatooGenerateModuleGfm", + "> Task :dokkatooGenerateModuleHtml", + "> Task :dokkatooGenerateModuleJavadoc", + "> Task :dokkatooGenerateModuleJekyll", + "> Task :dokkatooGeneratePublicationGfm", + "> Task :dokkatooGeneratePublicationHtml", + "> Task :dokkatooGeneratePublicationJavadoc", + "> Task :dokkatooGeneratePublicationJekyll", + "> Task :subproject-goodbye:clean", + "> Task :subproject-goodbye:dokkatooGenerateModuleGfm", + "> Task :subproject-goodbye:dokkatooGenerateModuleHtml", + "> Task :subproject-goodbye:dokkatooGenerateModuleJavadoc", + "> Task :subproject-goodbye:dokkatooGenerateModuleJekyll", + "> Task :subproject-goodbye:prepareDokkatooModuleDescriptorGfm", + "> Task :subproject-goodbye:prepareDokkatooModuleDescriptorHtml", + "> Task :subproject-goodbye:prepareDokkatooModuleDescriptorJavadoc", + "> Task :subproject-goodbye:prepareDokkatooModuleDescriptorJekyll", + "> Task :subproject-hello:clean", + "> Task :subproject-hello:dokkatooGenerateModuleGfm", + "> Task :subproject-hello:dokkatooGenerateModuleHtml", + "> Task :subproject-hello:dokkatooGenerateModuleJavadoc", + "> Task :subproject-hello:dokkatooGenerateModuleJekyll", + "> Task :subproject-hello:prepareDokkatooModuleDescriptorGfm", + "> Task :subproject-hello:prepareDokkatooModuleDescriptorHtml", + "> Task :subproject-hello:prepareDokkatooModuleDescriptorJavadoc", + "> Task :subproject-hello:prepareDokkatooModuleDescriptorJekyll", + ) + } + } + } +}) + +private fun initDokkatooProject( + testName: String, + config: GradleProjectTest.() -> Unit = {}, +): GradleProjectTest { + return gradleKtsProjectTest("multi-module-hello-goodbye/$testName") { + + settingsGradleKts += """ + | + |include(":subproject-hello") + |include(":subproject-goodbye") + | + """.trimMargin() + + buildGradleKts = """ + |plugins { + | // Kotlin plugin shouldn't be necessary here, but without it Dokka errors + | // with ClassNotFound KotlinPluginExtension... very weird + | kotlin("jvm") version "1.8.22" apply false + | id("org.jetbrains.dokka.dokkatoo") version "$DOKKATOO_VERSION" + |} + | + |dependencies { + | dokkatoo(project(":subproject-hello")) + | dokkatoo(project(":subproject-goodbye")) + | dokkatooPluginHtml( + | dokkatoo.versions.jetbrainsDokka.map { dokkaVersion -> + | "org.jetbrains.dokka:all-modules-page-plugin:${'$'}dokkaVersion" + | } + | ) + |} + | + """.trimMargin() + + dir("subproject-hello") { + buildGradleKts = """ + |plugins { + | kotlin("jvm") version "1.8.22" + | id("org.jetbrains.dokka.dokkatoo") version "$DOKKATOO_VERSION" + |} + | + """.trimMargin() + + createKotlinFile( + "src/main/kotlin/Hello.kt", + """ + |package com.project.hello + | + |/** The Hello class */ + |class Hello { + | /** prints `Hello` to the console */ + | fun sayHello() = println("Hello") + |} + | + """.trimMargin() + ) + + createKotlinFile("src/main/kotlin/HelloAgain.kt", "") + } + + dir("subproject-goodbye") { + + buildGradleKts = """ + |plugins { + | kotlin("jvm") version "1.8.22" + | id("org.jetbrains.dokka.dokkatoo") version "$DOKKATOO_VERSION" + |} + | + """.trimMargin() + + createKotlinFile( + "src/main/kotlin/Goodbye.kt", + """ + |package com.project.goodbye + | + |/** The Goodbye class */ + |class Goodbye { + | /** prints a goodbye message to the console */ + | fun sayHello() = println("Goodbye!") + |} + | + """.trimMargin() + ) + } + + config() + } +} diff --git a/dokka-runners/dokkatoo/settings.gradle.kts b/dokka-runners/dokkatoo/settings.gradle.kts new file mode 100644 index 00000000..497f3e34 --- /dev/null +++ b/dokka-runners/dokkatoo/settings.gradle.kts @@ -0,0 +1,95 @@ +rootProject.name = "dokkatoo" + +pluginManagement { + repositories { + mavenCentral() + gradlePluginPortal() + } +} + +@Suppress("UnstableApiUsage") +dependencyResolutionManagement { + + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + + repositories { + mavenCentral() + google() + + maven("https://www.jetbrains.com/intellij-repository/snapshots") + maven("https://www.jetbrains.com/intellij-repository/releases") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide") + maven("https://maven.pkg.jetbrains.space/kotlin/p/kotlin/kotlin-ide-plugin-dependencies") + maven("https://cache-redirector.jetbrains.com/intellij-dependencies") + maven("https://www.myget.org/F/rd-snapshots/maven/") + + ivy("https://github.com/") { + name = "GitHub Release" + patternLayout { + artifact("[organization]/[module]/archive/[revision].[ext]") + artifact("[organization]/[module]/archive/refs/tags/[revision].[ext]") + artifact("[organization]/[module]/archive/refs/tags/v[revision].[ext]") + } + metadataSources { artifact() } + } + } +} + +include( + ":examples", + + ":modules:docs", + ":modules:dokkatoo-plugin", + ":modules:dokkatoo-plugin-integration-tests", +) + + +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") +enableFeaturePreview("STABLE_CONFIGURATION_CACHE") + + +//if (file("./examples/build/tmp/prepareDokkaSource").exists()) { +// includeBuild("./examples/build/tmp/prepareDokkaSource") +//} + +// can only include one example project at a time https://github.com/gradle/gradle/issues/23939 +//@formatter:off +//includeBuild(file("./examples/multiplatform-example/dokkatoo" )) { name = "multiplatform-example" } +//includeBuild(file("./examples/kotlin-as-java-example/dokkatoo" )) { name = "kotlin-as-java-example" } +//includeBuild(file("./examples/versioning-multimodule-example/dokkatoo" )) { name = "versioning-multimodule-example" } +//includeBuild(file("./examples/custom-format-example/dokkatoo" )) { name = "custom-format-example" } +//includeBuild(file("./examples/gradle-example/dokkatoo" )) { name = "gradle-example" } +//includeBuild(file("./examples/library-publishing-example/dokkatoo" )) { name = "library-publishing-example" } +//includeBuild(file("./examples/multimodule-example/dokkatoo" )) { name = "multimodule-example" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-1/dokkatoo" )) { name = "it-multimodule-1" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-0/dokkatoo" )) { name = "it-multimodule-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-collector-0/dokkatoo" )) { name = "it-collector-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-multimodule-versioning-0/dokkatoo" )) { name = "it-multimodule-versioning-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-android-0/dokkatoo" )) { name = "it-android-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-basic/dokkatoo" )) { name = "it-basic" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-multiplatform-0/dokkatoo" )) { name = "it-multiplatform-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-js-ir-0/dokkatoo" )) { name = "it-js-ir-0" } +//includeBuild(file("./modules/dokkatoo-plugin-integration-tests/projects/it-basic-groovy/dokkatoo" )) { name = "it-basic-groovy" } +//@formatter:on + +//listOf( +// "examples", +// "modules/dokkatoo-plugin-integration-tests/projects", +//).forEach { exampleProjectDir -> +// file(exampleProjectDir) +// .walk() +// .filter { +// it.isDirectory +// && it.name == "dokkatoo" +// && ( +// it.resolve("settings.gradle.kts").exists() +// || +// it.resolve("settings.gradle").exists() +// ) +// }.forEach { file -> +// includeBuild(file) { +// name = file.parentFile.name +// println("$file $name") +// } +// } +//} |