what should a library built on guava depend upon?

164 views
Skip to first unread message

Alan Snyder

unread,
Sep 16, 2020, 1:45:21 PM9/16/20
to guava-discuss
Now that there are two flavors of guava (jre and android), how should a library that depends on guava document its dependency?
I've looked at two examples, and they both depend upon guava-jre.
Is that the recommended solution?
How does that affect users of the library who want android?

David P. Baker

unread,
Sep 17, 2020, 9:54:19 AM9/17/20
to Alan Snyder, guava-discuss
If you expect your library to be used by both Android applications and JRE applications, you should use the Android flavor. Otherwise, because some Guava APIs exist only in the JRE flavor, if your library uses them, then Android applications that use your library can fail at runtime.

--
guava-...@googlegroups.com
Project site: https://github.com/google/guava
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: https://github.com/google/guava/issues/new
To get help: http://stackoverflow.com/questions/ask?tags=guava
---
You received this message because you are subscribed to the Google Groups "guava-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to guava-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/guava-discuss/d84e3c96-039d-4cab-ade0-417e3762ba0en%40googlegroups.com.

Alan Snyder

unread,
Sep 17, 2020, 7:37:07 PM9/17/20
to guava-discuss
I see what you are saying from the perspective of compiling my library, but what I am more interested in (sorry for not making this clear) is how the library is documented in a repository such as Maven Central. In that context, my library needs guava-jre or guava-android, but I don't think there is any way to say that in the POM.

Thomas Broyer

unread,
Sep 18, 2020, 8:06:29 AM9/18/20
to guava-discuss


On Friday, September 18, 2020 at 1:37:07 AM UTC+2, Alan Snyder wrote:
I see what you are saying from the perspective of compiling my library, but what I am more interested in (sorry for not making this clear) is how the library is documented in a repository such as Maven Central. In that context, my library needs guava-jre or guava-android, but I don't think there is any way to say that in the POM.

No, and whatever you do it could cause problems to users depending on how they manage (or not) dependencies, and the tool they use (Maven and Gradle don't use the same algorithm for dependency resolution wrt which version to use)

If you declare a dependency on guava-android, you make it clear that it will work with Android.
Now, if a project also uses Guava (either directly or transitively through another dependency), the actual version being used will depend on… well, many things.

If they're using Maven, the version in their POM will always supersedes the version you use in your library, so if they use guava-jre, all is good (assuming they don't use an older version that wouldn't be compatible with your library).
If they're using Maven and not using Guava directly, Maven will use the "nearest" declaration, so it could be the one from your lib, or from the other lib. If it's yours, and the other lib uses a class or method that's not available in guava-android, it's going to fail at runtime. The same would be true though if you declared guava-jre and the other lib used classes/methods that are not available in the version you're using, because it needs a more recent one; or if the guava dependency from the other lib gets selected by the algorithm and it's incompatible (too old) with your lib.
Put simply: in Maven, the onus is on the project to know what its dependencies need and explicitly select a version that works for all of them (or make sure that the default one selected by Maven's algorithm is OK).

If they're using Gradle, the version will always be the highest of all declarations, direct or transitive.
So if they're using Guava but in a slightly older version that your lib, they'll get guava-android (from your lib), and that could break their project (either at build-time, or possibly only at runtime if the incompatibility comes from another dependency). The solution then is to have the project use at least the same version as your lib, but in its jre variant.
If you declare guava-jre though, it shouldn't cause any breakage for a JRE project; but it could bring in guava-jre to an Android project, forcing that project to enforce an Android version.
In any case, it's a good idea for a project to manage its dependencies, and therefore to explicitly select a "variant" of Guava that matches their environment and their dependencies' requirements.

Ideally, Guava could also publish Gradle Metadata so Gradle could automatically detect that both versions are variants of the same, and Gradle could possibly automatically upgrade a guava-android dependency to guava-jre when targeting JDK 8+.
Alternatively, your lib could publish Gradle Metadata in a way that it depends on guava-android when targeting JDK 7, and guava-jre when targeting JDK 8+ (although I'm not familiar enough with Android development to tell if the target Java version alone is enough; I know Android supports some of Java 8, but I have no idea if that means you could actually use some of guava-jre in such an Android project)

In the mean time, declaring a dependency on guava-android would make it clear that your lib is compatible with that; and downstream projects would be responsible with picking an appropriate version/variant.

David P. Baker

unread,
Sep 18, 2020, 8:55:33 AM9/18/20
to Thomas Broyer, guava-discuss
Ideally, Guava could also publish Gradle Metadata so Gradle could automatically detect that both versions are variants of the same, and Gradle could possibly automatically upgrade a guava-android dependency to guava-jre when targeting JDK 8+.

This is something I wasn't aware of, although it looks like it's been proposed already for Guava. If it will significantly help Gradle users, we should reprioritize that.

--
guava-...@googlegroups.com
Project site: https://github.com/google/guava
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: https://github.com/google/guava/issues/new
To get help: http://stackoverflow.com/questions/ask?tags=guava
---
You received this message because you are subscribed to the Google Groups "guava-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to guava-discus...@googlegroups.com.

Joachim Durchholz

unread,
Sep 18, 2020, 9:14:22 AM9/18/20
to guava-...@googlegroups.com
Am 18.09.20 um 14:06 schrieb Thomas Broyer:
>
> No, and whatever you do it could cause problems to users depending on
> how they manage (or not) dependencies, and the tool they use (Maven and
> Gradle don't use the same algorithm for dependency resolution wrt which
> version to use)

This is the exact reason why my production builds are set up with
maven-enforcer-plugin and the dependencyConvergence rule activated: I
prefer a message over a build where a dependency is not what I expected.
I have a chance to inspect all divergences, and actively decide which
one I want; the case where this actually matters is rare, but not rare
enough that it's not worth doing (a wrong dependency pulled in won't be
detected by any compiler, you'll just get runtime errors).

I don't know how to do something similar in Gradle (I would love to use
it but my job projects use Maven, so I'm much better with Maven than
with Gradle... sigh...)

> If they're using Gradle, the version will always be the highest of all
> declarations, direct or transitive.
> So if they're using Guava but in a slightly older version that your lib,
> they'll get guava-android (from your lib), and that could break their
> project (either at build-time, or possibly only at runtime if the
> incompatibility comes from another dependency). The solution then is to
> have the project use at least the same version as your lib, but in its
> jre variant.

Sounds like you need to check for dependency version divergence, too.
I think the Gradle strategy is saner than the Maven one. OTOH having a
resolution strategy in the first place is somewhat insane, it's one of
those gambles that usually work out but it's another source of potential
bugs.

> Ideally, Guava could also publish Gradle Metadata so Gradle could
> automatically detect that both versions are variants of the same, and
> Gradle could possibly automatically upgrade a guava-android dependency
> to guava-jre when targeting JDK 8+.

Even better, Guava could publish guava-android, guava-jdk, and guava-common.
Environment-agnostic code (libraries, mostly) can then depend on just
guava-common, and the whole issue is easier to handle.

Just barking from the trenches here :-)

Regards,
Jo

Alan Snyder

unread,
Sep 18, 2020, 10:33:46 AM9/18/20
to guava-discuss
Sounds like a no-win situation.

Perhaps the guava team should file an issue with the Maven project. They might pay more attention to Google than to me. :-)

