Add dependencies via transitive dependencies of another configuration in Gradle - gradle

I would like Gradle to compute the dependencies for a configuration by adding dependencies from dependencies loaded via a second configuration.
Example with two Gradle projects:
dependency/build.gradle:
configurations {
other
}
dependencies {
other "bar:artifact1:0.4"
}
top/build.gradle:
configurations {
other
modules
}
dependencies {
modules "foo:dependency:1.0"
other "bar:artifact2:0.1"
}
So the top project depends on dependency via the modules configuration, but I want the other configuration in top to contain both "bar:artifact1:0.4" and "bar:artifact2:0.1".
In my concrete case, the other dependencies are listed in an Ivy file for each artifact. I want to collect the other dependencies of all the artifacts a project depends on.
This is not a multi-project build, but other projects' artifacts are loaded from a remote repository.
Is there a way to configure Gradle to include the configurations of all transitive dependencies?

Related

How to build a jar from a multi-module project when using Gradle?

I'm working on a multi-module library project which I build with Gradle. I have a dependency to another module of this project in my dependencies section:
dependencies {
compile project(':my-other-module')
}
My problem is that I want to build a .jar file which only contains the local modules in the final file, not its transitive dependencies. I tried this:
jar {
from project(':my-other-module').configurations.compile.collect { zipTree it }
}
but this added all the transitive dependencies as well. I want to create a .jar which only contains my own files, so the users of this library can have their own versions of transitive dependencies. How can I do so?
Further clarification:
I have dependencies declared in my project to external jars like apache-commons. I want these not to be in my resulting .jar file but I want the users of my library to be able to just add my library as a dependency and let Maven/Gradle download the transitive dependencies. I don't want these transitive dependencies to be in the .jar file I deploy to Maven Central. compileOnly is not an option since the dependencies I use like apache-commons are not provided by a framework or a container. They need to be present as compile dependencies. I just want to build and deploy a .jar file which has all the files in my project which has multiple modules.
I am not sure it'll help you or not but, you can try this.
In your build.gradle file, customize your jar task as follows:
// This closure will return the full directory path of folder where your classes are built
ext.moduleClassPath = { moduleName ->
def classOutputDirConst = "/classes/java/main"
return "${project(":${moduleName}").buildDir}${classOutputDirConst}"
}
// Now jar task will include only the built file of specified project
jar {
from(moduleClassPath("projectName1"), moduleClassPath("projectName2"))
}
Check the reference for the from(SourcePaths) method here: Reference: https://docs.gradle.org/current/dsl/org.gradle.jvm.tasks.Jar.html#org.gradle.jvm.tasks.Jar:from(java.lang.Object[])
Gradle has a compile-only dependency concept, similar to Maven's provided scope:
Compile-only dependencies are distinctly different than regular compile dependencies. They are not included on the runtime classpath and they are non-transitive, meaning they are not included in dependent projects.
The dependencies you don't want can be declared in the compileOnly configuration, rather than compile, eg:
dependencies {
compileOnly 'javax.servlet:servlet-api:2.5'
}
compileOnly is not even visible to unit tests, by default. We change this in a common gradle snippet which we include in each build:
// compileOnly isn't visible to tests by default, add it
plugins.withType(JavaPlugin).whenPluginAdded {
sourceSets {
test.compileClasspath += configurations.compileOnly
test.runtimeClasspath += configurations.compileOnly
}
}
For the second part, for which I believe you want to create a single "fat" jar,
I would suggest creating your jar using the very good Shadow Plugin, rather than manually extending the jar task. By default, the shadow plugin will not include anything in the compileOnly configuration in the resulting jar.

Gradle fails to detect flat dependency

I have a multi-project Gradle build (Gradle 4.4).
And I encountered the following issue.
Let's say I have proj1 project with the following build.gradle:
repositories {
flatDir {
dirs 'libs'
}
}
dependencies {
compile name: 'lombok-edge'
}
proj1 has libs folder with lombok-edge.jar. Project is built successfully.
And I have proj2 that imports proj1:
dependencies {
compile project(':proj1')
}
When I try to build the whole project proj1 is build but Gradle fails to build proj2 with an error:
What went wrong:
Could not resolve all files for configuration ':proj2:compileClasspath'.
Could not find :lombok-edge:.
Required by:
project :proj2 > project :proj1
Please advise.
Well, Gradle supports transitive dependencies, but the way how to get a dependency (and therefor any repository information) is not transitive. Gradle (like Maven) does not care where a dependency comes from, as long as it matches a given signature (group, id, version).
In your example, project 1 defines a Maven dependency and can resolve it via the given flatDir repository. Project 2 has dependencies on both project 1 (which is resolved by Gradle) and the transitive Maven dependency. It's the responsibility of project 2 to resolve this dependency and it will check any defined repository, but cannot find it, because the local repository is unkown. This is the reason why the build fails.
If the two projects are related, as they should be if they are both parts of a multi-project build, you should define the local repository in a top-level build.gradle file and a subprojects closure.

Transitive project dependencies when dependency has a zip artifact

