From 0beee0ee90d52a03d4a117e832478ea405fbb173 Mon Sep 17 00:00:00 2001 From: IgnatBeresnev Date: Thu, 19 Oct 2023 14:35:33 +0200 Subject: [PATCH] Align gradle-plugin's formatting with the rest of the project --- dokka-runners/gradle-plugin/build.gradle.kts | 328 ++++++------ .../gradle-plugin/buildSrc/build.gradle.kts | 22 +- .../buildSrc/settings.gradle.kts | 30 +- .../conventions/android-setup.gradle.kts | 96 ++-- .../buildsrc/conventions/base.gradle.kts | 108 ++-- .../dokka-source-downloader.gradle.kts | 76 +-- .../dokkatoo-example-projects-base.gradle.kts | 18 +- .../dokkatoo-example-projects.gradle.kts | 200 ++++---- .../gradle-plugin-variants.gradle.kts | 56 ++- .../buildsrc/conventions/java-base.gradle.kts | 16 +- .../kotlin-gradle-plugin.gradle.kts | 40 +- .../conventions/maven-publish-test.gradle.kts | 120 ++--- .../conventions/maven-publishing.gradle.kts | 136 ++--- .../settings/DokkaSourceDownloaderSettings.kt | 12 +- .../settings/DokkaTemplateProjectSettings.kt | 122 ++--- .../DokkatooExampleProjectsSettings.kt | 78 +-- .../settings/MavenPublishTestSettings.kt | 14 +- .../settings/MavenPublishingSettings.kt | 88 ++-- .../buildsrc/tasks/SetupDokkaProjects.kt | 92 ++-- .../tasks/UpdateDokkatooExampleProjects.kt | 46 +- .../src/main/kotlin/buildsrc/utils/gradle.kt | 60 +-- .../main/kotlin/buildsrc/utils/intellij.kt | 58 ++- .../src/main/kotlin/buildsrc/utils/strings.kt | 18 +- .../gradle-plugin/gradle/libs.versions.toml | 48 ++ .../src/main/kotlin/DokkatooBasePlugin.kt | 355 ------------- .../src/main/kotlin/DokkatooExtension.kt | 130 ----- .../kotlin/adapters/DokkatooAndroidAdapter.kt | 214 -------- .../kotlin/adapters/DokkatooJavaAdapter.kt | 40 -- .../kotlin/adapters/DokkatooKotlinAdapter.kt | 459 ----------------- .../DokkatooConfigurationAttributes.kt | 59 --- .../src/main/kotlin/dokka/DokkaPublication.kt | 122 ----- .../DokkaExternalDocumentationLinkSpec.kt | 120 ----- .../DokkaGeneratorParametersSpec.kt | 93 ---- .../parameters/DokkaModuleDescriptionSpec.kt | 49 -- .../parameters/DokkaPackageOptionsSpec.kt | 84 ---- .../dokka/parameters/DokkaSourceLinkSpec.kt | 106 ---- .../dokka/parameters/DokkaSourceSetIdSpec.kt | 61 --- .../dokka/parameters/DokkaSourceSetSpec.kt | 366 -------------- .../HasConfigurableVisibilityModifiers.kt | 14 - .../kotlin/dokka/parameters/KotlinPlatform.kt | 54 -- .../dokka/parameters/VisibilityModifier.kt | 42 -- .../builders/DokkaParametersBuilder.kt | 77 --- .../builders/DokkaSourceSetBuilder.kt | 112 ----- .../plugins/DokkaHtmlPluginParameters.kt | 129 ----- .../plugins/DokkaPluginParametersBuilder.kt | 232 --------- .../DokkaVersioningPluginParameters.kt | 101 ---- .../DokkatooFormatDependencyContainers.kt | 152 ------ .../kotlin/formats/DokkatooFormatPlugin.kt | 174 ------- .../kotlin/formats/DokkatooFormatTasks.kt | 105 ---- .../main/kotlin/formats/DokkatooGfmPlugin.kt | 14 - .../main/kotlin/formats/DokkatooHtmlPlugin.kt | 72 --- .../kotlin/formats/DokkatooJavadocPlugin.kt | 14 - .../kotlin/formats/DokkatooJekyllPlugin.kt | 14 - .../src/main/kotlin/internal/LoggerAdapter.kt | 65 --- .../src/main/kotlin/internal/uriUtils.kt | 9 - .../dokka/dokkatoo/DokkatooBasePlugin.kt | 359 +++++++++++++ .../dokka/dokkatoo/DokkatooExtension.kt | 134 +++++ .../dokka/dokkatoo}/DokkatooPlugin.kt | 28 +- .../adapters/DokkatooAndroidAdapter.kt | 218 ++++++++ .../dokkatoo/adapters/DokkatooJavaAdapter.kt | 45 ++ .../adapters/DokkatooKotlinAdapter.kt | 465 +++++++++++++++++ .../DokkatooConfigurationAttributes.kt | 63 +++ .../dokka/dokkatoo/dokka/DokkaPublication.kt | 125 +++++ .../DokkaExternalDocumentationLinkSpec.kt | 124 +++++ .../DokkaGeneratorParametersSpec.kt | 97 ++++ .../parameters/DokkaModuleDescriptionSpec.kt | 53 ++ .../parameters/DokkaPackageOptionsSpec.kt | 88 ++++ .../dokka/parameters/DokkaParametersKxs.kt | 64 +-- .../dokka/parameters/DokkaSourceLinkSpec.kt | 110 ++++ .../dokka/parameters/DokkaSourceSetIdSpec.kt | 65 +++ .../dokka/parameters/DokkaSourceSetSpec.kt | 378 ++++++++++++++ .../HasConfigurableVisibilityModifiers.kt | 18 + .../dokka/parameters/KotlinPlatform.kt | 58 +++ .../dokka/parameters/VisibilityModifier.kt | 46 ++ .../builders/DokkaModuleDescriptionBuilder.kt | 34 +- .../builders/DokkaParametersBuilder.kt | 79 +++ .../builders/DokkaSourceSetBuilder.kt | 116 +++++ .../plugins/DokkaHtmlPluginParameters.kt | 133 +++++ .../plugins/DokkaPluginParametersBaseSpec.kt | 20 +- .../plugins/DokkaPluginParametersBuilder.kt | 241 +++++++++ .../DokkaVersioningPluginParameters.kt | 105 ++++ .../DokkatooFormatDependencyContainers.kt | 158 ++++++ .../dokkatoo/formats/DokkatooFormatPlugin.kt | 183 +++++++ .../dokkatoo/formats/DokkatooFormatTasks.kt | 109 ++++ .../dokkatoo/formats/DokkatooGfmPlugin.kt | 18 + .../dokkatoo/formats/DokkatooHtmlPlugin.kt | 78 +++ .../dokkatoo/formats/DokkatooJavadocPlugin.kt | 18 + .../dokkatoo/formats/DokkatooJekyllPlugin.kt | 18 + .../dokkatoo}/internal/DokkatooInternalApi.kt | 18 +- .../dokka/dokkatoo/internal/LoggerAdapter.kt | 69 +++ .../dokkatoo}/internal/collectionsUtils.kt | 8 +- .../internal/gradleExtensionAccessors.kt | 6 +- .../dokkatoo}/internal/gradleTypealiases.kt | 8 +- .../dokka/dokkatoo}/internal/gradleUtils.kt | 118 ++--- .../internal/kotlinxSerializationUtils.kt | 22 +- .../dokka/dokkatoo}/internal/stringUtils.kt | 6 +- .../dokka/dokkatoo/internal/uriUtils.kt | 13 + .../dokkatoo/tasks/DokkatooGenerateTask.kt | 195 ++++++++ .../DokkatooPrepareModuleDescriptorTask.kt | 68 +++ .../dokka/dokkatoo}/tasks/DokkatooTask.kt | 20 +- .../tasks/LogHtmlPublicationLinkTask.kt | 160 ++++++ .../dokkatoo/workers/DokkaGeneratorWorker.kt | 81 +++ .../main/kotlin/tasks/DokkatooGenerateTask.kt | 187 ------- .../DokkatooPrepareModuleDescriptorTask.kt | 62 --- .../tasks/LogHtmlPublicationLinkTask.kt | 156 ------ .../kotlin/workers/DokkaGeneratorWorker.kt | 77 --- .../src/test/kotlin/DokkatooPluginTest.kt | 76 --- .../DokkaExternalDocumentationLinkSpecTest.kt | 102 ---- .../parameters/DokkaSourceLinkSpecTest.kt | 58 --- .../dokka/parameters/KotlinPlatformTest.kt | 37 -- .../builders/DokkaSourceSetBuilderTest.kt | 198 -------- .../dokka/dokkatoo/DokkatooPluginTest.kt | 82 +++ .../DokkaExternalDocumentationLinkSpecTest.kt | 107 ++++ .../parameters/DokkaSourceLinkSpecTest.kt | 62 +++ .../dokka/parameters/KotlinPlatformTest.kt | 41 ++ .../parameters/VisibilityModifierTest.kt | 14 +- .../DokkaModuleDescriptionBuilderTest.kt | 4 + .../builders/DokkaParametersBuilderTest.kt | 4 + .../builders/DokkaSourceSetBuilderTest.kt | 203 ++++++++ .../src/testFixtures/kotlin/fileTree.kt | 61 --- .../src/testFixtures/kotlin/files.kt | 6 - .../testFixtures/kotlin/kotestConditions.kt | 10 - .../kotlin/kotestGradleAssertions.kt | 130 ----- .../kotlin/kotestStringMatchers.kt | 65 --- .../dokkatoo/utils}/GradleTestKitUtils.kt | 163 +++--- .../dokkatoo/utils}/KotestProjectConfig.kt | 10 +- .../dokka/dokkatoo/utils/fileTree.kt | 66 +++ .../jetbrains/dokka/dokkatoo/utils/files.kt | 10 + .../dokkatoo/utils}/gradleRunnerUtils.kt | 22 +- .../utils}/kotestCollectionMatchers.kt | 10 +- .../dokka/dokkatoo/utils/kotestConditions.kt | 14 + .../dokkatoo/utils/kotestGradleAssertions.kt | 134 +++++ .../dokkatoo/utils/kotestStringMatchers.kt | 69 +++ .../utils}/samWithReceiverWorkarounds.kt | 26 +- .../dokka/dokkatoo/utils}/stringUtils.kt | 14 +- .../dokkatoo/utils/systemVariableProviders.kt | 44 ++ .../jetbrains/dokka/dokkatoo/utils/text.kt | 28 ++ .../kotlin/systemVariableProviders.kt | 40 -- .../src/testFixtures/kotlin/text.kt | 24 - .../GradlePluginProjectIntegrationTest.kt | 110 ---- .../kotlin/MultiModuleFunctionalTest.kt | 468 ----------------- .../dokkatoo}/DokkatooPluginFunctionalTest.kt | 162 +++--- .../GradlePluginProjectIntegrationTest.kt | 114 +++++ .../KotlinMultiplatformFunctionalTest.kt | 180 +++---- .../dokkatoo/MultiModuleFunctionalTest.kt | 472 ++++++++++++++++++ 145 files changed, 7271 insertions(+), 6811 deletions(-) create mode 100644 dokka-runners/gradle-plugin/gradle/libs.versions.toml delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/DokkatooBasePlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/DokkatooExtension.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/DokkaPublication.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/internal/LoggerAdapter.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/internal/uriUtils.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooBasePlugin.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooExtension.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/DokkatooPlugin.kt (54%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooAndroidAdapter.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooJavaAdapter.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooKotlinAdapter.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/distributions/DokkatooConfigurationAttributes.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/DokkaPublication.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaModuleDescriptionSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaPackageOptionsSpec.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/parameters/DokkaParametersKxs.kt (58%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetSpec.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatform.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifier.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt (56%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilder.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilder.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/plugins/DokkaPluginParametersBaseSpec.kt (71%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatDependencyContainers.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatPlugin.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatTasks.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooGfmPlugin.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooHtmlPlugin.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJavadocPlugin.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJekyllPlugin.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/DokkatooInternalApi.kt (78%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/LoggerAdapter.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/collectionsUtils.kt (50%) rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/gradleExtensionAccessors.kt (66%) rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/gradleTypealiases.kt (80%) rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/gradleUtils.kt (64%) rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/kotlinxSerializationUtils.kt (60%) rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/internal/stringUtils.kt (58%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/uriUtils.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooGenerateTask.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooPrepareModuleDescriptorTask.kt rename dokka-runners/gradle-plugin/src/main/kotlin/{ => org/jetbrains/dokka/dokkatoo}/tasks/DokkatooTask.kt (65%) create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/LogHtmlPublicationLinkTask.kt create mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/workers/DokkaGeneratorWorker.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt delete mode 100644 dokka-runners/gradle-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt delete mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/DokkatooPluginTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt create mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginTest.kt create mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt create mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpecTest.kt create mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatformTest.kt rename dokka-runners/gradle-plugin/src/test/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/parameters/VisibilityModifierTest.kt (50%) rename dokka-runners/gradle-plugin/src/test/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt (59%) rename dokka-runners/gradle-plugin/src/test/kotlin/{ => org/jetbrains/dokka/dokkatoo}/dokka/parameters/builders/DokkaParametersBuilderTest.kt (58%) create mode 100644 dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/fileTree.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/files.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestConditions.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/GradleTestKitUtils.kt (57%) rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/KotestProjectConfig.kt (57%) create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/fileTree.kt create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/files.kt rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/gradleRunnerUtils.kt (70%) rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/kotestCollectionMatchers.kt (74%) create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestConditions.kt create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestGradleAssertions.kt create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestStringMatchers.kt rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/samWithReceiverWorkarounds.kt (83%) rename dokka-runners/gradle-plugin/src/testFixtures/kotlin/{ => org/jetbrains/dokka/dokkatoo/utils}/stringUtils.kt (65%) create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/systemVariableProviders.kt create mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/text.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/systemVariableProviders.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFixtures/kotlin/text.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt delete mode 100644 dokka-runners/gradle-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt rename dokka-runners/gradle-plugin/src/testFunctional/kotlin/{ => org/jetbrains/dokka/dokkatoo}/DokkatooPluginFunctionalTest.kt (62%) create mode 100644 dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/GradlePluginProjectIntegrationTest.kt rename dokka-runners/gradle-plugin/src/testFunctional/kotlin/{ => org/jetbrains/dokka/dokkatoo}/KotlinMultiplatformFunctionalTest.kt (60%) create mode 100644 dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/MultiModuleFunctionalTest.kt diff --git a/dokka-runners/gradle-plugin/build.gradle.kts b/dokka-runners/gradle-plugin/build.gradle.kts index 8bb60f576f..bc6e83234a 100644 --- a/dokka-runners/gradle-plugin/build.gradle.kts +++ b/dokka-runners/gradle-plugin/build.gradle.kts @@ -1,216 +1,220 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + @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") + buildsrc.conventions.`kotlin-gradle-plugin` + kotlin("plugin.serialization") - dev.adamko.kotlin.`binary-compatibility-validator` + dev.adamko.kotlin.`binary-compatibility-validator` - dev.adamko.`dokkatoo-html` - buildsrc.conventions.`maven-publishing` + dev.adamko.`dokkatoo-html` + buildsrc.conventions.`maven-publishing` - `java-test-fixtures` - `jvm-test-suite` - `test-report-aggregation` - buildsrc.conventions.`maven-publish-test` + `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) + // 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) + 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) + implementation(platform(libs.kotlinxSerialization.bom)) + implementation(libs.kotlinxSerialization.json) - testFixturesImplementation(gradleApi()) - testFixturesImplementation(gradleTestKit()) + testFixturesImplementation(gradleApi()) + testFixturesImplementation(gradleTestKit()) - testFixturesCompileOnly(libs.kotlin.dokkaCore) - testFixturesImplementation(platform(libs.kotlinxSerialization.bom)) - testFixturesImplementation(libs.kotlinxSerialization.json) + testFixturesCompileOnly(libs.kotlin.dokkaCore) + testFixturesImplementation(platform(libs.kotlinxSerialization.bom)) + testFixturesImplementation(libs.kotlinxSerialization.json) - testFixturesCompileOnly(libs.kotlin.dokkaCore) + 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) + 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 + // 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" + 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", + ) } - } - 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. + 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().configureEach { - useJUnitJupiter() + withType().configureEach { + useJUnitJupiter() - dependencies { - implementation(project.dependencies.gradleTestKit()) + dependencies { + implementation(project.dependencies.gradleTestKit()) - implementation(project.dependencies.testFixtures(project())) + implementation(project.dependencies.testFixtures(project())) - implementation(project.dependencies.platform(libs.kotlinxSerialization.bom)) - implementation(libs.kotlinxSerialization.json) - } + 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) - } + 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" - } + /** 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) + /** 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) - } + targets.all { + testTask.configure { + shouldRunAfter(test) + } + } } - } - tasks.check { dependsOn(test, testFunctional) } + 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")) + group = LifecycleBasePlugin.VERIFICATION_GROUP + destinationDirectory.set(layout.buildDirectory.dir("reports/tests/aggregated")) - dependsOn(tasks.withType()) + dependsOn(tasks.withType()) - // 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") + // 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") - } + doLast { + logger.lifecycle("Aggregated test report: file://${destinationDirectory.asFile.get()}/index.html") + } } binaryCompatibilityValidator { - ignoredMarkers.add("org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi") + ignoredMarkers.add("org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi") } val dokkatooVersion = provider { project.version.toString() } val dokkatooConstantsProperties = objects.mapProperty().apply { - put("DOKKATOO_VERSION", dokkatooVersion) - put("DOKKA_VERSION", libs.versions.kotlin.dokka) + put("DOKKATOO_VERSION", dokkatooVersion) + put("DOKKA_VERSION", libs.versions.kotlin.dokka) } val buildConfigFileContents: Provider = - dokkatooConstantsProperties.map { constants -> + dokkatooConstantsProperties.map { constants -> - val vals = constants.entries - .sortedBy { it.key } - .joinToString("\n") { (k, v) -> - """const val $k = "$v"""" - }.prependIndent(" ") + val vals = constants.entries + .sortedBy { it.key } + .joinToString("\n") { (k, v) -> + """const val $k = "$v"""" + }.prependIndent(" ") - resources.text.fromString( - """ + resources.text.fromString( + """ |package org.jetbrains.dokka.dokkatoo.internal | |@DokkatooInternalApi @@ -219,36 +223,36 @@ val buildConfigFileContents: Provider = |} | """.trimMargin() - ) - } + ) + } val generateDokkatooConstants by tasks.registering(Sync::class) { - group = project.name + group = project.name - val buildConfigFileContents = buildConfigFileContents + val buildConfigFileContents = buildConfigFileContents - from(buildConfigFileContents) { - rename { "DokkatooConstants.kt" } - into("dev/adamko/dokkatoo/internal/") - } + from(buildConfigFileContents) { + rename { "DokkatooConstants.kt" } + into("dev/adamko/dokkatoo/internal/") + } - into(layout.buildDirectory.dir("generated-source/main/kotlin/")) + into(layout.buildDirectory.dir("generated-source/main/kotlin/")) } kotlin.sourceSets.main { - kotlin.srcDir(generateDokkatooConstants.map { it.destinationDir }) + 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") + 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/gradle-plugin/buildSrc/build.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/build.gradle.kts index 832a98b95b..7188318da6 100644 --- a/dokka-runners/gradle-plugin/buildSrc/build.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/build.gradle.kts @@ -1,19 +1,23 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + import org.gradle.kotlin.dsl.support.expectedKotlinDslPluginsVersion plugins { - `kotlin-dsl` + `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") + 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)) - } + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/settings.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/settings.gradle.kts index 9f404a2bf9..ce1fa048cd 100644 --- a/dokka-runners/gradle-plugin/buildSrc/settings.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/settings.gradle.kts @@ -5,27 +5,27 @@ rootProject.name = "buildSrc" pluginManagement { - repositories { - mavenCentral() - gradlePluginPortal() - google() - } + repositories { + mavenCentral() + gradlePluginPortal() + google() + } } @Suppress("UnstableApiUsage") dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) + repositoriesMode.set(RepositoriesMode.PREFER_SETTINGS) - repositories { - mavenCentral() - gradlePluginPortal() - google() - } + repositories { + mavenCentral() + gradlePluginPortal() + google() + } - versionCatalogs { - create("libs") { - from(files("../gradle/libs.versions.toml")) + versionCatalogs { + create("libs") { + from(files("../gradle/libs.versions.toml")) + } } - } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts index ed22d79948..d1426621e7 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/android-setup.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import org.jetbrains.kotlin.util.suffixIfNot @@ -8,71 +12,71 @@ import org.jetbrains.kotlin.util.suffixIfNot */ plugins { - base - id("buildsrc.conventions.base") + base + id("buildsrc.conventions.base") } val androidSdkDirPath: Provider = 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 } + // 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 localPropertiesFile = temporaryDir.resolve("local.properties") + outputs.file(localPropertiesFile).withPropertyName("localPropertiesFile") - val androidSdkDirPath = androidSdkDirPath - inputs.property("androidSdkDirPath", androidSdkDirPath) + val androidSdkDirPath = androidSdkDirPath + inputs.property("androidSdkDirPath", androidSdkDirPath) - doLast { - localPropertiesFile.apply { - parentFile.mkdirs() - createNewFile() - writeText( - """ + 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") - } - ) - } - } + // 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/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts index 60bfa2fe55..5e3d0f3dc0 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/base.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import java.time.Duration @@ -8,67 +12,67 @@ import org.gradle.api.tasks.testing.logging.TestLogEvent */ plugins { - base + base } if (project != rootProject) { - project.version = rootProject.version - project.group = rootProject.group + project.version = rootProject.version + project.group = rootProject.group } tasks.withType().configureEach { - // https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives - isPreserveFileTimestamps = false - isReproducibleFileOrder = true + // https://docs.gradle.org/current/userguide/working_with_files.html#sec:reproducible_archives + isPreserveFileTimestamps = false + isReproducibleFileOrder = true } tasks.withType().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, - ) - } + 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().configureEach { - includeEmptyDirs = false + 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 + 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()) - mustRunAfter(tasks.withType()) + mustRunAfter(tasks.withType()) + mustRunAfter(tasks.withType()) - val cssFiles = layout.buildDirectory.asFileTree.matching { - include("reports/**/css/base-style.css") - include("reports/**/css/style.css") - } + val cssFiles = layout.buildDirectory.asFileTree.matching { + include("reports/**/css/base-style.css") + include("reports/**/css/style.css") + } - outputs.files(cssFiles.files) + outputs.files(cssFiles.files) - doLast { - cssFiles.forEach { cssFile -> - val fileContent = cssFile.readText() + 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 + """ + if ("/* Dark mode */" in fileContent) { + return@forEach + } else { + when (cssFile.name) { + "base-style.css" -> cssFile.writeText( + fileContent + """ /* Dark mode */ @media (prefers-color-scheme: dark) { @@ -101,10 +105,10 @@ val updateTestReportCss by tasks.registering { } } """.trimIndent() - ) + ) - "style.css" -> cssFile.writeText( - fileContent + """ + "style.css" -> cssFile.writeText( + fileContent + """ /* Dark mode */ @media (prefers-color-scheme: dark) { @@ -133,23 +137,23 @@ val updateTestReportCss by tasks.registering { } } """.trimIndent() - ) + ) + } + } } - } } - } } tasks.withType().configureEach { - finalizedBy(updateTestReportCss) + finalizedBy(updateTestReportCss) } tasks.withType().configureEach { - finalizedBy(updateTestReportCss) + 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) + // prevent warning + // Task ':validatePlugins' uses this output of task ':updateTestReportCss' without declaring an explicit or implicit dependency. + mustRunAfter(updateTestReportCss) } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts index 69e384e109..445930c813 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokka-source-downloader.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import buildsrc.settings.DokkaSourceDownloaderSettings @@ -8,61 +12,61 @@ import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE import org.gradle.kotlin.dsl.support.serviceOf plugins { - id("buildsrc.conventions.base") + id("buildsrc.conventions.base") } val dsdExt: DokkaSourceDownloaderSettings = extensions.create( - DokkaSourceDownloaderSettings.EXTENSION_NAME + DokkaSourceDownloaderSettings.EXTENSION_NAME ) val kotlinDokkaSource by configurations.creating { - asConsumer() - attributes { - attribute(USAGE_ATTRIBUTE, objects.named("externals-dokka-src")) - } + 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")) - } + asProvider() + attributes { + attribute(USAGE_ATTRIBUTE, objects.named("externals-dokka-src")) + } } dependencies { - kotlinDokkaSource(dsdExt.dokkaVersion.map { "kotlin:dokka:$it@zip" }) + 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" + group = "dokka setup" + description = "Download & unpack Kotlin Dokka source code" - inputs.property("dokkaVersion", dsdExt.dokkaVersion).optional(false) + inputs.property("dokkaVersion", dsdExt.dokkaVersion).optional(false) - val archives = serviceOf() + val archives = serviceOf() - from( - kotlinDokkaSource.incoming - .artifacts - .resolvedArtifacts - .map { artifacts -> - artifacts.map { archives.zipTree(it.file) } - } - ) { - // drop the first dir (dokka-$version) - eachFile { - relativePath = relativePath.dropDirectories(1) + 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) + into(temporaryDir) - exclude( - "*.github", - "*.gradle", - "**/gradlew", - "**/gradlew.bat", - "**/gradle/wrapper/gradle-wrapper.jar", - "**/gradle/wrapper/gradle-wrapper.properties", - ) + exclude( + "*.github", + "*.gradle", + "**/gradlew", + "**/gradlew.bat", + "**/gradle/wrapper/gradle-wrapper.jar", + "**/gradle/wrapper/gradle-wrapper.properties", + ) } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts index 5c2c45fa39..7196a0aa9a 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects-base.gradle.kts @@ -1,27 +1,31 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import buildsrc.utils.asConsumer import buildsrc.utils.asProvider plugins { - id("buildsrc.conventions.base") + id("buildsrc.conventions.base") } val exampleProjectsAttribute: Attribute = - Attribute.of("example-projects", String::class.java) + Attribute.of("example-projects", String::class.java) dependencies.attributesSchema { - attribute(exampleProjectsAttribute) + attribute(exampleProjectsAttribute) } val exampleProjects by configurations.registering { - asConsumer() - attributes { attribute(exampleProjectsAttribute, "dokka") } + asConsumer() + attributes { attribute(exampleProjectsAttribute, "dokka") } } val exampleProjectsElements by configurations.registering { - asProvider() - attributes { attribute(exampleProjectsAttribute, "dokka") } + asProvider() + attributes { attribute(exampleProjectsAttribute, "dokka") } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts index c6994a8376..768ae68f7a 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/dokkatoo-example-projects.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import buildsrc.settings.* @@ -5,156 +9,156 @@ 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") + 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() val dokkaTemplateProjectSettings = - extensions.create( - DokkaTemplateProjectSettings.EXTENSION_NAME, - { project.copySpec() } - ).apply { - this.destinationBaseDir.convention(layout.projectDirectory) - } + extensions.create( + 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 - }) - } + prepareDokkaSource.flatMap { + layout.dir(providers.provider { + it.destinationDir + }) + } ) tasks.withType().configureEach { - dependsOn(prepareDokkaSource) + dependsOn(prepareDokkaSource) - dokkaSourceDir.convention(dokkaTemplateProjectSettings.dokkaSourceDir) - destinationBaseDir.convention(dokkaTemplateProjectSettings.destinationBaseDir) + dokkaSourceDir.convention(dokkaTemplateProjectSettings.dokkaSourceDir) + destinationBaseDir.convention(dokkaTemplateProjectSettings.destinationBaseDir) - templateProjects.addAllLater(provider { - dokkaTemplateProjectSettings.templateProjects - }) + templateProjects.addAllLater(provider { + dokkaTemplateProjectSettings.templateProjects + }) } val setupDokkaTemplateProjects by tasks.registering(SetupDokkaProjects::class) fun createDokkatooExampleProjectsSettings( - projectDir: Directory = project.layout.projectDirectory + projectDir: Directory = project.layout.projectDirectory ): DokkatooExampleProjectsSettings { - return extensions.create( - 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) - } - } + return extensions.create( + 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" + exampleProjects.configureEach { + gradlePropertiesContent.add( + mavenPublishTestExtension.testMavenRepoPath.map { testMavenRepoPath -> + "testMavenRepo=$testMavenRepoPath" + } + ) } - ) } - } } val dokkatooExampleProjectsSettings = createDokkatooExampleProjectsSettings() val updateDokkatooExamplesGradleProperties by tasks.registering( - UpdateDokkatooExampleProjects::class + UpdateDokkatooExampleProjects::class ) { - group = DokkatooExampleProjectsSettings.TASK_GROUP + group = DokkatooExampleProjectsSettings.TASK_GROUP - mustRunAfter(tasks.withType()) + mustRunAfter(tasks.withType()) - exampleProjects.addAllLater(providers.provider { - dokkatooExampleProjectsSettings.exampleProjects - }) + 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" + group = DokkatooExampleProjectsSettings.TASK_GROUP + description = "Update the Gradle build files in the Dokkatoo examples" - outputs.upToDateWhen { false } + outputs.upToDateWhen { false } - mustRunAfter(tasks.withType()) - shouldRunAfter(updateDokkatooExamplesGradleProperties) + mustRunAfter(tasks.withType()) + shouldRunAfter(updateDokkatooExamplesGradleProperties) - val dokkatooVersion = dokkatooVersion + val dokkatooVersion = dokkatooVersion - val dokkatooDependencyVersionMatcher = """ + val dokkatooDependencyVersionMatcher = """ \"dev\.adamko\.dokkatoo\:dokkatoo\-plugin\:([^"]+?)\" """.trimIndent().toRegex() - val dokkatooPluginVersionMatcher = """ + 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()) + 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()) + } + ) } - .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, - ) + group = DokkatooExampleProjectsSettings.TASK_GROUP + description = "lifecycle task for all '${DokkatooExampleProjectsSettings.TASK_GROUP}' tasks" + dependsOn( + setupDokkaTemplateProjects, + updateDokkatooExamplesGradleProperties, + updateDokkatooExamplesBuildFiles, + ) } tasks.assemble { - dependsOn(updateDokkatooExamples) - dependsOn(setupDokkaTemplateProjects) + dependsOn(updateDokkatooExamples) + dependsOn(setupDokkaTemplateProjects) } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts index 1d9fc43b01..c8231c5ba4 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/gradle-plugin-variants.gradle.kts @@ -1,43 +1,47 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import org.gradle.api.attributes.plugin.GradlePluginApiVersion.GRADLE_PLUGIN_API_VERSION_ATTRIBUTE plugins { - id("buildsrc.conventions.base") - `java-gradle-plugin` + id("buildsrc.conventions.base") + `java-gradle-plugin` } fun registerGradleVariant(name: String, gradleVersion: String) { - val variantSources = sourceSets.create(name) + val variantSources = sourceSets.create(name) - java { - registerFeature(variantSources.name) { - usingSourceSet(variantSources) - capability("${project.group}", "${project.name}", "${project.version}") + 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)) - } + withJavadocJar() + withSourcesJar() + } } - tasks.named(variantSources.processResourcesTaskName) { - val copyPluginDescriptors = rootSpec.addChild() - copyPluginDescriptors.into("META-INF/gradle-plugins") + configurations + .matching { it.isCanBeConsumed && it.name.startsWith(variantSources.name) } + .configureEach { + attributes { + attribute(GRADLE_PLUGIN_API_VERSION_ATTRIBUTE, objects.named(gradleVersion)) + } + } + + tasks.named(variantSources.processResourcesTaskName) { + val copyPluginDescriptors = rootSpec.addChild() + copyPluginDescriptors.into("META-INF/gradle-plugins") // copyPluginDescriptors.into(tasks.pluginDescriptors.flatMap { it.outputDirectory }) - copyPluginDescriptors.from(tasks.pluginDescriptors) - } + copyPluginDescriptors.from(tasks.pluginDescriptors) + } - dependencies { - add(variantSources.compileOnlyConfigurationName, gradleApi()) - } + dependencies { + add(variantSources.compileOnlyConfigurationName, gradleApi()) + } } registerGradleVariant("gradle7", "7.6") diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts index 203b80f2b1..9dae3a3b7d 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/java-base.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import org.gradle.api.JavaVersion @@ -7,13 +11,13 @@ import org.gradle.kotlin.dsl.getByType import org.gradle.kotlin.dsl.`java-base` plugins { - id("buildsrc.conventions.base") - `java` + id("buildsrc.conventions.base") + `java` } extensions.getByType().apply { - toolchain { - languageVersion.set(JavaLanguageVersion.of(11)) - } - withSourcesJar() + toolchain { + languageVersion.set(JavaLanguageVersion.of(11)) + } + withSourcesJar() } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts index 4174088ab9..dbae74af34 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/kotlin-gradle-plugin.gradle.kts @@ -1,37 +1,41 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions plugins { - id("buildsrc.conventions.base") - id("buildsrc.conventions.java-base") - id("org.gradle.kotlin.kotlin-dsl") - id("com.gradle.plugin-publish") + 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) + enableStricterValidation.set(true) } val createJavadocJarReadme by tasks.registering(Sync::class) { - description = "generate a readme.txt for the Javadoc JAR" - from( - resources.text.fromString( - """ + 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) + ) + ) { + rename { "readme.txt" } + } + into(temporaryDir) } // The Gradle Publish Plugin enables the Javadoc JAR in afterEvaluate, so find it lazily tasks.withType() - .matching { it.name == "javadocJar" } - .configureEach { - from(createJavadocJarReadme) - } + .matching { it.name == "javadocJar" } + .configureEach { + from(createJavadocJarReadme) + } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts index 38678b5bab..baa1073b9b 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publish-test.gradle.kts @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import buildsrc.settings.MavenPublishTestSettings @@ -7,87 +11,87 @@ import buildsrc.utils.* /** Utility for publishing a project to a local Maven directory for use in integration tests. */ plugins { - base + base } val Gradle.rootGradle: Gradle get() = generateSequence(gradle) { it.parent }.last() val mavenPublishTestExtension = extensions.create( - "mavenPublishTest", - gradle.rootGradle.rootProject.layout.buildDirectory.dir("test-maven-repo"), + "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." + group = PublishingPlugin.PUBLISH_TASK_GROUP + description = "Publishes all Maven publications to the test Maven repository." } plugins.withType().all { - extensions - .getByType() - .publications - .withType().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(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) + extensions + .getByType() + .publications + .withType().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(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) + } } - } - - publishToTestMavenRepo.configure { - dependsOn(installTask) - } - - tasks.check { - mustRunAfter(installTask) - } - } } val testMavenPublication by configurations.registering { - asConsumer() - attributes { - attribute(MavenPublishTestSettings.attribute, "testMavenRepo") - } + 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) + asProvider() + extendsFrom(testMavenPublication.get()) + attributes { + attribute(MavenPublishTestSettings.attribute, "testMavenRepo") + } + outgoing { + artifact(mavenPublishTestExtension.testMavenRepo) { + builtBy(publishToTestMavenRepo) + } } - } } dependencies { - attributesSchema { - attribute(MavenPublishTestSettings.attribute) - } + attributesSchema { + attribute(MavenPublishTestSettings.attribute) + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts index 7af7b69f91..5ad6982631 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/conventions/maven-publishing.gradle.kts @@ -1,94 +1,98 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.conventions import buildsrc.settings.MavenPublishingSettings plugins { - `maven-publish` - signing + `maven-publish` + signing } val mavenPublishing = - extensions.create(MavenPublishingSettings.EXTENSION_NAME, project) + extensions.create(MavenPublishingSettings.EXTENSION_NAME, project) //region POM convention publishing { - publications.withType().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") + publications.withType().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" + 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 + 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 + // com.gradle.plugin-publish automatically adds a Javadoc jar } signing { - logger.info("maven-publishing.gradle.kts enabled signing for ${project.path}") + 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 + 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().any { - it.repository.name == "SonatypeRelease" + if (!keyId.isNullOrBlank() && !key.isNullOrBlank() && !password.isNullOrBlank()) { + useInMemoryPgpKeys(keyId, key, password) } - }) + + setRequired({ + gradle.taskGraph.allTasks.filterIsInstance().any { + it.repository.name == "SonatypeRelease" + } + }) } //afterEvaluate { @@ -103,20 +107,20 @@ signing { //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().configureEach { - val signingTasks = tasks.withType() - mustRunAfter(signingTasks) + val signingTasks = tasks.withType() + mustRunAfter(signingTasks) } //endregion //region publishing logging tasks.withType().configureEach { - val publicationGAV = provider { publication?.run { "$group:$artifactId:$version" } } - doLast("log publication GAV") { - if (publicationGAV.isPresent) { - logger.lifecycle("[task: ${path}] ${publicationGAV.get()}") + val publicationGAV = provider { publication?.run { "$group:$artifactId:$version" } } + doLast("log publication GAV") { + if (publicationGAV.isPresent) { + logger.lifecycle("[task: ${path}] ${publicationGAV.get()}") + } } - } } //endregion @@ -124,14 +128,14 @@ tasks.withType().configureEach { //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) + extensions.configure(configure) val Project.publishing: PublishingExtension - get() = extensions.getByType() + get() = extensions.getByType() fun Project.signing(configure: SigningExtension.() -> Unit): Unit = - extensions.configure(configure) + extensions.configure(configure) val Project.signing: SigningExtension - get() = extensions.getByType() + get() = extensions.getByType() //endregion diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt index c3f9906c79..51c8b46301 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaSourceDownloaderSettings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.settings import org.gradle.api.plugins.ExtensionAware @@ -5,9 +9,9 @@ import org.gradle.api.provider.Property abstract class DokkaSourceDownloaderSettings : ExtensionAware { - abstract val dokkaVersion: Property + abstract val dokkaVersion: Property - companion object { - const val EXTENSION_NAME = "dokkaSourceDownload" - } + companion object { + const val EXTENSION_NAME = "dokkaSourceDownload" + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt index 7bacafb9b2..046dc0e437 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkaTemplateProjectSettings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.settings import buildsrc.utils.adding @@ -22,75 +26,75 @@ import org.gradle.kotlin.dsl.* private typealias TemplateProjectsContainer = NamedDomainObjectContainer abstract class DokkaTemplateProjectSettings @Inject constructor( - private val objects: ObjectFactory, - private val copySpecs: () -> CopySpec + 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(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() + /** 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(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) - } + 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 { + /** + * 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 + @get:Input + abstract val sourcePath: Property - @get:Input - @get:Optional - abstract val destinationPath: Property + @get:Input + @get:Optional + abstract val destinationPath: Property - @get:Input - abstract val additionalPaths: SetProperty + @get:Input + abstract val additionalPaths: SetProperty - @get:InputFiles - abstract val additionalFiles: ConfigurableFileCollection + @get:InputFiles + abstract val additionalFiles: ConfigurableFileCollection - fun configureCopy(configure: CopySpec.() -> Unit) { - copySpec.configure() - } + fun configureCopy(configure: CopySpec.() -> Unit) { + copySpec.configure() + } - @Input - override fun getName(): String = named - } + @Input + override fun getName(): String = named + } - companion object { - const val EXTENSION_NAME = "dokkaTemplateProjects" - } + companion object { + const val EXTENSION_NAME = "dokkaTemplateProjects" + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt index a312490475..d2b2c73f28 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/DokkatooExampleProjectsSettings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.settings import buildsrc.utils.adding @@ -20,43 +24,43 @@ import org.gradle.api.tasks.OutputFile * Settings for the [buildsrc.conventions.Dokkatoo_example_projects_gradle] convention plugin */ abstract class DokkatooExampleProjectsSettings @Inject constructor( - objects: ObjectFactory, + objects: ObjectFactory, ) : ExtensionAware { - val exampleProjects: NamedDomainObjectContainer = - // 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 - 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 - - @Input - override fun getName(): String = name - } - - companion object { - const val TASK_GROUP = "dokkatoo examples" - const val EXTENSION_NAME = "dokkatooExampleProjects" - } + val exampleProjects: NamedDomainObjectContainer = + // 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 + 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 + + @Input + override fun getName(): String = name + } + + companion object { + const val TASK_GROUP = "dokkatoo examples" + const val EXTENSION_NAME = "dokkatooExampleProjects" + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt index 0a701986c8..d9169480b2 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishTestSettings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.settings import org.gradle.api.attributes.Attribute @@ -9,11 +13,11 @@ import org.gradle.api.provider.Provider * Settings for the [buildsrc.conventions.Maven_publish_test_gradle] convention plugin. */ abstract class MavenPublishTestSettings( - val testMavenRepo: Provider + val testMavenRepo: Provider ) : ExtensionAware { - val testMavenRepoPath: Provider = testMavenRepo.map { it.asFile.invariantSeparatorsPath } + val testMavenRepoPath: Provider = testMavenRepo.map { it.asFile.invariantSeparatorsPath } - companion object { - val attribute = Attribute.of("maven-publish-test", String::class.java) - } + companion object { + val attribute = Attribute.of("maven-publish-test", String::class.java) + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt index 9ec28faaf5..de2026aac8 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/settings/MavenPublishingSettings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.settings import java.io.File @@ -12,57 +16,57 @@ 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 project: Project, + private val providers: ProviderFactory, ) { - private val isReleaseVersion: Provider = - providers.provider { !project.version.toString().endsWith("-SNAPSHOT") } + private val isReleaseVersion: Provider = + providers.provider { !project.version.toString().endsWith("-SNAPSHOT") } - val sonatypeReleaseUrl: Provider = - 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 sonatypeReleaseUrl: Provider = + 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 = - d2Prop("mavenCentralUsername") - .orElse(providers.environmentVariable("MAVEN_SONATYPE_USERNAME")) - val mavenCentralPassword: Provider = - d2Prop("mavenCentralPassword") - .orElse(providers.environmentVariable("MAVEN_SONATYPE_PASSWORD")) + val mavenCentralUsername: Provider = + d2Prop("mavenCentralUsername") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_USERNAME")) + val mavenCentralPassword: Provider = + d2Prop("mavenCentralPassword") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_PASSWORD")) - val signingKeyId: Provider = - d2Prop("signing.keyId") - .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY_ID")) - val signingKey: Provider = - d2Prop("signing.key") - .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY")) - val signingPassword: Provider = - d2Prop("signing.password") - .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_PASSWORD")) + val signingKeyId: Provider = + d2Prop("signing.keyId") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY_ID")) + val signingKey: Provider = + d2Prop("signing.key") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_KEY")) + val signingPassword: Provider = + d2Prop("signing.password") + .orElse(providers.environmentVariable("MAVEN_SONATYPE_SIGNING_PASSWORD")) - val githubPublishDir: Provider = - providers.environmentVariable("GITHUB_PUBLISH_DIR").map { File(it) } + val githubPublishDir: Provider = + providers.environmentVariable("GITHUB_PUBLISH_DIR").map { File(it) } - private fun d2Prop(name: String): Provider = - providers.gradleProperty("org.jetbrains.dokka.dokkatoo.$name") + private fun d2Prop(name: String): Provider = + providers.gradleProperty("org.jetbrains.dokka.dokkatoo.$name") - private fun d2Prop(name: String, convert: (String) -> T): Provider = - d2Prop(name).map(convert) + private fun d2Prop(name: String, convert: (String) -> T): Provider = + d2Prop(name).map(convert) - companion object { - const val EXTENSION_NAME = "mavenPublishing" + companion object { + const val EXTENSION_NAME = "mavenPublishing" - /** Retrieve the [KayrayBuildProperties] extension. */ - internal val Project.mavenPublishing: MavenPublishingSettings - get() = extensions.getByType() + /** 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) - } + /** Configure the [KayrayBuildProperties] extension. */ + internal fun Project.mavenPublishing(configure: MavenPublishingSettings.() -> Unit) = + extensions.configure(configure) + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt index d473d28741..3ba48c1241 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/SetupDokkaProjects.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.tasks import buildsrc.settings.DokkaTemplateProjectSettings.DokkaTemplateProjectSpec @@ -12,62 +16,62 @@ 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, + 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: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:Internal // tracked by destinationDirs + abstract val destinationBaseDir: DirectoryProperty - @get:Nested - abstract val templateProjects: NamedDomainObjectContainer + @get:Nested + abstract val templateProjects: NamedDomainObjectContainer - @get:InputDirectory - abstract val dokkaSourceDir: DirectoryProperty + @get:InputDirectory + abstract val dokkaSourceDir: DirectoryProperty - @get:InputFiles - val additionalFiles: FileCollection - get() = layout.files( - providers.provider { - templateProjects.map { it.additionalFiles } - } - ) + @get:InputFiles + val additionalFiles: FileCollection + get() = layout.files( + providers.provider { + templateProjects.map { it.additionalFiles } + } + ) - init { - group = "dokka examples" - } + init { + group = "dokka examples" + } - @TaskAction - internal fun action() { - val dokkaSourceDir = dokkaSourceDir.get() - val destinationBaseDir = destinationBaseDir.get() - val templateProjects = templateProjects.filter { it.destinationPath.isPresent } + @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) + templateProjects.forEach { spec -> + fs.sync { + with(spec.copySpec) - from(dokkaSourceDir.dir(spec.sourcePath)) + from(dokkaSourceDir.dir(spec.sourcePath)) - from( - spec.additionalPaths.get().map { additionalPath -> - dokkaSourceDir.asFile.resolve(additionalPath) - } - ) + from( + spec.additionalPaths.get().map { additionalPath -> + dokkaSourceDir.asFile.resolve(additionalPath) + } + ) - from(spec.additionalFiles) + from(spec.additionalFiles) - into(destinationBaseDir.dir(spec.destinationPath)) - } + into(destinationBaseDir.dir(spec.destinationPath)) + } + } } - } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt index 7737e0988b..f4d7f90245 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/tasks/UpdateDokkatooExampleProjects.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.tasks import buildsrc.settings.DokkatooExampleProjectsSettings.DokkatooExampleProjectSpec @@ -15,35 +19,35 @@ import org.gradle.api.tasks.TaskAction */ @CacheableTask abstract class UpdateDokkatooExampleProjects @Inject constructor( - @get:Internal - val objects: ObjectFactory + @get:Internal + val objects: ObjectFactory ) : DefaultTask() { - @get:Nested - abstract val exampleProjects: NamedDomainObjectContainer + @get:Nested + abstract val exampleProjects: NamedDomainObjectContainer - private val taskPath: String = path // renamed for clarity + private val taskPath: String = path // renamed for clarity - @TaskAction - fun update() { - exampleProjects.forEach { exampleProject -> - updateGradleProperties(exampleProject) + @TaskAction + fun update() { + exampleProjects.forEach { exampleProject -> + updateGradleProperties(exampleProject) + } } - } - private fun updateGradleProperties(exampleProject: DokkatooExampleProjectSpec) { + private fun updateGradleProperties(exampleProject: DokkatooExampleProjectSpec) { - val gradlePropertiesContent = exampleProject.gradlePropertiesContent.orNull?.sorted() ?: return + val gradlePropertiesContent = exampleProject.gradlePropertiesContent.orNull?.sorted() ?: return - val content = buildString { - appendLine("# DO NOT EDIT - Generated by $taskPath") - appendLine() + val content = buildString { + appendLine("# DO NOT EDIT - Generated by $taskPath") + appendLine() - gradlePropertiesContent.forEach { - appendLine(it) - } - } + gradlePropertiesContent.forEach { + appendLine(it) + } + } - exampleProject.gradlePropertiesFile.get().asFile.writeText(content) - } + exampleProject.gradlePropertiesFile.get().asFile.writeText(content) + } } diff --git a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt index 0af662d496..608088cb10 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/gradle.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.utils import java.io.File @@ -20,11 +24,11 @@ import org.gradle.kotlin.dsl.* * ``` */ fun Configuration.asProvider( - visible: Boolean = true + visible: Boolean = true ) { - isVisible = visible - isCanBeResolved = false - isCanBeConsumed = true + isVisible = visible + isCanBeResolved = false + isCanBeConsumed = true } /** @@ -36,32 +40,32 @@ fun Configuration.asProvider( * ``` * */ fun Configuration.asConsumer( - visible: Boolean = false + visible: Boolean = false ) { - isVisible = visible - isCanBeResolved = true - isCanBeConsumed = 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()) + RelativePath(true, *segments.drop(count).toTypedArray()) /** Drop the first directory from the path */ fun RelativePath.dropDirectory(): RelativePath = - dropDirectories(1) + dropDirectories(1) /** Drop the first directory from the path */ fun RelativePath.dropDirectoriesWhile( - segmentPrediate: (segment: String) -> Boolean + segmentPrediate: (segment: String) -> Boolean ): RelativePath = - RelativePath( - true, - *segments.dropWhile(segmentPrediate).toTypedArray(), - ) + RelativePath( + true, + *segments.dropWhile(segmentPrediate).toTypedArray(), + ) /** @@ -70,9 +74,9 @@ fun RelativePath.dropDirectoriesWhile( * 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() } + val javaComponent = components["java"] as AdhocComponentWithVariants + javaComponent.withVariantsFromConfiguration(configurations["testFixturesApiElements"]) { skip() } + javaComponent.withVariantsFromConfiguration(configurations["testFixturesRuntimeElements"]) { skip() } } @@ -90,11 +94,11 @@ fun Project.skipTestFixturesPublications() { * create accessors). */ internal inline fun ExtensionContainer.adding( - name: String, - value: T, + name: String, + value: T, ): T { - add(name, value) - return value + add(name, value) + return value } /** @@ -106,13 +110,13 @@ internal inline fun ExtensionContainer.adding( * @see org.gradle.kotlin.dsl.domainObjectContainer */ internal inline fun ObjectFactory.domainObjectContainer( - factory: NamedDomainObjectFactory? = null + factory: NamedDomainObjectFactory? = null ): NamedDomainObjectContainer = - if (factory == null) { - domainObjectContainer(T::class) - } else { - domainObjectContainer(T::class, factory) - } + 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/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt index f93e76837d..ef7ad8f29b 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/intellij.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.utils import org.gradle.api.Project @@ -8,38 +12,38 @@ 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() - } - ) + 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 + 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) + 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/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt index 6a0749ceb7..53ad330cec 100644 --- a/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt +++ b/dokka-runners/gradle-plugin/buildSrc/src/main/kotlin/buildsrc/utils/strings.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package buildsrc.utils @@ -10,7 +14,7 @@ internal fun String.lowercaseFirstChar(): String = mapFirstChar(Character::toLow private inline fun String.mapFirstChar( - transform: (Char) -> Char + transform: (Char) -> Char ): String = if (isNotEmpty()) transform(this[0]) + substring(1) else this @@ -18,9 +22,9 @@ private inline fun String.mapFirstChar( * 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() + map { if (it.isLetterOrDigit()) it else ' ' } + .joinToString("") + .split(" ") + .filter { it.isNotBlank() } + .joinToString("") { it.uppercaseFirstChar() } + .lowercaseFirstChar() diff --git a/dokka-runners/gradle-plugin/gradle/libs.versions.toml b/dokka-runners/gradle-plugin/gradle/libs.versions.toml new file mode 100644 index 0000000000..4a6fa4ff5a --- /dev/null +++ b/dokka-runners/gradle-plugin/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/gradle-plugin/src/main/kotlin/DokkatooBasePlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooBasePlugin.kt deleted file mode 100644 index 9d67471a98..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooBasePlugin.kt +++ /dev/null @@ -1,355 +0,0 @@ -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 { - - 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() - - 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().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().configureEach { - moduleName.convention(dokkatooExtension.moduleName) - includes.from(providers.provider { dokkatooExtension.dokkatooSourceSets.flatMap { it.includes } }) - modulePath.convention(dokkatooExtension.modulePath) - } - - target.tasks.withType().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(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.configureDefaults( - sourceSetScopeConvention: Property, - ) { - 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(taskNames.generate) { - description = "Generates Dokkatoo publications for all formats" - dependsOn(withType()) - } - } - - // 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): 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/gradle-plugin/src/main/kotlin/DokkatooExtension.kt b/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooExtension.kt deleted file mode 100644 index d7b915416d..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooExtension.kt +++ /dev/null @@ -1,130 +0,0 @@ -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 - abstract val moduleVersion: Property - abstract val modulePath: Property - - /** - * 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 - - /** - * 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 = - 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 = - 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 - val jetbrainsMarkdown: Property - val freemarker: Property - val kotlinxHtml: Property - val kotlinxCoroutines: Property - - companion object - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt deleted file mode 100644 index f5261bb477..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooAndroidAdapter.kt +++ /dev/null @@ -1,214 +0,0 @@ -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 { - - override fun apply(project: Project) { - logger.info("applied DokkatooAndroidAdapter to ${project.path}") - - project.plugins.withType().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() - - 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() - 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 - - 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 { - val collector = objects.domainObjectSet(BaseVariant::class) - - val variants: DomainObjectSet = - 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 { - 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/gradle-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt deleted file mode 100644 index 0f83436382..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooJavaAdapter.kt +++ /dev/null @@ -1,40 +0,0 @@ -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 { - - 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().configureEach { - - // fetch the toolchain, and use the language version as Dokka's jdkVersion - val toolchainLanguageVersion = project.extensions.getByType() - .toolchain - .languageVersion - - val dokka = project.extensions.getByType() - dokka.dokkatooSourceSets.configureEach { - jdkVersion.set(toolchainLanguageVersion.map { it.asInt() }.orElse(8)) - } - } - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt deleted file mode 100644 index 82df651da2..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/adapters/DokkatooKotlinAdapter.kt +++ /dev/null @@ -1,459 +0,0 @@ -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 { - - override fun apply(project: Project) { - logger.info("applied DokkatooKotlinAdapter to ${project.path}") - - project.plugins.withType().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() - - // first fetch the relevant properties of all KotlinCompilations - val compilationDetailsBuilder = KotlinCompilationDetailsBuilder( - providers = providers, - objects = objects, - konanHome = dokkatooExtension.konanHome.asFile, - ) - val allKotlinCompilationDetails: ListProperty = - 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 = - 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, - ) { - // 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.register( - details: KotlinSourceSetDetails - ) { - val kssPlatform = details.compilations.map { values: List -> - 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 { - return details.compilations.map { compilations: List -> - 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() - } 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, - val publishedCompilation: Boolean, - val dependentSourceSetNames: Set, - 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, -) { - - fun createCompilationDetails( - kotlinProjectExtension: KotlinProjectExtension, - ): ListProperty { - - val details = objects.listProperty() - - 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> = - 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 - 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 - - /** Estimate if this Kotlin source set contains 'published' sources */ - fun isPublishedSourceSet(): Provider = - 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, - 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, - allKotlinCompilationDetails: ListProperty, - ): NamedDomainObjectContainer { - - val sourceSetDetails = objects.domainObjectContainer(KotlinSourceSetDetails::class) - - kotlinSourceSets.configureEach kss@{ - sourceSetDetails.register( - kotlinSourceSet = this, - allKotlinCompilationDetails = allKotlinCompilationDetails, - ) - } - - return sourceSetDetails - } - - private fun NamedDomainObjectContainer.register( - kotlinSourceSet: KotlinSourceSet, - allKotlinCompilationDetails: ListProperty, - ) { - - // 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 = dependsOn.toSet(), - allDependents: List = emptyList(), - ): List { - val next = queue.firstOrNull() ?: return allDependents - return next.allDependentSourceSets( - queue = (queue - next) union next.dependsOn, - allDependents = allDependents + next, - ) - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt b/dokka-runners/gradle-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt deleted file mode 100644 index 57ca5ef9ae..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/distributions/DokkatooConfigurationAttributes.kt +++ /dev/null @@ -1,59 +0,0 @@ -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("org.jetbrains.dokka.dokkatoo.base") - val DOKKATOO_CATEGORY_ATTRIBUTE = - Attribute("org.jetbrains.dokka.dokkatoo.category") - val DOKKA_FORMAT_ATTRIBUTE = - Attribute("org.jetbrains.dokka.dokkatoo.format") - - private inline fun Attribute(name: String): Attribute = - Attribute.of(name, T::class.java) - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/DokkaPublication.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/DokkaPublication.kt deleted file mode 100644 index 50c2641537..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/DokkaPublication.kt +++ /dev/null @@ -1,122 +0,0 @@ -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 - - @get:Input - abstract val moduleName: Property - - @get:Input - @get:Optional - abstract val moduleVersion: Property - - @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 - 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 - get() = cacheRoot.map { it.asFile.invariantSeparatorsPath } - - @get:Input - abstract val offlineMode: Property - -// /** 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 - - @get:Input - abstract val delayTemplateSubstitution: Property - - @get:Input - abstract val suppressObviousFunctions: Property - - @get:InputFiles - @get:PathSensitive(RELATIVE) - abstract val includes: ConfigurableFileCollection - - @get:Input - abstract val suppressInheritedMembers: Property - - @get:Input - // TODO probably not needed any more, since Dokka Generator now runs in an isolated JVM process - abstract val finalizeCoroutines: Property -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt deleted file mode 100644 index e91721aa43..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt +++ /dev/null @@ -1,120 +0,0 @@ -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 - - /** - * 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): 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 - - /** - * 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): 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 - - @Internal - override fun getName(): String = name -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt deleted file mode 100644 index 41090e6506..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaGeneratorParametersSpec.kt +++ /dev/null @@ -1,93 +0,0 @@ -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 - - @get:Input - abstract val finalizeCoroutines: Property - - @get:Input - abstract val moduleName: Property - - @get:Input - @get:Optional - abstract val moduleVersion: Property - - @get:Input - abstract val offlineMode: Property - - @get:Input - abstract val suppressObviousFunctions: Property - - @get:Input - abstract val suppressInheritedMembers: Property - - @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 = - 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/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt deleted file mode 100644 index af3e13b0b6..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaModuleDescriptionSpec.kt +++ /dev/null @@ -1,49 +0,0 @@ -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 -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt deleted file mode 100644 index 44e55a742c..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaPackageOptionsSpec.kt +++ /dev/null @@ -1,84 +0,0 @@ -@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 - - /** - * Whether this package should be skipped when generating documentation. - * - * Default is `false`. - */ - @get:Input - abstract val suppress: Property - - /** - * 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 - - /** - * 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 - - /** - * 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 -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt deleted file mode 100644 index c89b8b249e..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceLinkSpec.kt +++ /dev/null @@ -1,106 +0,0 @@ -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 - 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 - - /** - * 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): 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 -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt deleted file mode 100644 index 0248e387a8..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetIdSpec.kt +++ /dev/null @@ -1,61 +0,0 @@ -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(scopeId, sourceSetName) - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt deleted file mode 100644 index 9481885baa..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaSourceSetSpec.kt +++ /dev/null @@ -1,366 +0,0 @@ -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 - - /** - * The identifier for this source set, across all Gradle subprojects. - * - * @see sourceSetScope - * @see getName - */ - @get:Input - val sourceSetId: Provider - 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 = - 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 - - /** - * 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 - - /** - * Allows to customize documentation generation options on a per-package basis. - * - * @see DokkaPackageOptionsSpec for details - */ - @get:Nested - abstract val perPackageOptions: DomainObjectSet - - /** - * Allows linking to Dokka/Javadoc documentation of the project's dependencies. - */ - @get:Nested - val externalDocumentationLinks: NamedDomainObjectContainer = - 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * 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 - - /** - * [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 - - /** - * [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 - - /** - * 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 - - /** - * Configure and add a new source link to [sourceLinks]. - * - * @see DokkaSourceLinkSpec - */ - fun sourceLink(action: Action) { - sourceLinks.add( - objects.newInstance(DokkaSourceLinkSpec::class).also { - action.execute(it) - } - ) - } - - /** - * Action for configuring package options, appending to [perPackageOptions]. - * - * @see DokkaPackageOptionsSpec - */ - fun perPackageOption(action: Action) { - perPackageOptions.add( - objects.newInstance(DokkaPackageOptionsSpec::class).also { - action.execute(it) - } - ) - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt deleted file mode 100644 index 2ed5ddd95e..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/HasConfigurableVisibilityModifiers.kt +++ /dev/null @@ -1,14 +0,0 @@ -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 - - /** Sets [documentedVisibilities] (overrides any previously set values). */ - fun documentedVisibilities(vararg visibilities: VisibilityModifier): Unit = - documentedVisibilities.set(visibilities.asList()) -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt deleted file mode 100644 index c950fbbe06..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/KotlinPlatform.kt +++ /dev/null @@ -1,54 +0,0 @@ -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 = 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/gradle-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt deleted file mode 100644 index de61f97b8b..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/VisibilityModifier.kt +++ /dev/null @@ -1,42 +0,0 @@ -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 = 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/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt deleted file mode 100644 index d39969a204..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaParametersBuilder.kt +++ /dev/null @@ -1,77 +0,0 @@ -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, - 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/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt deleted file mode 100644 index 77935d8ca6..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaSourceSetBuilder.kt +++ /dev/null @@ -1,112 +0,0 @@ -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): List { - - 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, - ): 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/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt deleted file mode 100644 index a3252b5171..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaHtmlPluginParameters.kt +++ /dev/null @@ -1,129 +0,0 @@ -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 - - /** - * 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 - - /** The text displayed in the footer. */ - @get:Input - @get:Optional - abstract val footerMessage: Property - - /** - * 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/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt deleted file mode 100644 index a29b94c2b3..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBuilder.kt +++ /dev/null @@ -1,232 +0,0 @@ -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) { - properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) -} - -@JvmName("numberProperty") -fun DokkaPluginParametersBuilder.property(propertyName: String, provider: Provider) { - properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) -} - -@JvmName("booleanProperty") -fun DokkaPluginParametersBuilder.property( - propertyName: String, - provider: Provider -) { - 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) = - values.add(PluginConfigValue(value)) - -@JvmName("addNumber") -fun PluginConfigValue.Values.add(value: Provider) = - values.add(PluginConfigValue(value)) - -@JvmName("addBoolean") -fun PluginConfigValue.Values.add(value: Provider) = - 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 - ) : PluginConfigValue - - /** Multiple values. Analogous to a [JsonArray]. */ - class Values( - @Nested - val values: ListProperty - ) : 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): Provider = - value.map { PluginConfigValue(it) } - -@Suppress("FunctionName") -@JvmName("PluginConfigNumberValue") -fun PluginConfigValue(value: Provider): Provider = - value.map { PluginConfigValue(it) } - -@Suppress("FunctionName") -@JvmName("PluginConfigBooleanValue") -fun PluginConfigValue(value: Provider): Provider = - value.map { PluginConfigValue(it) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt b/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt deleted file mode 100644 index 1a4d75f288..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaVersioningPluginParameters.kt +++ /dev/null @@ -1,101 +0,0 @@ -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 - - /** - * 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 - - /** - * 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 - - 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/gradle-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt deleted file mode 100644 index 08eece7743..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatDependencyContainers.kt +++ /dev/null @@ -1,152 +0,0 @@ -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, - 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)) - } - - // - /** Fetch Dokka Module files from other subprojects */ - val dokkaModuleConsumer: NamedDomainObjectProvider = - 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 = - 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) - } - } - // - - // - /** - * Dokka plugins. - * - * Users can add plugins to this dependency. - * - * Should not contain runtime dependencies. - */ - val dokkaPluginsClasspath: NamedDomainObjectProvider = - 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 = - 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) - } - } - // - - // - /** - * 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 = - 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) - } - } - // -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt deleted file mode 100644 index c8f601a612..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatPlugin.kt +++ /dev/null @@ -1,174 +0,0 @@ -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 { - - @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().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 = - dokkatooExtension.versions.jetbrainsDokka.map { version -> create("org.jetbrains.dokka:$module:$version") } - - /** Add a dependency to the Dokka plugins classpath */ - fun DependencyHandler.dokkaPlugin(dependency: Provider): 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) { - 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): Provider = - 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/gradle-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt deleted file mode 100644 index ab3639bc5e..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooFormatTasks.kt +++ /dev/null @@ -1,105 +0,0 @@ -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( - 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( - 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( - 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/gradle-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt deleted file mode 100644 index 79df47df46..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooGfmPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ -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/gradle-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt deleted file mode 100644 index 5748f7d1ae..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooHtmlPlugin.kt +++ /dev/null @@ -1,72 +0,0 @@ -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(DOKKA_HTML_PARAMETERS_NAME) - withType().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(DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME) - withType().configureEach { - renderVersionsNavigationOnAllPages.convention(true) - } - } - } - - private fun DokkatooFormatPluginContext.registerLogHtmlUrlTask(): - TaskProvider { - - 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( - "logLink" + dokkatooTasks.generatePublication.name.uppercaseFirstChar() - ) { - serverUri.convention("http://localhost:63342") - this.indexHtmlPath.convention(indexHtmlPath) - } - } -} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt deleted file mode 100644 index 90f024dfa2..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJavadocPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ -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/gradle-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt deleted file mode 100644 index d8434732f8..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/formats/DokkatooJekyllPlugin.kt +++ /dev/null @@ -1,14 +0,0 @@ -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/gradle-plugin/src/main/kotlin/internal/LoggerAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/internal/LoggerAdapter.kt deleted file mode 100644 index 0a1b94fc10..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/LoggerAdapter.kt +++ /dev/null @@ -1,65 +0,0 @@ -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/gradle-plugin/src/main/kotlin/internal/uriUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/internal/uriUtils.kt deleted file mode 100644 index 942551c43e..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/uriUtils.kt +++ /dev/null @@ -1,9 +0,0 @@ -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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooBasePlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooBasePlugin.kt new file mode 100644 index 0000000000..37bffd4c2f --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooBasePlugin.kt @@ -0,0 +1,359 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo + +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 +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 + +/** + * 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 { + + 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() + + 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().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().configureEach { + moduleName.convention(dokkatooExtension.moduleName) + includes.from(providers.provider { dokkatooExtension.dokkatooSourceSets.flatMap { it.includes } }) + modulePath.convention(dokkatooExtension.modulePath) + } + + target.tasks.withType().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(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.configureDefaults( + sourceSetScopeConvention: Property, + ) { + 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(taskNames.generate) { + description = "Generates Dokkatoo publications for all formats" + dependsOn(withType()) + } + } + + // 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): 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooExtension.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooExtension.kt new file mode 100644 index 0000000000..099b31ab1c --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooExtension.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo + +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.newInstance +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 + +/** + * 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 + abstract val moduleVersion: Property + abstract val modulePath: Property + + /** + * 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 + + /** + * 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 = + 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 = + 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 + val jetbrainsMarkdown: Property + val freemarker: Property + val kotlinxHtml: Property + val kotlinxCoroutines: Property + + companion object + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPlugin.kt similarity index 54% rename from dokka-runners/gradle-plugin/src/main/kotlin/DokkatooPlugin.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPlugin.kt index 0ace2ca64b..88638e4664 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/DokkatooPlugin.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPlugin.kt @@ -1,13 +1,17 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.apply 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. @@ -18,15 +22,15 @@ abstract class DokkatooPlugin @DokkatooInternalApi constructor() : Plugin { - override fun apply(target: Project) { - with(target.pluginManager) { - apply(type = DokkatooBasePlugin::class) + 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) + // 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooAndroidAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooAndroidAdapter.kt new file mode 100644 index 0000000000..faf45263be --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooAndroidAdapter.kt @@ -0,0 +1,218 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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.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.* +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 + +@DokkatooInternalApi +abstract class DokkatooAndroidAdapter @Inject constructor( + private val objects: ObjectFactory, +) : Plugin { + + override fun apply(project: Project) { + logger.info("applied DokkatooAndroidAdapter to ${project.path}") + + project.plugins.withType().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() + + 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() + 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 + + 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 { + val collector = objects.domainObjectSet(BaseVariant::class) + + val variants: DomainObjectSet = + 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 { + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooJavaAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooJavaAdapter.kt new file mode 100644 index 0000000000..9758ed8eee --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooJavaAdapter.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.adapters + +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.getByType +import org.gradle.kotlin.dsl.withType +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject + +/** + * Apply Java specific configuration to the Dokkatoo plugin. + * + * **Must be applied *after* [org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin]** + */ +@DokkatooInternalApi +abstract class DokkatooJavaAdapter @Inject constructor() : Plugin { + + 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().configureEach { + + // fetch the toolchain, and use the language version as Dokka's jdkVersion + val toolchainLanguageVersion = project.extensions.getByType() + .toolchain + .languageVersion + + val dokka = project.extensions.getByType() + dokka.dokkatooSourceSets.configureEach { + jdkVersion.set(toolchainLanguageVersion.map { it.asInt() }.orElse(8)) + } + } + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooKotlinAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooKotlinAdapter.kt new file mode 100644 index 0000000000..8f39cc762d --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/adapters/DokkatooKotlinAdapter.kt @@ -0,0 +1,465 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.adapters + +import com.android.build.gradle.api.ApplicationVariant +import com.android.build.gradle.api.LibraryVariant +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.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 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 +import java.io.File +import javax.inject.Inject + +/** + * 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 { + + override fun apply(project: Project) { + logger.info("applied DokkatooKotlinAdapter to ${project.path}") + + project.plugins.withType().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() + + // first fetch the relevant properties of all KotlinCompilations + val compilationDetailsBuilder = KotlinCompilationDetailsBuilder( + providers = providers, + objects = objects, + konanHome = dokkatooExtension.konanHome.asFile, + ) + val allKotlinCompilationDetails: ListProperty = + 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 = + 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, + ) { + // 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.register( + details: KotlinSourceSetDetails + ) { + val kssPlatform = details.compilations.map { values: List -> + 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 { + return details.compilations.map { compilations: List -> + 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() + } 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, + val publishedCompilation: Boolean, + val dependentSourceSetNames: Set, + 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, +) { + + fun createCompilationDetails( + kotlinProjectExtension: KotlinProjectExtension, + ): ListProperty { + + val details = objects.listProperty() + + 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> = + 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 + 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 + + /** Estimate if this Kotlin source set contains 'published' sources */ + fun isPublishedSourceSet(): Provider = + 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, + 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, + allKotlinCompilationDetails: ListProperty, + ): NamedDomainObjectContainer { + + val sourceSetDetails = objects.domainObjectContainer(KotlinSourceSetDetails::class) + + kotlinSourceSets.configureEach kss@{ + sourceSetDetails.register( + kotlinSourceSet = this, + allKotlinCompilationDetails = allKotlinCompilationDetails, + ) + } + + return sourceSetDetails + } + + private fun NamedDomainObjectContainer.register( + kotlinSourceSet: KotlinSourceSet, + allKotlinCompilationDetails: ListProperty, + ) { + + // 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 = dependsOn.toSet(), + allDependents: List = emptyList(), + ): List { + val next = queue.firstOrNull() ?: return allDependents + return next.allDependentSourceSets( + queue = (queue - next) union next.dependsOn, + allDependents = allDependents + next, + ) + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/distributions/DokkatooConfigurationAttributes.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/distributions/DokkatooConfigurationAttributes.kt new file mode 100644 index 0000000000..a51bc69e6a --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/distributions/DokkatooConfigurationAttributes.kt @@ -0,0 +1,63 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.distributions + +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.named +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject + +/** + * 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("org.jetbrains.dokka.dokkatoo.base") + val DOKKATOO_CATEGORY_ATTRIBUTE = + Attribute("org.jetbrains.dokka.dokkatoo.category") + val DOKKA_FORMAT_ATTRIBUTE = + Attribute("org.jetbrains.dokka.dokkatoo.format") + + private inline fun Attribute(name: String): Attribute = + Attribute.of(name, T::class.java) + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/DokkaPublication.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/DokkaPublication.kt new file mode 100644 index 0000000000..ca860ecedb --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/DokkaPublication.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka + +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.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 + +/** + * 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 + + @get:Input + abstract val moduleName: Property + + @get:Input + @get:Optional + abstract val moduleVersion: Property + + @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 + 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 + get() = cacheRoot.map { it.asFile.invariantSeparatorsPath } + + @get:Input + abstract val offlineMode: Property + +// /** 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 + + @get:Input + abstract val delayTemplateSubstitution: Property + + @get:Input + abstract val suppressObviousFunctions: Property + + @get:InputFiles + @get:PathSensitive(RELATIVE) + abstract val includes: ConfigurableFileCollection + + @get:Input + abstract val suppressInheritedMembers: Property + + @get:Input + // TODO probably not needed any more, since Dokka Generator now runs in an isolated JVM process + abstract val finalizeCoroutines: Property +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt new file mode 100644 index 0000000000..100dedf13f --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpec.kt @@ -0,0 +1,124 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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 +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import java.net.URI +import javax.inject.Inject + +/** + * 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 + + /** + * 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): 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 + + /** + * 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): 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 + + @Internal + override fun getName(): String = name +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec.kt new file mode 100644 index 0000000000..d215c3cf0c --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaGeneratorParametersSpec.kt @@ -0,0 +1,97 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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 +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 + +/** + * 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 + + @get:Input + abstract val finalizeCoroutines: Property + + @get:Input + abstract val moduleName: Property + + @get:Input + @get:Optional + abstract val moduleVersion: Property + + @get:Input + abstract val offlineMode: Property + + @get:Input + abstract val suppressObviousFunctions: Property + + @get:Input + abstract val suppressInheritedMembers: Property + + @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 = + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaModuleDescriptionSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaModuleDescriptionSpec.kt new file mode 100644 index 0000000000..6043a60a51 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaModuleDescriptionSpec.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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 +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject + +/** + * 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 +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaPackageOptionsSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaPackageOptionsSpec.kt new file mode 100644 index 0000000000..58ca97c3fb --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaPackageOptionsSpec.kt @@ -0,0 +1,88 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +@file:Suppress("FunctionName") + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.gradle.api.provider.Property +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.Input +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable + +/** + * 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 + + /** + * Whether this package should be skipped when generating documentation. + * + * Default is `false`. + */ + @get:Input + abstract val suppress: Property + + /** + * 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 + + /** + * 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 + + /** + * 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 +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaParametersKxs.kt similarity index 58% rename from dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaParametersKxs.kt index df790bcbbd..ae7d543cc9 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/DokkaParametersKxs.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaParametersKxs.kt @@ -1,12 +1,13 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + @file:UseSerializers( - FileAsPathStringSerializer::class, + 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 @@ -15,9 +16,12 @@ 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.gradle.kotlin.dsl.java import org.jetbrains.dokka.DokkaConfiguration import org.jetbrains.dokka.DokkaModuleDescriptionImpl +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.File +import java.nio.file.Paths // Implementations of DokkaConfiguration interfaces that can be serialized to files. @@ -39,26 +43,26 @@ import org.jetbrains.dokka.DokkaModuleDescriptionImpl @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, - /** @see [org.gradle.api.Project.getPath] */ - val modulePath: String, + /** @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, + /** @see [org.gradle.api.Project.getPath] */ + val modulePath: String, ) { - internal fun convert() = - DokkaModuleDescriptionImpl( - name = name, - relativePathToOutputDirectory = File(modulePath.removePrefix(":").replace(':', '/')), - includes = includes, - sourceOutputDirectory = sourceOutputDirectory, - ) + internal fun convert() = + DokkaModuleDescriptionImpl( + name = name, + relativePathToOutputDirectory = File(modulePath.removePrefix(":").replace(':', '/')), + includes = includes, + sourceOutputDirectory = sourceOutputDirectory, + ) } @@ -67,12 +71,12 @@ data class DokkaModuleDescriptionKxs( * [invariant path separators][invariantSeparatorsPath] */ private object FileAsPathStringSerializer : KSerializer { - override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor("java.io.File", PrimitiveKind.STRING) + override val descriptor: SerialDescriptor = + PrimitiveSerialDescriptor("java.io.File", PrimitiveKind.STRING) - override fun deserialize(decoder: Decoder): File = - Paths.get(decoder.decodeString()).toFile() + override fun deserialize(decoder: Decoder): File = + Paths.get(decoder.decodeString()).toFile() - override fun serialize(encoder: Encoder, value: File): Unit = - encoder.encodeString(value.invariantSeparatorsPath) + override fun serialize(encoder: Encoder, value: File): Unit = + encoder.encodeString(value.invariantSeparatorsPath) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpec.kt new file mode 100644 index 0000000000..91331b0029 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpec.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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 +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import java.net.URI + +/** + * 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 + 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 + + /** + * 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): 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 +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec.kt new file mode 100644 index 0000000000..d68fd713e2 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetIdSpec.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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.newInstance +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.Serializable +import javax.inject.Inject + +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(scopeId, sourceSetName) + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetSpec.kt new file mode 100644 index 0000000000..fea81d2355 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceSetSpec.kt @@ -0,0 +1,378 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +import org.gradle.api.Action +import org.gradle.api.DomainObjectSet +import org.gradle.api.Named +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.provider.Provider +import org.gradle.api.provider.SetProperty +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.java +import org.gradle.kotlin.dsl.newInstance +import org.jetbrains.dokka.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec.Companion.dokkaSourceSetIdSpec +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.adding +import org.jetbrains.dokka.dokkatoo.internal.domainObjectContainer +import java.io.Serializable +import javax.inject.Inject + +/** + * [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 + + /** + * The identifier for this source set, across all Gradle subprojects. + * + * @see sourceSetScope + * @see getName + */ + @get:Input + val sourceSetId: Provider + 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 + + /** + * 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 + + /** + * 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 + + /** + * 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 = + 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 + + /** + * 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 + + /** + * Allows to customize documentation generation options on a per-package basis. + * + * @see DokkaPackageOptionsSpec for details + */ + @get:Nested + abstract val perPackageOptions: DomainObjectSet + + /** + * Allows linking to Dokka/Javadoc documentation of the project's dependencies. + */ + @get:Nested + val externalDocumentationLinks: NamedDomainObjectContainer = + 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 + + /** + * 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 + + /** + * 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 + + /** + * 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 + + /** + * 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 + + /** + * 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 + + /** + * 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 + + /** + * [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 + + /** + * [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 + + /** + * 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 + + /** + * Configure and add a new source link to [sourceLinks]. + * + * @see DokkaSourceLinkSpec + */ + fun sourceLink(action: Action) { + sourceLinks.add( + objects.newInstance(DokkaSourceLinkSpec::class).also { + action.execute(it) + } + ) + } + + /** + * Action for configuring package options, appending to [perPackageOptions]. + * + * @see DokkaPackageOptionsSpec + */ + fun perPackageOption(action: Action) { + perPackageOptions.add( + objects.newInstance(DokkaPackageOptionsSpec::class).also { + action.execute(it) + } + ) + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers.kt new file mode 100644 index 0000000000..3b4c6582b6 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/HasConfigurableVisibilityModifiers.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 + + /** Sets [documentedVisibilities] (overrides any previously set values). */ + fun documentedVisibilities(vararg visibilities: VisibilityModifier): Unit = + documentedVisibilities.set(visibilities.asList()) +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatform.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatform.kt new file mode 100644 index 0000000000..a7387c5cc7 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatform.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 = 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifier.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifier.kt new file mode 100644 index 0000000000..2b2bb94b9d --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifier.kt @@ -0,0 +1,46 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.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 = 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/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt similarity index 56% rename from dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt index c6ff889141..beffe6d354 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilder.kt @@ -1,10 +1,14 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders +import org.jetbrains.dokka.DokkaModuleDescriptionImpl +import org.jetbrains.dokka.DokkaSourceSetImpl 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, @@ -17,17 +21,17 @@ import org.jetbrains.dokka.DokkaSourceSetImpl @DokkatooInternalApi internal object DokkaModuleDescriptionBuilder { - fun build( - spec: DokkaModuleDescriptionSpec, - includes: Set, - sourceOutputDirectory: File, - ): DokkaModuleDescriptionImpl = - DokkaModuleDescriptionImpl( - name = spec.name, - relativePathToOutputDirectory = File( - spec.projectPath.get().removePrefix(":").replace(':', '/') - ), - includes = includes, - sourceOutputDirectory = sourceOutputDirectory, - ) + fun build( + spec: DokkaModuleDescriptionSpec, + includes: Set, + sourceOutputDirectory: File, + ): DokkaModuleDescriptionImpl = + DokkaModuleDescriptionImpl( + name = spec.name, + relativePathToOutputDirectory = File( + spec.projectPath.get().removePrefix(":").replace(':', '/') + ), + includes = includes, + sourceOutputDirectory = sourceOutputDirectory, + ) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilder.kt new file mode 100644 index 0000000000..e8c1b8adad --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilder.kt @@ -0,0 +1,79 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +import org.jetbrains.dokka.DokkaConfiguration +import org.jetbrains.dokka.DokkaConfigurationImpl +import org.jetbrains.dokka.DokkaSourceSetImpl +import org.jetbrains.dokka.PluginConfigurationImpl +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 java.io.File + +/** + * 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, + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilder.kt new file mode 100644 index 0000000000..d8b47d1b70 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilder.kt @@ -0,0 +1,116 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + + +import org.gradle.api.logging.Logging +import org.jetbrains.dokka.* +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 + + +/** + * 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): List { + + 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, + ): 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters.kt new file mode 100644 index 0000000000..59cd72025d --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaHtmlPluginParameters.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +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 +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 + + +/** + * 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 + + /** + * 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 + + /** The text displayed in the footer. */ + @get:Input + @get:Optional + abstract val footerMessage: Property + + /** + * 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/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec.kt similarity index 71% rename from dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec.kt index 486bb80ebf..7209ff2f97 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/dokka/plugins/DokkaPluginParametersBaseSpec.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBaseSpec.kt @@ -1,10 +1,14 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.dokka.plugins +import org.gradle.api.Named +import org.gradle.api.tasks.Input 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. @@ -20,13 +24,13 @@ abstract class DokkaPluginParametersBaseSpec @DokkatooInternalApi @Inject constructor( - private val name: String, - @get:Input - open val pluginFqn: String, + private val name: String, + @get:Input + open val pluginFqn: String, ) : Serializable, Named { - abstract fun jsonEncode(): String // to be implemented by subclasses + abstract fun jsonEncode(): String // to be implemented by subclasses - @Input - override fun getName(): String = name + @Input + override fun getName(): String = name } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder.kt new file mode 100644 index 0000000000..7d5f18ddbd --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaPluginParametersBuilder.kt @@ -0,0 +1,241 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +import kotlinx.serialization.json.JsonArray +import kotlinx.serialization.json.JsonElement +import kotlinx.serialization.json.JsonObject +import kotlinx.serialization.json.JsonPrimitive +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.containerWithType +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.mapProperty +import org.jetbrains.dokka.dokkatoo.internal.DokkaPluginParametersContainer +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import java.io.File +import javax.inject.Inject + + +/** + * 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) { + properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) +} + +@JvmName("numberProperty") +fun DokkaPluginParametersBuilder.property(propertyName: String, provider: Provider) { + properties.values.put(propertyName, provider.map { PluginConfigValue(it) }) +} + +@JvmName("booleanProperty") +fun DokkaPluginParametersBuilder.property( + propertyName: String, + provider: Provider +) { + 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) = + values.add(PluginConfigValue(value)) + +@JvmName("addNumber") +fun PluginConfigValue.Values.add(value: Provider) = + values.add(PluginConfigValue(value)) + +@JvmName("addBoolean") +fun PluginConfigValue.Values.add(value: Provider) = + 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 + ) : PluginConfigValue + + /** Multiple values. Analogous to a [JsonArray]. */ + class Values( + @Nested + val values: ListProperty + ) : 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): Provider = + value.map { PluginConfigValue(it) } + +@Suppress("FunctionName") +@JvmName("PluginConfigNumberValue") +fun PluginConfigValue(value: Provider): Provider = + value.map { PluginConfigValue(it) } + +@Suppress("FunctionName") +@JvmName("PluginConfigBooleanValue") +fun PluginConfigValue(value: Provider): Provider = + value.map { PluginConfigValue(it) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters.kt new file mode 100644 index 0000000000..5965c6b973 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/dokka/plugins/DokkaVersioningPluginParameters.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.plugins + +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 +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 + + +/** + * 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 + + /** + * 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 + + /** + * 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 + + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatDependencyContainers.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatDependencyContainers.kt new file mode 100644 index 0000000000..ea27b4c9d1 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatDependencyContainers.kt @@ -0,0 +1,158 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +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.named +import org.gradle.kotlin.dsl.newInstance +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 + +/** + * 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, + 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)) + } + + // + /** Fetch Dokka Module files from other subprojects */ + val dokkaModuleConsumer: NamedDomainObjectProvider = + 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 = + 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) + } + } + // + + // + /** + * Dokka plugins. + * + * Users can add plugins to this dependency. + * + * Should not contain runtime dependencies. + */ + val dokkaPluginsClasspath: NamedDomainObjectProvider = + 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 = + 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) + } + } + // + + // + /** + * 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 = + 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) + } + } + // +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatPlugin.kt new file mode 100644 index 0000000000..5306df92f0 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatPlugin.kt @@ -0,0 +1,183 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +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.apply +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.withType +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.DokkatooInternalApi +import javax.inject.Inject + +/** + * 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 { + + @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().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 = + dokkatooExtension.versions.jetbrainsDokka.map { version -> create("org.jetbrains.dokka:$module:$version") } + + /** Add a dependency to the Dokka plugins classpath */ + fun DependencyHandler.dokkaPlugin(dependency: Provider): 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) { + 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): Provider = + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatTasks.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatTasks.kt new file mode 100644 index 0000000000..5b0f227276 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooFormatTasks.kt @@ -0,0 +1,109 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +import org.gradle.api.Project +import org.gradle.api.provider.ProviderFactory +import org.gradle.kotlin.dsl.register +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 + +/** 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( + 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( + 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( + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooGfmPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooGfmPlugin.kt new file mode 100644 index 0000000000..9d4cd23a58 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooGfmPlugin.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi + +abstract class DokkatooGfmPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "gfm") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("gfm-plugin")) + } + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooHtmlPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooHtmlPlugin.kt new file mode 100644 index 0000000000..84dd3d85f6 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooHtmlPlugin.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +import org.gradle.api.tasks.TaskProvider +import org.gradle.kotlin.dsl.register +import org.gradle.kotlin.dsl.registerBinding +import org.gradle.kotlin.dsl.withType +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 + +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(DOKKA_HTML_PARAMETERS_NAME) + withType().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(DOKKA_VERSIONING_PLUGIN_PARAMETERS_NAME) + withType().configureEach { + renderVersionsNavigationOnAllPages.convention(true) + } + } + } + + private fun DokkatooFormatPluginContext.registerLogHtmlUrlTask(): + TaskProvider { + + 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( + "logLink" + dokkatooTasks.generatePublication.name.uppercaseFirstChar() + ) { + serverUri.convention("http://localhost:63342") + this.indexHtmlPath.convention(indexHtmlPath) + } + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJavadocPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJavadocPlugin.kt new file mode 100644 index 0000000000..e3730f46d7 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJavadocPlugin.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi + +abstract class DokkatooJavadocPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "javadoc") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("javadoc-plugin")) + } + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJekyllPlugin.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJekyllPlugin.kt new file mode 100644 index 0000000000..c912816fdb --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/formats/DokkatooJekyllPlugin.kt @@ -0,0 +1,18 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.formats + +import org.gradle.kotlin.dsl.dependencies +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi + +abstract class DokkatooJekyllPlugin +@DokkatooInternalApi +constructor() : DokkatooFormatPlugin(formatName = "jekyll") { + override fun DokkatooFormatPluginContext.configure() { + project.dependencies { + dokkaPlugin(dokka("jekyll-plugin")) + } + } +} diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/DokkatooInternalApi.kt similarity index 78% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/DokkatooInternalApi.kt index e3e6375375..6a4a7e2f5f 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/DokkatooInternalApi.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/DokkatooInternalApi.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.internal import kotlin.RequiresOptIn.Level.WARNING @@ -22,16 +26,16 @@ import kotlin.annotation.AnnotationTarget.* * [the Dokkatoo issue tracker](https://github.com/adamko-dev/dokkatoo/issues). */ @RequiresOptIn( - "Internal API - may change at any time without notice", - level = WARNING + "Internal API - may change at any time without notice", + level = WARNING ) @Retention(BINARY) @Target( - CLASS, - FUNCTION, - CONSTRUCTOR, - PROPERTY, - PROPERTY_GETTER, + CLASS, + FUNCTION, + CONSTRUCTOR, + PROPERTY, + PROPERTY_GETTER, ) @MustBeDocumented annotation class DokkatooInternalApi diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/LoggerAdapter.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/LoggerAdapter.kt new file mode 100644 index 0000000000..ba6e71b16e --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/LoggerAdapter.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.internal + +import org.jetbrains.dokka.utilities.DokkaLogger +import org.jetbrains.dokka.utilities.LoggingLevel +import java.io.File +import java.io.Writer +import java.util.concurrent.atomic.AtomicInteger + +/** + * 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/gradle-plugin/src/main/kotlin/internal/collectionsUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/collectionsUtils.kt similarity index 50% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/collectionsUtils.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/collectionsUtils.kt index 80b66f4be5..5cccfce1a1 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/collectionsUtils.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/collectionsUtils.kt @@ -1,7 +1,11 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.internal internal fun Set.mapToSet(transform: (T) -> R): Set = - mapTo(mutableSetOf(), transform) + mapTo(mutableSetOf(), transform) internal fun Set.mapNotNullToSet(transform: (T) -> R?): Set = - mapNotNullTo(mutableSetOf(), transform) + mapNotNullTo(mutableSetOf(), transform) diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleExtensionAccessors.kt similarity index 66% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleExtensionAccessors.kt index 8520889756..dbab1db3d4 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleExtensionAccessors.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleExtensionAccessors.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.internal import org.jetbrains.dokka.dokkatoo.DokkatooExtension @@ -5,5 +9,5 @@ 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) + versions.apply(configure) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleTypealiases.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleTypealiases.kt similarity index 80% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleTypealiases.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleTypealiases.kt index 7f59db868b..a4cd28f05f 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleTypealiases.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleTypealiases.kt @@ -1,11 +1,15 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.internal -import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec import org.gradle.api.ExtensiblePolymorphicDomainObjectContainer +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec /** Container for all [Dokka Plugin parameters][DokkaPluginParametersBaseSpec]. */ typealias DokkaPluginParametersContainer = - ExtensiblePolymorphicDomainObjectContainer + ExtensiblePolymorphicDomainObjectContainer /** diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleUtils.kt similarity index 64% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleUtils.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleUtils.kt index 53ba49b9de..52dd089e3c 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/gradleUtils.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/gradleUtils.kt @@ -1,6 +1,9 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + 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 @@ -14,7 +17,10 @@ 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.* +import org.gradle.kotlin.dsl.add +import org.gradle.kotlin.dsl.domainObjectContainer +import org.gradle.kotlin.dsl.polymorphicDomainObjectContainer +import org.jetbrains.dokka.dokkatoo.dokka.plugins.DokkaPluginParametersBaseSpec /** @@ -26,11 +32,11 @@ import org.gradle.kotlin.dsl.* * ``` */ internal fun Configuration.asProvider( - visible: Boolean = true, + visible: Boolean = true, ) { - isCanBeResolved = false - isCanBeConsumed = true - isVisible = visible + isCanBeResolved = false + isCanBeConsumed = true + isVisible = visible } /** @@ -42,11 +48,11 @@ internal fun Configuration.asProvider( * ``` * */ internal fun Configuration.asConsumer( - visible: Boolean = false, + visible: Boolean = false, ) { - isCanBeResolved = true - isCanBeConsumed = false - isVisible = visible + isCanBeResolved = true + isCanBeConsumed = false + isVisible = visible } @@ -56,8 +62,8 @@ internal operator fun Provider.not(): Provider = map { !it } /** Only matches components that come from subprojects */ internal object LocalProjectOnlyFilter : Spec { - override fun isSatisfiedBy(element: ComponentIdentifier?): Boolean = - element is ProjectComponentIdentifier + override fun isSatisfiedBy(element: ComponentIdentifier?): Boolean = + element is ProjectComponentIdentifier } @@ -66,8 +72,8 @@ internal operator fun Spec.not(): Spec = Spec { !this@not.isSatisfi internal fun Project.pathAsFilePath() = path - .removePrefix(GradleProjectPath.SEPARATOR) - .replace(GradleProjectPath.SEPARATOR, "/") + .removePrefix(GradleProjectPath.SEPARATOR) + .replace(GradleProjectPath.SEPARATOR, "/") /** @@ -76,13 +82,13 @@ internal fun Project.pathAsFilePath() = path * and return the same [TaskProvider]. */ internal fun TaskProvider.configuring( - block: Action + block: Action ): TaskProvider = apply { configure(block) } internal fun NamedDomainObjectContainer.maybeCreate( - name: String, - configure: T.() -> Unit, + name: String, + configure: T.() -> Unit, ): T = maybeCreate(name).apply(configure) @@ -98,29 +104,29 @@ internal fun NamedDomainObjectContainer.maybeCreate( * 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) - }, + 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) + 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) + } } - } } @@ -133,13 +139,13 @@ internal fun ConfigurationContainer.collectIncomingFiles( * @see org.gradle.kotlin.dsl.domainObjectContainer */ internal inline fun ObjectFactory.domainObjectContainer( - factory: NamedDomainObjectFactory? = null + factory: NamedDomainObjectFactory? = null ): NamedDomainObjectContainer = - if (factory == null) { - domainObjectContainer(T::class) - } else { - domainObjectContainer(T::class, factory) - } + if (factory == null) { + domainObjectContainer(T::class) + } else { + domainObjectContainer(T::class, factory) + } /** @@ -150,8 +156,8 @@ internal inline fun ObjectFactory.domainObjectContainer( * @see org.gradle.kotlin.dsl.polymorphicDomainObjectContainer */ internal inline fun ObjectFactory.polymorphicDomainObjectContainer() - : ExtensiblePolymorphicDomainObjectContainer = - polymorphicDomainObjectContainer(T::class) + : ExtensiblePolymorphicDomainObjectContainer = + polymorphicDomainObjectContainer(T::class) /** @@ -168,20 +174,20 @@ internal inline fun ObjectFactory.polymorphicDomainObjectConta * create accessors). */ internal inline fun ExtensionContainer.adding( - name: String, - value: T, + name: String, + value: T, ): T { - add(name, value) - return value + add(name, value) + return value } /** Create a new [DokkaPluginParametersContainer] instance. */ internal fun ObjectFactory.dokkaPluginParametersContainer(): DokkaPluginParametersContainer { - val container = polymorphicDomainObjectContainer() - container.whenObjectAdded { - // workaround for https://github.com/gradle/gradle/issues/24972 - (container as ExtensionAware).extensions.add(name, this) - } - return container + val container = polymorphicDomainObjectContainer() + 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/gradle-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/kotlinxSerializationUtils.kt similarity index 60% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/kotlinxSerializationUtils.kt index d4f9800403..cf0f369fd2 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/kotlinxSerializationUtils.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/kotlinxSerializationUtils.kt @@ -1,36 +1,40 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + 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 +import java.io.File @JvmName("addAllFiles") internal fun JsonArrayBuilder.addAll(files: Iterable) { - files - .map { it.canonicalFile.invariantSeparatorsPath } - .forEach { path -> add(path) } + files + .map { it.canonicalFile.invariantSeparatorsPath } + .forEach { path -> add(path) } } @JvmName("addAllStrings") internal fun JsonArrayBuilder.addAll(values: Iterable) { - values.forEach { add(it) } + values.forEach { add(it) } } internal fun JsonArrayBuilder.addAllIfNotNull(values: Iterable?) { - if (values != null) addAll(values) + if (values != null) addAll(values) } internal fun JsonObjectBuilder.putIfNotNull(key: String, value: Boolean?) { - if (value != null) put(key, JsonPrimitive(value)) + if (value != null) put(key, JsonPrimitive(value)) } internal fun JsonObjectBuilder.putIfNotNull(key: String, value: String?) { - if (value != null) put(key, JsonPrimitive(value)) + if (value != null) put(key, JsonPrimitive(value)) } internal fun JsonObjectBuilder.putIfNotNull(key: String, value: File?) { - if (value != null) put(key, JsonPrimitive(value.canonicalFile.invariantSeparatorsPath)) + if (value != null) put(key, JsonPrimitive(value.canonicalFile.invariantSeparatorsPath)) } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/internal/stringUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/stringUtils.kt similarity index 58% rename from dokka-runners/gradle-plugin/src/main/kotlin/internal/stringUtils.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/stringUtils.kt index 75b3b8ec69..2ac580b006 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/internal/stringUtils.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/stringUtils.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.internal @@ -8,4 +12,4 @@ package org.jetbrains.dokka.dokkatoo.internal * stable as possible.) */ internal fun String.uppercaseFirstChar(): String = - if (isNotEmpty()) Character.toTitleCase(this[0]) + substring(1) else this + if (isNotEmpty()) Character.toTitleCase(this[0]) + substring(1) else this diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/uriUtils.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/uriUtils.kt new file mode 100644 index 0000000000..ad839a475c --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/internal/uriUtils.kt @@ -0,0 +1,13 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooGenerateTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooGenerateTask.kt new file mode 100644 index 0000000000..0155e12736 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooGenerateTask.kt @@ -0,0 +1,195 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.tasks + +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.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.kotlin.dsl.newInstance +import org.gradle.kotlin.dsl.submit +import org.gradle.process.JavaForkOptions +import org.gradle.workers.WorkerExecutor +import org.jetbrains.dokka.DokkaConfiguration +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 org.jetbrains.dokka.toPrettyJsonString +import java.io.IOException +import javax.inject.Inject + +/** + * 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 + + /** @see org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.enabled */ + @get:Input + abstract val publicationEnabled: Property + + @get:Nested + val generator: DokkaGeneratorParametersSpec = objects.newInstance(pluginsConfiguration) + + /** @see JavaForkOptions.getDebug */ + @get:Input + abstract val workerDebugEnabled: Property + + /** @see JavaForkOptions.getMinHeapSize */ + @get:Input + @get:Optional + abstract val workerMinHeapSize: Property + + /** @see JavaForkOptions.getMaxHeapSize */ + @get:Input + @get:Optional + abstract val workerMaxHeapSize: Property + + /** @see JavaForkOptions.jvmArgs */ + @get:Input + abstract val workerJvmArgs: ListProperty + + @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 { + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooPrepareModuleDescriptorTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooPrepareModuleDescriptorTask.kt new file mode 100644 index 0000000000..5bce55357b --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooPrepareModuleDescriptorTask.kt @@ -0,0 +1,68 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.tasks + +import kotlinx.serialization.encodeToString +import org.gradle.api.file.ConfigurableFileCollection +import org.gradle.api.file.DirectoryProperty +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.* +import org.gradle.api.tasks.PathSensitivity.RELATIVE +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 + +/** + * 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 + + @get:Input + abstract val modulePath: Property + + @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/gradle-plugin/src/main/kotlin/tasks/DokkatooTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooTask.kt similarity index 65% rename from dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooTask.kt rename to dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooTask.kt index c125a64e06..eb919201f8 100644 --- a/dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooTask.kt +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/DokkatooTask.kt @@ -1,11 +1,15 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + 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 +import org.jetbrains.dokka.dokkatoo.DokkatooBasePlugin +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import javax.inject.Inject /** Base Dokkatoo task */ @CacheableTask @@ -13,10 +17,10 @@ abstract class DokkatooTask @DokkatooInternalApi constructor() : DefaultTask() { - @get:Inject - abstract val objects: ObjectFactory + @get:Inject + abstract val objects: ObjectFactory - init { - group = DokkatooBasePlugin.TASK_GROUP - } + init { + group = DokkatooBasePlugin.TASK_GROUP + } } diff --git a/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/LogHtmlPublicationLinkTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/LogHtmlPublicationLinkTask.kt new file mode 100644 index 0000000000..7576da38fc --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/tasks/LogHtmlPublicationLinkTask.kt @@ -0,0 +1,160 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.tasks + +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.of +import org.gradle.work.DisableCachingByDefault +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 + +/** + * 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 + + /** + * 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 + + 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 { + + interface Parameters : ValueSourceParameters { + /** E.g. `http://localhost:63342` */ + val uri: Property + } + + 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/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/workers/DokkaGeneratorWorker.kt b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/workers/DokkaGeneratorWorker.kt new file mode 100644 index 0000000000..34c1c70ecb --- /dev/null +++ b/dokka-runners/gradle-plugin/src/main/kotlin/org/jetbrains/dokka/dokkatoo/workers/DokkaGeneratorWorker.kt @@ -0,0 +1,81 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.workers + +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 +import org.jetbrains.dokka.dokkatoo.internal.DokkatooInternalApi +import org.jetbrains.dokka.dokkatoo.internal.LoggerAdapter +import java.io.File +import java.time.Duration + +/** + * 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 { + + @DokkatooInternalApi + interface Parameters : WorkParameters { + val dokkaParameters: Property + 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/gradle-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt deleted file mode 100644 index b27acbc5ae..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooGenerateTask.kt +++ /dev/null @@ -1,187 +0,0 @@ -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 - - /** @see org.jetbrains.dokka.dokkatoo.dokka.DokkaPublication.enabled */ - @get:Input - abstract val publicationEnabled: Property - - @get:Nested - val generator: DokkaGeneratorParametersSpec = objects.newInstance(pluginsConfiguration) - - /** @see JavaForkOptions.getDebug */ - @get:Input - abstract val workerDebugEnabled: Property - /** @see JavaForkOptions.getMinHeapSize */ - @get:Input - @get:Optional - abstract val workerMinHeapSize: Property - /** @see JavaForkOptions.getMaxHeapSize */ - @get:Input - @get:Optional - abstract val workerMaxHeapSize: Property - /** @see JavaForkOptions.jvmArgs */ - @get:Input - abstract val workerJvmArgs: ListProperty - @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 { - 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/gradle-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt deleted file mode 100644 index 1247ebc753..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/tasks/DokkatooPrepareModuleDescriptorTask.kt +++ /dev/null @@ -1,62 +0,0 @@ -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 - - @get:Input - abstract val modulePath: Property - - @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/gradle-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt b/dokka-runners/gradle-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt deleted file mode 100644 index c281ce56ca..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/tasks/LogHtmlPublicationLinkTask.kt +++ /dev/null @@ -1,156 +0,0 @@ -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 - - /** - * 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 - - 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 { - - interface Parameters : ValueSourceParameters { - /** E.g. `http://localhost:63342` */ - val uri: Property - } - - 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/gradle-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt b/dokka-runners/gradle-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt deleted file mode 100644 index 4ac58d030b..0000000000 --- a/dokka-runners/gradle-plugin/src/main/kotlin/workers/DokkaGeneratorWorker.kt +++ /dev/null @@ -1,77 +0,0 @@ -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 { - - @DokkatooInternalApi - interface Parameters : WorkParameters { - val dokkaParameters: Property - 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/gradle-plugin/src/test/kotlin/DokkatooPluginTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/DokkatooPluginTest.kt deleted file mode 100644 index 843708a3b2..0000000000 --- a/dokka-runners/gradle-plugin/src/test/kotlin/DokkatooPluginTest.kt +++ /dev/null @@ -1,76 +0,0 @@ -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() - - 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/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt deleted file mode 100644 index 28fb2b836b..0000000000 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt +++ /dev/null @@ -1,102 +0,0 @@ -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().dokkatooSourceSets - - return dssContainer.create_("test" + dssContainer.size) - .externalDocumentationLinks - .create("testLink", configure) -} diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt deleted file mode 100644 index f3171a571a..0000000000 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/DokkaSourceLinkSpecTest.kt +++ /dev/null @@ -1,58 +0,0 @@ -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 - get() = super.localDirectoryPath - } - - companion object { - private fun Project.createDokkaSourceLinkSpec( - configure: DokkaSourceLinkSpec.() -> Unit - ): DokkaSourceLinkSpec2 = - objects.newInstance(DokkaSourceLinkSpec2::class).apply(configure) - } -} diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt deleted file mode 100644 index c921df9ace..0000000000 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/KotlinPlatformTest.kt +++ /dev/null @@ -1,37 +0,0 @@ -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/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt deleted file mode 100644 index bb4bf8a74f..0000000000 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt +++ /dev/null @@ -1,198 +0,0 @@ -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 { - 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 { - 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 { - 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 { - 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() - .dokkatooSourceSets - .create_(name, configure) -} diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginTest.kt new file mode 100644 index 0000000000..e811d7b2c7 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginTest.kt @@ -0,0 +1,82 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo + +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.kotest.matchers.string.shouldEndWith +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.hasPlugin +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.dokka.dokkatoo.utils.create_ + +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() + + 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/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt new file mode 100644 index 0000000000..574bf63c55 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaExternalDocumentationLinkSpecTest.kt @@ -0,0 +1,107 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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.apply +import org.gradle.kotlin.dsl.getByType +import org.gradle.testfixtures.ProjectBuilder +import org.jetbrains.dokka.dokkatoo.DokkatooExtension +import org.jetbrains.dokka.dokkatoo.DokkatooPlugin +import org.jetbrains.dokka.dokkatoo.utils.create_ + + +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().dokkatooSourceSets + + return dssContainer.create_("test" + dssContainer.size) + .externalDocumentationLinks + .create("testLink", configure) +} diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpecTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpecTest.kt new file mode 100644 index 0000000000..f6638f9480 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/DokkaSourceLinkSpecTest.kt @@ -0,0 +1,62 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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.newInstance +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 + get() = super.localDirectoryPath + } + + companion object { + private fun Project.createDokkaSourceLinkSpec( + configure: DokkaSourceLinkSpec.() -> Unit + ): DokkaSourceLinkSpec2 = + objects.newInstance(DokkaSourceLinkSpec2::class).apply(configure) + } +} diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatformTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatformTest.kt new file mode 100644 index 0000000000..be24d0aa28 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/KotlinPlatformTest.kt @@ -0,0 +1,41 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters + +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 +import org.jetbrains.dokka.dokkatoo.dokka.parameters.KotlinPlatform.Companion.dokkaType + +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/gradle-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifierTest.kt similarity index 50% rename from dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt rename to dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifierTest.kt index ca5ad49a70..84d15b042d 100644 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/VisibilityModifierTest.kt +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/VisibilityModifierTest.kt @@ -1,17 +1,21 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + 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 +import org.jetbrains.dokka.dokkatoo.dokka.parameters.VisibilityModifier.Companion.dokkaType class VisibilityModifierTest : FunSpec({ - test("DokkaConfiguration.Visibility should have equivalent VisibilityModifier") { - DokkaConfiguration.Visibility.values().shouldForAll { dokkaVisibility -> - VisibilityModifier.entries.map { it.dokkaType }.shouldForOne { it shouldBe dokkaVisibility } + 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/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt similarity index 59% rename from dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt rename to dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt index ff4426632a..be4ad51edd 100644 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaModuleDescriptionBuilderTest.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders import io.kotest.core.spec.style.FunSpec diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilderTest.kt similarity index 58% rename from dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt rename to dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilderTest.kt index 6691819495..9a759b60ae 100644 --- a/dokka-runners/gradle-plugin/src/test/kotlin/dokka/parameters/builders/DokkaParametersBuilderTest.kt +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaParametersBuilderTest.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders import io.kotest.core.spec.style.FunSpec diff --git a/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt new file mode 100644 index 0000000000..8d4575d770 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/test/kotlin/org/jetbrains/dokka/dokkatoo/dokka/parameters/builders/DokkaSourceSetBuilderTest.kt @@ -0,0 +1,203 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +package org.jetbrains.dokka.dokkatoo.dokka.parameters.builders + +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 org.gradle.api.Project +import org.gradle.api.file.Directory +import org.gradle.api.internal.provider.MissingValueException +import org.gradle.kotlin.dsl.apply +import org.gradle.kotlin.dsl.getByType +import org.gradle.testfixtures.ProjectBuilder +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 java.io.File +import java.net.URI + +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 { + 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 { + 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 { + 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 { + 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() + .dokkatooSourceSets + .create_(name, configure) +} diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/fileTree.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/fileTree.kt deleted file mode 100644 index 4ba850d37f..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/fileTree.kt +++ /dev/null @@ -1,61 +0,0 @@ -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 = - 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/gradle-plugin/src/testFixtures/kotlin/files.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/files.kt deleted file mode 100644 index 6a423b550b..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/files.kt +++ /dev/null @@ -1,6 +0,0 @@ -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/gradle-plugin/src/testFixtures/kotlin/kotestConditions.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestConditions.kt deleted file mode 100644 index 7b692afbc8..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestConditions.kt +++ /dev/null @@ -1,10 +0,0 @@ -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): Boolean = - "win" !in System.getProperty("os.name").lowercase() -} diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt deleted file mode 100644 index e1863c8fa1..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestGradleAssertions.kt +++ /dev/null @@ -1,130 +0,0 @@ -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 NamedDomainObjectCollection?.shouldContainDomainObject( - name: String -): T { - this should containDomainObject(name) - return this?.getByName(name)!! -} - -infix fun NamedDomainObjectCollection?.shouldNotContainDomainObject( - name: String -): NamedDomainObjectCollection? { - this shouldNot containDomainObject(name) - return this -} - -private fun containDomainObject(name: String): Matcher?> = - 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 = - 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) { - this should haveAnyOutcome(outcomes) -} - - -infix fun BuildTask?.shouldNotHaveOutcome(outcome: TaskOutcome) { - this shouldNot haveOutcome(outcome) -} - - -private fun haveOutcome(outcome: TaskOutcome): Matcher = - haveAnyOutcome(listOf(outcome)) - - -private fun haveAnyOutcome(outcomes: Collection): Matcher { - 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) { - this shouldHaveRunTask taskPath shouldHaveAnyOutcome outcomes -} - -fun BuildResult.shouldHaveTasksWithOutcome( - vararg taskPathToExpectedOutcome: Pair -) { - assertSoftly { - taskPathToExpectedOutcome.forEach { (taskPath, outcome) -> - shouldHaveTaskWithOutcome(taskPath, outcome) - } - } -} - -fun BuildResult.shouldHaveTasksWithAnyOutcome( - vararg taskPathToExpectedOutcome: Pair> -) { - assertSoftly { - taskPathToExpectedOutcome.forEach { (taskPath, outcomes) -> - shouldHaveTaskWithAnyOutcome(taskPath, outcomes) - } - } -} diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt deleted file mode 100644 index 58bbe768cf..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestStringMatchers.kt +++ /dev/null @@ -1,65 +0,0 @@ -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? { - this should containAll(substrings) - return this -} - -infix fun String?.shouldNotContainAll(substrings: Iterable): 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) = - neverNullMatcher { 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? { - this should containAnyOf(substrings) - return this -} - -infix fun String?.shouldNotContainAnyOf(substrings: Iterable): 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) = - neverNullMatcher { 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/gradle-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/GradleTestKitUtils.kt similarity index 57% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/GradleTestKitUtils.kt index 2f9e1b4162..45d76f4252 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/GradleTestKitUtils.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/GradleTestKitUtils.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.utils import java.io.File @@ -14,45 +18,46 @@ import org.intellij.lang.annotations.Language class GradleProjectTest( - override val projectDir: Path, + override val projectDir: Path, ) : ProjectDirectoryScope { - constructor( - testProjectName: String, - baseDir: Path = funcTestTempDir, - ) : this(projectDir = baseDir.resolve(testProjectName)) + 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 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 + val testMavenRepoRelativePath: String = + projectDir.relativize(testMavenRepoDir).toFile().invariantSeparatorsPath - companion object { + companion object { - /** file-based Maven Repo that contains the Dokka dependencies */ - val testMavenRepoDir: Path by systemProperty(Paths::get) + /** file-based Maven Repo that contains the Dokka dependencies */ + val testMavenRepoDir: Path by systemProperty(Paths::get) - val projectTestTempDir: 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") - } + /** 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) - } + /** 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) + } } @@ -76,13 +81,13 @@ class GradleProjectTest( * @param[testProjectName] the path of the project directory, relative to [baseDir */ fun gradleKtsProjectTest( - testProjectName: String, - baseDir: Path = GradleProjectTest.funcTestTempDir, - build: GradleProjectTest.() -> Unit, + testProjectName: String, + baseDir: Path = GradleProjectTest.funcTestTempDir, + build: GradleProjectTest.() -> Unit, ): GradleProjectTest { - return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { + return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { - settingsGradleKts = """ + settingsGradleKts = """ |rootProject.name = "test" | |@Suppress("UnstableApiUsage") @@ -113,13 +118,13 @@ fun gradleKtsProjectTest( | """.trimMargin() - gradleProperties = """ + gradleProperties = """ |kotlin.mpp.stability.nowarn=true |org.gradle.cache=true """.trimMargin() - build() - } + build() + } } /** @@ -129,13 +134,13 @@ fun gradleKtsProjectTest( * @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, + testProjectName: String, + baseDir: Path = GradleProjectTest.funcTestTempDir, + build: GradleProjectTest.() -> Unit, ): GradleProjectTest { - return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { + return GradleProjectTest(baseDir = baseDir, testProjectName = testProjectName).apply { - settingsGradle = """ + settingsGradle = """ |rootProject.name = "test" | |dependencyResolutionManagement { @@ -155,48 +160,48 @@ fun gradleGroovyProjectTest( | """.trimMargin() - gradleProperties = """ + gradleProperties = """ |kotlin.mpp.stability.nowarn=true |org.gradle.cache=true """.trimMargin() - build() - } + build() + } } fun GradleProjectTest.projectFile( - @Language("TEXT") - filePath: String + @Language("TEXT") + filePath: String ): PropertyDelegateProvider> = - PropertyDelegateProvider { _, _ -> - TestProjectFileProvidedDelegate(this, filePath) - } + PropertyDelegateProvider { _, _ -> + TestProjectFileProvidedDelegate(this, filePath) + } /** Delegate for reading and writing a [GradleProjectTest] file. */ private class TestProjectFileProvidedDelegate( - private val project: GradleProjectTest, - private val filePath: String, + private val project: GradleProjectTest, + private val filePath: String, ) : ReadWriteProperty { - override fun getValue(thisRef: Any?, property: KProperty<*>): String = - project.projectDir.resolve(filePath).toFile().readText() + 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) - } + 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, + private val filePath: String, ) : ReadWriteProperty { - override fun getValue(thisRef: ProjectDirectoryScope, property: KProperty<*>): String = - thisRef.projectDir.resolve(filePath).toFile().readText() + 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) - } + override fun setValue(thisRef: ProjectDirectoryScope, property: KProperty<*>, value: String) { + thisRef.createFile(filePath, value) + } } @@ -205,38 +210,38 @@ annotation class ProjectDirectoryDsl @ProjectDirectoryDsl interface ProjectDirectoryScope { - val projectDir: Path + val projectDir: Path } private data class ProjectDirectoryScopeImpl( - override val projectDir: Path + override val projectDir: Path ) : ProjectDirectoryScope fun ProjectDirectoryScope.createFile(filePath: String, contents: String): File = - projectDir.resolve(filePath).toFile().apply { - parentFile.mkdirs() - createNewFile() - writeText(contents) - } + projectDir.resolve(filePath).toFile().apply { + parentFile.mkdirs() + createNewFile() + writeText(contents) + } @ProjectDirectoryDsl fun ProjectDirectoryScope.dir( - path: String, - block: ProjectDirectoryScope.() -> Unit = {}, + path: String, + block: ProjectDirectoryScope.() -> Unit = {}, ): ProjectDirectoryScope = - ProjectDirectoryScopeImpl(projectDir.resolve(path)).apply(block) + ProjectDirectoryScopeImpl(projectDir.resolve(path)).apply(block) @ProjectDirectoryDsl fun ProjectDirectoryScope.file( - path: String + path: String ): Path = projectDir.resolve(path) fun ProjectDirectoryScope.findFiles(matcher: (File) -> Boolean): Sequence = - projectDir.toFile().walk().filter(matcher) + projectDir.toFile().walk().filter(matcher) /** Set the content of `settings.gradle.kts` */ @@ -262,13 +267,13 @@ var ProjectDirectoryScope.buildGradle: String by TestProjectFileDelegate("build. /** Set the content of `gradle.properties` */ @delegate:Language("properties") var ProjectDirectoryScope.gradleProperties: String by TestProjectFileDelegate( - /* language=text */ "gradle.properties" + /* language=text */ "gradle.properties" ) fun ProjectDirectoryScope.createKotlinFile(filePath: String, @Language("kotlin") contents: String) = - createFile(filePath, contents) + createFile(filePath, contents) fun ProjectDirectoryScope.createKtsFile(filePath: String, @Language("kts") contents: String) = - createFile(filePath, contents) + createFile(filePath, contents) diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/KotestProjectConfig.kt similarity index 57% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/KotestProjectConfig.kt index d6eadba0cb..0972474cfc 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/KotestProjectConfig.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/KotestProjectConfig.kt @@ -1,10 +1,14 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + 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 - } + init { + displayFullTestPath = true + } } diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/fileTree.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/fileTree.kt new file mode 100644 index 0000000000..cdc3c6cdc0 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/fileTree.kt @@ -0,0 +1,66 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 = + 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/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/files.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/files.kt new file mode 100644 index 0000000000..78a7b5f9c4 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/files.kt @@ -0,0 +1,10 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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/gradle-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/gradleRunnerUtils.kt similarity index 70% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/gradleRunnerUtils.kt index 912d1df17b..43cc455db6 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/gradleRunnerUtils.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/gradleRunnerUtils.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.utils import org.gradle.testkit.runner.BuildResult @@ -9,24 +13,24 @@ 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.() -> Unit): GradleRunner { - val env = environment ?: mutableMapOf() - env.build() - return withEnvironment(env) + val env = environment ?: mutableMapOf() + env.build() + return withEnvironment(env) } inline fun GradleRunner.build( - handleResult: BuildResult.() -> Unit + handleResult: BuildResult.() -> Unit ): Unit = build().let(handleResult) inline fun GradleRunner.buildAndFail( - handleResult: BuildResult.() -> Unit + handleResult: BuildResult.() -> Unit ): Unit = buildAndFail().let(handleResult) fun GradleRunner.withJvmArguments( - vararg jvmArguments: String + vararg jvmArguments: String ): GradleRunner = (this as DefaultGradleRunner).withJvmArguments(*jvmArguments) @@ -35,13 +39,13 @@ fun GradleRunner.withJvmArguments( * [GradleRunner arguments][GradleRunner.getArguments]. */ fun GradleRunner.addArguments( - vararg arguments: String + vararg arguments: String ): GradleRunner = - withArguments(this@addArguments.arguments + arguments) + withArguments(this@addArguments.arguments + arguments) /** * Get the name of the task, without the leading [BuildTask.getPath]. */ val BuildTask.name: String - get() = path.substringAfterLast(':') + get() = path.substringAfterLast(':') diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestCollectionMatchers.kt similarity index 74% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestCollectionMatchers.kt index 8c33e3eb33..4a020e05c2 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/kotestCollectionMatchers.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestCollectionMatchers.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.utils import io.kotest.matchers.collections.shouldBeSingleton @@ -6,15 +10,15 @@ import io.kotest.matchers.maps.shouldContainExactly /** @see io.kotest.matchers.maps.shouldContainAll */ fun Map.shouldContainAll( - vararg expected: Pair + vararg expected: Pair ): Unit = shouldContainAll(expected.toMap()) /** @see io.kotest.matchers.maps.shouldContainExactly */ fun Map.shouldContainExactly( - vararg expected: Pair + vararg expected: Pair ): Unit = shouldContainExactly(expected.toMap()) /** Verify the sequence contains a single element, matching [match]. */ fun Sequence.shouldBeSingleton(match: (T) -> Unit) { - toList().shouldBeSingleton(match) + toList().shouldBeSingleton(match) } diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestConditions.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestConditions.kt new file mode 100644 index 0000000000..53f2e244bc --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestConditions.kt @@ -0,0 +1,14 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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): Boolean = + "win" !in System.getProperty("os.name").lowercase() +} diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestGradleAssertions.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestGradleAssertions.kt new file mode 100644 index 0000000000..1a76c71f59 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestGradleAssertions.kt @@ -0,0 +1,134 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 NamedDomainObjectCollection?.shouldContainDomainObject( + name: String +): T { + this should containDomainObject(name) + return this?.getByName(name)!! +} + +infix fun NamedDomainObjectCollection?.shouldNotContainDomainObject( + name: String +): NamedDomainObjectCollection? { + this shouldNot containDomainObject(name) + return this +} + +private fun containDomainObject(name: String): Matcher?> = + 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 = + 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) { + this should haveAnyOutcome(outcomes) +} + + +infix fun BuildTask?.shouldNotHaveOutcome(outcome: TaskOutcome) { + this shouldNot haveOutcome(outcome) +} + + +private fun haveOutcome(outcome: TaskOutcome): Matcher = + haveAnyOutcome(listOf(outcome)) + + +private fun haveAnyOutcome(outcomes: Collection): Matcher { + 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) { + this shouldHaveRunTask taskPath shouldHaveAnyOutcome outcomes +} + +fun BuildResult.shouldHaveTasksWithOutcome( + vararg taskPathToExpectedOutcome: Pair +) { + assertSoftly { + taskPathToExpectedOutcome.forEach { (taskPath, outcome) -> + shouldHaveTaskWithOutcome(taskPath, outcome) + } + } +} + +fun BuildResult.shouldHaveTasksWithAnyOutcome( + vararg taskPathToExpectedOutcome: Pair> +) { + assertSoftly { + taskPathToExpectedOutcome.forEach { (taskPath, outcomes) -> + shouldHaveTaskWithAnyOutcome(taskPath, outcomes) + } + } +} diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestStringMatchers.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestStringMatchers.kt new file mode 100644 index 0000000000..68ddb87f51 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/kotestStringMatchers.kt @@ -0,0 +1,69 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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? { + this should containAll(substrings) + return this +} + +infix fun String?.shouldNotContainAll(substrings: Iterable): 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) = + neverNullMatcher { 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? { + this should containAnyOf(substrings) + return this +} + +infix fun String?.shouldNotContainAnyOf(substrings: Iterable): 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) = + neverNullMatcher { 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/gradle-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/samWithReceiverWorkarounds.kt similarity index 83% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/samWithReceiverWorkarounds.kt index 62cd5860f7..09d31b7067 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/samWithReceiverWorkarounds.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/samWithReceiverWorkarounds.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + @file:Suppress("FunctionName") package org.jetbrains.dokka.dokkatoo.utils @@ -44,34 +48,34 @@ import org.gradle.api.artifacts.DependencySet private object Explain fun Project.subprojects_(configure: Project.() -> Unit) = - subprojects(configure) + subprojects(configure) @Suppress("SpellCheckingInspection") fun Project.allprojects_(configure: Project.() -> Unit) = - allprojects(configure) + allprojects(configure) fun DomainObjectCollection.configureEach_(configure: T.() -> Unit) = - configureEach(configure) + configureEach(configure) fun DomainObjectCollection.all_(configure: T.() -> Unit) = - all(configure) + all(configure) fun Configuration.withDependencies_(action: DependencySet.() -> Unit): Configuration = - withDependencies(action) + withDependencies(action) fun NamedDomainObjectContainer.create_(name: String, configure: T.() -> Unit = {}): T = - create(name, configure) + create(name, configure) fun NamedDomainObjectContainer.register_( - name: String, - configure: T.() -> Unit + name: String, + configure: T.() -> Unit ): NamedDomainObjectProvider = - register(name, configure) + register(name, configure) fun DokkaSourceSetSpec.sourceLink_( - action: DokkaSourceLinkSpec.() -> Unit + action: DokkaSourceLinkSpec.() -> Unit ): Unit = sourceLink(action) fun DokkaSourceSetSpec.perPackageOption_( - action: DokkaPackageOptionsSpec.() -> Unit + action: DokkaPackageOptionsSpec.() -> Unit ): Unit = perPackageOption(action) diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/stringUtils.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/stringUtils.kt similarity index 65% rename from dokka-runners/gradle-plugin/src/testFixtures/kotlin/stringUtils.kt rename to dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/stringUtils.kt index eb8777e75f..713381ee9b 100644 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/stringUtils.kt +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/stringUtils.kt @@ -1,8 +1,12 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo.utils fun String.splitToPair(delimiter: String): Pair = - substringBefore(delimiter) to substringAfter(delimiter) + substringBefore(delimiter) to substringAfter(delimiter) /** Title case the first char of a string */ @@ -10,12 +14,12 @@ fun String.uppercaseFirstChar(): String = mapFirstChar(Character::toTitleCase) private inline fun String.mapFirstChar( - transform: (Char) -> Char + 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) + lines() + .sorted() + .joinToString(separator) diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/systemVariableProviders.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/systemVariableProviders.kt new file mode 100644 index 0000000000..eb9a59e6ad --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/systemVariableProviders.kt @@ -0,0 +1,44 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 optionalSystemProperty( + convert: (String) -> T? +): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val value = System.getProperty(property.name) + if (value != null) convert(value) else null + } + + +internal fun systemProperty() = systemProperty { it } + +internal fun systemProperty( + convert: (String) -> T +): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val value = requireNotNull(System.getProperty(property.name)) { + "system property ${property.name} is unavailable" + } + convert(value) + } + + +internal fun optionalEnvironmentVariable() = optionalEnvironmentVariable { it } + +internal fun optionalEnvironmentVariable( + convert: (String) -> T? +): ReadOnlyProperty = + ReadOnlyProperty { _, property -> + val value = System.getenv(property.name) + if (value != null) convert(value) else null + } diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/text.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/text.kt new file mode 100644 index 0000000000..ded6d98890 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/org/jetbrains/dokka/dokkatoo/utils/text.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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.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/gradle-plugin/src/testFixtures/kotlin/systemVariableProviders.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/systemVariableProviders.kt deleted file mode 100644 index b15b3edbb6..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/systemVariableProviders.kt +++ /dev/null @@ -1,40 +0,0 @@ -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 optionalSystemProperty( - convert: (String) -> T? -): ReadOnlyProperty = - ReadOnlyProperty { _, property -> - val value = System.getProperty(property.name) - if (value != null) convert(value) else null - } - - -internal fun systemProperty() = systemProperty { it } - -internal fun systemProperty( - convert: (String) -> T -): ReadOnlyProperty = - ReadOnlyProperty { _, property -> - val value = requireNotNull(System.getProperty(property.name)) { - "system property ${property.name} is unavailable" - } - convert(value) - } - - -internal fun optionalEnvironmentVariable() = optionalEnvironmentVariable { it } - -internal fun optionalEnvironmentVariable( - convert: (String) -> T? -): ReadOnlyProperty = - ReadOnlyProperty { _, property -> - val value = System.getenv(property.name) - if (value != null) convert(value) else null - } diff --git a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/text.kt b/dokka-runners/gradle-plugin/src/testFixtures/kotlin/text.kt deleted file mode 100644 index ce0ebd9df0..0000000000 --- a/dokka-runners/gradle-plugin/src/testFixtures/kotlin/text.kt +++ /dev/null @@ -1,24 +0,0 @@ -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.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/gradle-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt deleted file mode 100644 index d35150a260..0000000000 --- a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/GradlePluginProjectIntegrationTest.kt +++ /dev/null @@ -1,110 +0,0 @@ -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 { - | override fun apply(project: Project) { - | println(objects.property().getOrElse("empty")) - | } - |} - - """.trimMargin() - ) - - createKotlinFile( - "MyCustomGradlePluginExtension.kt", - """ - |package com.project.gradle.plugin - | - |import org.gradle.api.provider.* - | - |interface MyCustomGradlePluginExtension { - | val versionProperty: Property - | val versionProvider: Provider - |} - | - """.trimMargin() - ) - } - - config() - } -} diff --git a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt deleted file mode 100644 index cac20f69df..0000000000 --- a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/MultiModuleFunctionalTest.kt +++ /dev/null @@ -1,468 +0,0 @@ -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/gradle-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginFunctionalTest.kt similarity index 62% rename from dokka-runners/gradle-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt rename to dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginFunctionalTest.kt index 90d587ce13..abd485a0b4 100644 --- a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/DokkatooPluginFunctionalTest.kt +++ b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/DokkatooPluginFunctionalTest.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants.DOKKATOO_VERSION @@ -9,28 +13,28 @@ import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder import io.kotest.matchers.string.shouldContain class DokkatooPluginFunctionalTest : FunSpec({ - val testProject = gradleKtsProjectTest("DokkatooPluginFunctionalTest") { - buildGradleKts = """ + 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 + } + + 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", @@ -45,33 +49,33 @@ class DokkatooPluginFunctionalTest : FunSpec({ "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('\\', '/') + 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) } + val dokkatooVariants = variants.lines() + .filter { it.contains("dokka", ignoreCase = true) } + .mapNotNull { it.substringAfter("Variant ", "").takeIf(String::isNotBlank) } - dokkatooVariants.shouldContainExactlyInAnyOrder( - "dokkatooModuleElementsGfm", - "dokkatooModuleElementsHtml", - "dokkatooModuleElementsJavadoc", - "dokkatooModuleElementsJekyll", - ) + dokkatooVariants.shouldContainExactlyInAnyOrder( + "dokkatooModuleElementsGfm", + "dokkatooModuleElementsHtml", + "dokkatooModuleElementsJavadoc", + "dokkatooModuleElementsJekyll", + ) - fun checkVariant(format: String) { - val formatCapitalized = format.uppercaseFirstChar() + fun checkVariant(format: String) { + val formatCapitalized = format.uppercaseFirstChar() - variants shouldContain /* language=text */ """ + variants shouldContain /* language=text */ """ |-------------------------------------------------- |Variant dokkatooModuleElements$formatCapitalized |-------------------------------------------------- @@ -88,41 +92,41 @@ class DokkatooPluginFunctionalTest : FunSpec({ | - build/dokka-module/$format (artifactType = directory) | """.trimMargin() - } + } - checkVariant("gfm") - checkVariant("html") - checkVariant("javadoc") - checkVariant("jekyll") - } - } + checkVariant("gfm") + checkVariant("html") + checkVariant("javadoc") + checkVariant("jekyll") + } + } - test("expect Dokka Plugin creates Dokka resolvable configurations") { + test("expect Dokka Plugin creates Dokka resolvable configurations") { - val expectedFormats = listOf("Gfm", "Html", "Javadoc", "Jekyll") + val expectedFormats = listOf("Gfm", "Html", "Javadoc", "Jekyll") - testProject.runner - .addArguments("resolvableConfigurations", "-q") - .build { - output.invariantNewlines().asClue { allConfigurations -> + 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) } + val dokkatooConfigurations = allConfigurations.lines() + .filter { it.contains("dokka", ignoreCase = true) } + .mapNotNull { it.substringAfter("Configuration ", "").takeIf(String::isNotBlank) } - dokkatooConfigurations.shouldContainExactlyInAnyOrder( - buildList { - add("dokkatoo") + 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" }) - } - ) + 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 */ """ + withClue("Configuration dokka") { + output.invariantNewlines() shouldContain /* language=text */ """ |-------------------------------------------------- |Configuration dokkatoo |-------------------------------------------------- @@ -132,12 +136,12 @@ class DokkatooPluginFunctionalTest : FunSpec({ | - org.jetbrains.dokka.dokkatoo.base = dokkatoo | """.trimMargin() - } + } - fun checkConfigurations(format: String) { - val formatLowercase = format.lowercase() + fun checkConfigurations(format: String) { + val formatLowercase = format.lowercase() - allConfigurations shouldContain /* language=text */ """ + allConfigurations shouldContain /* language=text */ """ |-------------------------------------------------- |Configuration dokkatooGeneratorClasspath$format |-------------------------------------------------- @@ -157,7 +161,7 @@ class DokkatooPluginFunctionalTest : FunSpec({ | """.trimMargin() - allConfigurations shouldContain /* language=text */ """ + allConfigurations shouldContain /* language=text */ """ |-------------------------------------------------- |Configuration dokkatooPlugin$format |-------------------------------------------------- @@ -175,7 +179,7 @@ class DokkatooPluginFunctionalTest : FunSpec({ | """.trimMargin() - allConfigurations shouldContain /* language=text */ """ + allConfigurations shouldContain /* language=text */ """ |-------------------------------------------------- |Configuration dokkatooPluginIntransitive$format |-------------------------------------------------- @@ -194,12 +198,12 @@ class DokkatooPluginFunctionalTest : FunSpec({ | - dokkatooPlugin$format | """.trimMargin() - } - - expectedFormats.forEach { - checkConfigurations(it) - } - } - } - } + } + + expectedFormats.forEach { + checkConfigurations(it) + } + } + } + } }) diff --git a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/GradlePluginProjectIntegrationTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/GradlePluginProjectIntegrationTest.kt new file mode 100644 index 0000000000..820b95f73d --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/GradlePluginProjectIntegrationTest.kt @@ -0,0 +1,114 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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 { + | override fun apply(project: Project) { + | println(objects.property().getOrElse("empty")) + | } + |} + + """.trimMargin() + ) + + createKotlinFile( + "MyCustomGradlePluginExtension.kt", + """ + |package com.project.gradle.plugin + | + |import org.gradle.api.provider.* + | + |interface MyCustomGradlePluginExtension { + | val versionProperty: Property + | val versionProvider: Provider + |} + | + """.trimMargin() + ) + } + + config() + } +} diff --git a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/KotlinMultiplatformFunctionalTest.kt similarity index 60% rename from dokka-runners/gradle-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt rename to dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/KotlinMultiplatformFunctionalTest.kt index 23a6744c0f..7c45f632af 100644 --- a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/KotlinMultiplatformFunctionalTest.kt +++ b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/KotlinMultiplatformFunctionalTest.kt @@ -1,3 +1,7 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + package org.jetbrains.dokka.dokkatoo import org.jetbrains.dokka.dokkatoo.internal.DokkatooConstants @@ -13,52 +17,52 @@ 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("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") { + 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("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("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 */ """ + 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 @@ -68,37 +72,37 @@ class KotlinMultiplatformFunctionalTest : FunSpec({ |${'$'}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") } + ) + } + + 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 = {}, + config: GradleProjectTest.() -> Unit = {}, ): GradleProjectTest { - return gradleKtsProjectTest("kotlin-multiplatform-project") { + return gradleKtsProjectTest("kotlin-multiplatform-project") { - settingsGradleKts += """ + settingsGradleKts += """ | |dependencyResolutionManagement { | @@ -136,7 +140,7 @@ private fun initKotlinMultiplatformProject( | """.trimMargin() - buildGradleKts = """ + buildGradleKts = """ |plugins { | kotlin("multiplatform") version "1.8.22" | id("org.jetbrains.dokka.dokkatoo") version "${DokkatooConstants.DOKKATOO_VERSION}" @@ -180,11 +184,11 @@ private fun initKotlinMultiplatformProject( | """.trimMargin() - dir("src/commonMain/kotlin/") { + dir("src/commonMain/kotlin/") { - createKotlinFile( - "Hello.kt", - """ + createKotlinFile( + "Hello.kt", + """ |package com.project | |import kotlinx.serialization.json.JsonObject @@ -196,11 +200,11 @@ private fun initKotlinMultiplatformProject( |} | """.trimMargin() - ) + ) - createKotlinFile( - "goodbye.kt", - """ + createKotlinFile( + "goodbye.kt", + """ |package com.project | |import kotlinx.serialization.json.JsonObject @@ -209,13 +213,13 @@ private fun initKotlinMultiplatformProject( |expect fun goodbye(json: JsonObject) | """.trimMargin() - ) - } + ) + } - dir("src/jvmMain/kotlin/") { - createKotlinFile( - "goodbyeJvm.kt", - """ + dir("src/jvmMain/kotlin/") { + createKotlinFile( + "goodbyeJvm.kt", + """ |package com.project | |import kotlinx.serialization.json.JsonObject @@ -224,13 +228,13 @@ private fun initKotlinMultiplatformProject( |actual fun goodbye(json: JsonObject) = println("[JVM] goodbye ${'$'}json") | """.trimMargin() - ) - } + ) + } - dir("src/jsMain/kotlin/") { - createKotlinFile( - "goodbyeJs.kt", - """ + dir("src/jsMain/kotlin/") { + createKotlinFile( + "goodbyeJs.kt", + """ |package com.project | |import kotlinx.serialization.json.JsonObject @@ -239,9 +243,9 @@ private fun initKotlinMultiplatformProject( |actual fun goodbye(json: JsonObject) = println("[JS] goodbye ${'$'}json") | """.trimMargin() - ) - } + ) + } - config() - } + config() + } } diff --git a/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/MultiModuleFunctionalTest.kt b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/MultiModuleFunctionalTest.kt new file mode 100644 index 0000000000..3ad73ec3c5 --- /dev/null +++ b/dokka-runners/gradle-plugin/src/testFunctional/kotlin/org/jetbrains/dokka/dokkatoo/MultiModuleFunctionalTest.kt @@ -0,0 +1,472 @@ +/* + * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license. + */ + +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() + } +}