Is there a way to include a specific module from a group while excluding rest in gradle? - gradle

I am using a library which has a transitive dependency on a module lets assume "abc.xyz:abc-module:1.1.1", the problem is, however, all of the modules from that group are excluded in my build.gradle for some reason using
configurations {
compile.exclude group: "abc.xyz"
}
It causes that transitive dependency to be ignored as expected. Is there a way I can specify only to include abc-module while excluding the remaining one as previously?

I think you should be able to do what you want with a component selection rule like
configurations {
compile {
resolutionStrategy {
componentSelection {
all { ComponentSelection selection ->
if (selection.candidate.group == 'abc.xyz' && selection.candidate.module != 'abc-module') {
selection.reject('Dependencies from group "abc.xyz" except of "abc-module" are not allowed.')
}
}
}
}
}
}

Related

Remove transitive classpath dependency - plugins block

I want to remove the log4j transitive dependency from the shadow gradle plugin.
plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
}
I search for a solution but did not get any. I know I can remove this using the following code -
buildscript {
repositories {
gradlePluginPortal()
}
dependencies {
classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.1.2', {
exclude module: 'log4j'
}
}
}
But is there any way to do this with plugins block?
No, the plugins { } is itself a DSL which is limited by design. This is documented in the documentation:
https://docs.gradle.org/current/userguide/plugins.html#sec:constrained_syntax
The plugins {} block does not support arbitrary code. It is constrained, in order to be idempotent (produce the same result every time) and side effect free (safe for Gradle to execute at any time).
It is designed to do one thing and one thing only: apply plugins.
I used following code to exclude the dependency -
buildscript {
repositories {
..
}
dependencies {
..
}
configurations.classpath {
exclude module: 'log4j'
}
}
plugins {
id 'com.github.johnrengelman.shadow' version '7.1.2'
}

Gradle - Configuration to exclude modules from all "except"

I have the following configuration in my root build.gradle file:
subprojects {
configuration {
all {
exclude module: 'slf4j-log4j12'
exclude module: 'slf4j-log4j-impl'
}
}
}
I want the configuration applied for all sub-projects, except for one. Is there a way to disable the configuration just for one sub-project?
You can select all but one like this . this should apply for all sub projects but the one you want .
//for all sub projects
subprojects {
if (it.name != 'project name') {
//do something
}
}

gradle - listing multiple subprojects for 'project'

Is there a way to list multiple projects for a common dependency. I tried as follows, it doesn't work. I don't want to list individually for a better maintenance.
project(':com.example.bundle1', ':com.example.bundle2') {
dependencies {
compile project(':com.example.common')
}
}
you can leverage groovy list support here:
[':com.example.bundle1', ':com.example.bundle2'].each {
project(it) {
dependencies {
compile project(':com.example.common')
}
}
}

In Gradle, how programmatically to exclude then force a dependency?

