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
Related
I created a simple java app using gradle init. I am using Intellij 2021.2.2.
When i build the project i am getting following.
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java application project to get you started.
* For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
* User Manual available at https://docs.gradle.org/7.2/userguide/building_java_projects.html
*/
plugins {
// Apply the application plugin to add support for building a CLI application in Java.
id 'application'
id 'java'
}
repositories {
// Use Maven Central for resolving dependencies.
mavenCentral()
}
dependencies {
testImplementation('org.junit.jupiter:junit-jupiter:5.5.2')
}
application {
// Define the main class for the application.
mainClass = 'JunitPOC.App'
}
test{
useJUnitPlatform()
}
tasks.named('test') {
// Use JUnit Platform for unit tests.
useJUnitPlatform()
}
Looking into the dependencies i can see that all the dependencies are in the test compile & runtime path.
Looking a bit deeper into the sources i found that Intellij is not picking up the source file for junit.api jar just for Test Class correctly. All the other classes i can refer to.
Has anybody run into this issue? Any help would be appriciated.
I was able to make it working after going into FILE > Manage IDE Settings > Restore Default Settings.
I can successfully add a generated openapi client to my project via source sets. But then I have to copy dependencies into the main build-gradle, resolve conflicts -> I think it would be a better design to have the client as a subproject with its own build.gradle.
So I add include = 'build:openapi-java-client' to my settings.gradle and compile project(':build:openapi-java-client') to my dependencies. So that I have the following files:
build.gradle:
plugins {
id 'java'
id 'application'
id "org.openapi.generator" version "4.3.1"
}
repositories {
jcenter()
}
openApiGenerate {
generatorName = "java"
inputSpec = "$rootDir/specs/petstore.yaml".toString()
outputDir = "$buildDir/openapi-java-client".toString()
apiPackage = "org.openapi.example.api"
invokerPackage = "org.openapi.example.invoker"
modelPackage = "org.openapi.example.model"
configOptions = [
dateLibrary: "java8"
]
}
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
testImplementation 'junit:junit:4.13'
compile project(':build:openapi-java-client')
}
application {
mainClassName = 'a.aa.App'
}
and settings.gradle:
rootProject.name = 'simple-java-app'
include = 'build:openapi-java-client'
I execute openApiGenerate in advance, after adding it as a subproject, I do Gradle -> Refresh Gradle Project and Refresh.
Eclipse then shows me a problem:
Could not run phased build action using Gradle distribution 'https://services.gradle.org/distributions/gradle-6.5.1-bin.zip'.
Settings file 'C:\...\simple-java-app\settings.gradle' line: 11
A problem occurred evaluating settings 'simple-java-app'.
Could not set unknown property 'include' for settings 'simple-java-app' of type org.gradle.initialization.DefaultSettings.
I don't know where to go from here, addressing subprojects in subfolders worked just fine when I worked through https://guides.gradle.org/creating-multi-project-builds/ and put greeting-library in a subfolder.
You are trying to make build/ a project when that directory specifically is not meant to be a project directory. It's Gradle default build directory and likely 99% of other plugins and other Gradle plugins.
Simply change output directory to something else other than build/:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$rootDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
Then include the project in your build with the correct syntax:
// settings.gradle
include("openapi-java-client")
However, using the org.openapi.generator seems to generate an invalid build.gradle since I get the following error:
FAILURE: Build failed with an exception.
* Where:
Build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle' line: 23
* What went wrong:
Could not compile build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle'.
> startup failed:
build file 'C:\Users\fmate\code\example\openapi-java-client\build.gradle': 23: unexpected char: '\' # line 23, column 35.
main.java.srcDirs = ['src/main\java']
This obviously won't work how you wanted it to since it appears to be an issue with the Gradle plugin itself. If you just need to include the generate code in your project, then just include the generated Java code as part of your main Java source:
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
But with this approach, you'll run into missing imports/dependencies. It doesn't appear this plugin offers the ability to just generate the models/POJOs only, so updating the library property to native and including some missing dependencies manually, it all works:
plugins {
java
id("org.openapi.generator") version "5.0.0-beta"
}
repositories {
mavenCentral()
}
group = "io.mateo.test"
dependencies {
implementation(platform("com.fasterxml.jackson:jackson-bom:2.11.1"))
implementation("com.fasterxml.jackson.core:jackson-databind")
implementation("com.fasterxml.jackson.datatype:jackson-datatype-jsr310")
implementation("org.openapitools:jackson-databind-nullable:0.2.1")
implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("io.swagger:swagger-core:1.6.2")
}
openApiGenerate {
generatorName.set("java")
inputSpec.set("$rootDir/specs/petstore.json")
outputDir.set("$buildDir/openapi-java-client")
apiPackage.set("org.openapi.example.api")
invokerPackage.set("org.openapi.example.invoker")
modelPackage.set("org.openapi.example.model")
library.set("native")
configOptions.put("dateLibrary", "java8")
}
tasks {
compileJava {
dependsOn(openApiGenerate)
}
}
sourceSets {
main {
java {
srcDir(files("${openApiGenerate.outputDir.get()}/src/main"))
}
}
}
You cannot configure it alike this, because build most certainly is an output directory, which would create a circular reference. Better try to add a new module and add that generator plugin into that module. If you can configure another module as outputDir, this could be referenced.
Even if the plugin resides in the root project, the destination needs to be a module.
The point is, that the root project always executes, opposite to module configutions.
I’ve just answered a very similar question. While my answer there is not perfect, I would personally still prefer the approach suggested there – and kind of repeated here:
Suggested Approach
I would keep the builds of the modules that depend on the generated API completely separate from the build that generates the API. The only connection between such builds should be a dependency declaration. That means, you’ll have to manually make sure to build the API generating project first and only build the dependent projects afterwards.
By default, this would mean to also publish the API module before the dependent projects can be built. An alternative to this default would be Gradle composite builds – for example, to allow you to test a newly generated API locally first before publishing it. However, before creating/running the composite build, you would have to manually run the API generating build each time that the OpenAPI document changes.
Example
Let’s say you have project A depending on the generated API. Its Gradle build would contain something like this:
dependencies {
implementation 'com.example:api:1.0'
}
Of course, the simple-java-app build described in the question would have to be adapted to produce a module with these coordinates:
openApiGenerate {
// …
groupId = "com.example"
id = "api"
version = "1.0"
}
Before running A’s build, you’d first have to run
./gradlew openApiGenerate from your simple-java-app project.
./gradlew publish from the simple-java-app/build/openapi-java-client/ directory.
Then A’s build could fetch the published dependency from the publishing repository.
Alternatively, you could drop step 2 locally and run A’s build with an additional Gradle CLI option:
./gradlew --include-build $path_to/simple-java-app/build/openapi-java-client/ …
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 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
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