I have a jar that I am including in my Android project using the following syntax
implementation files('lib/fm.liveswitch.jar')
fm.liveswitch has a dependency of org.bouncycastle that I am already including as a dependency of my project. How do I exclude bouncycastle from being included as part of the jar?
I have already tried each of the following:
implementation (files('lib/fm.liveswitch.jar')) {
exclude group: 'org.bouncycastle'
}
implementation (files('lib/fm.liveswitch.jar')) {
exclude module: 'org.bouncycastle'
}
implementation (files('lib/fm.liveswitch.jar'), {
exclude group: 'org.bouncycastle'
})
implementation files('lib/fm.liveswitch.jar') {
exclude group: 'org.bouncycastle'
}
But each time I get an error similar to the below:
Could not find method exclude() for arguments [{group=org.bouncycastle}] on object of type org.gradle.api.internal.artifacts.dependencies.DefaultSelfResolvingDependency
When you add file based dependencies, they have no dependency metadata and thus no transitive dependency information.
Given this, there is no point in trying to add an exclude.
Adding implementation files('lib/fm.liveswitch.jar') will only add fm.liveswitch.jar on the classpath, nothing else.
Now if that JAR contains some bouncycastle classes inside, it is a different story. Gradle has no direct way of letting you control the visibility of the contents of a JAR.
You would then have two options:
Since it is a local JAR, you can yourself remove the offending classes from it.
You could attempt to leverage artifact transforms
Related
I have a dependency in one of my project
implementation (group: 'com.ibm.cloud', name: 'ibmcloudsql-jdbc', version: '2.5.34')
I want to specifically exclude a package from this jar, i.e
com.ibm.cloud.sdk.core.http
Is there a way? I already tried this but did not work.
implementation (group: 'com.ibm.cloud', name: 'ibmcloudsql-jdbc', version: '2.5.34'){
exclude module: 'com.ibm.cloud.sdk.core.http'
}
You can't use dependency management to remove part of a dependency - with dependency management it's either the complete dependency or no dependency (and this is not limited to gradle - other dependency managers / build systems like maven or ivy also don't have this feature).
The exclude module can only be used to prevent inclusion of transitive dependencies (i.e. you need dependency com.xy:A and com.xy:A declares that it also needs com.xy:B - with exclude module you can prevent the inclusion com.xy:B)
It might be possible to create your own version of that dependency by manually removing the package com.ibm.cloud.sdk.core.http from the jar itself and adding that modified jar file to your project.
There are two modules in my project and module A depends on module B.
For building this with Gradle I've used a simple code in the A's build.gradle file:
compile project(":B")
My issue is a collision of resources directories.
What I want to do is to exclude certain B's module "resource" directory.
What I've tried:
compile (project(":B")) {
exclude group : 'src.main', module : 'resources'
}
What I've tried as well:
compile(project(":B")) {
exclude module: 'src'
exclude module: 'main'
exclude module: 'src/main'
exclude module: 'src.main'
}
By excluding the root of module B, I'm expecting to get failed build of whole project, but nothing happens.
P.S. Every time I'm running build, I'm cleaning compiled jars, so they are not cached.
Gradle modules don't work like this. A java module is the compiled classes plus the resources. You can only exclude dependencies (ie whole modules or jars) using your attempted exclude method. You can't exclude a specific part (eg a directory) of a dependency
In my opinion excluding things is an anti-pattern. I suggest breaking things onto smaller pieces and joining them together (ie "include" instead of "exclude")
So I suggest you create a ":common" project which both "A" and "B" have as a dependency. All common stuff goes in common and any specific stuff goes in A or B
I am trying to get Gradle to select different dependencies in my multiproject build based on whether I am building for desktop or for Android. I have a common subproject (a library) I am trying to reuse. However, I cannot get Gradle to correctly switch dependency configurations.
My main settings.gradle simply includes all the dependencies:
// /settings.gradle
rootProject.name = 'myProject'
include 'androidUI'
include 'reusableLibrary'
include 'desktopUI'
Now both androidUI and desktopUI specify reusableLibrary as a dependency:
// /androidUI/build.gradle and /desktopUI/build.gradle
apply plugin: 'java'
dependencies {
compile project(path: ':reusableLibrary', configuration: 'desktop')
}
reusableLibrary itself specifies two configurations, because its dependencies are different whether it's building on desktop or Android:
// /reusableLibrary/build.gradle
apply plugin: 'java'
configurations {
desktop {
extendsFrom compile
}
android {
extendsFrom compile
}
}
dependencies {
// Just examples, the real list is longer.
// The point is that h2database is only included on desktop,
// and ormlite is only included on Android.
android 'com.j256.ormlite:ormlite-jdbc:5.0'
desktop 'com.h2database:h2:1.4.192'
}
This looks fine to me. But when I compile either desktopUI or androidUI, I can see that although the dependencies of reusableLibrary are being included on the classpath in the manner I desire, the actual JAR provided by reusableLibrary itself is not included. This of course causes the build to fail. I suspect I'm not setting up reusableLibrary correctly; I'm not clear on what the configurations {} blocks do.
Why aren't the compiled items in reusableLibrary being included on the classpaths of the UI projects? And what is the canonical way to include platform-specific dependencies in this manner?
The original configuration is pretty close to right. The key is to understand this dependency graph from the Gradle Java plugin's documentation:
This is a visualization of the Java plugin's various dependency configurations, which is Gradle-ese for "list of dependencies." When you add compile lines to a dependencies {...} block, you're adding Dependency elements to the compile dependency list.
The default dependency configuration is special; it is the one included by a compile project("path") line unless a different one is chosen with the configuration: argument. This means that when you build the library, the runtime dependency list (which includes the compiled jar from the library itself) is added to the classpath of the client project.
The original configuration creates two new nodes, desktop and android in this graph, and couples them both to compile by using extendsFrom. They are not otherwise connected to the graph! Now the problem with the original configuration is apparent: by switching the upstream project to either of these, it is missing the compiled code from runtime. This explains the classpath omission.
The solution is a bit more subtle than just aiming desktop and android at runtime. In order to ensure that everything is correctly decoupled when we add tests, we need one extra layer of dependency configurations to keep testCompile from indirectly depending on runtime. Additionally, the library's source code itself may need things on its classpath just to typecheck; we can use compileOnly for this. The end solution looks like this:
configurations {
desktopCompile
androidCompile
compileOnly.extendsFrom desktopCompile
testCompile.extendsFrom desktopCompile // Assuming tests run on the desktop
desktop {
extendsFrom desktopCompile
extendsFrom runtime
}
android {
extendsFrom androidCompile
extendsFrom runtime
}
}
dependencies {
androidCompile "some.android:dependency"
desktopCompile "other.desktop:dependency"
}
Given a gradle project A having a dependency on project B (no common parent)
compile project('B'){
exclude group: 'org.slf4j'
}
how do we exclude transitive dependency group from a project we depend upon? (this piece of script will fail as there is no exclude for compile projet(..))
and more general question : is there an elegant way to exclude a particular group from all dependancies except if its a first level dependency?
for example we may have a bunch of libraries, and each may declare its logging environment, but by excluding all known groups of slf4j, its implementations and declaring specific version, we would ensure we don't have any version conflicts and would control version on module level.
Here is an example from the Gradle documentation of how to exclude a transitive dependency (I guess that is what you meant by "except if its a first level dependency") on project level:
configurations {
compile.exclude module: 'commons'
all*.exclude group: 'org.gradle.test.excludes', module: 'reports'
}
See 52.4.7. Excluding transitive dependencies here
You can either specify the dependency directly with the desired version or use a forced version resolution strategy
To add a simple dependency on test source sets from an another subproject I can do:
testCompile project(':subFoo1').sourceSets.test.output
This solution works, but in many cases it is not intended to add the whole source set as a dependency. For example I would like to use only test data builders and in that case files like test-logback.xml (and regular tests) pollute my test classpath in the master module.
I tried the idea with test JAR (which can have filtered content, but is problematic as a dependency) and some combination with eachFileRecurse, but with no luck.
My question. How can I add only a subset of given source set(s) (e.g. only classes with builders matching **/*Builder.* pattern) as a testCompile dependency in another subproject?
You'll want something along the lines of:
upstream/build.gradle:
apply plugin: "java"
task testJar(type: Jar) {
classifier = "tests"
from sourceSets.test.output
exclude "**/*Test.class"
}
artifacts {
testRuntime testJar
}
downstream/build.gradle:
apply plugin: "java"
dependencies {
testCompile project(path: ":upstream", configuration: "testRuntime")
}
Instead of using testRuntime, you could also declare (e.g. configurations { testFixture }) and use a custom configuration, which would give you more control over which external dependencies are passed on to downstream projects. Yet another option would be to declare a separate source set for the part of the test code that is to be passed on. (This would also give you separate compile and runtime configurations to work with.)
PS: Reaching out into another project's object model (e.g. project(':subFoo1').sourceSets.test.output) is problematic, and should be avoided when possible.