Issue getting Gradle apt plugin to work with QueryDSL, lombok & mapstruct - gradle

I am trying to get gradle apt plugin to work with:
QueryDSL
Mapstruct
Lombok
Here is what I have attempted:
plugins {
id 'net.ltgt.apt' version '0.10'
}
description = "Bignibou Common"
apply plugin: 'org.springframework.boot'
dependencyManagement {
dependencies {
dependency "org.elasticsearch:elasticsearch:${elasticsearchVersion}"
}
}
dependencies {
compile("org.springframework.boot:spring-boot-starter-data-jpa") {
exclude group: 'org.apache.tomcat', module: 'tomcat-jdbc'
}
compile("org.springframework.boot:spring-boot-starter-mail")
compile('org.springframework.security:spring-security-core')
compile('org.hibernate:hibernate-validator')
compile("org.hibernate:hibernate-java8")
compile("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
//Spring cloud
compile("org.springframework.cloud:spring-cloud-spring-service-connector")
compile("org.springframework.cloud:spring-cloud-localconfig-connector")
compile("org.springframework.cloud:spring-cloud-cloudfoundry-connector")
// Relational Database
compile("org.postgresql:postgresql:${postgresqlVersion}")
compile("org.flywaydb:flyway-core")
// Connection pooling
compile("com.zaxxer:HikariCP")
//Shield
compile("org.elasticsearch.client:x-pack-transport:${elasticsearchVersion}")
compile("org.elasticsearch:elasticsearch:${elasticsearchVersion}")
compile("org.apache.logging.log4j:log4j-api")
compile("org.apache.logging.log4j:log4j-core")
// QueryDSL
compile("com.querydsl:querydsl-core:${queryDslVersion}")
compile("com.querydsl:querydsl-jpa:${queryDslVersion}")
compileOnly('org.projectlombok:lombok')
compileOnly('org.mapstruct:mapstruct-jdk8:1.2.0.Beta3')
apt "com.querydsl:querydsl-apt:${queryDslVersion}", 'org.mapstruct:mapstruct-processor:1.2.0.Beta3', 'org.projectlombok:lombok'
// Jackson
compile("com.fasterxml.jackson.core:jackson-core")
compile("com.fasterxml.jackson.core:jackson-annotations")
compile("org.apache.httpcomponents:httpclient:${httpClientVersion}")
compile("org.jasypt:jasypt:${jasyptVersion}")
}
sourceSets {
main {
output.dir("build/generated-mail-templates")
}
}
bootRepackage {
enabled = false
}
task npmInstall(type: Exec) {
description "npm install"
commandLine 'npm', 'install'
}
task processMailTemplates {
description "Processes mail templates"
dependsOn npmInstall
outputs.upToDateWhen { false }
doLast {
def templateSrcDir = "src/main/templates/mail/"
def templateDestDir = "build/generated-mail-templates/META-INF/templates/mail/"
mkdir templateDestDir
def templateNames = []
fileTree(dir: templateSrcDir, include: '**/*.html').visit {
FileVisitDetails details -> templateNames << details.file.name
}
templateNames.each { templateName -> inlineCss(templateSrcDir + templateName, templateDestDir + templateName) }
}
}
static def inlineCss(src, dest) {
def juice = 'node_modules/.bin/juice'
def juiceResourcesDir = 'src/main/templates/misc/'
def juiceArgs = "--options-file ${juiceResourcesDir}juiceOptions.json --css ${juiceResourcesDir}mailStyle.css"
"${juice} ${juiceArgs} ${src} ${dest}".execute(null, new File('bignibou-common'))
}
compileJava {
aptOptions.processors = ['com.querydsl.apt.jpa.JPAAnnotationProcessor']
}
processResources.dependsOn processMailTemplates
Here is the error I get:
> Task :bignibou-common:compileJava
Putting task artifact state for task ':bignibou-common:compileJava' into context took 0.001 secs.
Resolving dependency management for configuration 'apt' of project 'bignibou-common'
Resolving global dependency management for project 'bignibou-common'
Excluding []
Resolving dependency management for configuration 'compileClasspath' of project 'bignibou-common'
Resolving dependency management for configuration 'compileOnly' of project 'bignibou-common'
Resolving dependency management for configuration 'implementation' of project 'bignibou-common'
Resolving dependency management for configuration 'compile' of project 'bignibou-common'
Excluding []
Executing task ':bignibou-common:compileJava' (up-to-date check took 0.628 secs) due to:
Task ':bignibou-common:compileJava' has additional actions that have changed
All input files are considered out-of-date for incremental task ':bignibou-common:compileJava'.
Excluding []
Compiling with JDK Java compiler API.
Note: Running JPAAnnotationProcessor
:bignibou-common:compileJava (Thread[Task worker,5,main]) completed. Took 0.727 secs.
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':bignibou-common:compileJava'.
> java.lang.NoClassDefFoundError: javax/persistence/Entity
Indicating that the javaCompile task has somehow lost the compile classpath...
Can someone please help?
edit: Taking into account Thomas Broyer's advice, I was able to come up with the following changes:
compileJava {
aptOptions.processors = ['com.querydsl.apt.jpa.JPAAnnotationProcessor', 'lombok.launch.AnnotationProcessorHider$AnnotationProcessor', 'org.mapstruct.ap.MappingProcessor']
}
compileOnly('org.projectlombok:lombok')
compileOnly('org.mapstruct:mapstruct-jdk8:1.2.0.Beta3')
apt "com.querydsl:querydsl-apt:${queryDslVersion}"
apt "org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.0.Final"
apt "org.mapstruct:mapstruct-processor:1.2.0.Beta3"
apt "org.projectlombok:lombok"
Notice the added hibernate-jpa-2.1-api apt dependency and the three explicit processors defined on the aptOptions.
Thanks a lot Thomas!

Apparently, with querydsl-apt, you need to add the additional dependencies required by the specific processor you're using; in this case you need to add the JPA API to the apt configuration.
Also, note that by configuring aptOptions.processors explicitly with only the QueryDSL processor, the Lombok and MapStruct processors won't run.

Related

gradle - Add custom configuration to installDist for a specific task

I'm trying to make a custom runtime dependency configuration, so that the specified dependencies will only be installed for a specific task. The dependencies are installed using the installDist task. So it seems like I need the configuration to be added to the runtimeClasspath for one task and not the other. I'm thinking I need a custom distribution, but I'm not sure how to set that to have a different runtimeClasspath.
In the example below, I want the run2 task to have the myRuntimeDep dependencies installed, but for the run1 task I do not.
I've be struggling to figure this out all day, does someone know what I'm missing?
Example build.gradle:
configurations {
myRuntimeDep.extendsFrom runtimeOnly
}
dependencies {
...
myRuntimeDep 'the:dependency:1.0'
}
task run1(type: JavaExec, dependsOn: installDist) {
// does not need myRuntimeDep dependencies
}
task run2(type: JavaExec, dependsOn: installDist) {
// needs myRuntimeDep dependencies
}
So after a long weekend, I sort of got something working. Maybe someone can tell me if there's a better way? Also, it doesn't fully work because it doesn't follow transitive dependencies with the configuration (which is kind of a pain because all sub-dependencies need to be manually added).
Solution:
top-level build.gradle
...
subprojects {
configurations {
fooRuntime.extendsFrom runtimeOnly
fooClasspath.extendsFrom runtimeClasspath, fooRuntime
}
distributions {
foo {
contents {
from installDist.destinationDir
from(configurations.fooClasspath) {
into 'lib'
}
}
}
}
installFooDist.dependsOn installDist
}
project A build.gradle
dependencies {
fooRuntime project(':projectB')
fooRuntime project(':projectC') // only need this because transitive dependencies won't work
}
task run(type: JavaExec, dependsOn: installFooDist) {
classpath = fileTree("$installFooDist.destinationDir/lib")
}
project B build.gradle
dependencies {
fooRuntime project(':projectC')
}
task run(type: JavaExec, dependsOn: installFooDist) {
classpath = fileTree("$installFooDist.destinationDir/lib")
}

Gradle - Generate source and compile

I'm migrating from Maven to Gradle and I have an issue with generated sources. Here's the build.gradle of one of the subprojects
plugins {
id 'war'
id 'net.ltgt.apt-idea' version '0.15'
}
dependencies {
...
compileOnly 'com.querydsl:querydsl-apt:4.2.1:jpa'
compileOnly 'org.hibernate:hibernate-jpamodelgen:1.3.0.Final'
}
def generatedSources = "${buildDir}/generated/source/apt/main"
def generatedOutputDir = file("$generatedSources")
task generateSources(type: JavaCompile, group: 'build') {
doFirst {
generatedOutputDir.exists() || generatedOutputDir.mkdirs()
sourceSets.main.java.srcDirs = ["${generatedSources}", 'src/main/java']
}
options.compilerArgs += [
'-processor', '-proc:none',
'org.hibernate.jpamodelgen.JPAMetaModelEntityProcessor,' +
'com.querydsl.apt.jpa.JPAAnnotationProcessor',
'-AaddSuppressWarningsAnnotation=true',
'-Aquerydsl.entityAccessors=true',
'-s', "${generatedSources}"
]
}
compileJava.finalizedBy generateSources
The classes are generated correctly, but right after the generation I receive the error "no suitable method found for..."
Basically the compilation fails because the generated classes are not taken into account during the compilation phase.
With the finalizedBy I was pretty sure it would work, but no..
Any ideas on how else I can get the class generation to run before the compileJava phase?
This line
compileJava.finalizedBy generateSources
Should be
compileJava.dependsOn generateSources
See https://docs.gradle.org/current/userguide/more_about_tasks.html#sec:adding_dependencies_to_tasks

Custom Metadata in Gradle dependency descriptor

This is how i have added dependencies in my build.gradle
// Dependency Versioning
apply plugin: 'io.spring.dependency-management'
dependencyManagement {
imports {
mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Edgware.RELEASE'
mavenBom 'io.pivotal.spring.cloud:spring-cloud-services-dependencies:1.5.0.RELEASE'
mavenBom 'org.springframework.boot:spring-boot-dependencies:1.5.15.RELEASE'
}
dependencies {
dependency 'io.springfox:springfox-swagger2:2.8.0'
dependency 'io.springfox:springfox-swagger-ui:2.8.0'
dependency 'org.flywaydb:flyway-core:4.2.0'
dependency 'com.microsoft.sqlserver:mssql-jdbc:6.2.2.jre8'
}
}
I am looking to add a custom-number with each dependency. This number is our Approval number provided by our Architecture team for approval of using that dependency within our enterprise..
Say if my Architecture team has Approved to use io.springfox:springfox-swagger2:2.8.0 dependency and if the approval number is APPL-1054 then i have to add this number also as a metadata along within the dependency tag with which i will have a different gradle task to consume those numbers
something that looks like dependency 'io.springfox:springfox-swagger2:2.8.0' : APPL-1054
Please help with your ideas
You could set the approvals in a Map then use dependency resolution to validate the approvals. The map could come from some web source as long as you can get it to a map somehow. Here is a simple example
buildscript {
repositories {
jcenter()
}
dependencies {
gradleApi()
}
}
group 'com.stackoverflow'
version '1.0-SNAPSHOT'
repositories {
jcenter()
}
configurations {
audited.extendsFrom(compile)
}
Map<String, Object> approvedDeps = [
'junit:junit:4.12': 'APPROVAL-1234'
]
dependencies {
compile gradleApi()
audited 'junit:junit:4.12'
audited 'org.mockito:mockito-android:2.22.0'
}
dependencies {
components {
all { ComponentMetadataDetails details ->
String requestedArtifact = "${details.id.group}:${details.id.name}:${details.id.version}"
String approvalCode = approvedDeps[requestedArtifact]
if (approvalCode == null) {
throw new GradleException("Use of unapproved dependency (${requestedArtifact})")
}
logger.lifecycle("Requested dependency (${requestedArtifact}) is approved: ${approvalCode}")
return details
}
}
}
// lets fake some action that would trigger dependency resolution
configurations.eachWithIndex { Configuration entry, int index ->
if (entry.canBeResolved) {
entry.resolve()
print("Resolved index: ${index}")
}
}
Now if we run ./gradlew clean build we get an error as an unapproved dependency was added.
$ ./gradlew clean build
> Configure project :
Requested dependency (junit:junit:4.12) is approved: APPROVAL-1234
FAILURE: Build failed with an exception.
* Where:
Build file '/Users/jonstanford/dev/stackoverflow/q52427676/build.gradle' line: 36
* What went wrong:
A problem occurred evaluating root project 'q52427676'.
> Could not resolve all dependencies for configuration ':audited'.
> There was an error while evaluating a component metadata rule for org.mockito:mockito-android:2.22.0.
> Use of unapproved dependency (org.mockito:mockito-android:2.22.0)
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 0s
Of course you could move this functionality to a custom plugin or such but I think the base idea holds.

Fail the Gradle build when dependencies are not available

build.gradle (unnecessary parts were ommited):
apply plugin: 'java'
repositories {
mavenCentral()
maven {
credentials {
username "$mavenUser"
password "$mavenPassword"
}
url "http://localhost:8081/nexus/content/groups/public"
}
}
dependencies {
compile("com.example:some-lib:1.0.0-RELEASE")
}
Assume that the defined dependency is missing in configured Maven repository. When ./gradlew clean build tasks are executed the application is built successfully, although the required dependencies are missing.
Is there a way to configure Gradle to fail if there are unresolved dependencies?
Relates to:
How to make Gradle fail the build if a file dependency is not found? - Solution provided there is not applicable.
Consider this build.gradle (note: intentionally bogus jar specified in dependencies):
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile("junit:junit:4.12")
compile("junit:junitxyz:51.50")
}
task checkDependencies() {
doLast {
configurations.compile.each { file ->
println "TRACER checking: " + file.name
assert file.exists()
}
}
}
compileJava.dependsOn checkDependencies
example output:
$ gradle -q clean compileJava
FAILURE: Build failed with an exception.
[snip]
* What went wrong:
Execution failed for task ':checkDependencies'.
> Could not resolve all files for configuration ':compile'.
> Could not find junit:junitxyz:51.50.
Searched in the following locations:
- https://repo.maven.apache.org/maven2/junit/junitxyz/51.50/junitxyz-51.50.pom
- https://repo.maven.apache.org/maven2/junit/junitxyz/51.50/junitxyz-51.50.jar

