Why parent project classes is not accessable in modules - gradle

I am trying to access some files of parent project into sub modules, but i am unable to access that.
My project structure looks like
project
|---build.gradle
src
|---main
|--java
|---EnvConfig.java
API_MODULE
src
|----main
|---java
main build.gradle looks like
dependencies {
compile("org.springframework.boot:spring-boot-devtools")
compile project(":API_Module")
}
I want to access EnvConfig class in API_Module which is in parent, how i will do this. I am already compile API_Module in parent build.gradle, should in compile parent in child. I think it's not a good practice. Please suggest me the good practice.
Thanks

Not much experience in Gradle, but this is obviously more related to your design of packaging and dependency of modules.
For a typical Maven multi-module project (which should also be safe to apply in Gradle), what you are trying to achieve should look like:
project
+ project-core/ <- things share across modules
| + src/main/java/
| + EnvConfig
|
+ project-api/ <- Your original API module
| + dependency: project-core
|
+ project-mod2/ <- Some other module that need to use EnvConfig
| + dependency: project-core
|
+ project-main/ <- Your original top-level project,
| which build on top of project-api & project-mod2
+ dependency: project-core, project-api, project-mod2

This would not work as adding a dependency from API_MODULE to parent will create circular dependency.
Are you sure your main build.gradle needs to have a compile dependency to API_MODULE?

Related

Grade Force Version Not Work at Higher Level

I use below config to force using old version of library_common in project my_library
my_library
dependencies{
compile("library_A")
compile("library_B")
compile("library_common:old_version"){
force = true
}
}
Library_common is resolved to old_version, which match our expectation
dependency tree
+---library_A
| +---library_common: new_version -> old_version
+---library_B
| +---library_common: old_version
+---library_common: old_version
Another Project my_application then have dependencies on my_library and a non_related_lib
my_application
dependencies{
compile("my_library")
compile("non_related_lib"){transitive = false}
}
Library_common is somehow resolved to new_version which causes the conflicts in my_library.
dependency tree
+---my_library
| +---library_A
| | +---library_common: new_version
| \---library_B
| | +---library_common: old_version -> new_version
| \---library_common: old_version -> new_version
+---non_related_lib
I know I can force library_common version again in my_application, but it seems not right, as my_library should handle this conflict by itself.
It seems to me Gradle ignores force=true constraint declared in a direct dependency. Could somebody help explain if this is the way Gradle works?
Also, another thought is to exclude library_common in library_A and library_B. Will need to test it. But this way may become complicated when library_common is included as a transitive dependency of library_A/B, and nested level is deep.
Could someone provide any suggestions on the best practice to downgrade a dependency in a library project?
Thank you!!
If you want to force an older version of a transitive dependency, you should use dependency constraints. If your libraries are all published using Gradle, the constraints are also packaged within the Gradle module metadata, which means they will be preserved whenever you add your libraries as dependencies to some other project.
This all means, your buildscript has to change to something like this:
// my_library
dependencies{
compile("library_A") // do not specify a version of library_A here
constraints {
compile("library_A:version") // specify a version of library_A here
compile("library_common") { // do not specify a version of library_common here
version {
strictly(old_version) // specify a version of library_common here
}
}
}
compile("library_B")
}

build.gradle.kts & multi-module-project: how to add other project so that transitive dependencies are available too

Having the following multi-module-setup:
multi
├── projA
│ └── build.gradle.kts
├── projB
│ └── build.gradle.kts
├── build.gradle.kts
└── settings.gradle.kts
with the following content (abbreviated):
settings.gradle.kts
rootProject.name = "multi"
include("projA", "projB")
projA\build.gradle.kts
dependencies {
implementation("important-library:1.0")
}
projB\build.gradle.kts
dependencies {
implementation(project(":projA"))
}
Why don't I have access to that importantlibrary:1.0 from projB?
What works: if I have a class within projA that uses the library, it works perfectly even if that class is called from a class within projB (so indirect access works). Directly accessing any class from importantlibrary:1.0 within projB doesn't work (unresolved reference).
What am I missing here? Or what needs to be set up so that it works?
Gradle version: 5.6.1
I think a good way to achieve what you want would be to use api instead of implementation. implementation is meant to only keep the dependency inside the module, while api is meant to export them along with the module. The dependencies for projA would then become:
dependencies {
api("important-library:1.0")
}
This is the link to the official documentation: https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation
I found lots of sources mentioning configuration to handle how transitive dependencies are dealt with. Digging deeper I found that the default configuration should make the runtime, runtimeOnly and implementation available to the referencing project.
Either I misinterpreted "default" here or you really need to call it using the "default"-configuration explicitly. Declaring the dependency as follows in projB, made the dependencies from projA available to projB as well:
implementation(project(":projA", "default"))
// or with named parameters:
implementation(project(path = ":projA", configuration = "default"))
Wondering whether this is/was really intended or whether that is rather an unfortunate default value for the configuration-parameter of the project-function.
There are 2 themes: transitivity of dependencies and /src as dependency
Dependency transitivity
We use api so that the dependency can be exported to the higher hierarchy of modules.
build.gradle.kts (:library_base)
dependencies {
api("group", "name", "version")
}
build.gradle.kts (:common)
dependencies {
implementation(project(":domain"))
implementation(project(":library_base"))
}
Transitivity of /src as dependency
We pass "default" in the second parameter as configuration to be able to import the /src from the lower hierarchy of modules.
build.gradle.kts (:feature_a)
dependencies {
implementation(project(":common", "default"))
}
To be able to access from :feature_a to /src of eg. :domain we use "default" as configuration and to be able to access the dependencies of :library_base we make sure that they are defined with api in that module so that they can be exported.
GL
Source

