I'm setting up a multi-project Gradle build to replace an existing Ant build, where all the sub projects have their own build.gradle file in which the sub-project's dependencies are defined.
My sub projects all have their own dependencies, and there's a small subset of those (all 3rd party jars) that are only needed as a compile-time dependency in all the sub projects, like Maven's provided scope. (I have already found how I can create a configuration in Gradle that behaves like that.)
However, since this is a small set of jars, and they're the same for all sub projects, I'd like to extract the knowledge of which jars are provided out of the individual sub projects's dependency lists, and into the main build script, somehow. I want this so that developers who add a sub project to the system, don't need to know about which dependencies are provided and which aren't.
In other words, I would like to have a configuration that behaves as provided for a specific list of dependencies, and as compile for all the others.
I have this in the main build.gradle:
ext.providedDependencies = [
"org.apache.log4j:log4j:1.2.16",
"joda-time:joda-time:2.0"
]
...
configurations { myconfig }
And one or more of the sub projects have a build.gradle like this:
dependencies {
myconfig "org.apache.log4j:log4j:1.2.16"
myconfig "org.apache.commons:commons-lang3:3.3.2"
}
Then I want the behavior to be like this:
dependencies {
provided "org.apache.log4j:log4j:1.2.16"
compile "org.apache.commons:commons-lang3:3.3.2"
}
Because log4j is in ext.providedDependencies, and commons-lang3 isn't. I want this same behavior for all of my subprojects.
I have tried to (partially) implement myconfig like this:
subprojects {
dependencies {
configurations.myconfig.dependencies.each { dep ->
if (providedDependencies.contains(dep)) {
compile dep
}
}
}
}
But that does not work, because it turns out that configurations.myconfig.dependencies is empty (I don't understand why, though). In other words, the dependencies don't get added.
I also tried this, but here the opposite happens: all the dependencies are added, including those that are in the list of provided dependencies.
subprojects {
dependencies {
compile(configurations.myconfig) {
providedDependencies.each { exclude it }
}
}
}
Please let me know how I can make this work, or if there is a different, better way to achieve what I want.
EDIT Partially re-written for clarity
ext{
dependencies = [
"dependencyGroup:dependencyName:dependencyVersion",
"dependency2Group:dependency2Name:dependency2Version"
]
}
subprojects{
dependencies {
provided dependencies
}
configurations {
compile.exclude group: 'dependencyGroup'
compile.exclude group: 'dependency2Group'
}
}
In the above example, we have defined a list of dependencies with the name dependencies.
EDIT -- We are excluding the dependencies from the compile configuration if they need to be provided. This means that all even if a subproject adds a dependency that you want to be provided as compile, it won't happen.
Related
I am referring to my post here :Moving Jib configuration of one module into a new module, to refactor multi-module gradle project
With the same refactoring goal of keeping jib related configuration in a separate module, I want to inject some "runtimeOnly" dependencies from the new module so that jar gets added in the classpath.
['jib', 'jibDockerBuild', 'jibBuildTar'].each { jibTaskName ->
task "${jibTaskName}Webapp" {
doFirst {
project(':webapp').jib {
to.image = 'jib-webapp'
// and more ....
}
***project(':webapp').configurations {
dependencies {
runtimeOnly project(':abc')
}
}***
}
finalizedBy ":webapp:$jibTaskName"
}
task "${jibTaskName}App" {
doFirst {
project(':app').jib {
to.image = 'jib-app'
// and more ...
}
}
finalizedBy ":app:$jibTaskName"
}
}
but ./gradlew jibDockerBuildWebapp won't add the ":abc" module artifacts (jar) in the war lib directory.
Only way I am able to get this working by adding "runtimeOnly project(':abc') in the ":webapp" module's build.gradle file.. but that's not I intend to do.
Can you please suggest how to get this working?
I was searching for diff options in Jib settings if I could add the module dependency there for it to add the artifacts in the lib directory. I need to add additional jars to run a setup program before starting tomcat.
You can just add dependencies to a module from another module.
// to make sure :webapp is evaluated before this project
evaluationDependsOn(':webapp')
project(':webapp').dependencies {
runtimeOnly 'junit:junit:4.13'
}
['jib', 'jibDockerBuild', 'jibBuildTar'].each { jibTaskName ->
...
However, I'm not sure if this is a good practice. It's the same as defining dependencies in the target build.gradle, and the only difference is that it's being done at a different place. I think this may confuse first-time readers of your repo as to how and why some dependencies are added out of the blue.
BTW, the following also worked for me:
...
task "${jibTaskName}Webapp" {
doFirst {
project(':sub1') {
jib.to.image='jib-sub1'
dependencies {
runtimeOnly 'junit:junit:4.13'
}
}
}
}
However, it's not exactly same as above in that the dependencies are added in a later phase (not in the Gradle configuration phase) and only when the task is executed. Again, I'm not sure if this is a good practice.
Can someone explain to me how depedencies listed in the "buildscript" in the build.gradle file are different than regular dependencies listed in the dependencies block { } ? and why they have to be listed with the syntax "implementation"? I've googled this and responses say the dependencies in the buildscript and used to "build the project" but I don't understand this? can anyone give a more clear picture and answer?
buildscript:
buildscript
{
repositories
{
maven {
url 'myMavenFeed'
credentials {
username "myUsername"
password myPassword
}
}
mavenCentral()
jcenter()
}
dependencies
{
classpath "com.microsoft.azure.sdk.iot:iot-device-client:1.14.1"
}
}
Dependencies block:
dependencies
{
compile group: 'com.microsoft.azure.sdk.iot', name: 'iot-device-client', version: '1.16.0'
}
Can someone explain to me how depedencies listed in the "buildscript" in the build.gradle file are different than regular dependencies listed in the dependencies block { } ?
Dependencies defined in the buildscript { } block are dependencies to use to build your project. These dependencies are available to use in your Gradle build file (build.gradle or build.gradle.kts)
Dependencies defined in the dependencies { } are for your application code.
So for your samples in your questions, does it make sense for Gradle (the build system) to have iot-device-client on its classpath? Why does a build system need iot-device-client on its classpath to build your project? It doesn't make sense therefore it should be removed.
Now let's say you are developing an application the requires some functionality or class from iot-device-client. You need a way to add this library to your application's code/classpath. You when then declare it as a dependency as you have done above:
dependencies {
implementation("com.microsoft.azure.sdk.iot:iot-device-client:1.16.0")
}
References:
External dependencies for the build script
Declaring depenedncies
and why they have to be listed with the syntax "implementation"?
implementation is known as a configuration: A Configuration represents a group of artifacts and their dependencies
There are many more configurations depending on the plugins you apply to your project. For example, if you apply the Java plugin:
plugins {
id("java")
}
The following configurations are available to use:
implementation
compileOnly
compileClasspath
...and many more
Each one has their own meaning/usage and I strongly suggest reading about them here.
My application gets packaged as ear and I have used earlib and deploy configuration. However for all those dependencies version gets mentioned in the jar names.
For example, if I mention dependencies as below,
earlib 'com.xyz:abc:1.0.1'
In generated ear I can see jar name as abc-1.0.1.jar but I want to get it included simply as abc.jar.
Declare a dependency without a version
Gradle lets you declare a dependency without a version but you have to define a dependency constraint, which basically is the definition of your dependency version. This is commonly used in large projects:
dependencies {
implementation 'org.springframework:spring-web'
}
dependencies {
constraints {
implementation 'org.springframework:spring-web:5.0.2.RELEASE'
}
}
Declare a dynamic version
Another option is to declare a dynamic version by using the plus operator. This allows you to use the latest relase of a dependency while you pack your application. Doing so is potentially dangerous since its bears the risk of breaking the application:
apply plugin: 'java-library'
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework:spring-web:5.+'
}
Declaring a file dependency
If you don't want to rely on a binary repository at all but provide dependencies yourself, you can declare a file dependency, from the directories ant, libs and tools. This allows you to name and version dependencies as you like but you have to maintain them yourself:
configurations {
antContrib
externalLibs
deploymentTools
}
dependencies {
antContrib files('ant/antcontrib.jar')
externalLibs files('libs/commons-lang.jar', 'libs/log4j.jar')
deploymentTools fileTree(dir: 'tools', include: '*.exe')
}
Note I recommend against removing the versions as they are important diagnostic information when the application doesn't work.
The ear task is an instance of the Ear task type, which in turn is basically a specialised form of the standard Zip task type. All archiving tasks allow you to rename files as they are packed.
For example, the following might work:
ear {
rename '(.+)-[^-].+(\\.jar)', '$1$2'
lib {
rename '(.+)-[^-].+(\\.jar)', '$1$2'
}
}
I strongly recommend that you check out the new user manual chapter on Working with files for more information about copying and archiving files. Hopefully I'll remember to update this answer with the non-release-candidate link once Gradle 4.7 is out.
Also, if you have any feedback on that chapter let me know.
EDIT Based on OP's feedback, I discovered that the Ear task uses a child copy specification for the JARs in the earlib configuration. Child specifications are independent of both the main one and other child specs, so the main rename() doesn't apply to the earlib files. That's why we add a rename() via the lib {} block.
I have a library, which I call core, which is a dependency of another project, called Museum. In core's build.gradle, I am using gson-fire, which is specified as a dependency in the following manner:
repositories {
maven { url 'https://raw.github.com/julman99/mvn-repo/master'}
}
...
dependencies {
compile 'com.github.julman99:gson-fire:0.11.0'
}
This works fine - core is compiled. When I go to use it in my Museum project, though, I get the following:
A problem occurred configuring project ':Museum'.
> Could not resolve all dependencies for configuration ':Museum:_debugCompile'.
> Could not find com.github.julman99:gson-fire:0.11.0.
Searched in the following locations:
file:/Users/jwir3/.m2/repository/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.pom
file:/Users/jwir3/.m2/repository/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.jar
http://download.crashlytics.com/maven/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.pom
http://download.crashlytics.com/maven/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.jar
https://repo1.maven.org/maven2/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.pom
https://repo1.maven.org/maven2/com/github/julman99/gson-fire/0.11.0/gson-fire-0.11.0.jar
Required by:
museum:Museum:unspecified > com.jwir3.core:core:1.4.0-SNAPSHOT
The build.gradle of Museum looks like the following:
dependencies {
compile ('com.thisclicks.core:core:' + project.CORE_LIB_VERSION+ '+#aar') {
transitive = true
}
}
Presumably, this is because the core library is specified as transient = true in the build.gradle of Museum, but it doesn't have the correct location to search for the Maven repository of gson-fire. Is there a way to make these search locations transient as well as the dependencies themselves?
Not automatically, no. Transitive dependencies do not bring in repository information, only the artifacts themselves. If you want this to work you'll have to add the repositories { } block from the core project to the Museum project.
Additionally, adding transitive = true is unnecessary in this case. This is the default anyway, and as explained above, is unrelated to this particular issue.
How can I tell gradle to build a certain sub-projects first, even though I don't have a compile dependency to them? How are project dependencies handled internally?
Example:
settings.gradle:
include "app", "schema"
build.gradle:
allprojects {
apply plugin: 'java'
}
schema/build.gradle:
// empty
app/build.gradle:
configurations {
schemas
}
dependencies {
schemas project(":schema")
schemas "org.example:example-schema:1.0"
}
task extractSchema(type: Copy) {
from {
configurations.schemas.collect { zipTree(it) }
}
into "build/schemas"
}
//compileJava.dependsOn extractSchema
And when running:
$ cd app
$ gradle extractSchema
I get:
Cannot expand ZIP 'schema/build/libs/schema.jar' as it does not exist.
What I want is that gradle automatically builds all sub-projectes defined in the configurations.schemas dependency list first (if they are projects).
Note: I want to be able to share the extractSchema task across multiple gradle projects, so it is important that gradle takes the list of sub-project to be built first from the configurations.schemas list.
Thanks
Gradle build order is never on the project level, but always on the task level. from(configuration.schemas) would infer task dependencies automatically, but in case of from(configuration.schemas.collect { ... }), this doesn't work because the resulting value is no longer Buildable. Adding dependsOn configurations.schemas should solve the problem.