Gradle Project Build Order For Maven Tasks - maven

Consider the following multi-project build script:
build.gradle
subprojects {
apply plugin: 'java'
apply plugin: 'maven'
group = "myorg"
version = "1.0.0-SNAPSHOT"
}
project(':client') {
dependencies {
compile 'myorg:shared:1.0.0-SNAPSHOT'
}
}
With the following files:
├── build.gradle
├── client
│   └── src
│   └── main
│   └── java
│   └── myorg
│   └── client
│   └── MyOrgClient.java
├── settings.gradle
└── shared
└── src
└── main
└── java
└── myorg
└── shared
└── MyOrgObj.java
In the above files MyOrgClient.java includes myorg.shared.MyOrgObj and settings.gradle has the single line include 'client', 'shared'
Problem
The project/task build order for maven related tasks like installing locally and deploying to remote repositories does not take into account the implied project dependency. Because gradle does not know that 'myorg:shared:1.0.0-SNAPSHOT' is created by project(':shared'), the build order is :client -> :shared and causes errors like the one below:
$ gradle install
:client:compileJava
FAILURE: Build failed with an exception.
* What went wrong:
Could not resolve all dependencies for configuration ':client:compile'.
> Could not find myorg:shared:1.0.0-SNAPSHOT.
Required by:
myorg:client:1.0.0-SNAPSHOT
Question:
Is there a standard way to deal with this problem? I have tried these solutions without success:
Using mustRunAfter but ran into problems with tasks not existing yet. I also don't think this would scale well with a large number of projects
Adding archives project(':shared') to the client's dependencies
Adding compile project(':shared') to the client's dependencies and then removing it from the generated pom. Unfortunately this doesn't add the dependency to the install task or artifactoryPublish Edit: This actually was the solution. A project dependency will provide the correct version/name/group in the generated pom.xml so the explicit group:name:version dependency is not needed

You have to define the dependencies between the projects more or less the same way as in Maven:
For example like this:
project(':app') {
apply plugin: 'ear'
dependencies {
compile project (':webgui')
compile project (':service')
}
}
But you need to define the settings.gradle which contains the modules like this:
include 'app'
include 'domain'
include 'service'
include 'service-client'
include 'webgui'

Related

What is a proper way to include runtime classpath in compile classpath in Gradle?

