Skip to content

Commit

Permalink
fix Dokka javadoc jar set up (#559)
Browse files Browse the repository at this point in the history
* fix Dokka javadoc jar set up

* ktlint
  • Loading branch information
gabrielittner committed Apr 16, 2023
1 parent df8aee6 commit 899b4da
Show file tree
Hide file tree
Showing 7 changed files with 117 additions and 45 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -237,4 +237,25 @@ class MavenPublishPluginSpecialCaseTest {
assertThat(result).javadocJar().exists()
assertThat(result).javadocJar().isSigned()
}

@TestParameterInjectorTest
fun dokka() {
val kotlinVersion = KotlinVersion.values().last()
val original = kotlinJvmProjectSpec(kotlinVersion)
val project = original.copy(
plugins = original.plugins + dokkaPlugin,
basePluginConfig = original.basePluginConfig.replace("JavadocJar.Empty()", "JavadocJar.Dokka(\"dokkaHtml\")"),
)
val result = project.run(fixtures, testProjectDir, testOptions)

assertThat(result).outcome().succeeded()
assertThat(result).artifact("jar").exists()
assertThat(result).pom().exists()
assertThat(result).pom().matchesExpectedPom(kotlinStdlibJdk(kotlinVersion))
assertThat(result).module().exists()
assertThat(result).sourcesJar().exists()
assertThat(result).sourcesJar().containsAllSourceFiles()
assertThat(result).javadocJar().exists()
assertThat(result).javadocJar().containsFiles(ignoreAdditionalFiles = true, "index.html")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@ private fun TestOptions.supportsConfigCaching(plugins: List<PluginSpec>): Boolea
if (plugins.any { it.id == kotlinMultiplatformPlugin.id }) {
return false
}
// TODO https://github.com/Kotlin/dokka/issues/2231
if (plugins.any { it.id == dokkaPlugin.id }) {
return false
}
// publishing supports configuration cache starting with 7.6
// signing only supports configuration cache starting with 8.1
if (gradleVersion >= GradleVersion.GRADLE_8_1) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ val kotlinJsPlugin = PluginSpec("org.jetbrains.kotlin.js")
val kotlinAndroidPlugin = PluginSpec("org.jetbrains.kotlin.android")
val androidLibraryPlugin = PluginSpec("com.android.library")
val gradlePluginPublishPlugin = PluginSpec("com.gradle.plugin-publish")
val dokkaPlugin = PluginSpec("org.jetbrains.dokka", "1.8.10")

val fixtures = Paths.get("src/integrationTest/fixtures2").toAbsolutePath()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import com.vanniktech.maven.publish.PomSubject.Companion.pomSubject
import com.vanniktech.maven.publish.SourcesJarSubject.Companion.sourcesJarSubject
import java.io.StringWriter
import java.nio.file.Path
import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import kotlin.io.path.exists
import kotlin.io.path.inputStream
Expand Down Expand Up @@ -130,66 +131,67 @@ open class ArtifactSubject internal constructor(
failWithoutActual(fact("expected not to exist", signedArtifact))
}
}
}