Joachim Durchholz

unread,
Sep 18, 2020, 4:41:47 PM9/18/20
to guava-...@googlegroups.com
No chance.

They's lose build repeatability if they did that.
And that's Mavens core selling point.

They could do a new major version (Maven 4).
Or they could add an option to the pom that switches the default
resolution strategy.

However, the basic problem is this: A library tested with base-library.5
may be pulled into a context where some other library requires
base-library.6, so it will run against a dependency it was never tested
against.
This is independent of what resolution strategy you use.
Which means it's hard to have a convincing answer to "but is is worth
doing?"

Besides, Maven has so many other, more urgent problems.
E.g. total lack of IDE integration.
- No LSP integration
- Undecidable plugin semantic (IDEs can't interpret <execution>s)
- No incremental compilation support whatsoever
(simply out of scope, currently)
- rigid lifecycles (sequencing is complicated and does not compose)
- XML is a far too verbose specification language

Regards,
Jo

Am 18.09.20 um 16:33 schrieb Alan Snyder:
> Sounds like a no-win situation.
>
> Perhaps the guava team should file an issue with the Maven project. They
> might pay more attention to Google than to me. :-)
>
> On Friday, September 18, 2020 at 5:06:29 AM UTC-7Thomas Broyer wrote:
>
>
>
> On Friday, September 18, 2020 at 1:37:07 AM UTC+2, Alan Snyder wrote:
>
> I see what you are saying from the perspective of compiling my
> library, but what I am more interested in (sorry for not making
> this clear) is how the library is documented in a repository
> such as Maven Central. In that context, my library needs
> guava-jre /or/ guava-android, but I don't think there is any way
> to say that in the POM.
>
>
> No, and whatever you do it could cause problems to users depending
> on how they manage (or not) dependencies, and the tool they use
> (Maven and Gradle don't use the same algorithm for dependency
> resolution wrt which version to use)
>
>
> --
> guava-...@googlegroups.com
> Project site: https://github.com/google/guava
> This group: http://groups.google.com/group/guava-discuss
>
> This list is for general discussion.
> To report an issue: https://github.com/google/guava/issues/new
> To get help: http://stackoverflow.com/questions/ask?tags=guava
> ---
> You received this message because you are subscribed to the Google
> Groups "guava-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to guava-discus...@googlegroups.com
> <mailto:guava-discus...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/guava-discuss/0e4650ee-3125-4d59-bfa6-8e15d7c42583n%40googlegroups.com
> <https://groups.google.com/d/msgid/guava-discuss/0e4650ee-3125-4d59-bfa6-8e15d7c42583n%40googlegroups.com?utm_medium=email&utm_source=footer>.