Gradle + PlayFramework: Cannot resolve sources dependency

I'm using the new Play Framework support in Gradle 2.7.
Ironically, Play 2.3.x explicitly depends on org.scala-sbt:io:0.13.8.
Gradle is able to resolve the JAR (not the sources, just the classes) from typesafe's repository if I add
model {
components {
play {
platform play: "2.3.7", scala: "2.10", java: "1.7"
}
}
}
repositories {
maven {
name "typesafe-maven-release"
url "https://repo.typesafe.com/typesafe/maven-releases"
}
ivy {
name "typesafe-ivy-release"
url "https://repo.typesafe.com/typesafe/ivy-releases"
layout "ivy"
}
}
dependencies {
play group: "org.scala-sbt", name: "io", version: "0.13.8", classifier: "jar", configuration: "compile"
}
however it seems that it cannot resolve the io-sources.jar. I get this:
FAILURE: Build failed with an exception.
What went wrong:
Execution failed for task ':runPlayBinary'.
Could not find io-sources.jar (org.scala-sbt:io:0.13.8).
Searched in the following locations:
https://repo.typesafe.com/typesafe/ivy-releases/org.scala-sbt/io/0.13.8/srcs/io.jar
I actually don't care about these sources, I just want to avoid this runtime exception when running gradlew runPlay
Execution exception
[RuntimeException: java.lang.NoClassDefFoundError: sbt/Path$]
Any advice? I can't seem to figure out how to exclude or resolve the sources dependency.
I ran into the same RuntimeException (NoClassDefFound sbt/Path$) with Play 2.4 and Gradle 2.7. In my case the root problem was to not define all repositories correctly (didn't include typesafe-ivy -> sbt-io was not resolved -> thought i need to state sbt-io-dependency -> wrong sbt-io led to mentioned Exception...).
I would advise you to add jcenter() as repository, remove the explicit dependency on sbt and state the play version in your build.gradle. As an example my working gradle.build:
plugins {
id 'play'
}
dependencies {
repositories {
jcenter()
maven {
name "typesafe-maven-release"
url "https://repo.typesafe.com/typesafe/maven-releases"
}
ivy {
name "typesafe-ivy-release"
url "https://repo.typesafe.com/typesafe/ivy-releases"
layout "ivy"
}
}
play 'com.typesafe.play:play-jdbc_2.11:2.4.3'
[...other dependencies - but not "org.scala-sbt"!]
}
model {
components {
play {
platform play: '2.4.3', scala: '2.11'
injectedRoutesGenerator = true
}
}
}
In your case the last part should be:
model {
components {
play {
platform play: '2.3.7', scala: '2.10'
}
}
}
A kind Gradle dev answered my question on the Gradle forums
TL;DR - Gradle/Play bug specific to 2.3.7 that can be resolved by using
repositories {
ivy {
url "https://repo.typesafe.com/typesafe/ivy-releases/"
layout "pattern", {
ivy "[organisation]/[module]/[revision]/ivys/ivy.xml"
artifact "[organisation]/[module]/[revision]/jars/[artifact].[ext]"
}
}
}
In my case, upgrading to Play 2.3.9 fixed my problem.

Resources