Conditional dependencies - java

On Gradle (more specifically with Android) how can I have conditional dependency so that they get loaded either from the parent project like this:
compile project(':lib')
or from a repository is not present in the parent project:
compile 'com.sample:lib:+'
one possible solution I thought of is to do this:
compile allprojects.find({ it.name.equal('lib')}) != null ? project(':lib') : 'com.sample:lib:+'
but the allProjects property is not available in the dependencies task.
Update:
The reason I'm searching for this because we have two setups, one with our sub-projects imported from different git repositories at the development machines and the second one importing the dependencies from our dependencies server for CI.
An alternative I've tried is to set this in the parent project:
ext.projectNames = allprojects.collect{ it.name }
and in the subproject I'm doing this:
compile projectNames.contains('lib') ? project(':lib') : 'com.sample:lib:+'
This works, but however since in the current setup we run just one of the subproject in the CI server this property won't exist and will fail so may have to extract this behaviour to a plugin and maybe play with getParent()

I had a need for the same feature. Getting it to work with gradle was a cinch. The hard part was figuring out how Android Studio syncs the gradle files. Without a successful sync, the IDE will complain it can't find any dependencies. At any rate, I figured it out, here is my solution to make it work with gradle and Android Studio.
https://gist.github.com/vangorra/c1383c355ce8fe56adf8
It essentially boils down to defining the project in settings.gradle:
include 'library'
project(':library').projectDir = file('../Library/library')
Then you have to use a one-liner with options closure for your dependency:
compile ( project(':library').projectDir.exists() ? project(':library'): 'Library:library:unspecified#aar') {
transitive = true
}

Dynamically resolving to either project dependency or external dependency isn't yet a first-class Gradle feature, and it takes some effort (and may incur some limitations) to implement it in a build. You can find a proof-of-concept here: https://github.com/pniederw/elastic-deps
PS: It's allprojects, not allProjects.

Related

How to use a parent in Gradle across different projects? [duplicate]

At my work we use Maven. I am going to try gradle for the first time. We use a common parent pom for all project which has setting for commonly used maven plugins and few comon dependencies. Is there a similar option available in gradle?
My second question is regarding release management. We use maven release plugin, which works pretty good for us. Is there something similar available in Gradle?
To share stuff within multiple projects of the same build, use allprojects { ... }, subprojects { ... }, etc. Also, extra properties (ext.foo = ...) declared in a parent project are visible in subprojects. A common idiom is to have something like ext.libs = [junit: "junit:junit:4.11", spring: "org.springframework:spring-core:3.1.0.RELEASE", ...] in the top-level build script. Subprojects can then selectively include dependencies by their short name. You should be able to find more information on this in the Gradle Forums.
To share logic across builds, you can either write a script plugin (foo.gradle), put it up on a web server, and include it in builds with apply from: "http://...", or write a binary plugin (a class implementing org.gradle.api.Plugin), publish it as a Jar to a repository, and include it in builds with apply plugin: ... and a buildscript {} section. For details, see the Gradle User Guide and the many samples in the full Gradle distribution.
A current limitation of script (but not binary) plugins is that they aren't cached. Therefore, a build will only succeed if it can connect to the web server that's serving the plugin.
As to your second question (which should have been a separate question), there are a couple of third-party release plugins available, for example https://github.com/townsfolk/gradle-release.
The io.spring.dependency-management plugin allows you to use a Maven bom to control your build's dependencies:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath "io.spring.gradle:dependency-management-plugin:0.5.3.RELEASE"
}
}
apply plugin: "io.spring.dependency-management"
Next, you can use it to import a Maven bom:
dependencyManagement {
imports {
mavenBom 'io.spring.platform:platform-bom:1.1.1.RELEASE'
}
}
Now, you can import dependencies without specifying a version number:
dependencies {
compile 'org.springframework:spring-core'
}
I think the best way to do things like maven parent pom is to to use gradle "apply from".
Something like this:
allprojects { // or: subprojects { ... }
apply from: "gradle/script/common.gradle"
}
The link and be a related path or an URL. Hope it helps.
Reference:
Import a Gradle script from the root into subprojects
Super POM, Parent POM type of hierarchy management in Gradle
I too wanted this type of feature, I have created a plugin to provide this here: https://github.com/boxheed/gradle-pater-build-plugin
You can convert the Parent pom content in to Gradle init file very easily.
Gradle init script provides same functionality as Maven super/parent pom. The basic difference is that you can call init script
Run time
As many as of them This gives us flexibility to change the init
script on run time but doubt of not tracking the changes.
You need to take repository, distribution management, profiling and other checks like findbugs, checkstyle etc in to init script.
The detail is huge, You can find complete information here by me.
http://www.scmtechblog.net/2015/12/how-to-migrate-parent-pom-from-maven-to.html
I have explained about gradle release plugin which is similar to maven release plugin.
to achive your goal you could apply the concept of 'multiproject build' explained in the gradel user guide here
Basically you can create an umbrella project which define a set of common configurations by creating a gradle.build file and a gradle.settings file.
The build file contains the properties, dependencies and plugins commons to all projects, the settings.gradle defines what subprojects inherits those configurations.
Moreover, to have an idea of the gradle plugin ecosystem you could check this source.
It is currently not possible, if you want the parent to be cached locally and stored in a Maven repository.
I have added feature request here:
http://forums.gradle.org/gradle/topics/support_for_gradle_parent_shared_between_projects_cached_locally