Alan Snyder

unread,
Sep 19, 2020, 11:25:09 AM9/19/20
to guava-discuss
Jo,

Perhaps we are talking about different possible changes?

What I would like is for my library to indicate a dependence on guava, and perhaps a dependence on specific versions of guava, but have no effect whatsoever on the selection of guava-jre vs guava-android. How would that break build repeatability?

Joachim Durchholz

unread,
Sep 19, 2020, 3:26:24 PM9/19/20
to guava-...@googlegroups.com
Repeatability for your library is not the concern.
It's repeatability for code that has a direct dependency on your
library. They might declare a direct dependency on a specific guava
version, or they might pull in another library that in turn pulls in guava.
Whatever these details, your library will be confronted with a guava
library that's different from the one was made for.
Most of the time, this actually works. Unless it doesn't.

One data point about how often it may _not_ work:
I'm the primary maintainer of an application of 180,000 lines of code,
plus roughly ten times of that in library code.
Roughly a dozen direct dependencies and maybe 40 indirect ones (I really
have to make some statistics but I can't access the code right now).
Version incompatibilities are actually unavoidable. There's always the
odd couple of libraries that insist on some specific version of SLF4J
despite never using any of the newer features; there's Jasper reports
which is a flaming mess in general (the Jasper main library itself has
version inconsistencies in the libraries it's pulling in...); beyond
these two high-profile libraries, there's usually half a dozen other
dependency version inconsistencies.
Most dependency incompatibilities are fine. Autoresolution picks a
version that everybody can work with.
However, there's usually two or three dependencies where this does not
work. For example, I have seen libraries that wouldn't work for a newer
version of SLF4J; there are libraries that directly pull in logback
(which you should never do, it's the main application that selects
them), there are libraries that forget to specify a dependency, and more
stuff like this.
So... I have to manually validate the dependencies anyway.

For small applications with two or three dependencies, this kind of
problem is rare, but as the number of libraries increases, you get an
O(N²) of potential interactions, and I'd say the critical mass is at
roughly 20 dependencies that are pulled in directly or indirectly. (Just
*very* rule-of-thumb estimate. It depends a lot on how
backwards-compatible libraries are.)

Am 19.09.20 um 17:25 schrieb Alan Snyder:
> https://groups.google.com/d/msgid/guava-discuss/5f5bb918-3e76-407b-9505-e4de6693035an%40googlegroups.com
> <https://groups.google.com/d/msgid/guava-discuss/5f5bb918-3e76-407b-9505-e4de6693035an%40googlegroups.com?utm_medium=email&utm_source=footer>.

Alan Snyder

unread,
Sep 19, 2020, 3:49:03 PM9/19/20
to guava-discuss
That problem appear unavoidable. If my library declares itself dependent on guava-jre and someone wants to use it on Android, my library "will be confronted with a guava library that's different from the one it was made for". Is there a better way?

Chris Povirk

unread,
Sep 21, 2020, 10:19:21 AM9/21/20
to Alan Snyder, guava-discuss
The good news is that guava-XX-jre is as good a substitute for guava-XX-android as you could hope for two versions to be: The APIs in -jre are always a superset of the APIs in -android. It's technically possible for there to be behavior differences between the two (and there might well be trivial differences in things like the iteration order of hash data structures), but they are, in most cases, exactly the same code.

David's advice is the best we could make of the unfortunate situation. Hopefully we are closing in on the day when we can drop Java 7 support and assume that most Java 8 APIs are available on Android, but we're not there yet. In the meantime, we will try to pursue the Gradle Metadata approach to help Gradle users.

Joachim Durchholz

unread,
Sep 21, 2020, 11:27:46 AM9/21/20
to guava-...@googlegroups.com
Am 19.09.20 um 21:49 schrieb Alan Snyder:
> That problem appear unavoidable If my library declares itself dependent
> on guava-jre and someone wants to use it on Android, my library "will be
> confronted with a guava library that's different from the one it was
> made for". Is there a better way?

As library consumer, yes - manually check that the library that's
getting an unexpected version is compatible.

As library author, you can be very, very strict about semantic versioning.
Your consumers may still get version divergence complaints from Maven
Enforcer, but they will find it easy to resolve them.
> https://groups.google.com/d/msgid/guava-discuss/1434a5c4-a650-4c92-a3b0-f5aaca110c1dn%40googlegroups.com
> <https://groups.google.com/d/msgid/guava-discuss/1434a5c4-a650-4c92-a3b0-f5aaca110c1dn%40googlegroups.com?utm_medium=email&utm_source=footer>.

Alan Snyder

unread,
Sep 22, 2020, 3:49:33 PM9/22/20
to guava-discuss
I'm guessing that the fact that the APIs of the two variants are not identical is why the option of a separate API artifact was not considered and explicitly rejected in your otherwise thorough design document.

Alan Snyder

unread,
Sep 22, 2020, 3:58:26 PM9/22/20
to guava-discuss
Actually, I would test my library on both JRE and Android, so neither variant is unexpected as long as the version number matches.
Unfortunately, there is no way in the deployed POM to declare a dependency on the version number but not on the jre/android variant.
(In case it is not obvious, I do not assume that the POM in the repository is the same POM used to build the library, or even that
the library is built using Maven.) I'm not sure whether is better to pick a version (with variant) somewhat arbitrarily (as recommended) or not
mention any version in the deployed POM.

Chris Povirk

unread,
Sep 23, 2020, 3:55:02 PM9/23/20
to Alan Snyder, guava-discuss
On Tue, Sep 22, 2020 at 3:49 PM Alan Snyder <alan.the...@gmail.com> wrote:
I'm guessing that the fact that the APIs of the two variants are not identical is why the option of a separate API artifact was not considered and explicitly rejected in your otherwise thorough design document.

Sorry, can you explain what a "separate API artifact" would look like? Would it have APIs but no implementations?

On Tue, Sep 22, 2020 at 3:58 PM Alan Snyder <alan.the...@gmail.com> wrote:
Actually, I would test my library on both JRE and Android, so neither variant is unexpected as long as the version number matches.
Unfortunately, there is no way in the deployed POM to declare a dependency on the version number but not on the jre/android variant.
(In case it is not obvious, I do not assume that the POM in the repository is the same POM used to build the library, or even that
the library is built using Maven.) I'm not sure whether is better to pick a version (with variant) somewhat arbitrarily (as recommended) or not
mention any version in the deployed POM.

It's a good point that we should separate the questions "What do you build against?" and "What do you list in your published POM?" I would recommend -android in both cases:
  • (as already discussed, but I'll repeat for anyone skimming this thread) Building against -android ensures that you don't use any APIs specific to -jre. This way, your library works when run against either -android or -jre.
  • Listing -android in your published POM documents that your library works with it (as opposed to requiring some of the APIs specific to -jre). The other alternative you mentioned was to not list Guava at all. The downside of that is that, if someone uses your library and doesn't otherwise depend on Guava, Guava will be missing at runtime. The user can always provide it, of course, but by listing it yourself, you save the user the trouble, and you document which version your library was built and tested against.

Joachim Durchholz

unread,
Sep 23, 2020, 7:12:56 PM9/23/20
to guava-...@googlegroups.com
Am 22.09.20 um 21:58 schrieb Alan Snyder:
> Actually, I would test my library on both JRE and Android, so neither
> variant is unexpected as long as the version number matches.

Your consumers will get unexpected versions if some *other* library
imports the library at a different version (and gets prioritized).

jre vs jdk does not factor in at all.

This is because Maven will always accept the dependency's name as it,
but it may decide to use a different version.

> Unfortunately, there is no way in the deployed POM to declare a
> dependency on the version number but not on the jre/android variant.

Indeed. You can't say "I don't care whether I get android or jre but it
should be version X or later". (Actually you *can* say "X or later" in
Maven. Except nobody does this because you don't know whether the "or
later" will work at all.)

The usual setup would be three libraries:
- guava for those who want to be able to run on both
- guava-android for those who need Android-specific guava API
- guava-jre for the JRE-specific guava API

These might depend on common implementation libraries, but that's of no
concern to those who pull in one of the three above libraries.

If you feel the urge to allow either of two libraries, then that's a
design flaw in the library, not in the tooling or the versions ;-)

> (In case it is not obvious, I do not assume that the POM in the
> repository is the same POM used to build the library,

That's possible but why should anybody do that?

> or even that the library is built using Maven.)
This is in fact pretty commonplace: Whenever you use something other
than Maven, it needs to generate a POM if the result is to be published
to a Maven repository.

> I'm not sure whether is better to pick a version (with variant)
> somewhat arbitrarily (as recommended) or not mention any version in
> the deployed POM.
a) no variant, either android or jre in this case
(android is essentially the portable version)
b) you pick the version that you test with
consumers of your library can always override that

