I have Project A and B in my system and B uses some of the packages defined in A and I have added some classes in project A but i am not able to reference them in B.
What I have tried
Did ./gradlew publishToMavenLocal in project A
Did ./gradlew build --refresh-dependencies
Can some please advise me how can I achieve this ?
Thanks
Assuming you did step 2 in project B, it should work (although I would like do a clean as well): ./gradlew clean build --refresh-dependencies
If you do this a lot (and esp if you find it tedious to type --refresh-dependencies every time, you could consider marking the dependency from project A as changing...
configurations.all { resolutionStrategy.cacheChangingModulesFor 0, "seconds" }
dependencies {
implementation("com.example.group:my-artifact:3.2.1")) { changing = true }
// ...
}
The configurations block tells Gradle how to handle changing modules, and the the changing = true will flag individual dependencies. Then, every Gradle build, it will try to update the dependency.
Related
I have a Gradle project (say project A) that depends on another Gradle project (say B). Each project live in their own git repository. Sometimes when I have to make a non-trivial change across both projects, I will make the code changes in B and push to remote where our CI will pick it up and release it as a new version.
Then in project A, I will update the version for B with the new version number, then run ./gradlew generateLock saveLock and then I will be able to use the new changes made in B to implement the feature in A.
I want to avoid having to push B to remote and release a new version before I can start using the new code in A. Ideally, I want to make change to B locally and have A reference the local version of the project. That way I can test both and test the whole feature before I push any code. How can I achieve that using Gradle. I am using Gradle 7.5?
I declare my dependency on B in A's build.gradle as implementation 'com.something.project-b'. Note that B might have a bunch of other library dependencies of its own declared in its own build.gradle which should still be transitively pulled when I want to use the local version of B in A.
Any pointers?
There is a local JAR option in Gradle but that will not pull all the transitive dependencies of B and I do not want to build a combined JAR of B and all its dependencies and use that since then I lose all dependency resolution benefits in case of conflicts.
Also note that B is an independent project and cannot simply be moved to Project A as a sub-project
You don't need to do any sort of local workstation hodgepodge. All you need is the following:
Declare mavenLocal() as the first repository in the dependent project (you probably already have mavenCentral().
Publish the dependency to Maven local repository using the task .gradlew publishToMavenLocal.
Now, your dependent project can use the latest code from the dependency.
in the project 'A'
settings.gradle
include ':prjB'
project (':prjB').projectDir = new File(settingsDir, '../relativePath')
build.gradle
def prjBExists = file('path-to-prjB').exists
dependencies {
...
if (prjBExists) {
implementation project(':prjB')
} else {
implementation 'g:a:v'
}
}
PS - this referencing is under discussions as gradle-8 is expected to deprecate ...
I have a Jenkins build where "goals" (or tasks, in Gradle) are passed as an environment variable to my Jenkinsfile.
e.g. sh "gradle ${GRADLE_TASKS}" where GRADLE_TASKS has the value "clean build" will run gradle clean build.
This works very well until I get to sub-projects.
Let's say I have subprojects
root
|-- com.mycompany
|-- com.mycompany.api
\-- com.mycompany.ui
As far as I'm aware, if I want to run a clean build on just com.mycompany.api I'd need the command:
gradle :com.mycompany.api:clean :com.mycompany.api:build
My problem is that the passed variable may represent one (or many) tasks to run.clean, build, clean build, deploy, clean deploy, clean publish deploy etc.
Note: I need to keep this to a solution that uses the Jenkinsfile (not the Gradle plugin, for example) because it is a hybrid build that doesn't only utilise Gradle.
My first instinct was to loop through the tasks (as below), but it seems Jenkins doesn't let you run loops like this.
def tasks = GRADLE_TASKS.split(' ')
tasks.each { task ->
sh "gradle :com.mycompany.api:${task}"
}
How should I go about this?
[edit] I should have also specified, is there some way to run this nested, so that I can clean all subprojects and then build?
e.g. The equivalent of:
gradle :com.mycompany.api:clean :com.mycompany.ui:clean
gradle :com.mycompany.api:build :com.mycompany.ui:build
I would do it like this (below) but obviously Jenkins doesn't like groovy closures...
tasks.each { task ->
subprojects.each {project ->
gradle :${project}:${task}
}
}
It looks like the answer provided by #tim_yates goes some way to answering the problem in a very 'groovy-like' manner.
sh "gradle ${GRADLE_TASKS.split(' ').collect { ":com.mycompany.api:${it}" }.join(' ')}"
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/ …
I build my project (call this project B) and some of its upstream dependency projects with Gradle composite builds. One of these upstream projects (call this project A) has an alternate source set configured to avoid producing warnings on generated code.
This is configured like:
sourceSets {
generated {
java {
srcDir "$buildDir/generated-sources/generated/main/java"
}
}
}
dependencies {
compile sourceSets.generated.compileClasspath
compile sourceSets.generated.output
}
compileGeneratedJava.options.warnings = false
jar { from sourceSets.generated.output }
This works fine building with gradle from the command line. But, in IntelliJ Idea, it imports the two source sets as separate modules: A_main and A_generated. It creates a dependency from B_main on A_main, but not on A_generated.
This results in run-time errors when running from IntelliJ IDEA. (B does not directly use any generated classes from A).
How can this be resolved?
The versions I'm using are:
IntelliJ IDEA: 2017.2.5
Gradle: 4.2.1
On a new environment gradle build takes quite a while because all dependencies have to be downloaded.
Is there a way to only download dependencies in order to speed up the following build?
That way we could for example already prefill a CI build environment.
Edit: Updated for Gradle 6+.
Some notes:
This new approach downloads jars into a folder, and then deletes the folder. So the result of having the jars in the Gradle cache is a side-effect.
It currently uses jars configured for the main source-set but could be generalized.
Even though it is neither efficient nor elegant, it can be useful if you actually want the jars (and transitive dependencies): simply comment-out the deletion of the runtime folder.
This solution can be handy when you want the jars (and transitive dependencies), as you simply have to comment-out deleting the folder.
Consider this build.gradle (as an arbitrary, concrete example):
apply plugin: 'java'
dependencies {
implementation 'org.apache.commons:commons-io:1.3.2'
implementation 'org.kie.modules:org-apache-commons-lang3:6.2.0.Beta2'
}
repositories {
jcenter()
}
task getDeps(type: Copy) {
from sourceSets.main.runtimeClasspath
into 'runtime/'
doFirst {
ant.delete(dir: 'runtime')
ant.mkdir(dir: 'runtime')
}
doLast {
ant.delete(dir: 'runtime')
}
}
Example run:
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
$ gradle getDeps
$ find /Users/measter/.gradle/caches -name "commons-io*1.3.2.jar"
/Users/measter/.gradle/caches/modules-2/files-2.1/commons-io/commons-io/1.3.2/[snip]/commons-io-1.3.2.jar
I've found ./gradlew dependencies (as suggested by this user) to be very handy for Docker builds.
You can create a custom task that resolves all the configurations( in doing so, it will also download the dependencies without building the project)
task downloadDependencies {
doLast {
configurations.findAll{it.canBeResolved}.each{it.resolve()}
}
}
Run command ./gradlew downloadDependencies
My answer will favor the gradle plugins and built-in tasks.
I would use "gradle assemble" in the command-line.
It is a minor version of "gradle build".
This way, you may reduce the time of your preparations before running or building anything.
Check the link bellow for the documentation:
https://docs.gradle.org/current/userguide/java_plugin.html#lifecycle_tasks
In general, what is my recipe when I clone a new repository:
-gradle assemble
-do some coding
-gradle run (and basically test until done)
-gradle build (to make distributable files)
note: this last step may have adicional configurations for .jar files as outputs (depends on you).