The end goal is to replace versions of transitive dependencies with a different version. The caveat is that the replacement should be picked up by those dependent upon the library being built (I don't know if it's a standard Gradle or a plugin but if we exclude a transitive dependency, the produced ivy.xml file will have that information).
One possible way to achieve the end goal is to exclude the dependency in question then force that dependency later on.
The way to exclude a dependency is with something like:
dependencies {
compile 'org:name:version' {
exclude(group: 'group', module: 'module')
}
}
The way to force a dependency is with something like:
configurations.all {
resolutionStrategy {
eachDependency { DependencyResolveDetails dependencyResolveDetails ->
final requestedDependency = dependencyResolveDetails.requested
if (requestedDependency.group == 'org' && requestedDependency.name == 'name') {
force 'group:module:good_version'
}
}
}
}
In order to tie the two together, the resolutionStrategy must know which dependencies actually excluded the transitive dependency that will later be forced. How can this be done in a generic way (assuming there's a generic way to do the exclude)? If there isn't a way to tie the two together, is there a different way to achieve the end goal?
First, abstract the dependencies so they look something like:
dependencies {
compile dep('org:name')
}
The dep function can then be defined in a way which will take care of the automatic exclusions:
final rev = [
'group:module': 'good_version'
'org:name': 'version']
ext.rev = { m ->
rev[m]
}
final dep = { m ->
"${m}:${ext.rev(m)}"
}
final forcedTransitiveDeps = [
'org:name': [
dep('group:module')]].withDefault({[]})
ext.forcedTransitiveDeps = { m ->
forcedTransitiveDeps[m]
}
ext.dep = { m ->
if (!forcedTransitiveDeps.containsKey(m)) {
project.dependencies.create(dep(m))
} else {
project.configure(
project.dependencies.create(dep(m)),
{
forcedTransitiveDeps[m].each { dependency ->
final dependencyParts = dependency.split(':')
exclude(group: dependencyParts[0], module: dependencyParts[1])
}
})
}
}
Finally, to re-introduce and force the dependencies:
subprojects { subproject ->
subproject.afterEvaluate {
subproject.configurations.all { Configuration configuration ->
final dependencies = configuration.getDependencies()
final forcedDependencies = (dependencies.collect { dependency ->
forcedTransitiveDeps("${dependency.group}:${dependency.name}")
}).flatten()
if (!forcedDependencies.isEmpty()) {
logger.info("Forcing ${subproject} dependencies on ${forcedDependencies} as per `ext.forcedTransitiveDeps`.")
dependencies.addAll(forcedDependencies.collect { forcedDependency ->
subproject.dependencies.create(forcedDependency)
})
}
}
}
}

Gradle: replace module dependency with project dependency

Using Gradle 1.12, is it possible to create a resolution strategy rule that replaces a module dependency with a project one under certain circumstances?
The reason for this is that we have many projects in the company (dozens), and I don't want to pollute the build scripts with things like:
dependencies {
elastic "company:somelib:1.0.+", "SomeLib"
}
Instead i'd like to achieve something along the lines of:
dependencies {
compile "company:somelib:1.0.+"
}
...
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (/* ... Check if project is in build ... */) {
details.useTarget project(':SomeLib')
}
}
}
So far I have not been able to replace a module dependency with a project one in a resolution strategy rule. Is there a way to achieve this?
EDIT: These are things I tried (all resulted in an error):
configurations.all {
resolutionStrategy.eachDependency { DependencyResolveDetails details ->
if (/* ... Check if project is in build ... */) {
details.useTarget project(':SomeLib')
details.useTarget ':SomeLib'
// Since I noticed this is how actual project dependencies look like
details.useTarget 'Project:SomeLib:version'
details.useTarget new DefaultProjectDependency(...)
}
}
}
For future reference, this is the code that I've used in the end. This example implements our very specific flavor of this desired behavior, but others could take it as a starting point and tweak as needed.
gradle.projectsEvaluated {
def prjMap = [:]
allprojects.each { prj ->
prjMap[prj.archivesBaseName] = prj
}
allprojects.each { prj ->
def replace = []
prj.configurations.each { conf ->
conf.dependencies.each { dep ->
if (dep.group == 'company' && prjMap[dep.name] != null) {
replace += [conf: conf.name, dep: dep]
}
}
}
replace.each { rep ->
println "Replacing: $prj.name\t$rep.conf\t$rep.dep.name ==>> ${prjMap[rep.dep.name].name}"
prj.configurations.all*.exclude(group: 'company', module: rep.dep.name)
rep.dep.properties.excludeRules.each { ex ->
prj.configurations.all*.exclude(group: ex.group, module: ex.module)
}
prj.dependencies.add(rep.conf, prjMap[rep.dep.name])
}
}
}
Note that while replacing, I used aggressive exclude statements. This is because we have a hellish nightmare of cyclic dependencies and lib projects declaring whole apps as a transitive dep because they need some value class. In a more sane environment, one could simply eliminate the previous dependency entry like so:
replace.each { rep ->
println "Replacing: $prj.name\t$rep.conf\t$rep.dep.name ==>> ${prjMap[rep.dep.name].name}"
prj.dependencies.remove(rep.dep)
prj.dependencies.add(rep.conf, prjMap[rep.dep.name])
}
No, a resolution strategy cannot do this. It might be possible to implement a solution that, at the end of the configuration phase, iterates over the configurations' dependencies and replaces certain external dependencies with project dependencies. Not sure if it can be done without using Gradle internals, though.

Resources