Proper way of setting up deps for Gradle project in IDE?


(Tim Fox) #1

What is the “correct” way of setting up project dependencies when creating a new Gradle project in an IDE (specifically IDEA but this probably applies to Eclipse too).

The IDE needs to know where the jars are so it can compile / run tests. Project deps seem to be added by pointing the IDE at jars in the Gradle cache (.gradle/caches/blah). Or should they point at the jars in the local Maven repo? (.m2/repository/blah). It really isn’t clear to me.

Sometimes there are multiple versions of a snapshot (with the same version number - probably timestamped) in the Gradle cache at once - these are only distinguishable by a UUID which doesn’t tell me which is the correct (latest) one to point at.

And what if that snapshot expires? Or the cache gets wiped? Do I have to setup my project deps again, this time pointing at different UUIDs?

This all seems very hit and miss, and there’s got to be a better way. I am struggling at the moment to find a good consistent way to setup a Gradle project in IDEA.


(Peter Niederwieser) #2

Are you talking about project dependencies within the same Gradle build, or across Gradle builds? In any case, you’ll have to declare the dependency in the Gradle build script, and your preferred way of IDE integration (Gradle IDE plugin or IDE Gradle plugin) should do the rest.


(Tim Fox) #3

I’m talking about a single, simple Gradle project in IDEA. No cross build dependencies.

The Gradle idea plugin works to a certain extent - but also seems to fail if there are more than one version of the same snapshot in the cache. What should I do in this case.

Also what happens if the cache is wiped? Any deps pointing at entries in the cache with a UUID will be broken, and hard to fix manually.

It seems a poor solution to wire IDEA dependencies to potentially transient entries in the Gradle cache.


(Peter Niederwieser) #4

So you are talking about external dependencies, rather than project dependencies? The latter has a very specific meaning in Gradle.


(Tim Fox) #5

I am simply talking about the jar dependencies of a trivial Gradle project.

E.g. very simple project that uses a third party jar - that jar is in a Maven repo somewhere (e.g. Nexus)


(Peter Niederwieser) #6

First, how do you integrate with IDEA? Do you generate IDE files with Gradle’s ‘idea’ plugin, or import the build using IDEA’s Gradle support?

The Gradle idea plugin works to a certain extent - but also seems to fail if there are more than one version of the same snapshot in the cache. What should I do in this case.

What exactly do you mean by “fail”? What exactly do you mean by “more than one version of the same snapshot”? Can you provide a reproducible example?

Also what happens if the cache is wiped? Any deps pointing at entries in the cache with a UUID will be broken, and hard to fix manually.

Gradle itself won’t wipe the cache. If someone else does, the necessary dependencies will be downloaded again next time the IDE files are generated, or the build is synced in the IDE (depending on which mode of IDE integration you are using).

Note that it’s not recommended to put generated IDE files under source control. Instead they should be regenerated whenever necessary.


(Tim Fox) #7

By fail - I mean it doesn’t create the dependencies when running the idea task.

What do I mean by “more than one version of the same snapshot”? Quite often I end up with multiple versions of the same artifact in the Gradle cache - these are in separate sub directories only distinguished by an opaque UUID. They both contain a file with the exact same file name (e.g. foo.jar). I’m assuming these correspond to two different timestamped snapshots of the same artifact. The problem is there is no way for me to tell which one is the “correct” one to use.

And if Gradle is wiring dependencies to timestamped versions then the dependencies are going to get out of date when another timestamped snapshot comes along.


(Tim Fox) #8

Ok here’s an example:

As you can see there are two versions of the same artifact in the cache. The only thing that distinguishes them is that they are in different folders with different UUIDs

tim@tim-laptop ~/.gradle/caches/artifacts-23/filestore/io.vertx/testtools/1.0.0-SNAPSHOT/jar $ ls * 870f8d2ec5509849cb4df93730b502af71d26949: testtools-1.0.0-SNAPSHOT.jar

be915b661869ea52d0d0e98bc2c6d506d70c4c3e: testtools-1.0.0-SNAPSHOT.jar

These correspond to different timestamped snapshots pulled from a Maven repo (Nexus). I have verified this by pushing a new snapshot then rebuilding the Gradle project with --refresh-depdendencies. Every time this happens a new entry appears in the cache with a different UUID.

So the problem is this. Let’s say I only have a single snapshot in the cache for an artifact. I then run ./gradlew idea to create the IDEA project files. This creates an IDEA project with dependencies which are hardwired to the timestamp UUID cache directory.

I then push another snapshot for that artifact, and run the Gradle build again, this pulls the new artifact into the Gradle cache, but, wait a second the IDEA project is now pointing at the OLD stale artifact ==> BROKEN.

Any clearer now?


(Tim Fox) #9