How to find if I need to exclude dependencies in a maven java project?

I use both Intellij IDEA (2018.3.5) & Eclipse IDEs, but I prefer Intellij. I have a maven based Java project with multiple poms. I added some dependencies to one of the pom files. I need to find out if there are any dependency conflicts which could prevent the build from running when its deployed, and then exclude them. I tried the steps given below to find conflicts which could cause problems. Are they enough or do I need to do more ?
Check if there are any compile time dependency conflicts with mvn clean install -DskipTests. Build was successful with no errors.
Check if Intellij shows no problems under File > Project Structure > Problems. There are no problems.
I also saw the dependency tree with mvn dependency:tree -Dverbose. It has a lot of "omitted for duplicate" and "omitted for conflict with" items, but the build was successful. I don't see any errors though. Does this mean that everything is okay or do I have to do something more about these conflicts ?
The best way to tell if everything is fine with your application is to have good tests.
However normally one doesn't exclude transitive dependencies from project's <dependency> libraries. Doing it can potentially break the dependency in a subtle and hard to notice way. It's usually safer to remove the whole <dependency>.
There are few scenario when one should use <exclude>:
Dealing with incompatible transitive dependencies between different libraries e.g. A requires library C-1.0 but library B requires library C-2.0 while C-1.0 and C-2.0 can't coexist on the classpath.
Having transitive dependencies already provided by system e.g. deploying to Tomcat with additional JARs in the TOMCAT_HOME/lib directory.
If you decide to exclude a dependency it's important that you check the final artifact because sometimes plugins do weird things e.g. there were versions of maven-assembly-plugin affected by a bug that resulted in different dependencies being resolved during shaded JAR creation than maven-dependency-plugin used for compilation.

fail gradle build if transitive dependency is used directly?

im looking for a way to fail my gradle build if my code directly uses (so imports) a transitive dependency.
what i mean is if my project has a (compile) dependency on module A, and module A depends on B (so B is in my transitive dependencies and available on my runtime classpath) and my code directly imports and uses classes from B, i want my build to fail.
here's a maven plugin that does what i want - https://github.com/Scout24/illegal-transitive-dependency-check - but i cant find a gradle one?
I think the 2 plugins below may be what you're looking for.
https://github.com/wfhartford/gradle-dependency-analyze
From the README:
This plugin attempts to replicate the functionality of the maven dependency plugin's analyze goals which fail the build if dependencies are declared but not used or used but not declared.
https://github.com/nebula-plugins/gradle-lint-plugin
On the Unused Dependency Rule wiki page:
Promotes transitive dependencies that are used directly by your code to explicit first order dependencies
I have to say I haven't used either one myself, but they seem to address your concern.
I think that you want to use the java library plugin and the api/implementation configurations.
For the legacy java plugin you could do
dependencies {
compile('group:module-b:1.0') { transitive = false }
}
This would force you to explicitly declare module-b's transitive dependencies if you need to use them.
Note: You'll probably get lots of ClassNotFoundException using module-b at runtime since there's now jars missing from your classpath. So this might be better
dependencies {
compile('group:module-b:1.0') { transitive = false }
runtime 'group:module-b:1.0'
}

