-
Notifications
You must be signed in to change notification settings - Fork 10.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Gradle Module Metadata makes Guava's -android version dangerous for libraries #7575
Comments
Ouch :( Thanks for the report, and sorry. It's convenient that we just exposed a bunch more APIs in the Android flavor in Guava 33.4.0 and that, as you note, most of what's been "missing" in Android is APIs that would trigger Animal Sniffer / NewApi errors, anyway. But as you also note, there's at least one high-profile exception, We've made some attempts to make our However, I don't think we'd considered what you've pointed out, which is that we could add |
|
Even adding As before, it's possible that everything would work fine if I were to also add it externally, but I'm not sure, so I would be nervous about trying it. I'm not sure if it's worth falling back to the Animal Sniffer route or to playing with Gradle. Do you have any additional ideas? |
gRPC has a parallel Bazel build for some components. That build doesn't notice the Gradle Module Metadata so can detect API cases. That's how we found -android didn't support I'm most disappointed by how hard it was/is to figure out what was going on. I tried the various attributes and select() goo. I got some of it working, but it was fickle and half the time when it breaks it quietly swaps back to the jre verion, so I don't have any real confidence in it. It also gave confusing errors about (I didn't get our re-implementation of I played with it a lot yesterday writing this reply, and I think the only reasonable play is to disable Module Metadata. It is simply too complicated with poor control and debugging tools. Even if I learned it enough to control it, there's no way my wider team does. Dependencies are hard enough without having to read every dependency's module metadata to understand what may impact my project. Without a high-level view like
We'll see how long it takes for this to bite us. I'm suspecting Kotlin will be the problem. FWIW, the failures caused by adding If |
The module metadata in Guava causes the -jre version to be selected even when you choose the -android version. Gradle did not give any clues that this was happening, and while `println(configurations.compileClasspath.resolve())` shows the different jar in use, most other diagonstics don't. dependencyInsight can show you this is happening, but only if you know which dependency has a problem and read Guava's module metadata first to understand the significance of the results. You could argue this is a Guava-specific problem. I was able to get parts of our build working with attributes and resolutionStrategy configurations mentioned at https://github.com/google/guava/releases/tag/v32.1.0 , so that only Guava would be changed. But it was fickle giving poor error messages or silently swapped back to the -jre version. Given the weak debuggability, the added complexity, and the lack of value module metadata is providing us, disabling module metadata for our entire build seems prudent. See google/guava#7575
The module metadata in Guava causes the -jre version to be selected even when you choose the -android version. Gradle did not give any clues that this was happening, and while `println(configurations.compileClasspath.resolve())` shows the different jar in use, most other diagonstics don't. dependencyInsight can show you this is happening, but only if you know which dependency has a problem and read Guava's module metadata first to understand the significance of the results. You could argue this is a Guava-specific problem. I was able to get parts of our build working with attributes and resolutionStrategy configurations mentioned at https://github.com/google/guava/releases/tag/v32.1.0 , so that only Guava would be changed. But it was fickle giving poor error messages or silently swapping back to the -jre version. Given the weak debuggability, the added complexity, and the lack of value module metadata is providing us, disabling module metadata for our entire build seems prudent. See google/guava#7575
Thanks, that's probably the best course of action. (And we'll likely follow suit with any KMP libraries that we publish, since presumably they'll end up using Gradle, too.) I assume that completely removing the Gradle Module Metadata at this point would be worse than leaving it in place, but maybe the problems will continue to trickle in until I have to reevaluate. It's probably still possible to produce an error that wouldn't be caught by Animal Sniffer, but I'm hoping it would have to be something like "Call one Guava method that returns a |
Yeah, I have no clue whether it is better to keep or remove the Gradle Module Metadata. Thanks for double-checking the diffs. I knew you had them between versions, but I didn't realize you have them between -jre and -android (and yes, they are pretty easy to find in the release notes). That makes sense and is very helpful. I looked over them as well and agree; the only other missing methods that technically could be implemented are useless mutation methods for I see what you mean about the Animal Sniffer hole of not calling any methods on a Java 8 class, but just using Guava APIs to interact with it. That could crop up in other cases, but Guava is much easier to do it with. I actually didn't realize y'all excluded Duration overloads in -android. gRPC's most recent release actually added them multiple places. I had thought missing methods was more limited to things like Function, which wouldn't be efficient to provide as you'd have to allocate an adapter. Good to realize the hole is there. |
I'm comfortable calling this resolved. I wanted it documented in case others were bit and thought you would want to be aware. I also feel comfortable that there aren't many ways for a gradle-using library to be surprised. I'll let you decide when to close it, in case you want it open to mull over. |
RE: I'll keep this issue open for now: Ideally I'd provide better documentation than this issue (though it is already a big help to me and probably to some users someday), and we may have to confront this again ourselves for the KMP work that I'd mentioned. |
I think the gradle preferred solution is to use capabilities. If an android capability the module metadata uses the android jar, but if it is only a jre capability it redirects to the jre jar because a user might have specified the wrong version in their build. If the consumer uses capabilities correctly then I think the module works, but if not then it’s finicky. You might try the jvm-dependency-conflict-resolution plugin which helps for these common gotchas without needing to muck with gradle’s complexity. |
The module metadata in Guava causes the -jre version to be selected even when you choose the -android version. Gradle did not give any clues that this was happening, and while `println(configurations.compileClasspath.resolve())` shows the different jar in use, most other diagonstics don't. dependencyInsight can show you this is happening, but only if you know which dependency has a problem and read Guava's module metadata first to understand the significance of the results. You could argue this is a Guava-specific problem. I was able to get parts of our build working with attributes and resolutionStrategy configurations mentioned at https://github.com/google/guava/releases/tag/v32.1.0 , so that only Guava would be changed. But it was fickle giving poor error messages or silently swapping back to the -jre version. Given the weak debuggability, the added complexity, and the lack of value module metadata is providing us, disabling module metadata for our entire build seems prudent. See google/guava#7575
Guava Version
Noticed on 33.2.1-android because of an old PR, but currently using 33.3.1-android
Description
TL;DR: Gradle uses the Module Metadata to use -jre version, but Maven and AGP will use -android meaning we compile and test with a superset of what our users will use.
In gRPC Java we mostly use Guava's -android versions because we support older Android versions. We also use the Gradle build system. However, we discovered that when compiling the -jre version is actually what's being used.
gradle dependencies
shows -android, but the jar file passed to javac is -jre.This was quite the riddle until I remembered Guava uses Gradle Module Metadata, and yep, the -android version pulls in the -jre version. See also #7154
gRPC Java is a library, and used by Maven and Android users. While Gradle used the -jre version to compile, the published artifacts still depend on the -android version. So Maven and Android users will use -android. This means it is possible for -jre-only symbols to be used accidentally and then cause runtime failures when unavailable.
Most cases when -jre symbols are used will cause animalnsiffer failures, because it uses APIs not available on the target platform. But that is not universally the case. For example, the way we noticed this was
Predicate.test()
. The -jre version provides the test() method, because it extendsjava.util.function.Predicate
. But the -android version does not even though it could without requiring Java 8 APIs.Example
Expected Behavior
Javac could not find symbol Predicate.test().
Actual Behavior
./gradlew compileJava
succeeds.Packages
No response
Platforms
No response
Checklist
I agree to follow the code of conduct.
I can reproduce the bug with the latest version of Guava available.
The text was updated successfully, but these errors were encountered: