Skip to content

Commit

Permalink
Merge branch 'main' into tests/kn-func-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
aSemy authored Aug 31, 2023
2 parents 2e44a77 + 6624e8c commit 0575000
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 60 deletions.
2 changes: 1 addition & 1 deletion examples/multiplatform-example/dokkatoo/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
plugins {
kotlin("multiplatform") version "1.8.22"
kotlin("multiplatform") version "1.9.0"
id("dev.adamko.dokkatoo") version "2.0.0-SNAPSHOT"
}

Expand Down
1 change: 1 addition & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ gradlePlugin-dokkatoo = { module = "dev.adamko.dokkatoo:dokkatoo-plugin", versio
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]
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class KotlinMultiplatformExampleTest : FunSpec({
output shouldContainAll listOf(
"> Task :dokkatooGeneratePublicationHtml UP-TO-DATE",
"BUILD SUCCESSFUL",
"3 actionable tasks: 3 up-to-date",
"2 actionable tasks: 2 up-to-date",
)
withClue("Dokka Generator should not be triggered, so check it doesn't log anything") {
output shouldNotContain "Generation completed successfully"
Expand Down
1 change: 1 addition & 0 deletions modules/dokkatoo-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies {
implementation(libs.kotlin.dokkaCore)

compileOnly(libs.gradlePlugin.kotlin)
compileOnly(libs.gradlePlugin.kotlin.klibCommonizerApi)
compileOnly(libs.gradlePlugin.android)
compileOnly(libs.gradlePlugin.androidApi)

Expand Down
21 changes: 20 additions & 1 deletion modules/dokkatoo-plugin/src/main/kotlin/DokkatooBasePlugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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.*
Expand Down Expand Up @@ -98,7 +99,12 @@ constructor(
publicationEnabled.convention(true)
onlyIf("publication must be enabled") { publicationEnabled.getOrElse(true) }

generator.dokkaSourceSets.addAllLater(providers.provider { dokkatooExtension.dokkatooSourceSets })
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
Expand All @@ -115,6 +121,15 @@ constructor(
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"))
Expand Down Expand Up @@ -254,6 +269,10 @@ constructor(
private fun RegularFileProperty.convention(file: File): RegularFileProperty =
convention(objects.fileProperty().fileValue(file))

// workaround for https://github.com/gradle/gradle/issues/23708
private fun RegularFileProperty.convention(file: Provider<File>): RegularFileProperty =
convention(objects.fileProperty().fileProvider(file))

companion object {

const val EXTENSION_NAME = "dokkatoo"
Expand Down
11 changes: 11 additions & 0 deletions modules/dokkatoo-plugin/src/main/kotlin/DokkatooExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import dev.adamko.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
Expand Down Expand Up @@ -45,6 +46,16 @@ constructor(
*/
abstract val sourceSetScopeDefault: Property<String>

/**
* The Konan home directory, which contains libraries for Kotlin/Native development.
*
* This is only required as a workaround to fetch the compile-time dependencies in Kotlin/Native
* projects with a version below 2.0.
*/
// This property should be removed when Dokkatoo only supports KGP 2 or higher.
@DokkatooInternalApi
abstract val konanHome: RegularFileProperty

/**
* Configuration for creating Dokka Publications.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,19 @@ import com.android.build.gradle.api.ApplicationVariant
import com.android.build.gradle.api.LibraryVariant
import dev.adamko.dokkatoo.DokkatooBasePlugin
import dev.adamko.dokkatoo.DokkatooExtension
import dev.adamko.dokkatoo.adapters.DokkatooKotlinAdapter.Companion.currentKotlinToolingVersion
import dev.adamko.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec
import dev.adamko.dokkatoo.dokka.parameters.DokkaSourceSetIdSpec.Companion.dokkaSourceSetIdSpec
import dev.adamko.dokkatoo.dokka.parameters.DokkaSourceSetSpec
import dev.adamko.dokkatoo.dokka.parameters.KotlinPlatform
import dev.adamko.dokkatoo.internal.DokkatooInternalApi
import dev.adamko.dokkatoo.internal.collectIncomingFiles
import dev.adamko.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.artifacts.ConfigurationContainer
import org.gradle.api.attributes.Usage.JAVA_API
import org.gradle.api.attributes.Usage.USAGE_ATTRIBUTE
import org.gradle.api.file.ConfigurableFileCollection
import org.gradle.api.file.FileCollection
import org.gradle.api.logging.Logging
Expand All @@ -29,14 +27,21 @@ 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.
Expand Down Expand Up @@ -75,8 +80,7 @@ abstract class DokkatooKotlinAdapter @Inject constructor(
val compilationDetailsBuilder = KotlinCompilationDetailsBuilder(
providers = providers,
objects = objects,
configurations = project.configurations,
projectPath = project.path,
konanHome = dokkatooExtension.konanHome.asFile,
)
val allKotlinCompilationDetails: ListProperty<KotlinCompilationDetails> =
compilationDetailsBuilder.createCompilationDetails(
Expand Down Expand Up @@ -127,7 +131,7 @@ abstract class DokkatooKotlinAdapter @Inject constructor(
val kssClasspath = determineClasspath(details)

register(details.name) dss@{
suppress.set(!details.isMainSourceSet())
suppress.set(!details.isPublishedSourceSet())
sourceRoots.from(details.sourceDirectories)
classpath.from(kssClasspath)
analysisPlatform.set(kssPlatform)
Expand Down Expand Up @@ -157,9 +161,9 @@ abstract class DokkatooKotlinAdapter @Inject constructor(

@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()
Expand All @@ -175,6 +179,13 @@ abstract class DokkatooKotlinAdapter @Inject constructor(
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)
}
}
}

Expand All @@ -190,21 +201,18 @@ private data class KotlinCompilationDetails(
val target: String,
val kotlinPlatform: KotlinPlatform,
val allKotlinSourceSetsNames: Set<String>,
val mainCompilation: Boolean,
val compileDependencyFiles: FileCollection,
val publishedCompilation: Boolean,
val dependentSourceSetNames: Set<String>,
val compilationClasspath: FileCollection,
val defaultSourceSetName: String,
)

/** Utility class, encapsulating logic for building [KotlinCompilationDetails] */
private class KotlinCompilationDetailsBuilder(
private val objects: ObjectFactory,
private val providers: ProviderFactory,
private val configurations: ConfigurationContainer,
/** Used for logging */
private val projectPath: String,
private val konanHome: Provider<File>,
) {
private val logger = Logging.getLogger(KotlinCompilationDetails::class.java)

fun createCompilationDetails(
kotlinProjectExtension: KotlinProjectExtension,
Expand All @@ -231,9 +239,6 @@ private class KotlinCompilationDetailsBuilder(
val allKotlinSourceSetsNames =
compilation.allKotlinSourceSets.map { it.name } + compilation.defaultSourceSet.name

val compileDependencyFiles = objects.fileCollection()
.from(providers.provider { compilation.compileDependencyFiles })

val dependentSourceSetNames =
compilation.defaultSourceSet.dependsOn.map { it.name }

Expand All @@ -244,10 +249,10 @@ private class KotlinCompilationDetailsBuilder(
target = compilation.target.name,
kotlinPlatform = KotlinPlatform.fromString(compilation.platformType.name),
allKotlinSourceSetsNames = allKotlinSourceSetsNames.toSet(),
mainCompilation = compilation.isMain(),
compileDependencyFiles = compileDependencyFiles,
publishedCompilation = compilation.isPublished(),
dependentSourceSetNames = dependentSourceSetNames.toSet(),
compilationClasspath = compilationClasspath,
defaultSourceSetName = compilation.defaultSourceSet.name
)
}

Expand All @@ -266,55 +271,66 @@ private class KotlinCompilationDetailsBuilder(
private fun collectKotlinCompilationClasspath(
compilation: KotlinCompilation<*>,
): FileCollection {

val compilationClasspath = objects.fileCollection()
fun collectConfiguration(named: String) {
configurations.collectIncomingFiles(named = named, collector = compilationClasspath)

// need to fetch JAVA_RUNTIME files explicitly, because Android Gradle Plugin is weird and
// doesn't seem to register the attributes explicitly on its configurations
@Suppress("UnstableApiUsage")
configurations.collectIncomingFiles(named = named, collector = compilationClasspath) {
withVariantReselection()
attributes {
attribute(USAGE_ATTRIBUTE, objects.named(JAVA_API))

// 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)
}
lenient(true)
}
)
}

val standardConfigurations = buildSet {
addAll(compilation.relatedConfigurationNames)
addAll(compilation.kotlinSourceSets.flatMap { it.relatedConfigurationNames })
}
return compilationClasspath
}

logger.info("[$projectPath] compilation ${compilation.name} has ${standardConfigurations.size} standard configurations $standardConfigurations")
private fun kotlinNativeDependencies(konanHome: File, target: KonanTarget): FileCollection {
val konanDistribution = KonanDistribution(konanHome)

standardConfigurations.forEach { collectConfiguration(it) }
val dependencies = objects.fileCollection()

if (compilation is AbstractKotlinNativeCompilation) {
// K/N doesn't correctly set task dependencies, the configuration
// `defaultSourceSet.implementationMetadataConfigurationName`
// will trigger a bunch of Gradle warnings about "using file outputs without task dependencies",
// so K/N compilations need to explicitly depend on the compilation tasks
// UPDATE: actually I think is wrong, it's a bug with the K/N 'commonize for IDE' tasks
// see: https://github.com/Kotlin/dokka/issues/2977
collectConfiguration(
named = compilation.defaultSourceSet.implementationMetadataConfigurationName,
// builtBy = kotlinCompilation.compileKotlinTaskProvider
)
}
dependencies.from(konanDistribution.stdlib)

return compilationClasspath
// 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 {
private fun KotlinCompilation<*>.isMain(): Boolean {

/**
* 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 KotlinJvmAndroidCompilation ->
is KotlinMetadataCompilation<*> -> true

is KotlinJvmAndroidCompilation ->
androidVariant is LibraryVariant || androidVariant is ApplicationVariant

else ->
else ->
name == MAIN_COMPILATION_NAME
}
}
Expand All @@ -340,10 +356,10 @@ private abstract class KotlinSourceSetDetails @Inject constructor(
/** The specific compilations used to build this source set */
abstract val compilations: ListProperty<KotlinCompilationDetails>

/** Estimate if this Kotlin source set are 'main' sources (as opposed to 'test' sources). */
fun isMainSourceSet(): Provider<Boolean> =
/** Estimate if this Kotlin source set contains 'published' sources */
fun isPublishedSourceSet(): Provider<Boolean> =
compilations.map { values ->
values.any { it.mainCompilation }
values.any { it.publishedCompilation }
}

override fun getName(): String = named
Expand Down

0 comments on commit 0575000

Please sign in to comment.