BTW consumers of your library can also override the dependency you
specify: They exclude>any indirect dependency they don't want (via
<exclusion>) and add a direct dependency on what they want (via
<dependency>).

I.e. the dependencies you declare are just recommendations.
The main difference being that changing the name cannot happen
unintentionally, changing the version can (but only if your library has
become used so widely that somebody pulls it in, twice, via different
direct dependencies - that's why library authors tend to be unaware that
there might be an issue).

HTH
Jo

Bob Lee

unread,
Sep 23, 2020, 7:27:23 PM9/23/20
to Alan Snyder, guava-discuss
FWIW, Guice uses "classifier" for this (https://maven.apache.org/pom.html#Dependencies):

classifier:
The classifier distinguishes artifacts that were built from the same POM but differ in content. It is some optional and arbitrary string that - if present - is appended to the artifact name just after the version number.

As a motivation for this element, consider for example a project that offers an artifact targeting Java 11 but at the same time also an artifact that still supports Java 1.8. The first artifact could be equipped with the classifier jdk11 and the second one with jdk8 such that clients can choose which one to use.

Another common use case for classifiers is to attach secondary artifacts to the project's main artifact. If you browse the Maven central repository, you will notice that the classifiers sources and javadoc are used to deploy the project source code and API docs along with the packaged class files.


--
guava-...@googlegroups.com
Project site: https://github.com/google/guava
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: https://github.com/google/guava/issues/new
To get help: http://stackoverflow.com/questions/ask?tags=guava
---
You received this message because you are subscribed to the Google Groups "guava-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to guava-discus...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/guava-discuss/d84e3c96-039d-4cab-ade0-417e3762ba0en%40googlegroups.com.

Joachim Durchholz

unread,
Sep 23, 2020, 9:00:58 PM9/23/20
to guava-...@googlegroups.com
Using nonstandard classifiers means that no tool will know to use them.

E.g. an IDE won't know about the -tests.jar and the -test-sources.jar.
(These classifiers seem to be used in other projects as well, but not in
many - or I threw the wrong keywords at the search engine. Anyway:)
I don't even see value in publishing such artifacts. If the code is unit
tests, it should be in the source repository and you don't need to have
Maven package it.

Specifically in Guice, I don't know what -classes.jar or -no_aop.jar is
about, and I couldn't find documentation what these are supposed to be.
Which is the other kind of problem you get if you use nonstandard
classifiers: People won't know them and won't use the artifacts.

Is it really the same use case as for guava-android and guava-jre?
I.e. would somebody want to use one of the classifier-appended artifacts?
(It's certainly possible to pick them in a <dependency>, by saying
<type>. It's just unclear how to get e.g. the source code for such a
dependency.)

Regards,
Jo

Am 24.09.20 um 01:27 schrieb Bob Lee:
> FWIW, Guice uses "classifier" for this
> (https://maven.apache.org/pom.html#Dependencies):
>
> *classifier*:
> The classifier distinguishes artifacts that were built from the same
> POM but differ in content. It is some optional and arbitrary string
> that - if present - is appended to the artifact name just after the
> version number.
>
> As a motivation for this element, consider for example a project
> that offers an artifact targeting Java 11 but at the same time also
> an artifact that still supports Java 1.8. The first artifact could
> be equipped with the classifier |jdk11| and the second one with
> |jdk8| such that clients can choose which one to use.
>
> Another common use case for classifiers is to attach secondary
> artifacts to the project's main artifact. If you browse the Maven
> central repository, you will notice that the classifiers
> |sources| and |javadoc| are used to deploy the project source code
> and API docs along with the packaged class files.
>
>
> On Wed, Sep 16, 2020 at 10:45 AM Alan Snyder <alan.the...@gmail.com
> <mailto:alan.the...@gmail.com>> wrote:
>
> Now that there are two flavors of guava (jre and android), how
> should a library that depends on guava document its dependency?
> I've looked at two examples, and they both depend upon guava-jre.
> Is that the recommended solution?
> How does that affect users of the library who want android?
>
> --
> guava-...@googlegroups.com <mailto:guava-...@googlegroups.com>
> Project site: https://github.com/google/guava
> This group: http://groups.google.com/group/guava-discuss
>
> This list is for general discussion.
> To report an issue: https://github.com/google/guava/issues/new
> To get help: http://stackoverflow.com/questions/ask?tags=guava
> ---
> You received this message because you are subscribed to the Google
> Groups "guava-discuss" group.
> To unsubscribe from this group and stop receiving emails from it,
> send an email to guava-discus...@googlegroups.com
> <mailto:guava-discus...@googlegroups.com>.
> <https://groups.google.com/d/msgid/guava-discuss/d84e3c96-039d-4cab-ade0-417e3762ba0en%40googlegroups.com?utm_medium=email&utm_source=footer>.
>
> --
> guava-...@googlegroups.com
> Project site: https://github.com/google/guava
> This group: http://groups.google.com/group/guava-discuss
>
> This list is for general discussion.
> To report an issue: https://github.com/google/guava/issues/new
> To get help: http://stackoverflow.com/questions/ask?tags=guava
> ---
> You received this message because you are subscribed to the Google
> Groups "guava-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to guava-discus...@googlegroups.com
> <mailto:guava-discus...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/guava-discuss/CAGmsiP7Q3KOMFRdd%3DPRLkpBm_idiDHuBP%3Desp%2B4VzxXUQ0p5cA%40mail.gmail.com
> <https://groups.google.com/d/msgid/guava-discuss/CAGmsiP7Q3KOMFRdd%3DPRLkpBm_idiDHuBP%3Desp%2B4VzxXUQ0p5cA%40mail.gmail.com?utm_medium=email&utm_source=footer>.

Thomas Broyer

unread,
Sep 24, 2020, 4:44:17 AM9/24/20
to guava-discuss
Using different classifiers for the same version means sharing the same POM, sharing the same dependencies. That means people choosing to use the no_aop variant will have the aopalliance dependency even though they don't need it, unless they explicitly exclude it (and know that they can/should exclude it).
Using different classifiers for the same version means that you could have both variants in your dependency tree, and will have to use exclusions (which in Maven means you'll have to track which of your direct dependencies brings the variant your don't want, and exclude it for each of those direct dependency); but because those variants will just "shadow" the other one in the classpath (assuming you don't use JPMS' module-path), you could ship both without even noticing it (AFAICT, Android tooling will tell you, in the case of Guava, but still)

Despite what the Maven documentation says, in practice this is the worst solution for things that should be mutually exclusive (it's completely OK for sources and javadoc, or even possibly a with_dependencies JAR like ErrorProne does, because that one is not meant to be used as a "managed dependency" so it shouldn't have its own GAV).
Maven is not equipped to deal with classifiers other than for "secondary artifacts".

On Thursday, September 24, 2020 at 1:27:23 AM UTC+2, Bob Lee wrote:
FWIW, Guice uses "classifier" for this (https://maven.apache.org/pom.html#Dependencies):

classifier:
The classifier distinguishes artifacts that were built from the same POM but differ in content. It is some optional and arbitrary string that - if present - is appended to the artifact name just after the version number.

As a motivation for this element, consider for example a project that offers an artifact targeting Java 11 but at the same time also an artifact that still supports Java 1.8. The first artifact could be equipped with the classifier jdk11 and the second one with jdk8 such that clients can choose which one to use.

Another common use case for classifiers is to attach secondary artifacts to the project's main artifact. If you browse the Maven central repository, you will notice that the classifiers sources and javadoc are used to deploy the project source code and API docs along with the packaged class files.


On Wed, Sep 16, 2020 at 10:45 AM Alan Snyder <alan.th...@gmail.com> wrote:
Now that there are two flavors of guava (jre and android), how should a library that depends on guava document its dependency?
I've looked at two examples, and they both depend upon guava-jre.
Is that the recommended solution?
How does that affect users of the library who want android?

--
guava-...@googlegroups.com
Project site: https://github.com/google/guava
This group: http://groups.google.com/group/guava-discuss
 
This list is for general discussion.
To report an issue: https://github.com/google/guava/issues/new
To get help: http://stackoverflow.com/questions/ask?tags=guava
---
You received this message because you are subscribed to the Google Groups "guava-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to guava-...@googlegroups.com.

Joachim Durchholz

unread,
Sep 25, 2020, 2:41:18 AM9/25/20
to guava-...@googlegroups.com
Am 24.09.20 um 10:44 schrieb Thomas Broyer:
> Using different classifiers for the same version means sharing the same
> POM, sharing the same dependencies.

Agreed.

> That means people choosing to use the no_aop variant will have the
> aopalliance dependency even though they don't need it, unless they
> explicitly exclude it (and know that they can/should exclude it).
As far as I understand Maven's dependency handling, a dependency without
<type> will give you just the standard jar, with <type>no_aop</type> you
get the no_aop artifact instead.

Caveat: I have never used <type> in a <dependency>, so I'm going by theory.

> Using different classifiers for the same version means that you could
> have both variants in your dependency tree, and will have to use
> exclusions (which in Maven means you'll have to track which of your
> direct dependencies brings the variant your don't want, and exclude it
> for each of those direct dependency); but because those variants will
> just "shadow" the other one in the classpath (assuming you don't use
> JPMS' module-path), you could ship both without even noticing it
> (AFAICT, Android tooling will tell you, in the case of Guava, but still)

I believe you'll get problems not with the Guice library itself but with
Guice's dependencies: If the no_aop variant has different dependencies
than the normal one, Guice's POM cannot express that, and any consumers
of Guice will have all indirect dependencies that any variant of Guice
needs unless they do exclusions.

> Despite what the Maven documentation says, in practice this is the worst
> solution for things that should be mutually exclusive (it's completely
> OK for sources and javadoc, or even possibly a with_dependencies JAR
> like ErrorProne does, because that one is not meant to be used as a
> "managed dependency" so it shouldn't have its own GAV).

Exactly this.

> Maven is not equipped to deal with classifiers other than for "secondary
> artifacts".

Matches my experience. The name <type> for identifying a dependency with
a classifier is a big giveaway.

BTW it's not just Maven, it's Maven conventions that aren't equipped. If
you publish a library, you are adhering to these conventions, and since
they don't cover all corner cases, straying from the beaten path is
going to hit more unexpected effects.
Which means consumer complaints about incompatibilities; if the
incompatibility is with a pom, the user will fix it, but if it's between
a plugin, a user, and the library, the resulting three-way discussion is
going to be difficult.

My recommendation for the Guice guys would be to publish two artifacts
and use classifiers the standard way.

But well, that's too far off-topic here :-)

Regards,
Jo

Alan Snyder

unread,
Sep 28, 2020, 10:23:42 AM9/28/20
to guava-discuss
Yes, that is what I meant.

Alan Snyder

unread,
Sep 28, 2020, 10:39:22 AM9/28/20
to guava-discuss
I'm not understanding your point.

If I write a library that works with any flavor of guava, how it is a "design flaw" to allow all flavors?

It seems to me the flaw would be for my library to recommend one flavor over the other if doing so influences the resolution process used by clients.

I would call it a flaw in the tooling if the selection of a flavor is made arbitrarily and the person who needs to make the choice is not given any warning.

(I understand that one flavor is a subset of the other and that one can deduce something by naming the "smaller" flavor as the dependency, but the subset relationship potentially could change in the future, so I'm interested in the more general case.)

Chris Povirk

unread,
Sep 28, 2020, 10:50:31 AM9/28/20
to Alan Snyder, guava-discuss
It is very important to us to preserve the subset relationship for exactly this reason. That, at least, should be safe to rely on.

The other difficulties remain, though. My understanding is that there is no great solution to this problem (at least under Maven -- we should look at the Gradle-specific approach sometime).

Thomas Broyer

unread,
Sep 28, 2020, 12:33:01 PM9/28/20
to guava-discuss


On Friday, September 25, 2020 at 8:41:18 AM UTC+2, Toolforger wrote:
Am 24.09.20 um 10:44 schrieb Thomas Broyer:
> Using different classifiers for the same version means sharing the same
> POM, sharing the same dependencies.

Agreed.

> That means people choosing to use the no_aop variant will have the
> aopalliance dependency even though they don't need it, unless they
> explicitly exclude it (and know that they can/should exclude it).
As far as I understand Maven's dependency handling, a dependency without
<type> will give you just the standard jar, with <type>no_aop</type> you
get the no_aop artifact instead.

Caveat: I have never used <type> in a <dependency>, so I'm going by theory.

Nope, <type> relies on Artifact Handlers, either the built-in ones, or ones that can be registered by extensions: https://maven.apache.org/ref/3.6.3/maven-core/artifact-handlers.html

Joachim Durchholz

unread,
Sep 28, 2020, 3:25:38 PM9/28/20
to guava-...@googlegroups.com
Am 28.09.20 um 16:50 schrieb 'Chris Povirk' via guava-discuss:
> (at least under Maven -- we should
> look at the Gradle-specific approach sometime).

I suspect there's little hope in that.
Gradle reuses Maven's dependency information (at least when consuming
Maven artifacts). This dependency information is designed for Maven's
consumption, so other build tools can't do anything that the
already-available metadata doesn't already provide.

It can use the metadata differently.

Even that is dangerous.
For example, the difference in version conflict resolution.
Which means the same dependency metadata will be interpreted differently.
That's bad if you pull in a library that has such conflicts in its own
dependencies. E.g. Jasper Reports comes with this situation:

net.sf.jasperreports:jasperreports:6.3.0
- commons-beanutils:commons-beanutils:1.9.0
- commons-digester:commons-digester:2.1
- commons-beanutils:commons-beanutils:1.8.3

I.e. jasperreports pulls in beanutils-1.9.0, but it also pulls in
commons-digester which expects commons-beanutils-1.8.3.
In this case, Maven and Gradle will do the same thing and pull in 1.9.0,
Gradle, because 1.9.0 is newer than 1.8.3, Maven, because the 1.9.0
dependency is direct and the 1.8.3 dependency is indirect.

(Source:
https://community.jaspersoft.com/jasperreports-library/issues/8541 )


Now consider the same situation with the beanutils version reversed:

net.sf.jasperreports:jasperreports:6.3.0
- commons-beanutils:commons-beanutils:1.8.3
- commons-digester:commons-digester:2.1
- commons-beanutils:commons-beanutils:1.9.0

Now Maven will pull in 1.8.3 (and commons-digester may now have bugs
because it's expecting version 1.9.0), but Gradle will still pull in
1.9.0 because it's newer.

Jasper pulls in a ton of indirect dependencies, so it's pretty likely
that there are bugs present here. Probably not very many since the
Jaspersoft people tend to specify newer direct dependencies, and given
Jasper Reports mission it's not very likely that an actual bug will
cause very obvious errors (some outputs will be wrong, and the
phenomenon is likely gone with the next release anyway), but with the
differences it's possible that the Jaspersoft engineers won't be able to
reproduce a problem reported by a Gradle user.

Regards,
Jo

Joachim Durchholz

unread,
Sep 28, 2020, 4:28:08 PM9/28/20
to guava-...@googlegroups.com
Ah, that's where the -tests.jar stuff is coming from.
Also, there's a difference between <type> and classifier that I wasn't
aware of before.
You learn something new every day...

There doesn't seem to be an artifact handler for no_aop though. At least
the search engine doesn't give me anything useful.

Am 28.09.20 um 18:33 schrieb Thomas Broyer:
> --
> guava-...@googlegroups.com
> Project site: https://github.com/google/guava
> This group: http://groups.google.com/group/guava-discuss
>
> This list is for general discussion.
> To report an issue: https://github.com/google/guava/issues/new
> To get help: http://stackoverflow.com/questions/ask?tags=guava
> ---
> You received this message because you are subscribed to the Google
> Groups "guava-discuss" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to guava-discus...@googlegroups.com
> <mailto:guava-discus...@googlegroups.com>.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/guava-discuss/5aed2a25-ed9f-40c2-980c-f6e924f833b0o%40googlegroups.com
> <https://groups.google.com/d/msgid/guava-discuss/5aed2a25-ed9f-40c2-980c-f6e924f833b0o%40googlegroups.com?utm_medium=email&utm_source=footer>.

Joachim Durchholz

unread,
Sep 28, 2020, 4:45:38 PM9/28/20
to guava-...@googlegroups.com
Am 28.09.20 um 16:39 schrieb Alan Snyder:
> I'm not understanding your point.
>
> If I write a library that works with any flavor of guava, how it is a
> "design flaw" to allow all flavors?

The design flaw is that a library that doesn't have a xxx-common base
forces your consumers to select one or the other dependency.

So if you depend on guava-android and a consumer needs guava-jre for
reasons unrelated to your library, they won't be able to use your library.
Unless they verify that your library will work with guava-jre as well.
However, that now means reading the documentation, validating that the
document is correct, so they will have the unlucky choice between
checking compatibility manually or running a risk.

Now if Guava were published as a separate artifact per use case, like this:
- guava-android - if you want the Android optimizations
Contains implementation classes for Android
- guava-jre - if you want the JRE optimizations and/or features
Contains implementation classes for the JRE
- guava-api - if you don't care about what version you get
Contains the interfaces (and possibly common code)

Your library will likely pull in guava-api, and one of guava-android or
guava-jre as a "test dependency", which will make your own build process
depend on that implementation library but not the people who pull your
library in as a dependency (so they still have the choice).
You will have to tell your consumers that they need to pull in either
guava-android or guava-jre.

For the Guava implementors, they'd want to create common implementation
libraries that guava-android and guava-jre would depend on. But the
exact content and dependency structure of these background libraries is
at the discretion of the Guava team.

Regards,
Jo

P.S.: This advice is written from the "how to play best with Maven"
perspective.
The Guava team will have to consider a lot more factors, like workload,
available multi-project build management tools, consumers to educate,
and possibly other things I can't think of right now.
I.e. I know full well that this advice is likely inapplicable.

Thomas Broyer

unread,
Sep 29, 2020, 6:33:20 AM9/29/20
to guava-discuss


On Monday, September 28, 2020 at 9:25:38 PM UTC+2, Toolforger wrote:
Am 28.09.20 um 16:50 schrieb 'Chris Povirk' via guava-discuss:
> (at least under Maven -- we should
> look at the Gradle-specific approach sometime).

I suspect there's little hope in that.
Gradle reuses Maven's dependency information (at least when consuming
Maven artifacts). This dependency information is designed for Maven's
consumption, so other build tools can't do anything that the
already-available metadata doesn't already provide.

Chris was talking about the Gradle Module Metadata we already talked about above: https://groups.google.com/d/msg/guava-discuss/r6gJrMudYr4/4MdFbClPBQAJ
Reply all
Reply to author
Forward
0 new messages