Gradle test library in multi module project - gradle

I want to create a library that other modules in my project can depend on, but only for tests. I've read the Gradle documentation extensively and couldn't find how to do it. This is my project structure:
gradle-example:
app:
src/main/kotlin ...
src/test/kotlin <- this is supposed to use 'com.example.SomeTestUtil'
testlib:
src/test/kotlin/com.example.SomeTestUtil
settings.gradle.kts:
rootProject.name = "gradle-example"
include("app")
include("testlib")
In the app module I tried adding
dependencies {
testImplementation(project(:testlib"))
}
But I get compilation error trying to import the SomeTestUtil in the app module test classes.
What is the correct way to declare dependency on a module in the same project that allows to use test sources in test code?
To clarify, we want to create a library that can only be used by other modules under src/test and not src/main.

Apparently this can be done by using the https://docs.gradle.org/current/userguide/java_testing.html#sec:java_test_fixtures
they can see the main source set classes
test sources can see the test fixtures classes
So changes to the example in the question are:
changing the src/test to src/testFixtures
declaring the dependency as testImplementation(testFixtures(project(":testlib")))
Adding java-test-fixtures plugin to the build.gradle.kts for the testlib module

Related

Multi module gradle: how to run module A before compiling modules B in a multi module project

We have a multi-module project in gradle. We have a "data" module that needs to be compiled (simple java 11 app) then run.
When we run java -jar data.jar after compiling the data module source is generated into the "webapp" module. With this generated source code we are now able to build the webapp module.
I am new to Gradle and struggling to figure out how do achieve this properly. I am especially concerned if I do it wrong I will make build caching fail.
Note: I am using Kotlin with Gradle 7.5.
The answer appears to be to create a custom configuration which produces a different artifact than the default.
task(taskJarGenerated, Jar::class) {
group = groupJooq
dependsOn(taskCompileGeneratedSource)
inputs.dir("$projectDir/build/classes/java/main")
outputs.file("${projectDir}/build/libs/data-1.0.jar")
from("$projectDir/build/classes/java/main")
}
artifacts {
add("generatedDbDataJar", tasks[taskJarGenerated])
}
Then you can reference this custom configuration "generatedDbDataJar" in other projects like this:
dependencies {
implementation(project(":data","generatedDbDataJar"))
}

import dependencies from another module

I think I am missing some point in how dependencies management works in gradle.
Let's say I have the following project structure:
project
---api
------api-commons
------api-v1
------api-v2
------api-v3
where all api* directories are modules. All api-v* need a particular dependency (let's say common-dependency).
My aim would be to import it in api-commons build.gradle file:
dependencies {
implementation 'common-dependency'
}
while in the build.gradle files of the other modules api-v* put:
dependencies{
implementation project(':api:api-commons')
}
I would expect this to work but it doesn't. The code in the api-v* modules simply acts like the dependency is not declared. Indeed, if I import the dependency in the single modules, the code works as expected.
Am I doing a wrong assumption? Doesn't the dependency inheritance work like that?
Declaring a dependency in the implementation configuration conceptually means it is internal to the module (it is used in the implementation but not part of the public API). Such a dependency will not be exposed to the compile classpath of consumers, though it will still be on the runtime classpath.
An advantage of this way of modelling dependencies is that you do not need to recompile consuming projects if an implementation dependency changes. Another is that by encapsulating them, it is less likely that a consumer will start depending on them directly and then breaking if you change them.
If you want to expose the dependency to consumers, you have to declare it as part of the API for the module. You do that by applying the java-library plugin and using the api configuration.
Example:
// api-commons/build.gradle
plugins {
id 'java-library'
}
dependencies {
api 'common-dependency'
}
Read more about it in the Gradle user guide
Let say, I have to moved below common code(*.java files) from below 2 service/modules to sharedprocessing-data which is present inside shared-libraries service/modules :
abc-service
xyz-servcie
Address.java
Employee.java
Department.java
Vaccation.java
Name.java
City.java
Order.java
Steps0:
In shared- service/modules folder create additional module inside existing shared-libraries module
Name it as sharedprocessing-data
Steps1:
Move(refactor) common code inside this module
Steps2:
In parent folder (root) update settings.gradle file
rootProject.name = "root"
include 'abc-service'
include 'xyz-service'
include 'shared-libraries:sharedprocessing-data'
Step3:
In each of abc-service and xyz-flow service modules, update build.gradle file
dependencies
{
implementation project(':shared-libraries:sharedprocessing-data')
}

how do you set up log4j for a gradle project with modules?

how do you set up log4j for a gradle project with modules?
I have a project set up like the following:
project root
build.gradle
gradle.properties
settings.gradle
// this root project does:
// include CommonModule
// includeBuild <all composite modules within module folder>
---CommonModule
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...<common-code>
------src/main/resources/log4j2.xml
Modules
---OtherModule-1
// this is a gradle composite module
// it also includes the common module
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...module1-code
------src/main/resources/log4j2.xml
---OtherModule-2
// this is a gradle composite module
// it also includes the common module
------build.gradle
------gradle.properties
------settings.gradle
------src/main/groovy/...module2-code
------src/main/resources/log4j2.xml
As shown above, we have a common gradle module and a module folder that contains module related composite module, each which depends on the included CommonModule
(core common code goes in common, the composite modules each contain code that extends common stuff
My question is hopefully simple:
where do I configure my log4j module?
e.g. can I put it in the common include module ?
or does each composite module need to have their own log4j xml?
I went and asked this same question on the gradle forums https://discuss.gradle.org/t/how-do-you-set-up-log4j-for-a-gradle-project-with-modules/28262 and got an answer -
Common module doesn’t include a log4j config Individual library
modules don’t include a log4j config either Top-level application
module does include a log4j config, but doesn’t put it on the
classpath (I set aside a src/[configuration]/config directory just
for that)
Launcher for the application itself (whether from a release
build or from a JavaExec in the Gradle build) specifies where the
log4j config is loaded from, so that you can’t get accidentally
poisoned by other configs on the classpath
you can define the xml location by providing a -D flag in the gradle properties file e.g. -Dlog4j.configurationFile=
also -Dlog4j.debug might help to track down issues

Setting dependency of a component to mantle-usl - custom Groovy class

I have a custom groovy class inside mantle-usl component. I would like to use the class in other component. Hence, I need to add a dependency so that the new component (or project) has the jar of mantle-usl ready for use.
Is there anyone who can help with this? I attempted to modify the build.gradle file of the project. And add a project dependency, but it returned an error.
project(':runtime/component/warehouse-items-masterenumerator') {
dependencies {
compile project(':runtime/component/mantle-usl')
}
}
As you would expect, this does not work. It seems that I do not have the project references set correctly.
The mantle-usl component doesn't have any compiled code in it so the build.gradle file does not build a jar file, it is only used for running the Spock tests.
I wouldn't recommend adding your own code to mantle-usl, it is easier and cleaner to put it in a separate component. For an example of a build.gradle file that does build a jar file look at the moqui/example component or most of the moqui tool components (such as moqui-elasticsearch).
You also don't need to modify the main build.gradle file from the moqui-framework repository, dependencies should be declared in the build.gradle file in each component (which are picked up automatically in the main build).

Gradle - Recursively include external project and its subprojects

I have a gradle project with many submodules named shared-library.
I have a project named service that depends on one of the modules of shared-library. e.g., it depends on :shared-library:module1. Normally, I get this dependency from maven.
Now I want to modify shared-library and test my changes using the dependent project. Instead of making a change to shared-library, building, deploying to maven, then rebuilding my service, I'd like to instead have service depend on the shared-library gradle project directly.
So I found out that you can point gradle to arbitrary project directories on the filesystem:
service/settings.gradle
include "shared-library"
project(":shared-library").projectDir = new File("/projects/shared-library")
But when I do this, the project is not aware of shared-library's submodules. I cannot do this:
service/build.gradle
compile(
project(":shared-library:module1"),
)
So I tried includeing them directly. :shared-library:module1 depends on :shared-library:module2 so I include that one as well:
service/settings.gradle
include "shared-library"
project(":shared-library").projectDir = new File("/projects/shared-library")
include "shared-library:module2"
include "shared-library:module1"
But now when I try to run this, it complains that :shared-library:module1 cannot locate a project named :module2. This is because its dependency is configured as such:
shared-library/module1/build.gradle
compile(
project(":module2")
)
But if I change that to an absolute project path, now shared-library cannot compile on its own:
shared-library/module1/build.gradle
compile(
project(":shared-library:module2")
)
tl;dr, it seems like there is a mismatch between the way service resolves the shared-library submodule names and how shared-library does it.
You're right. You can import an external project, or even external subprojects, and reference them in your main project, but as soon as you compile the external entities they fail to resolve with the expected names.
I found that you can rename the external projects in your main project so that they match the names of the external projects. That way your main project and the external projects use the same name.
Change your service/settings.gradle to:
include "shared-library"
project(":shared-library").projectDir = new File("/projects/shared-library")
include "shared-library:module2"
project('shared-library:module2').name = ':module2'
include "shared-library:module1"
project('shared-library:module1').name = ':module1'
Now in your project and external project refer to your modules always as :module1 and :module2. In service/build.gradle use:
compile(project(":module1"))

Resources