class SourcesJarSubject private constructor(
failureMetadata: FailureMetadata,
private val artifact: Path,
private val result: ProjectResult,
) : ArtifactSubject(failureMetadata, artifact, result) {

companion object {
private val BUILD_RESULT_SUBJECT_FACTORY: Factory<SourcesJarSubject, Pair<Path, ProjectResult>> =
Factory { metadata, actual -> SourcesJarSubject(metadata, actual.first, actual.second) }

fun sourcesJarSubject() = BUILD_RESULT_SUBJECT_FACTORY
}

fun containsAllSourceFiles() {
containsSourceFiles(result.projectSpec.sourceFiles)
}

fun containsSourceSetFiles(vararg sourceSets: String) {
containsSourceFiles(result.projectSpec.sourceFiles.filter { sourceSets.contains(it.sourceSet) })
fun containsFiles(ignoreAdditionalFiles: Boolean, vararg files: String) {
containsMatchingFiles(
filesToFind = files.toList(),
filesToIgnore = emptyList(),
failWhenAdditionalFilesFound = !ignoreAdditionalFiles,
fileMatcher = { sourceFile, zipEntry -> zipEntry.name == sourceFile },
fileDescriptor = { it },
fileContent = { null },
)
}

private fun containsSourceFiles(sourceFiles: List<SourceFile>) {
protected fun <T : Any> containsMatchingFiles(
filesToFind: List<T>,
filesToIgnore: List<String>,
failWhenAdditionalFilesFound: Boolean,
fileMatcher: (T, ZipEntry) -> Boolean,
fileDescriptor: (T) -> String,
// only match file content if this does not return null
fileContent: (T) -> String?,
) {
val zip = ZipFile(artifact.toFile())
val zipFiles = zip.entries()
.toList()
.filter { !it.isDirectory && !it.name.contains("META-INF") && !it.name.contains("BuildConfig.java") }
.filter { zipEntry -> !zipEntry.isDirectory && filesToIgnore.none { zipEntry.name.contains(it) } }
.toMutableList()

val missingFiles = mutableListOf<String>()
val notMatchingFiles = mutableListOf<Fact>()

sourceFiles.forEach { sourceFile ->
filesToFind.forEach { sourceFile ->
// fallback is a workaround for KotlinJs creating a main folder inside the jar
val entry = zipFiles.find { it.name == sourceFile.file }
?: zipFiles.find { it.name == "${sourceFile.sourceSet}/${sourceFile.file}" }
val entry = zipFiles.find { fileMatcher(sourceFile, it) }
if (entry == null) {
missingFiles.add("${sourceFile.sourceSet}/${sourceFile.file}")
missingFiles.add(fileDescriptor(sourceFile))
} else {
zipFiles.remove(entry)

val content = zip.getInputStream(entry)?.reader()?.buffered()?.readText()
val expectedContent = sourceFile.resolveIn(result.project).readText()
if (content != expectedContent) {
notMatchingFiles += fact("expected ${sourceFile.file} to equal", expectedContent)
val expectedContent = fileContent(sourceFile)
if (expectedContent != null && expectedContent != content) {
notMatchingFiles += fact("expected ${fileDescriptor(sourceFile)} to equal", expectedContent)
notMatchingFiles += fact("but was", content)
}
}
}

val facts = mutableListOf<Fact>()

if (missingFiles.isNotEmpty()) {
facts += fact("expected to contain", missingFiles)
facts += simpleFact("but did not.")
}
if (zipFiles.isNotEmpty()) {
facts += fact("expected not to contain", zipFiles.map { it.name })
facts += simpleFact("but did.")

if (failWhenAdditionalFilesFound) {
if (zipFiles.isNotEmpty()) {
facts += fact("expected not to contain", zipFiles.map { it.name })
facts += simpleFact("but did.")
}
}

facts += notMatchingFiles

if (facts.isNotEmpty()) {
Expand All @@ -198,6 +200,41 @@ class SourcesJarSubject private constructor(
}
}

class SourcesJarSubject private constructor(
failureMetadata: FailureMetadata,
private val artifact: Path,
private val result: ProjectResult,
) : ArtifactSubject(failureMetadata, artifact, result) {

companion object {
private val BUILD_RESULT_SUBJECT_FACTORY: Factory<SourcesJarSubject, Pair<Path, ProjectResult>> =
Factory { metadata, actual -> SourcesJarSubject(metadata, actual.first, actual.second) }

fun sourcesJarSubject() = BUILD_RESULT_SUBJECT_FACTORY
}

fun containsAllSourceFiles() {
containsSourceFiles(result.projectSpec.sourceFiles)
}

fun containsSourceSetFiles(vararg sourceSets: String) {
containsSourceFiles(result.projectSpec.sourceFiles.filter { sourceSets.contains(it.sourceSet) })
}

private fun containsSourceFiles(sourceFiles: List<SourceFile>) {
containsMatchingFiles(
filesToFind = sourceFiles,
filesToIgnore = listOf("META-INF", "BuildConfig.java"),
failWhenAdditionalFilesFound = true,
fileMatcher = { sourceFile, zipEntry ->
zipEntry.name == sourceFile.file || zipEntry.name == "${sourceFile.sourceSet}/${sourceFile.file}"
},
fileDescriptor = { "${it.sourceSet}/${it.file}" },
fileContent = { it.resolveIn(result.project).readText() },
)
}
}

class PomSubject private constructor(
failureMetadata: FailureMetadata,
private val artifact: Path,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import org.gradle.api.JavaVersion
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.provider.Provider
import org.gradle.api.tasks.javadoc.Javadoc
import org.gradle.external.javadoc.StandardJavadocDocletOptions
import org.jetbrains.dokka.gradle.DokkaTask
Expand Down Expand Up @@ -73,7 +74,7 @@ private fun Project.configurePlatform() {

private fun Project.defaultJavaDocOption(): JavadocJar? {
return if (plugins.hasPlugin("org.jetbrains.dokka") || plugins.hasPlugin("org.jetbrains.dokka-android")) {
JavadocJar.Dokka(provider { findDokkaTask() })
JavadocJar.Dokka(findDokkaTask())
} else {
null
}
Expand Down Expand Up @@ -107,11 +108,7 @@ private fun Project.javaVersion(): JavaVersion {
return JavaVersion.current()
}

private fun Project.findDokkaTask(): String {
private fun Project.findDokkaTask(): Provider<String> = provider {
val tasks = project.tasks.withType(DokkaTask::class.java)
return if (tasks.size == 1) {
tasks.first().name
} else {
tasks.findByName("dokkaHtml")?.name ?: "dokka"
}
tasks.singleOrNull()?.name ?: "dokkaHtml"
}
11 changes: 8 additions & 3 deletions plugin/src/main/kotlin/com/vanniktech/maven/publish/Platform.kt
Original file line number Diff line number Diff line change
Expand Up @@ -437,10 +437,15 @@ sealed class JavadocJar {
* for that purpose.
*/
class Dokka private constructor(
internal val taskName: Any,
internal val taskName: DokkaTaskName,
) : JavadocJar() {
constructor(taskName: String) : this(taskName as Any)
constructor(taskName: Provider<String>) : this(taskName as Any)

internal sealed interface DokkaTaskName
internal data class StringDokkaTaskName(val value: String) : DokkaTaskName
internal data class ProviderDokkaTaskName(val value: Provider<String>) : DokkaTaskName

constructor(taskName: String) : this(StringDokkaTaskName(taskName))
constructor(taskName: Provider<String>) : this(ProviderDokkaTaskName(taskName))

override fun equals(other: Any?): Boolean = other is Dokka && taskName == other.taskName
override fun hashCode(): Int = taskName.hashCode()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.vanniktech.maven.publish.tasks

import com.vanniktech.maven.publish.JavadocJar as JavadocJarOption
import com.vanniktech.maven.publish.JavadocJar.Dokka.DokkaTaskName
import com.vanniktech.maven.publish.JavadocJar.Dokka.ProviderDokkaTaskName
import com.vanniktech.maven.publish.JavadocJar.Dokka.StringDokkaTaskName
import org.gradle.api.Project
import org.gradle.api.tasks.TaskProvider
import org.gradle.jvm.tasks.Jar
Expand Down Expand Up @@ -31,10 +34,14 @@ open class JavadocJar : Jar() {
}
}

private fun Project.dokkaJavadocJar(taskName: Any): TaskProvider<*> {
private fun Project.dokkaJavadocJar(taskName: DokkaTaskName): TaskProvider<*> {
return tasks.register("dokkaJavadocJar", JavadocJar::class.java) {
it.dependsOn(taskName)
it.from(taskName)
val task = when (taskName) {
is ProviderDokkaTaskName -> taskName.value.flatMap { tasks.named(it) }
is StringDokkaTaskName -> tasks.named(taskName.value)
}
it.dependsOn(task)
it.from(task)
}
}
}
Expand Down

0 comments on commit 899b4da

Please sign in to comment.