With Buildship 2 subprojects are not linked as Project Dependencies

For several months we've been using Buildship 1.X plus some manual .launch/tasks to build our Eclipse/WTP config files per development environment. I am currently attempting to migrate to using Buildship 2 (which I'm hoping will rid us of the need for the manual bits.)
However, when I import the projects (which have 0 eclipse config files at this point) via the buildship/gradle import, the subprojects are included via 'Libraries' rather than as 'Projects' (see image below.) In contrast, if I use gradle's eclipse task to generate the eclipse config files (i.e. .classpath) then the configuration ends up as I would expect it to be. Is this a current limitation of Buildship, or do I need to do something differently in my gradle files to coerce Buildship to bring them in as Projects?
Ultimately I don't know that I should care about this difference, but I do know that I'm getting compiler errors saying classes from the subprojects are missing from the classpath. As long as I can fix that issue, I'm perfectly happy.
Potentially helpful info
settings.gradle:
rootProject.name = 'projectroot'
include 'Project2.0'
project(':Project2.0').name = 'projectx'
include 'the-platform'
include 'the-platform:central-repo:central-repo-common'
include 'the-platform:central-repo:central-repo-model'
include 'the-platform:central-repo:central-repo-persist'
include 'the-platform:central-repo:central-repo-service'
Project2.0/build.gradle (snippet):
dependencies {
...
compile project(':the-platform:central-repo:central-repo-common')
compile project(':the-platform:central-repo:central-repo-model')
compile project(':the-platform:central-repo:central-repo-persist')
compile project(':the-platform:central-repo:central-repo-service')
...
}
Hmmm, nevermind. My intuition about the difference between the behavior of buildship vs the eclipse plugin to gradle being responsible for my classpath issues was incorrect. Something else (as yet unexplained) must've been the issue as it is working correctly now.

What is the proper Gradle project structure for switching between 'project' and 'external' dependencies

I have this small set of libraries and app:
forge-base - java project (Intellij IDEA project)
forge-android - android library project, depends on forge-base (AS project)
forge-android-skeleton - sample android app, depends on forge-android (AS project)
During the initial development I used structure with project dependencies like:
settings.gradle:
...
include ':forge-base'
project(':forge-base').projectDir=new File("$settingsDir/../forge/base")
...
and then in build.gradle:
compile project(':forge-base')
This worked like a charm but later I needed to publish the libs on maven repo and dependencies had to be changed like:
build.gradle:
compile 'com.bolyartech.forge:forge-base:2.0-SNAPSHOT'
The problem that I am facing now is that I am trying to do some major refactoring in all the 3 projects and I need the old deps structure in order easily to confirm the consistency of the projects, e.g. to build just the skeleton app and all the recursive recompile/building to take place automatically (as it does when a lib project is referenced with compile project(':forge-base')). If I use the 'new' structure with publishing to the (local) maven I have to publish the lib each time (with incremented version) I change something in it in order changes to be visible by the other two projects.
What (is there) is the usual/canonical why to handle situations like this?
Is there an easy way to switch between the two 'modes', e.g. 'internal' / 'external' dependencies?
It turns out it is pretty easy to do it. You can use different dependencies for the different build types, i.e. debug/release so for example for my forge-android-skeleton project now I have the following:
in settings.gradle:
include ':app'
include ':forge-base' project(':forge-base').projectDir=new
File("$settingsDir/../../../forge/base")
include ':forge-android' project(':forge-android').projectDir=new
File("$settingsDir/../../../forge-android/forge-android")
in app/build.gradle:
...
releaseCompile ('com.bolyartech.forge:forge-android:2.7-SNAPSHOT')
debugCompile project(':forge-android')
...
Please note that in the settings.gradle you need to have all the dependencies back to the bottom otherwise it will not work (that is the reason forge-base is defined there even not excplicitly used).
You can also define yet another build type like direct-deps and use it in case you don't like to mess with debug/release types.
Please note that in case you are using different IDEs for some of the projects (like in my case IDEA and AS) probably it will be good idea to ensure that both are using same (version of) gradle otherwise unexpected problems may occur.

Categories