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
Changes from 2 commits
4c8cfcd
d3c4c7c
baefed3
bf195e8
c7617d6
f7db0d3
91f0314
835c6e5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package org.jetbrains.conventions | ||
|
||
import org.gradle.kotlin.dsl.support.serviceOf | ||
|
||
/** | ||
* Utility for downloading and installing a Maven binary. | ||
*/ | ||
|
||
plugins { | ||
base | ||
} | ||
|
||
abstract class SetupMavenProperties { | ||
val mavenVersion = "3.5.0" | ||
val mavenPluginToolsVersion = "3.5.2" | ||
abstract val mavenBuildDir: DirectoryProperty | ||
abstract val mavenBinDir: DirectoryProperty | ||
abstract val mvn: RegularFileProperty | ||
} | ||
|
||
val setupMavenProperties = | ||
extensions.create("setupMavenProperties", SetupMavenProperties::class).apply { | ||
mavenBuildDir.set(layout.buildDirectory.dir("maven")) | ||
mavenBinDir.set(layout.buildDirectory.dir("maven-bin")) | ||
mvn.set(mavenBinDir.map { it.file("apache-maven-$mavenVersion/bin/mvn") }) | ||
} | ||
|
||
val mavenBinary by configurations.registering { | ||
description = "used to download the Maven binary" | ||
isCanBeResolved = true | ||
isCanBeConsumed = false | ||
isVisible = false | ||
|
||
defaultDependencies { | ||
add( | ||
project.dependencies.create( | ||
group = "org.apache.maven", | ||
name = "apache-maven", | ||
version = setupMavenProperties.mavenVersion, | ||
classifier = "bin", | ||
ext = "zip" | ||
) | ||
) | ||
} | ||
} | ||
|
||
tasks.clean { | ||
delete(setupMavenProperties.mavenBuildDir) | ||
delete(setupMavenProperties.mavenBinDir) | ||
} | ||
|
||
val installMavenBinary by tasks.registering(Sync::class) { | ||
val archives = serviceOf<ArchiveOperations>() | ||
from( | ||
mavenBinary.flatMap { conf -> | ||
conf.incoming.artifacts.resolvedArtifacts.map { artifacts -> | ||
artifacts.map { archives.zipTree(it.file) } | ||
} | ||
} | ||
) | ||
into(setupMavenProperties.mavenBinDir) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,10 @@ | ||
import org.jetbrains.SetupMaven | ||
import org.jetbrains.dependsOnMavenLocalPublication | ||
|
||
plugins { | ||
org.jetbrains.conventions.`dokka-integration-test` | ||
org.jetbrains.conventions.`maven-cli-setup` | ||
} | ||
|
||
evaluationDependsOn(":runners:maven-plugin") | ||
|
||
dependencies { | ||
implementation(project(":integration-tests")) | ||
implementation(kotlin("stdlib")) | ||
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||
IgnatBeresnev marked this conversation as resolved.
Show resolved
Hide resolved
|
||
environment("MVN_BINARY_PATH", mvn.get().asFile.invariantSeparatorsPath) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,95 +1,101 @@ | ||
import org.gradle.kotlin.dsl.support.appendReproducibleNewLine | ||
import org.jetbrains.CrossPlatformExec | ||
import org.jetbrains.SetupMaven | ||
import org.jetbrains.registerDokkaArtifactPublication | ||
|
||
plugins { | ||
org.jetbrains.conventions.`kotlin-jvm` | ||
org.jetbrains.conventions.`maven-publish` | ||
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}") | ||
implementation("org.apache.maven:maven-plugin-api:${setupMavenProperties.mavenVersion}") | ||
implementation("org.apache.maven.plugin-tools:maven-plugin-annotations:${setupMavenProperties.mavenPluginToolsVersion}") | ||
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.tpl.xml") | ||
aSemy marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
from(pomTemplateFile) { | ||
rename { it.replace(".tpl.xml", ".xml") } | ||
|
||
expand( | ||
"mavenVersion" to setupMavenProperties.mavenVersion, | ||
"dokka_version" to dokka_version, | ||
"mavenPluginToolsVersion" to setupMavenProperties.mavenPluginToolsVersion, | ||
) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. using the Gradle To achieve this, I needed to update the properties in |
||
} | ||
|
||
into(temporaryDir) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of worrying about Sync/Copy tasks overwriting, and having to use |
||
} | ||
|
||
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 | ||
|
||
dependsOn(tasks.compileKotlin, tasks.compileJava) | ||
from("$buildDir/classes/kotlin", "$buildDir/classes/java") | ||
into("${setupMaven.mavenBuildDir}/classes/java") | ||
from(tasks.compileKotlin.flatMap { it.destinationDirectory }) { into("classes/java/main") } | ||
from(tasks.compileJava.flatMap { it.destinationDirectory }) { into("classes/java/main") } | ||
|
||
preserve { | ||
include("**/*.class") | ||
} | ||
from(generatePom) | ||
|
||
into(setupMavenProperties.mavenBuildDir) | ||
} | ||
|
||
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") | ||
|
||
outputs.dir(layout.buildDirectory.dir("maven")) | ||
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) } | ||
} | ||
) | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unfortunately the generated I wrote this code to fix it - but maybe there's a Maven flag that will disable the timestamp? This is perhaps a bit OTT/pre-optimising. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I actually don't understand what helpMojo is supposed to achieve, and I can't google anything sensible. It looks like it should either generate the Nothing looks broken to me, so I think it's fine to ignore the timestamp. Perhaps, at some point our Maven plugin should be revisited as well 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it auto generates a 'help' command based on the properties in the Maven plugin The file it generates is in But, like you, I'm not sure! I've never developed a Maven plugin. package org.jetbrains.dokka.maven;
/**
* Display help information on dokka-maven-plugin.<br>
* Call <code>mvn dokka:help -Ddetail=true -Dgoal=<goal-name></code> to display parameter details.
* @author maven-plugin-tools
* @goal help
* @requiresProject false
* @threadSafe
*/
public class HelpMojo
extends AbstractMojo
{
/**
* If <code>true</code>, display all settable properties for each goal.
*
* @parameter property="detail" default-value="false"
*/
private boolean detail;
/**
* The name of the goal for which to show help. If unspecified, all goals will be displayed.
*
* @parameter property="goal"
*/
private java.lang.String goal;
/**
* The maximum length of a display line, should be positive.
*
* @parameter property="lineLength" default-value="80"
*/
private int lineLength;
... I was looking at other options, and I found this Gradle plugin: https://github.com/britter/maven-plugin-development. It might help with developing a Maven plugin, but on the other hand, the current approach seems to be working. |
||
} | ||
|
||
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") | ||
group = mavenPluginTaskGroup | ||
|
||
outputs.dir(layout.buildDirectory.dir("maven/classes/java/main/META-INF/maven")) | ||
} | ||
dependsOn(tasks.installMavenBinary, prepareMavenPluginBuildDir) | ||
workingDir(setupMavenProperties.mavenBuildDir) | ||
executable(setupMavenProperties.mvn.get()) | ||
args("-e", "-B", "org.apache.maven.plugins:maven-plugin-plugin:descriptor") | ||
|
||
val sourceJar by tasks.registering(Jar::class) { | ||
archiveClassifier.set("sources") | ||
from(java.sourceSets["main"].allSource) | ||
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 -> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very much optional, while we're at it: I think we actually forget to check/update the Maven version because it's buried in a place we don't visit that much. Extracting it to
gradle.properties
(or into thetoml
in the other PR) would not hurtThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, I think so too. For now I'll put it in
gradle.properties
, and I think that later they can be moved tolibs.versions.toml
as part of #2884There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was a little more work than I thought to refactor this, but I think it was worth it.
It revealed that the
CrossPlatformExec
task wasn't really needed, and could be replaced with a Provider that would determine the current operating system and usemvn.cmd
on Windows, else usemvn
.Hopefully the GitHub Workflow tests pass 🤞
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oof, sorry for asking you to do that then, and thanks for doing! I do think it was worth it though!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no apology needed :)