So what I am supposed to do when a new snapshot is available?

Am I expected to manually edit the project dependencies and point it to the correct, newer directory? Note that to do this I will have to manually cd into the directory on the command line and ls -lrt to find out which is newer => PITA

Or, perhaps I can regenerate the idea files? Tried this and it doesn’t work - it doesn’t update the dependency to the newer one automatically.


(Peter Niederwieser) #10

Regenerating IDE files (or syncing dependencies if you happen to use the IDE tooling) is the correct action to take. Note that like Maven, Gradle caches snapshot resolution results for 24 hours by default. See ResolutionStrategy for how to configure this to your liking.


(Tim Fox) #11

Won’t regenerating IDEA project files overwrite any other changes I have made in them?

New snapshots can some on very frequently during development. Having to recreate them every time a new snapshot arrives seems onerous.

And how will I know if a new snapshot is available for a particular dependency? I won’t without checking the cache manually every time.

So the only thing to do would be to regenerate the idea project files every time I do a build. This seems absurd. Isn’t there a better solution?


(Peter Niederwieser) #12

It’s recommended to do all customizations within the Gradle build, so that the IDE files can be regenerated at all times. Nevertheless, ‘gradle idea’ will keep file sections that aren’t generated by itself.

Concerning how will you know, from my experience you either know (because you are waiting for some upstream change) or you don’t have to know, in which case it’s good enough to update, say, once or twice a day.

If you find regenerating IDE files too uncomfortable, try syncing dependencies with IDEA’s Gradle support. From what I’ve heard, it has improved significantly in 12.1 EAP. Of course, you’ll still have to configure ‘ResolutionStrategy’.


(Tim Fox) #13

Last time I checked (a few weeks ago) the IDEA Gradle plugin wasn’t much use at all.

My 2c would be to suggest you guys put some resources into getting the IDEA Gradle plugin up to scratch. I’d say good IDE support for Gradle is extremely important for a good developer experience, and this is sadly lacking at the moment.


(Peter Niederwieser) #14

The Gradle IDEA plugin works reasonably well for a lot of people. We don’t currently have plans to make major enhancements because the future is integrated IDE support (which we are also involved in via the Gradle tooling API). That said, there have been discussions around copying/symlinking dependencies into consuming projects, and maybe this is going to happen at some point.


#15

As you can see there are two versions of the same artifact in the cache. The only thing that distinguishes them is that they are in different folders with different UUIDs > > tim@tim-laptop ~/.gradle/caches/artifacts-23/filestore/io.vertx/testtools/1.0.0-SNAPSHOT/jar $ ls *

870f8d2ec5509849cb4df93730b502af71d26949:

testtools-1.0.0-SNAPSHOT.jar >

be915b661869ea52d0d0e98bc2c6d506d70c4c3e:

testtools-1.0.0-SNAPSHOT.jar

The UUID is the SHA1 has of the jar file itself, so this indicates that these are actually 2 different artifacts with the same version.

These correspond to different timestamped snapshots pulled from a Maven repo (Nexus). I have verified this by pushing a new snapshot then rebuilding the Gradle project with --refresh-depdendencies. Every time this happens a new entry appears in the cache with a different UUID.

So these snapshots are different binary artifacts, correct? (I assume that the timestamp is encoded within the binary itself).

Gradle maintains these artifacts separately in order to provide repository isolation. So if your project is configured to point at ‘Repository A’, then Gradle will only use the SNAPSHOT version that is available in ‘Repository A’, and will not make newer SNAPSHOTs available if they were downloaded from a different repository.

This repository isolation is different from Maven, but it provides a very important feature to ensure reproducible builds: you will not encounter the situation where a build “works on my machine” due to the presence of an artifact in the Gradle cache.

The downside to this more sophisticated caching mechanism is that the cache itself is more opaque. We have plans to provide a project-specific cache directory that would have a supported, transparent layout, but at this stage the cache layout is considered “private”.

If the ‘idea’ plugin isn’t working for you, an alternative would be to create your own project-level directory containing the jars used by your project, and wire that into your IDE project files instead.


(Tim Fox) #16

Well… I was advised by a Gradleware employee it wasn’t worth using. And my own experiences back that up


(Tim Fox) #17

“Nevertheless, gradle idea will keep file sections that aren’t generated by itself.”

Except it doesn’t work. Every time I regenerate my project because there’s a new snapshot it seems to overwrite project config - e.g. on re-opening I have to reset the project to Java 7 code compatibility - for some reason it defaults to 6. This workflow is broken.


(Peter Niederwieser) #18

I think you are mixing up the Gradle and IDEA side of things.


(Peter Niederwieser) #19

I said “file sections that are not generated by itself”. You can configure the IDEA language level in the build script.