How do I use Gradle to build a special JAR with only a subset of classes?

I have been given a project A that needs access to class files from another project B. More precisely, A only needs classes compiled from the B/ejb/C/src portion of the B/ tree:
B/ejb/C/src/com/company/admin/Foo.java
B/ejb/C/src/com/company/admin/FooHome.java
B/ejb/C/src/com/company/admin/FooBean.java
B/ejb/NOTNEEDED/src/com/company/data/...
The person who had this A project before used JBuilder and included in the source definition pointers to the parallel project's B/ejb/C/src. The A project builds a jar which includes classes compiled from this other tree. I'm trying to figure out how to do this using Gradle. I want to make a B/build.gradle in the B project that will create a B-C-version.jar of .class files compiled from these sources:
B/ejb/C/src/com/company/admin/Foo.java
B/ejb/C/src/com/company/admin/FooHome.java
B/ejb/C/src/com/company/admin/FooBean.java
that I would then publish to Maven and access from the A project.
i.e., the B-C-version.jar would ideally only have these classes:
com/company/admin/Foo.class
com/company/admin/FooHome.class
but if B-C-version.jar had these classes:
com/company/admin/*.class
that would also be OK. How can I make such a thing using a build.gradle in the B project?
You can simply declare a custom Jar task like
task cJar(type: Jar) {
baseName = project.name + '-C'
from sourceSets.main.output
include 'com/company/admin/Foo.class', 'com/company/admin/FooHome.class'
}
or you can make a dedicated sourceset for your api that you then use from your other B code and from your A code, then you don't need to work with includes and update the include if you need to add files, but you just place them in the source folder of the source set and you are done, something like
sourceSets { c }
task cJar(type: Jar) {
baseName = project.name + '-C'
from sourceSets.c.output
}
Then you could also declare dependencies separately and get the correct ones drawn in transitively and so on. But it might be overkill in your situation.

How to let subprojects choose their own repository configurations in a multi-project build?

I'm new to using build tools and working on multi-project environments in general but I'll try explain the best I can.
Structure
This is the directory structure. (there's more modules actually but I tried to keep it simple for the purpose of this question)
Project vcs repo root
+---Project A/
| build.gradle
| settings.gradle
|
+---Project B/
| build.gradle
|
+---Project C/
| | build.gradle
| \---libs/
|
+---Project D/
| build.gradle
| settings.gradle
|
+---Shared libs/
|
\---build.gradle
Project depedencies:
A depends on B and C.
D depends on B and A.
Each project can get its dependency from one of three places:
mavenCentral
a libs directory from its own root directory. (the libs dir under Project C is only for Project C to use)
Shared libs directory in the project's parent directory.
however the libs folders contents are not guaranteed to be in certain format, (it is not a dir with all jars, there might be sub dirs and such)
Goal
I want each project to be buildable from the project's root directory, and I do not want to keep a redundant copy of repositories/dependencies of a child project in a parent project build. I should only have to provide the locations and possibly version numbers of child projects in the parent's build file.
My Attempt
in C/build.gradle:
apply plugin: 'java'
repositories {
mavenCentral()
def myLibsDir = 'libs'
flatDir name: 'Foo', dirs: "$myLibsDir/foo/4.20"
//and then do a similar thing for shared libs if needed.
}
dependencies {
// atrifacts we can get from mavenCentral or 'C/libs/foo/4.20'
}
This works just fine for Project C, All tasks work as expected.
then when setting up A:
settings.gradle:
includeFlat 'Project B', 'Project C'
build.gradle:
repositories {
mavenCentral()
def sharedLibsDir = '../Shared libs'
flatDir name: 'Blazer', dirs: "$sharedLibsDir/blz/pls/13.37"
}
dependencies {
//non-subproject dependencies
compile project('Project B')
compile project('Project C')
}
this almost works. only problem is that, unlike dependencies, each sub project's repositories {} is ignored and the parent's is used instead. So if C depends on something in C/libs I'd have to include that in A/build.gradle 's repositories{}
my guess is I could also use something like this and then depend on the subproject's artifact(s) directly. Idk why exactly but this method seems harder to maintain.
This answer is obviously late, but I thought I'd post it for anybody else who runs into this problem (as I did). In the subproject's build.gradle, instead of writing:
repositories { ... }
you can write:
[rootProject, this].each {
it.repositories { ... }
}
This effectively copies the repository block to both the current subproject, and to whatever project is acting as root. The root project no longer needs to know about the repositories its dependents need, which should make this more maintainable.

Gradle multiprojects with same name, different paths

I have a monolith Gradle project that contains multiple subprojects, each with its own subprojects, like so:
project
|
|-- subprojectA
| |-- models
|
|-- subprojectB
| |-- models
This compiles fine but the issue is when I try to add a dependency on :subprojectB:models to :subprojectA:models, Gradle thinks :subprojectA:models is trying to add a dependency on itself and complains of a circular dependency, even though I specify the fully qualified path like so (in subprojectA's build.gradle):
compile project(':subprojectB:models')
How can I avoid this? Can subprojects not have the same name even if their paths are unique?
Project identity for dependency resolution is based on the group:name:version or GAV coordinates, as explained in the linked Gradle issue.
So you need to make sure your different models project have different GAVs.
One way to make this happen is to make the subprojectA (or B) part of the group.
Another way is to assign names that are not based on the containing folder.
That's currently a known Gradle issue as Gradle by default uses the parent directory name as the project name. You can work around the issue as described here by assigning unique subproject names in the root project's settings.gradle like so:
include ':subprojectA:models', ':subprojectB:models'
project(':subprojectA:models-a').projectDir = file('subprojectA/models')
project(':subprojectB:models-b').projectDir = file('subprojectA/models')

Resources