Typically, I define project dependencies simply:
dependencies {
compile project(':someProject')
}
This works well for java projects. However, I have a project that produces a zip artifact:
artifacts {
archives file: zipDistribution.archivePath, type: 'zip', builtBy: zipDistribution
}
And when I do the dependency definition above, I don't get the zip artifact when I iterate over the dependencies in the compile configuration. A jar file is there, but no zip. This, however, gets the zip file:
dependencies {
compile project(path: ':someProject', configuration: 'archives')
}
Great! It works! Except "someProject" has its own dependencies... and we're not getting those transitively any more. Adding transitive: true to the project dependency didn't help, either.
How can I get the project's zip artifact AND the transitive dependencies associated with it? I can add both flavors of dependencies, but I feel there's got to be a way to do it in one dependency definition.
The archives configuration contains the artifacts to be uploaded, but it doesn't contain any dependencies. Try this instead:
dependencies {
runtime ...
}
artifacts {
runtime zipDistribution // assuming this is a `Zip` task
}
dependencies {
compile project(':someProject')
}
Instead of the Java plugin's runtime configuration, the former script can also add dependencies and artifacts to a custom configuration, which would then have to be reflected in the project dependency (project(path: ..., configuration: ...)).

How do I define a simple Gradle project with only a single jar file?

I have a Gradle project that depends on an external jar file. Currently I'm defining the dependency like this:
dependencies {
compile files('/path/to/my/jar/library.jar')
}
However I want to include it as a project dependency instead, like this:
dependencies {
compile project(':whatGoesHere?')
}
I assume I need to define a new Gradle project that contains the jar file but I don't know how to do this. I'm wondering about things like:
Do I just need to create a new build.gradle or are there more steps?
What would go in the build.gradle file?
Assume the new project contains nothing but the jar file (since it does). Also assume I know almost nothing about Gradle (because I don't!).
P.S. If it matters, this is an Android Gradle project.
As a roundup for our discussion, I'll bring simple example of "build.gradle" file, using maven local and maven central repositories:
apply plugin: 'maven'
apply plugin: 'java'
repositories {
mavenLocal()
mavenCentral()
}
dependencies {
compile 'commons-io:commons-io:2.4'
testCompile 'junit:junit:4.11'
}
Explanation:
"apply plugin: 'maven'" enables maven plugin, which is needed for dependency download.
"apply plugin: 'java'" enables java compilation tasks for your project.
"repositories" declares one or more repositories (maven or ivy), from where artifacts (jar libraries) will be downloaded.
"mavenLocal" refers to so-called local maven repository, which is located in "~/.m2/repository" folder on your computer. local maven repository effectively caches external repositories, but it also allows installation of local-only artifacts.
"mavenCentral" refers to maven central.
"dependencies" lists your project dependencies, either other projects or artifacts (jars).
"compile" is a configuration supported by "java" and "groovy" plugins, it tells gradle: "add these libraries to the classpath of the application during compilation phase".
"testCompile" is another configuration supported by "java" and "groovy" plugins, it tells gradle: "add these libraries to the classpath of the application during test phase".
'commons-io:commons-io:2.4' is "coordinates" of the artifact within maven repository, in form group:name:version.
You can search for well-known java libraries at address: http://mvnrepository.com/ and then include their coordinates in "build.gradle". You don't need to download anything - gradle does it for you automatically.

Gradle: What Is The Default Configuration and How Do I Change It

When I run the "dependencies" task there are several sections: compile, runtime, testCompile ...
One of those sections is "default - Configuration for default artifacts." What is this section and what is it used for?
How do I change what is in the "default configuration"?
Details: Gradle 1.7
Unless your build is publishing Ivy modules, the default configuration is mainly relevant when dealing with project dependencies in a multi-project build. Given a multi-project build with projects A and B, if A declares a project dependency on B without explicitly naming a configuration (e.g. dependencies { compile project(":B") }, A (more precisely A's compile configuration) will depend on project B's default configuration. In other words, dependencies { compile project(":B") } is a shortcut for dependencies { compile project(path: ":B", configuration: "default") }.
The default configuration extends from the runtime configuration, which means that it contains all the dependencies and artifacts of the runtime configuration, and potentially more. You can add dependencies and artifacts in the usual way (using a dependencies/artifacts block in B's build script). Alternatively, B could declare a custom configuration, and A could depend on that by explicitly naming that configuration (e.g. dependencies { compile project(path: ":B", configuration: "myCustomConfig") }.
When using the gradle java plugin the 'default' configuration extendsFrom 'runtime', 'runtimeOnly', 'implementation'
If you do not use the java plugin then you can define it yourself like this
configurations {
"default"
}
The java plugin sets up the default configuration here: https://github.com/gradle/gradle/blob/85d30969f4672bb2739550b4de784910a6810b7a/subprojects/plugins/src/main/java/org/gradle/api/plugins/JavaPlugin.java#L437
The documentation is not that good in this area.
An example of "serving" a default artifact from a composite build.
The example creates a subproject that refers to a dependency in another project. This can be nessesary when working with composite builds, as only the "default" group can be depended upon.
We use this to take many jars from a single project and serve it as different dependencies when referencing the project as a composite build.
apply plugin: 'base'
configurations {
depend
}
dependencies {
depend project(path: ':', configuration: 'ConfWithArtifact')
}
artifacts {
"default" (file: configurations.depend.singleFile) {
builtBy(configurations.depend)
}
}
The default configuration is actually created by the base plugin, and so you don't need to define it yourself.
I've also had the problem with composite builds only compositing from the default configuration, but I solved it slightly differently:
plugins {
id 'base'
}
configurations {
bootstrap
it.'default'.extendsFrom bootstrap
}
dependencies {
bootstrap project(path: ':other', configuration: 'otherConfiguration')
}
This approach allows the artifact from the :other project to keep its transitive dependencies, assuming you're interested in keeping them.

Resources