I have a following Gradle project structure:
project-root/
├── adapters/
│ ├── adapter1/
│ │ ├── main
│ │ └── test
│ ├── adapter2/
│ │ ├── main
│ │ └── test
│ └── adapter3/
│ ├── main
│ └── test
└── app-spring-boot/
├── main
├── test
└── integrationTest
In the app-spring-boot module, the adapters are included only as runtime dependency:
// project-root/app-spring-boot/build.gradle.kts
dependencies {
runtimeOnly(project(":adapters:adapter1")
runtimeOnly(project(":adapters:adapter2")
runtimeOnly(project(":adapters:adapter3")
}
In the app-spring-boot module for integrationTest source set, I would like to be able to access all dependencies at compile time not only directly from app-spring-boot, but from all of the included :adapters projects as well.
I've used following configuration:
// project-root/app-spring-boot/build.gradle.kts
plugins {
`jvm-test-suite`
}
testing {
suites {
val test by getting(JvmTestSuite::class)
val integrationTest by registering(JvmTestSuite::class) {
useJUnitJupiter()
dependencies {
implementation(project())
}
sources {
compileClasspath += sourceSets.main.get().runtimeClasspath
}
}
}
}
compileClasspath += sourceSets.main.get().runtimeClasspath does the trick and all dependencies from included runtimeOnly projects are accessible at compile time, but I'm wondering what it is the correct and idiomatic Gradle way of doing it, especially since I saw #chalimartines comment.
I agree with the comment you found, saying that adding to the compile classpath is not the right way as you end up with duplicated dependencies.
When applying the test suites plugin, it will create a set of configurations similar to the ones from the main and test source sets, prefixed with the name of the test suite. Because your test suite is called integrationTest, the "implementation" configuration is named integrationTestImplementation.
With this, you can add the runtime dependencies to the compile classpath by making this implementation configuration of the test suite extend the regular runtimeClasspath configuration from the main source set. E.g.:
testing {
// ...
}
configurations["integrationTestImplementation"].extendsFrom(configurations["runtimeClasspath"])

How to tell Gradle and Intellij that the project's folder structure is different?

I'm using Gradle with the wrapper, and the folder structure by default is like so:
.
├── settings.gradle
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
However, I would like to change it to so:
.
├── gradle
| ├── build.gradle
│ ├── settings.gradle
│ ├── gradle.properties
│ └── wrapper
│ ├── gradlew
│ ├── gradlew.bat
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── src
├── main
└── test
Other than the fact that I don't know how to tell IntelliJ about the folder structure, I don't know how to change it for Gradle since the Environment Options related with changing the folder structure are deprecated:
-b, --build-file (deprecated)
Specifies the build file. For example: gradle --build-file=foo.gradle. The default is build.gradle, then build.gradle.kts.
-c, --settings-file (deprecated)
Specifies the settings file. For example: gradle --settings-file=somewhere/else/settings.gradle
You can't tell Gradle and Intellij IDEA that you use a non-standard Gradle build layout. And in all honesty, you shouldn't even consider that unless you have strong reasons to do so. There are mainly two reasons for that:
Developers familiar with one Gradle project feel immediately at home when starting with your Gradle project.
A non-standard build file and directory layout requires additional logic in IDE's (which is not present) and requires to provide extra parameters when building on the command line.
To put things into context, please have look at Gradle issue #16402.
Deprecate command-line options that describe the build layout
The -b and -c command-line options are effectively used to describe a non-standard build layout to Gradle. This is problematic because it means that a specific combination of options must be used whenever Gradle is used on that build, for example whenever invoked from the IDE, CI, command-line or some other tool. These command-line options also have some potentially surprising behaviours, such as running a settings script present in the target directory.
We don't think there are any use cases that are strong enough to justify keeping these options, and we should remove them (via deprecation). If we discover there are some use cases, we might consider replacing the options with more self-describing contracts, for example conventions for build script names.

Make gradle point to subdirectory and treat it as a rootProject

I encountered a problem with gradle project structure. I have a task that needs to be realized and some tests are meant to be executed to check whether my project structure is correct and the tasks in gradle execute correctly. However I think I misunderstood instruction a bit and I'm wondering whether I can do something with my current folders structure or If I will have to rewrite the whole project. My current project structure looks like this:
main-repo-folder/
├── docker-related-file
├── rootProject
│ ├── sub-project-1
│ ├── build(output from tasks is created here)
│ ├── build.gradle
│ ├── sub-project-2
│ ├── gradle
│ ├── gradlew
│ ├── gradlew.bat
│ ├── settings.gradle
│ └── src
As you can see, the root project is a directory inside a repo. In order for my tests to execute I think the repo itself must be a root folder (or act as one) because the tests seem to be trying executing there. And here is my question, is it possible to add f.e settings.gradle file in main-repo-folder (at the same level as rootProject folder) to "point" gradle to build from rootProject and treat that folder as the root?(I mean f.e if I call gradle clean build task_name in main-repo-folder I want to make gradle execute it as I would be in rootProject folder)
I've tried to find some information but I'm at the path of learning gradle and I don't know if it is even possible :/ .
Rename main-repo-folder/rootProject to main-repo-folder.

generate code coverage on JenkinsPipelineUnit tests

I am building a jenkins shared library (in groovy) and testing this with JenkinsPipelineUnit and in gradle. Running ./gradlew test jacocoTestReport runs fine, but the report is almost empty (just headers); no coverage is present.
Here are the relevant parts of my build.gradle:
plugins {
id 'groovy'
id 'application'
id 'jacoco'
}
dependencies {
compile 'org.codehaus.groovy:groovy-all:2.5.4'
testCompile 'junit:junit:4.12'
testCompile 'com.lesfurets:jenkins-pipeline-unit:1.1.1-custom' // minor adaptations, but that's another story
}
test {
systemProperty "pipeline.stack.write", System.getProperty("pipeline.stack.write")
}
jacocoTestReport {
group = "Reporting"
reports {
xml.enabled true
csv.enabled false
}
additionalSourceDirs = files('vars')
sourceDirectories = fileTree(dir: 'vars')
}
I think the trouble resides in the fact that my "source" files reside in the vars directory and not in src/groovy as expected in a normal groovy project. This is however a requirement for a Jenkins shared library.
I tried specifying
sourceSets {
main {
groovy {
srcDir 'vars'
}
}
}
but then gradle would start compiling this shared library while it's supposed to be loaded upon use; and this breaks everything...
My folder structure looks like this:
├── build.gradle
├── src
│   └── test
│   ├── groovy
│   │   └── TestSimplePipeline.groovy
│   └── resources
│   └── simplePipeline.jenkins
└── vars
├── MyPipeline.groovy
└── sh.groovy
I think my problem is linked to https://github.com/jenkinsci/JenkinsPipelineUnit/issues/119 , but I wouldn't know how to use the changes proposed for maven in gradle (not even sure they apply to jacoco).
The problem is that JenkinsPipelineUnit evaluates your scripts in runtime. It means jacoco agent cannot instrument the byte-code generated in runtime.
To overcome this issue you need to do two changes.
Use jacoco offline instrumentalisation
In my case I used maven, so I cannot provide you with a specific example of a gradle configuration.
Load compiled classes instead of groovy scripts in your test. Something like this:
def scriptClass = helper.getBaseClassloader().loadClass("fooScript")
def binding = new Binding()
script = InvokerHelper.createScript(scriptClass, binding)
InterceptingGCL.interceptClassMethods(script.metaClass, helper, binding)
Here fooScript is the name of the class (say you have a source file called fooScript.groovy in this case).
Now you can call methods of this class via
def result = script.invokeMethod(methodName, args)

How to include a Gradle distribution in the project to be used for the wrapper

I want to include a specific version of Gradle in the project folder so that when I use the Gradle wrapper it doesn't download it from the remote repository.
I downloaded the version of Gradle I need (gradle-4.0-bin.zip) and I put that zip fine inside of gradle/wrapper/ folder of the project (created with the gradle wrapper command).
Then I edited the gradle-wrapper.properties file in this way:
distributionUrl=file:///Users/pathj/to/the/project/gradle/wrapper/gradle-4.0-bin.zip
But when I run the first command, such as gradle task it returns:
What went wrong: A problem occurred configuring root project '03-gradle-wrapper-local'.
java.io.FileNotFoundException: /Users/myself/.gradle/wrapper/dists/gradle-4.0-bin/3p92xsbhik5vmig8i90n16yxc/gradle-4.0/lib/plugins/gradle-diagnostics-4.0.jar
(No such file or directory)
How do I tell Gradle to get the zip file from the current project folder, with a relative path, instead of downloading it, and to use that zip file to create a wrapper to be used in my builds?
Apart from storing gradle wrapper locally make sense or not it is possible. I assume that gradle-4.0-rc-3-bin distro is used.
Here is the project structure:
.
├── gradle
│   └── wrapper
│   ├── gradle-4.0-rc-3-bin.zip
│   ├── gradle-wrapper.jar
│   └── gradle-wrapper.properties
├── gradlew
└── gradlew.bat
And here the content of gradle-wrapper.properties:
distributionBase=PROJECT
distributionPath=gradle
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=gradle-4.0-rc-3-bin.zip
Since wrapper files will be downloaded to the project dir adding gradle/gradle-4.0-rc-3-bin to SCM ignore file is recommended.
Demo can be found here.

Resources