Detaching default functionality from gradle lifecycle tasks - java

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.

Related

JsonSchema2Pojo and manual task in gradle

What I would like to achieve
I would like to have a manual gradle task that I can generate Java classes based on Json schema. However, I don't want this task to run when I run other fx. gradle build.
What I did
Firstly I've create simple gradle java project with
gradle init
Then I have added jsonschema2dataclass plugin and configure it as follows (my current build.gradle):
/*
* This file was generated by the Gradle 'init' task.
* (...)
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
id "org.jsonschema2dataclass" version "4.5.0"
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
// Use JUnit Jupiter API for testing.
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.6.2'
// Use JUnit Jupiter Engine for testing.
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
// This dependency is used by the application.
implementation 'com.google.guava:guava:29.0-jre'
}
application {
// Define the main class for the application.
mainClass = 'GradlePlayground.App'
}
jsonSchema2Pojo {
includeGeneratedAnnotation = true
generateBuilders = true
targetPackage = 'org.example.api' // specify package for your needs
targetDirectoryPrefix = file("${project.rootDir}/app/src/main/java")
source.setFrom files("${project.rootDir}/app/src/main/resources/json")
}
What I've tried
Add task.enabled = false
Put plugin configuration into another task
Check source code of plugin to find a way to disconnect this task from build task
But all above trails have failed. When I run gradle tasks I can always see generateJsonSchema2DataClass and generateJsonSchema2DataClass0 as part of build tasks.
I'm using java 8 and gradle 6.9.3
I'm the author of the gradle plugin.
Short answer for your question is "no, it's not an intended flow for a normal project". However, it's always possible to create a new project, build it once and extract the sources.
Could you please explain, what the reason you want to exclude run from the build chain?
My only guess is to build and publish models. And if this is a case, it's possible to do this using some gradle magic, which described in discussions in the GitHub project
UPD: Based on your gradle script, I have a lot of "why" questions I'd like to ask, it'll be easier if you

Gradle 7.3 with Java 17 with task processIntTestResources: Entry [filename] is a duplicate but no duplicate handling strategy has been set

I am trying to update a gradle 6.x version multi project application to gradle 7.3 as it is the first version to support Java 17. However, I am unable to progress past an issue arising from a task which is not declared in any of my build.gradle files. The error is below: ([] pieces are redacted)
Execution failed for task ':[root module]:[module]:processIntTestResources'.
> Entry [filename] is a duplicate but no duplicate handling strategy has been set. Please refer to https://docs.gradle.org/7.3/dsl/org.gradle.api.tasks.Copy.html#org.gradle.api.tasks.Copy:duplicatesStrategy for details.
I think is associated with a sourceSet for integration tests, however, I never explicitly copy and files for those tests. I have also set all copy instructions to have this configuration with regard to duplicate handling:
duplicatesStrategy = DuplicatesStrategy.INCLUDE
I also tried adding the above to projects which failed because that setting is only valid for Copy type tasks or blocks. I am by no means an expert in gradle and can add any relevant information needed, but I believe I have included what might be most relevant. I am really just looking for a direction I can head in to further debug this issue.
You didn't show much about how you have integration tests configured, but I ran into a similar problem. What saved me was this blog post by Tom Gregory:
Running integration tests in Gradle
Since links can disappear, let me copy and paste the most important part that I found relevant, which is regarding the new (as of Gradle 7.3) JVM Test Suite Plugin that adds support for integration tests. For me, this replaced my old integration test configuration:
testing {
suites {
integrationTest(JvmTestSuite) {
dependencies {
implementation project
}
}
}
}
tasks.named('check') {
dependsOn testing.suites.integrationTest
}
I also found the following useful, which is not in the above blog post, but is a leftover from my previous Gradle 6 configuration. This for me duplicates the 'test' dependencies for 'integrationTest'. This is not the recommended way of handling the test depedencies now (see the JVM Test Suite Plugin documentation), but I still found it useful to get me back running quickly:
configurations {
integrationTestImplementation.extendsFrom testImplementation
integrationTestRuntime.extendsFrom testRuntime
}

In Buildship: how can I substitute a Gradle project with a built jar?

I would like to be a able to get Eclipse to ignore one Gradle project, and instead use a pre-built version of it.
Background
I have a project "parser" written in Scala, and a dozen others written in Java. The weakest link in my tool-set is Scala IDE. I use this plugin to edit & compile Scala code, but unfortunately it breaks the Java (JDT) tooling quite badly in mixed-language projects*.
Specifically: Call-hierarchy is missing results, searches crash and so on. Also Scala IDE appears to have lost funding and the issues sound fairly fundamental, so I'm not holding my breath for these issues to be fixed.
With Maven (m2e) I had a workaround I was quite happy with:
Build as a .jar put into my local .m2 repository:
cd parser; mvn install
In Eclipse, close the "parser" project
"Like magic", m2e simply picked up the most recent 'installed' .jar and used it in place of the closed project.
An awesome answer would be how to get Gradle to do that!
However all I wish for is any solution that meets these...
Requirements
That I can open Project parser when necessary (which is seldom),
to edit and build changes via the Gradle command-line.
I will close it when done.
Other projects use the built .jar from my local .m2 repo.
(It's fine if they always do so.)
The change must not affect others who don't use Eclipse
(ideally) the change can be used by other Eclipse users
Approaches
A similar question had this good answer by #lance-java with a number of general suggestions. I think I can rule out these ideas:
composite build support / multiple repos. Other team members wouldn't think it makes sense to build this project separately, as it is quite closely integrated with the others.
dependency substitution rules - doesn't appear to meet requirement 3.
Something along the lines of lance-java's idea #4 sounds viable. Paraphrasing...
"use the eclipse plugin [in conjunction with] Buildship, e.g. using the whenMerged hook to tweak the generated .classpath [of all the Java projects]."
UPDATE: [18 Apr]: I had hit a brick wall in this approach. Buildship was not putting the built .jar onto the runtime classpath. (UPDATE 2: Now resolved - see my answer.)
Questions
The main question: How can I structure a solution to this, that will actually work & avoid any major pitfalls?
Note that the project itself has a few dependencies, specifically:
dependencies {
compile 'org.scala-lang:scala-library:2.12.4'
compileOnly 'com.google.code.findbugs:jsr305:1.3.9'
antlr 'org.antlr:antlr4:4.5.3'
}
So a sub-question may be: How to pull these in into the other projects without duplicating the definition? (If that doesn't work automatically.)
So the solution was a bit involved. After adding 'maven-publish' to create the library, I then implemented the following to force Eclipse to use the prebuilt library:
subprojects {
// Additional configuration to manipulate the Eclipse classpaths
configurations {
parserSubstitution
}
dependencies {
parserSubstitution module("com.example:parser:${project.version}")
}
apply plugin: 'eclipse'
eclipse {
classpath {
plusConfigurations += [ configurations.pseLangSubstitution ]
file {
whenMerged { cp ->
// Get Gradle to add the depedency upon
// parser-xxx.jar via 'plusConfigurations' above.
// Then this here if we have a dependency on Project(':parser')
// - If so, remove it (completing the project -> jar substitution).
// - If not, remove the .jar dependency: it wasn't needed.
def usesParser = entries.removeAll {
it instanceof ProjectDependency && it.path.startsWith('/parser')
}
def parserJar =
cp.entries.find { it instanceof Library && it.path.contains('parser-') }
if (usesParser) {
// This trick stops Buildship deleting it from the runtime classpath
parserJar ?. entryAttributes ?. remove("gradle_used_by_scope")
} else {
cp.entries.remove { parserJar }
}
}
}
}
So there are 2 parts to this:
Using 'plusConfigurations' felt a bit round-about. I ended up doing this because I could not see how to construct class Library classpath entries directly. However it could well be that this is required to implement the 'transient dependencies' correctly anyway. (See the end of the question.)
The trick to stop Buildship removing the .jar from the runtime classpath (thus deviating from a Gradle command-line launch) was provided to me by a Gradle developer in this discussion.
Usage
The solution works just as I hoped. Every time some code in this library is modified, I execute the following task of mine on the command line (which also does some other code & resource generation steps, in addition to building the parser jar):
./gradlew generateEclipse
Then in Eclipse I press keyboard shortcuts for "Gradle -> Refresh Gradle Projects", Build.
And harmony is restored. :-)
Navigating to the (prebuilt) source of parser works.
If I need to edit the source, I can open the parser project and edit it. Scala-IDE still does a good job for this.
When I'm done I execute the command, close the project and my Java tools are happy.
In parser project
You shoud use the maven-publish plugin with the publishToMavenLocal task
apply plugin: 'maven-publish'
group = 'your.company'
version = '1.0.0'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
pom.withXml {
def root = asNode()
root.appendNode('name', 'Your parser project name')
root.appendNode('description', 'Your parser project description')
}
}
}
}
Everytime you make a modification, just change the version number if necessary and go with gradle publishToMavenLocal
In other java project using parser
Just use parser as a regular dependency :
repositories {
mavenLocal()
...
}
compile 'your.company:parser:1.0.0'
If my understanding of your situation is good, it should do the trick.

Maven-publish gradle plugin skips the version

I have gradle multi-module project configured with kotlin-script. I'd like to add publishing to maven repository and I found maven-publish plugin for it. But it seems to skip the version configured for each project:
MyProject/build.gradle.kts:
subprojects {
apply {
plugin("maven-publish")
}
configure<PublishingExtension>() {
publications {
repositories { ... }
create<MavenPublication>("myPublication") {
from(components.getByName("java"))
logger.lifecycle("test: ${project.group} ${project.name} ${project.version}")
}
}
MyProject/subproject1/build.gradle.kts:
version = "1.0.0-SNAPSHOT"
gradle publish output:
test: my.project subproject1 unspecified
artifact file does not exist: '.../MyProject/subproject1/build/libs/subproject1.jar'
File subproject1.jar doesn't exist, but subproject1-1.0.0-SNAPSHOT.jar does. How to make gradle get the correct version of module?
I found a similar problem while using the maven-publish plugin:
I was trying to set the repository URL depending on project version as described in the gradle docs here and this answer.
But I found the version always resolved to (as in the question) as the default (un-set) value: unspecified.
So I guess those documentation examples are for a project's build.gradle and not a general gradle script.
Anyway, I believe the problem is due to the timing of the execution of the blocks in the gradle script. The project.version could not be accessed where I wanted it. So I ended up passing a parameter to the gradlew command with the -Pparameter flag.
Gradle has a configuration and then an execution stage.
Refer to documentation:
https://docs.gradle.org/current/userguide/build_lifecycle.html
https://www.oreilly.com/library/view/gradle-beyond-the/9781449373801/ch03.html
and an apparently similar problem, https://discuss.gradle.org/t/maven-publication-closure-is-evaluated-too-early/19911
About your problem, it may be the same as I have described, or perhaps the reason is simpler:
Looking at the structure of your gradle file, it does not appear to match the hierarchy specified in the maven-publish documentation. In particular, repositories {} block should be at the same level as publications {}, not inside of it.
Possibly related:
Gradle maven publish plugin config has reference to dynamically created gradle task
Gradle shouldRunAfter not available for a task

How do I specify different (PMD, Checkstyle, Findbugs) rulesets for test and production code using Gradle?

I am Building a Java project with Gradle. I would like to separate tasks:
I would like to use strict rules (i.e. PMD, Checkstyle, Findbugs) for production code
as well as more relaxed rules (i.e. allowing duplicate Strings and magic numbers) for tests .
I have done this previously with ant (quite simple), and even though I know I could just call the ant tasks from Gradle, I would rather use the respective plugins.
How do I go about this?
my current build script for PMD is as follows:
apply plugin: 'pmd'
pmd {
ignoreFailures = true
ruleSetFiles = files("$staticAnalysisCfgDir/pmd/pmdruleset.xml")
toolVersion = '5.1.3'
sourceSets = [sourceSets.main, sourceSets.test]
}
Instead of configuring rule sets on the pmd extension, configure them on the pmdMain and pmdTest tasks (same syntax). Likewise for Checkstyle and FindBugs.

Categories

Resources