Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Maven Runner build config #2911

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,103 @@
package org.jetbrains.conventions

import org.gradle.kotlin.dsl.support.serviceOf

/**
* Utility for downloading and installing a Maven binary.
*
* Provides the `setupMavenProperties` extension that contains the default versions and locations
* of the Maven binary.
*
* The task [installMavenBinary] will download and unzip the Maven bianry.
*/

plugins {
base
}

abstract class SetupMavenProperties {
abstract val mavenVersion: Property<String>
abstract val mavenPluginToolsVersion: Property<String>
abstract val mavenBuildDir: DirectoryProperty

/** Directory that will contain the unpacked Apache Maven dependency */
abstract val mavenInstallDir: DirectoryProperty

/**
* Path to the Maven executable.
*
* This should be different per OS:
*
* * Windows: `$mavenInstallDir/bin/mvn.cmd`
* * Unix: `$mavenInstallDir/bin/mvn`
*/
abstract val mvn: RegularFileProperty
}

val setupMavenProperties =
extensions.create("setupMavenProperties", SetupMavenProperties::class).apply {
mavenVersion.convention(providers.gradleProperty("mavenVersion"))
mavenPluginToolsVersion.convention(providers.gradleProperty("mavenPluginToolsVersion"))

mavenBuildDir.convention(layout.buildDirectory.dir("maven"))
mavenInstallDir.convention(layout.buildDirectory.dir("apache-maven"))

val isWindowsProvider =
providers.systemProperty("os.name").map { "win" in it.toLowerCase() }

mvn.convention(
providers.zip(mavenInstallDir, isWindowsProvider) { mavenInstallDir, isWindows ->
mavenInstallDir.file(
when {
isWindows -> "bin/mvn.cmd"
else -> "bin/mvn"
}
)
}
)
}

val mavenBinary by configurations.registering {
description = "used to download the Maven binary"
isCanBeResolved = true
isCanBeConsumed = false
isVisible = false

defaultDependencies {
addLater(setupMavenProperties.mavenVersion.map { mavenVersion ->
project.dependencies.create(
group = "org.apache.maven",
name = "apache-maven",
version = mavenVersion,
classifier = "bin",
ext = "zip"
)
})
}
}

tasks.clean {
delete(setupMavenProperties.mavenBuildDir)
delete(setupMavenProperties.mavenInstallDir)
}

val installMavenBinary by tasks.registering(Sync::class) {
val archives = serviceOf<ArchiveOperations>()
from(
mavenBinary.flatMap { conf ->
@Suppress("UnstableApiUsage")
val resolvedArtifacts = conf.incoming.artifacts.resolvedArtifacts

resolvedArtifacts.map { artifacts ->
artifacts.map { archives.zipTree(it.file) }
}
}
) {
eachFile {
// drop the first directory inside the zipped Maven bin (apache-maven-$version)
relativePath = RelativePath(true, *relativePath.segments.drop(1).toTypedArray())
}
includeEmptyDirs = false
}
into(setupMavenProperties.mavenInstallDir)
}
3 changes: 3 additions & 0 deletions gradle.properties
Expand Up @@ -14,6 +14,9 @@ jackson_version=2.12.7
# fixes CVE-2022-42003
jackson_databind_version=2.12.7.1
freemarker_version=2.3.31
# Dokka Maven Plugin versions
mavenVersion=3.5.0
mavenPluginToolsVersion=3.5.2
# Code style
kotlin.code.style=official
# Gradle settings
Expand Down
13 changes: 7 additions & 6 deletions integration-tests/maven/build.gradle.kts
@@ -1,12 +1,10 @@
import org.jetbrains.SetupMaven
import org.jetbrains.dependsOnMavenLocalPublication

plugins {
id("org.jetbrains.conventions.dokka-integration-test")
id("org.jetbrains.conventions.maven-cli-setup")
}

evaluationDependsOn(":runners:maven-plugin")

