I am writing a custom Gradle plugin which shall unifiedly abstract from my custom gradle configurations which are spread across multiple Java projects. For this purpose, I generally try to find fitting extensions for the various tasks that need custom configuration.
So, I got to the point where I wanted to move my JaCoCo configuration to the plugin. The excerpt from build.gradle looks like this:
jacocoTestReport {
reports {
xml.required = true
}
}
The first part is manageable: check if the plugin is loaded.
project.getPlugins().withType(JacocoPlugin.class, jacocoPlugin -> {
// configure it
})
However, I am stuck with how to actually configure the plugin via an extension method. The only extension that is available seems to be JacocoPluginExtension. From there, I don't see a way how to add the reports part from build.gradle.
Is there some other mechanism besides extensions that I missed?
'jacocoTestReport' is actually a task, so you can configure it like this:
proj.tasks.getByName("jacocoTestReport", {
reports {
xml.required = true
}
})
If you'd like to dig deeper, it's class is JacocoReport, which has a
JacocoReportsContainer reports property, so you may use type safe property getters / setters from there, but IMHO the above solution is more like build.gradle, so it's more readable.
Related
Currently I'm working with an old Kotlin DSL build script that publishes an Android AAR library to a Maven repository. The dependencies are added to the pom-default.xml via simplistic iteration through the implementation configurations. Similar to this:
withXml {
val dependenciesNode = asNode().appendNode("dependencies")
configurations.getByName("implementation") {
dependencies.forEach {
val dependencyNode = dependenciesNode.appendNode("dependency")
dependencyNode.appendNode("groupId", it.group)
dependencyNode.appendNode("artifactId", it.name)
dependencyNode.appendNode("version", it.version)
}
}
}
This doesn't translate the exclusion statements, and so now publishes useless broken libraries with conflicting classes. I'd like to be able to use the Android Gradle Plugin's generated build artifact components instead, because I understand that by using these, the pom file will be automatically generated with the right list of dependencies (presumably with the exclusion clauses).
The documentation to do this is given here:
https://developer.android.com/studio/build/maven-publish-plugin
Unfortunately, as always, the example given is Groovy, and I cannot find any example of the syntax you would use in a Kotlin Gradle script.
from(components["release"])
...didn't work.
Okay, turns out...
from(components["release"])
...does work, I just didn't put in the...
afterEvaluate {
...clause, recklessly assuming this was just a Groovy thing.
Remember kids, stay away from drugs, work hard at school and follow the documentation faithfully, to a tee.
Tl;dr
Is there a way to detach specific plugin behavior (such as checkstyle's check behavior) from existing gradle lifecycle tasks (gradle check, in this particular case)?
Longer version
In our current gradle Java project setup, we've included checkstyle as one of our plugins for static code checking. It currently runs as a part of Jenkins pipeline through gradle's build task. While this has mostly worked out for what we've needed - namely running our tests and making sure we're sticking to code standards - I've also noticed that we could make our feedback loop a little faster if we could run just the checkstyle's plugin's checks before build kicks in the tests.
To do so, as far as I understand, we'd have to create a custom task that runs only the checkstyle functions checkstyleMain and checkstyleTest and decouple the default checkstyle behavior from gradle's build lifecycle task. I've been looking through both gradle and the checkstyle plugin's docs, but quickly found I'm out of my depth.
Code:
plugins {
id "checkstyle"
}
checkstyle {
toolVersion "8.24"
configFile file("config/checkstyle/checkstyle.xml")
}
checkstyleMain {
source = "src/main/java"
}
checkstyleTest {
source = "src/test/java"
}
That is everything checkstyle related inside of build.gradle, the check task itself isn't customized.
I have a java based gradle plugin that does some common configurations for our gradle projects. Now I want to add tasks like (currently present in our build.gradle for each project)
task javadocJar(type: Jar) {
classifier = "javadoc"
from javadoc
}
What I need is a hint on how to do this in a java based gradle plugin.
In another plugin I already register tasks using project.getTasks().create("myTask", MyTask.class); where MyTask extends AbstractTask and has a #TaskAction method to do its duty. But I'm found no way to adapt this to work with what i want to do with the sample above.
I tried to google for a solution but until now i did not find a helpfull soution as everything i find is using groovy or something similar to my snipped above directly in the build.gradle.
Thanks in advance!
Looks like I found a solution, at least for the javadoc task...
Task javadocTask = project.getTasks().getByName("javadoc");
project.getTasks().create("javadocJar", Jar.class, task -> {
task.dependsOn(javadocTask);
task.setClassifier("javadoc");
task.from(javadocTask);
});
this seems to work... but now i have an issue with the next gradle task:
task sourcesJar(type: Jar, dependsOn: classes) {
classifier = 'sources'
from sourceSets.main.allSource
}
until now i haven't found a way to get a hand on the sourceSets varialbe in Java.
Thanks in advance! :)
response to asettoufs comment:
The docs you have linked are related to projects with sub projects. What I have are multiple single projects that are not related. For those we have a plugin that already applies some plugins and configures them. so the projects have a common configuration by just applying our plugin.
Our plugin is written in java. And should continue to be java. Now we want to move some more common build config stuff to the plugin - for example some of those basic tasks as above.
To get your the source set you can ask the convention
project.getTasks().create("sourceJar", Jar.class, task -> {
task.setClassifier("sources");
// grab the convention that holds the sourceSets
JavaPluginConvention javaConvention = project.getConvention().getPlugin(JavaPluginConvention.class);
// find our source set
SourceSet mainSourceSet = javaConvention.sourceSets.findByName('main');
// user `from` like normal
task.from(mainSourceSet.getAllSource());
});
Some source code to see how the JavaPlugin creates the sourceSet written in Java
Creating the java convention
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L266
Creating the sourceSets
https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L298
my screencast, i hope my aExtFunc can work in build.gradle in any projects, but intellij idea raise
Cannot resolve symbol 'gradleExt'
my question is: how to fix it.
Defining dependency on the project
dependencies{
compile project(':gradleExt')
}
Doesn't mean that you can work with the content of the project in your build script:
task ATask() {
new gradleExt.Ext().aExtFunc()
}
You probably need to create new groovy class directly in the build.gradle file implementing desired behaviour. Or if you really want to have the implementation in different place you need to add the classpath dependecy into the buildScript{} block. See the documentation on implementing custom tasks:
http://www.gradle.org/docs/current/userguide/custom_tasks.html
If you want custom dsl, then custom gradle plugin is probably what you are looking for
http://www.gradle.org/docs/current/userguide/custom_plugins.html
And also please double check the documentation on multi-project builds if that might be of any use to you, it is not clear from the "my screencast" what your overall gradle setup is. I would post the link, but I don't have enough reputation.
In my plugin I call static bootstrapping method MutationCoverageReport.main(arg) from a library which is a compile dependency of my plugin. I would like to allow a plugin user to change the version of the library which should be used (assuming the selected version has compatible API).
I could advice user to exclude a transitive dependency from my plugin and add a dependency to the library in the requested version manually to buildscript.dependencies.classpath in his build.gradle, but this is not very elegant:
buildscript {
(...)
dependencies {
classpath('info.solidsoft.gradle.pitest:gradle-pitest-plugin:0.32.0') {
exclude(group: 'org.pitest')
}
classpath 'org.pitest:pitest-command-line:0.33'
}
}
I would like to be able to use libVersion parameter in my configuration closure, but I have some problems with two solutions I tried.
1. I could need to remove transitive dependencies from my plugin (an original library version and its dependencies) and add a library in requested version (from configuration closure) as a buildscript dependency.
In afterEvaluate it is to late (configurations are resolved) and I had problem to successfully hook with beforeEvaluate (is it triggered for single module project?).
2. I could change a classpath which is used to execute a static method from a library.
To do that I could create a custom class loader which would use my library classes (in requested version) before delegating to the parent class loader. I also would have to replace one liner with MutationCoverageReport.main(arg) with the reflection call using the new class loader.
Maybe there is a simpler way to modify a classpath by a plugin with which the mentioned method will be called?
Main questions. What would be the best way to allow plugin users to define the version of the library the plugin executes?
I'm not sure this is what you're looking for, but here's how the Jacoco plugin allows the user to configure the library being used.
JacocoPluginExtension
class JacocoPluginExtension {
// ...
String toolVersion = '0.32.0'
// ...
}
JacocoPlugin
class JacocoPlugin implements Plugin<Project> {
// ...
config.dependencies.add(this.project.dependencies.create("org.jacoco:org.jacoco.ant:${extension.toolVersion}"))
// ...
}
In another question about executing Java class with a separate classpath Peter Niederwieser provided 3 approaches. In the end modify my task to extend JavaExec. I set explicit set execution classpath which is separate from plugin execution classpath and can contain different library version.
The drawback is allows only to run main method, but it wasn't a problem in my case.