dependencies {
implementation(project(":integration-tests"))
implementation(kotlin("stdlib"))
Expand All @@ -16,10 +14,13 @@ dependencies {
tasks.integrationTest {
dependsOnMavenLocalPublication()

val setupMavenTask = project(":runners:maven-plugin").tasks.withType<SetupMaven>().single()
dependsOn(setupMavenTask)
Comment on lines -19 to -20
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of using a cross-project dependency to re-use the Maven exec, just download it again.

Gradle will cache the downloaded dependency, so it's fine to set it up twice in different subprojects.

dependsOn(tasks.installMavenBinary)
val mvn = setupMavenProperties.mvn
inputs.file(mvn)

val dokka_version: String by project
environment("DOKKA_VERSION", dokka_version)
environment("MVN_BINARY_PATH", setupMavenTask.mvn.absolutePath)
doFirst("workaround for https://github.com/gradle/gradle/issues/24267") {
environment("MVN_BINARY_PATH", mvn.get().asFile.invariantSeparatorsPath)
}
}
117 changes: 66 additions & 51 deletions runners/maven-plugin/build.gradle.kts
@@ -1,90 +1,105 @@
import org.jetbrains.CrossPlatformExec
import org.jetbrains.SetupMaven
import org.gradle.kotlin.dsl.support.appendReproducibleNewLine
import org.jetbrains.registerDokkaArtifactPublication

plugins {
id("org.jetbrains.conventions.kotlin-jvm")
id("org.jetbrains.conventions.maven-publish")
id("org.jetbrains.conventions.maven-cli-setup")
}

val setupMaven by tasks.register<SetupMaven>("setupMaven")

dependencies {
implementation(project(":core"))
implementation("org.apache.maven:maven-core:${setupMaven.mavenVersion}")
implementation("org.apache.maven:maven-plugin-api:${setupMaven.mavenVersion}")
implementation("org.apache.maven.plugin-tools:maven-plugin-annotations:${setupMaven.mavenPluginToolsVersion}")
implementation("org.apache.maven:maven-core:${setupMavenProperties.mavenVersion.get()}")
implementation("org.apache.maven:maven-plugin-api:${setupMavenProperties.mavenVersion.get()}")
implementation("org.apache.maven.plugin-tools:maven-plugin-annotations:${setupMavenProperties.mavenPluginToolsVersion.get()}")
implementation("org.apache.maven:maven-archiver:2.5")
implementation(kotlin("stdlib-jdk8"))
}

val mavenBuildDir = setupMaven.mavenBuildDir
val mavenBinDir = setupMaven.mavenBinDir

tasks.clean {
delete(mavenBuildDir)
delete(mavenBinDir)
}
val mavenPluginTaskGroup = "maven plugin"

val generatePom by tasks.registering(Copy::class) {
val generatePom by tasks.registering(Sync::class) {
description = "Generate pom.xml for Maven Plugin Plugin"
group = mavenPluginTaskGroup

val dokka_version: String by project
inputs.property("dokka_version", dokka_version)

from("$projectDir/pom.tpl.xml") {
rename("(.*).tpl.xml", "$1.xml")
}
into(setupMaven.mavenBuildDir)

eachFile {
filter { line ->
line.replace("<maven.version></maven.version>", "<maven.version>${setupMaven.mavenVersion}</maven.version>")
}
filter { line ->
line.replace("<version>dokka_version</version>", "<version>$dokka_version</version>")
}
filter { line ->
line.replace(
"<version>maven-plugin-plugin</version>",
"<version>${setupMaven.mavenPluginToolsVersion}</version>"
)
}
val pomTemplateFile = layout.projectDirectory.file("pom.template.xml")

val mavenVersion = setupMavenProperties.mavenVersion.get()
val mavenPluginToolsVersion = setupMavenProperties.mavenPluginToolsVersion.get()

from(pomTemplateFile) {
rename { it.replace(".template.xml", ".xml") }

expand(
"mavenVersion" to mavenVersion,
"dokka_version" to dokka_version,
"mavenPluginToolsVersion" to mavenPluginToolsVersion,
)
}

into(temporaryDir)
Copy link
Contributor Author

@aSemy aSemy Mar 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of worrying about Sync/Copy tasks overwriting, and having to use preserve {}, I think it's best to always use Sync into the task's temporary dir, and use a final Sync task (prepareMavenPluginBuildDir) to sync the files from the other Sync tasks in one go.

}

val syncClasses by tasks.registering(Sync::class) {
description = "Copy compiled classes to the Maven build dir, for Maven Plugin task execution"
val prepareMavenPluginBuildDir by tasks.registering(Sync::class) {
description = "Prepares all files for Maven Plugin task execution"
group = mavenPluginTaskGroup

from(tasks.compileKotlin.flatMap { it.destinationDirectory }) { into("classes/java/main") }
from(tasks.compileJava.flatMap { it.destinationDirectory }) { into("classes/java/main") }

dependsOn(tasks.compileKotlin, tasks.compileJava)
from("$buildDir/classes/kotlin", "$buildDir/classes/java")
into("${setupMaven.mavenBuildDir}/classes/java")
from(generatePom)

preserve {
include("**/*.class")
into(setupMavenProperties.mavenBuildDir)
}

val helpMojo by tasks.registering(Exec::class) {
group = mavenPluginTaskGroup

dependsOn(tasks.installMavenBinary, prepareMavenPluginBuildDir)

workingDir(setupMavenProperties.mavenBuildDir)
executable(setupMavenProperties.mvn.get())
args("-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:helpmojo")

outputs.dir(setupMavenProperties.mavenBuildDir)

doLast("normalize maven-plugin-help.properties") {
// The maven-plugin-help.properties file contains a timestamp by default.
// It should be removed as it is not reproducible and impacts Gradle caching
val pluginHelpProperties = workingDir.resolve("maven-plugin-help.properties")
pluginHelpProperties.writeText(
buildString {
val lines = pluginHelpProperties.readText().lines().iterator()
// the first line is a descriptive comment
appendReproducibleNewLine(lines.next())
// the second line is the timestamp, which should be ignored
lines.next()
// the remaining lines are properties
lines.forEach { appendReproducibleNewLine(it) }
}
)
}
}

val helpMojo by tasks.registering(CrossPlatformExec::class) {
dependsOn(setupMaven, generatePom, syncClasses)
workingDir(setupMaven.mavenBuildDir)
commandLine(setupMaven.mvn, "-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:helpmojo")
val pluginDescriptor by tasks.registering(Exec::class) {
group = mavenPluginTaskGroup

outputs.dir(layout.buildDirectory.dir("maven"))
}
dependsOn(tasks.installMavenBinary, prepareMavenPluginBuildDir)

val pluginDescriptor by tasks.registering(CrossPlatformExec::class) {
dependsOn(setupMaven, generatePom, syncClasses)
workingDir(setupMaven.mavenBuildDir)
commandLine(setupMaven.mvn, "-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:descriptor")
workingDir(setupMavenProperties.mavenBuildDir)
executable(setupMavenProperties.mvn.get())
args("-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:descriptor")

outputs.dir(layout.buildDirectory.dir("maven/classes/java/main/META-INF/maven"))
}

tasks.jar {
dependsOn(pluginDescriptor, helpMojo)
metaInf {
from("${setupMaven.mavenBuildDir}/classes/java/main/META-INF")
from(setupMavenProperties.mavenBuildDir.map { it.dir("classes/java/main/META-INF") })
}
manifest {
attributes("Class-Path" to configurations.runtimeClasspath.map { configuration ->
Expand Down
Expand Up @@ -4,17 +4,17 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.jetbrains.dokka</groupId>
<artifactId>dokka-maven-plugin</artifactId>
<version>dokka_version</version>
<version>${dokka_version}</version>
<packaging>maven-plugin</packaging>
<properties>
<maven.version></maven.version>
<maven.version>${mavenVersion}</maven.version>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>maven-plugin-plugin</version>
<version>${mavenPluginToolsVersion}</version>
<configuration>
<helpPackageName>org.jetbrains.dokka.maven</helpPackageName>
</configuration>
Expand Down