Skip to content
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

Guava + JPMS #7094

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Guava + JPMS #7094

wants to merge 1 commit into from

Conversation

sgammon
Copy link
Contributor

@sgammon sgammon commented Mar 10, 2024

Summary

This PR adds modular Java support to Guava, by adding a module-info.java definition to the guava and guava-testlib modules. Upstream work in the Error Prone Compiler, J2ObjC Annotations, and Checker Framework has also completed, rendering Guava able to build entirely on the modulepath.

Fixes and closes #2970

Current Status

  • ✅ Guava builds as a module
  • ✅ Guava builds completely on modulepath
  • ✅ All tests pass
  • ✅ All warnings fixed (within reason, some cannot be fixed or suppressed)
  • ✅ Javadoc builds at JVM21
  • ✅ Integration and smoke tests
  • ✅ Final PR cleanup + autorebase
Old PR tree

PR Tree

On the way to a finished PR, there were a number of other changes and improvements that surfaced. These have been broken out into their own PRs for easier review; they are listed below as Downstream PRs.

PRs that are Included in this PR can be rebased away, either when they are merged or closed without merging, at the Guava team's choosing.

The following PRs relate to this one:

Using Modular Guava

From a Java 9+ application, in module-info.java:

module my.module {
  requires com.google.common;
}

Maven:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>33.4.0-jre-jpms</version>
</dependency>

Gradle (Groovy):

dependencies {
  api('com.google.guava:guava:33.4.0-jre-jpms')
}

Gradle (Kotlin):

dependencies {
  api("com.google.guava:guava:33.4.0-jre-jpms")
}

Gradle (Version Catalog + Kotlin):

[versions]
guava = "33.4.0-jre-jpms"

[libraries]
guava = { module = "com.google.guava:guava", version.ref = "guava" }
dependencies {
  api(libs.guava)
}

Forcing Resolution

Sometimes, your build will resolve to a different version of Guava. This usually happens because of a transitive dependency. Here are some instructions for Gradle:

build.gradle.kts:

configurations.all {
  resolutionStrategy.force("com.google.guava:guava:33.4.0-jre-jpms")
}

Changelog

Expand for full changelog
- feat: add `module-info.java` to `guava` module
- feat(jpms): add `module-info.java` to `failureaccess`
- feat(jpms): add `module-info.java` to `testlib`
- fix: necessary fixes to get testsuite running on modular java
- chore: update `guava` to build mrjar
- chore: adjust dev version → `1.0-HEAD-[jre|android]-SNAPSHOT`
- chore: upgrade maven compiler plugin → `3.14.0`

@sgammon
Copy link
Contributor Author

sgammon commented Mar 12, 2024

New complication to this: failureaccess will need to see a modular release. I've added a definition and set the current version to 1.0.3-jpms. It will be added shortly to the attic repo.

@Sineaggi
Copy link

What's left to get this merged?

@sgammon
Copy link
Contributor Author

sgammon commented May 9, 2024

@Sineaggi probably at least a rebase and a cleanup round. I'm waiting for feedback but if I don't receive any in the next few days/week, I'll go ahead and rebase and cleanup anyway. If there's anything specific that needs attention please let me know of course

@sgammon

This comment was marked as outdated.

@sgammon

This comment was marked as outdated.

@sgammon
Copy link
Contributor Author

sgammon commented Feb 8, 2025

@cpovirk This is cleaned up now and ready for review. One commit, 18 files changed, and less than 400 net lines adjusted.

@sgammon sgammon requested a review from cpovirk February 8, 2025 03:10
@sgammon sgammon force-pushed the feat/jpms branch 2 times, most recently from bf0885d to 09330c3 Compare February 8, 2025 03:25
This changeset adds full support for modular Java builds in Guava,
and in libraries which depend on Guava.

The Guava JAR for JRE now structures as a Multi-Release JAR, with
a module definition situated in `META-INF/versions/9/`. Guava
remains compatible with JDK 8.

- feat: add `module-info.java` to `guava` module
- feat(jpms): add `module-info.java` to `failureaccess`
- feat(jpms): add `module-info.java` to `testlib`
- fix: necessary fixes to get testsuite running on modular java
- chore: update `guava` to build MRJAR
- chore: adjust dev version → `1.0-HEAD-[jre|android]-SNAPSHOT`
- chore: upgrade maven compiler plugin → `3.12.1`

Fixes and closes google#2970

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
Copy link
Member

@cpovirk cpovirk left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. I'm probably going to see if I can submit the change to the format of the snapshot version numbers first, and then I may see what else I can carve off before the modularization itself.

@@ -87,6 +87,7 @@
<module>guava-gwt</module>
<module>guava-testlib</module>
<module>guava-tests</module>
<module>futures/failureaccess</module>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect that we omitted this here before because we don't want to implicitly build and release failureaccess each time that we release Guava. (That's mainly because we're trying to release only one two three four versions of it. I'm not sure if we had a great reason for that, and now that we're up to four, it's possible that we should just give up.)

So I'm wondering if you'd expect this to be necessary for us to change in our repo or if it would be enough for us to manually publish failureaccess:1.0.3 and then update guava to depend on that.

Alternatively, I assume that we could set up Maven to just not automatically deploy failureaccess (while still giving us some way to do so when desired).

Copy link
Contributor Author

@sgammon sgammon Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, and that makes sense. It has been re-added here because 1.0.3 cannot be resolved from Maven Central until it is published. After that, it may be removed again.

failureaccess last was released when Automatic-Module-Name was added. This attribute causes problems with jlink and other use cases, so a release must take place to make failureaccess a true module.

It just needs to be present in the reactor project order so it can be noticed by install in CI, which needs to build it instead of fetching it through Maven.

If I've missed something here and we don't need to update failureaccess for whatever reason, it would simplify the PR a lot. That JAR can't be on the modulepath without module-info.class, and can't be on the classpath because of encapsulation and split packages.

/**
* Guava Testlib
*/
open module com.google.common.testlib {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems fine to be more permissive for the test library here by using open module: The stakes are just lower for guava-testlib than for guava. So, at this point, I'm just curious as to whether this ends up being necessary for something in particular or whether you left it this way to be conservative in general. I think that our tests pass even without open, assuming that I tested it right.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Left it this way to be conservative in general :) although happy to drop as well.

@@ -6,7 +6,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.google.guava</groupId>
<artifactId>guava-parent</artifactId>
<version>HEAD-android-SNAPSHOT</version>
<version>1.0-HEAD-android-SNAPSHOT</version>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you don't know of any specific harm that would arise if we were to modularize the android flavor, as well? There is probably some risk, since Android-focused tools are less likely to be aware of modules than JVM-focused tools, but I'd expect it to be pretty small at this point. The upside would be that cross-platform libraries (which may declare a dependency on guava-android even when most of their users run on the JVM) would still be able to use a properly modularized Guava.

I ask because our internal setup actually makes it easier for me to make the change in both places. This wouldn't require any changes on your part; in my internal copy of this PR, our internal setup already made the changes to android :) But in the unlikely event that anyone knows of reasons for me to undo that, I can do so.

Copy link
Contributor Author

@sgammon sgammon Feb 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume that you don't know of any specific harm that would arise if we were to modularize the android flavor, as well?

I am not aware of any problem this would cause. Android supports up to JVM 21 bytecode, and IIRC modules are ignored past the javac phase.

There is probably some risk [for Android...]

Yeah, but keep in mind we are packaging the module within the version root at META-INF/versions/9/*, so a badly behaving tool would need to (1) be unaware of, or lack support for, modules, and would have to (2) scan JARs on the modulepath blindly, and would have to (3) violate JDK 8 spec (by not skipping that as an invalid class name), and would have to (4) have updated Guava to the latest version, implying it is maintained.

This fear keeps me up at night, too, but I think that actually affords quite a bit of protection. Even ancient tools which were released pre-JDK8 should have no issue since classes were not valid in META-INF/* at all back then. Thus, the risky part would be shipping the module-info.class at the root of the JAR, which probably isn't even that risky. That could be done in a few versions once confidence is in place, or when Java 9 becomes the minimum.

I ask because our internal setup actually makes it easier for me to make the change in both places

Ah, I see. If you want to apply the change uniformly, I am all for it. I stayed out of Android and GWT because I am not sure if there is other dark magic going on.

*/

/**
* Google Guava
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am noting for the record that we still get non-modular Javadoc output. That's good, since it avoids breaking any links, as would happen with the modular directory structure. (It also means that we don't need to think hard about what to put in this Javadoc comment :))

@sgammon
Copy link
Contributor Author

sgammon commented Feb 11, 2025

@cpovirk Responses in :) also, CI run should pass on approval

copybara-service bot pushed a commit that referenced this pull request Feb 11, 2025
This is the first piece of #7094, which is progress toward [modularization](#2970): javac (rightly or wrongly) wants a version number that starts with a number. We saw this previously [with Error Prone](google/error-prone#4311 (comment)) and [with JSpecify](jspecify/jspecify@0d39a0e).

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=n/a
PiperOrigin-RevId: 725320956
copybara-service bot pushed a commit that referenced this pull request Feb 11, 2025
This is the first piece of #7094, which is progress toward [modularization](#2970): javac (rightly or wrongly) wants a version number that starts with a number. We saw this previously [with Error Prone](google/error-prone#4311 (comment)) and [with JSpecify](jspecify/jspecify@0d39a0e).

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=n/a
PiperOrigin-RevId: 725320956
copybara-service bot pushed a commit that referenced this pull request Feb 11, 2025
This is the first piece of #7094, which is progress toward [modularization](#2970): javac (rightly or wrongly) wants a version number that starts with a number. We saw this previously [with Error Prone](google/error-prone#4311 (comment)) and [with JSpecify](jspecify/jspecify@0d39a0e).

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=n/a
PiperOrigin-RevId: 725625326
copybara-service bot pushed a commit that referenced this pull request Feb 12, 2025
This is the next piece of #7094, which is progress toward [modularization](#2970).

I've modified this CL somewhat from the original version so that I can deploy a new version of `failureaccess` without needing to make any updates to `guava-parent` first. `failureaccess` does still use `guava-parent` (and I've bumped it to use the newest released version) for its configuration for Sonatype, Javadoc, etc. But I've inlined all the configuration that I need for the modularization.

I did note a few differences from the original version:
- This version includes `LICENSE` under `META-INF`, both in the main jar and in the sources jars.
- This version uses a different configuration for Javadoc, I assume because my recent changes there didn't make it into 33.4.0.

I also notice that _neither_ version contains `module-info.java` in its source jar. We could presumably fix that in the future if anyone is interested.

(And while this isn't strictly related, I do notice that we could consider also releasing a modularized version of `listenablefuture` someday.)

I have tested with:

```
$ JAVA_HOME=$HOME/.m2/jdks/jdk-17.0.13+11 ./mvnw clean install -Psonatype-oss-release -Dmaven.test.redirectTestOutputToFile=true -Dsurefire.printSummary=false -Drelease -f futures/failureaccess
```

(Some of those flags aren't necessary, but I found it easiest to copy what our release script does for "normal" releases.)

I would use `deploy` instead of `install` for the real thing.

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=Changed the `failureaccess` jar to be a modular jar.
PiperOrigin-RevId: 725708714
copybara-service bot pushed a commit that referenced this pull request Feb 12, 2025
This is the next piece of #7094, which is progress toward [modularization](#2970).

I've modified this CL somewhat from the original version so that I can deploy a new version of `failureaccess` without needing to make any updates to `guava-parent` first. `failureaccess` does still use `guava-parent` (and I've bumped it to use the newest released version) for its configuration for Sonatype, Javadoc, etc. But I've inlined all the configuration that I need for the modularization.

I did note a few differences from the original version:
- This version includes `LICENSE` under `META-INF`, both in the main jar and in the sources jars.
- This version uses a different configuration for Javadoc, I assume because my recent changes there didn't make it into 33.4.0.

I also notice that _neither_ version contains `module-info.java` in its source jar. We could presumably fix that in the future if anyone is interested.

(And while this isn't strictly related, I do notice that we could consider also releasing a modularized version of `listenablefuture` someday.)

I have tested with:

```
$ JAVA_HOME=$HOME/.m2/jdks/jdk-17.0.13+11 ./mvnw clean install -Psonatype-oss-release -Dmaven.test.redirectTestOutputToFile=true -Dsurefire.printSummary=false -Drelease -f futures/failureaccess
```

(Some of those flags aren't necessary, but I found it easiest to copy what our release script does for "normal" releases.)

I would use `deploy` instead of `install` for the real thing.

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=Changed the `failureaccess` jar to be a modular jar.
PiperOrigin-RevId: 726100871
@sgammon
Copy link
Contributor Author

sgammon commented Feb 12, 2025

@cpovirk I'm keeping this PR rebased and up to date as you pluck things off. If you'd rather I keep this static, for reference, just let me know and I can restore it

@cpovirk
Copy link
Member

cpovirk commented Feb 12, 2025

Ah, sorry, I don't mean to make work for you. I do have what's left pending in two internal CLs (one to bump the version of failureaccess and one to do the rest). I can turn those into PRs so that there's a cleanly mergeable PR available so that you don't need to deal with the merge conflicts that I'm creating. Since I changed failureaccess around to keep it "separate," both of those PRs will fail CI until I release failureaccess-1.0.3. I hope to get back to that all soon; I just need to set aside time for some routine licensing reviews.

copybara-service bot pushed a commit that referenced this pull request Feb 12, 2025
This changeset adds full support for modular Java builds in Guava and in libraries which depend on Guava.

The Guava JAR for JRE now structures as a Multi-Release JAR, with a module definition situated in `META-INF/versions/9/`. Guava remains compatible with JDK 8.

- Fixes #2970
- Fixes #7094

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=Changed the Guava jar (and guava-testlib jar) to be a modular jar.
PiperOrigin-RevId: 725281837
copybara-service bot pushed a commit that referenced this pull request Feb 12, 2025
This is the next piece of #7094, which is progress toward [modularization](#2970).

(Also bump `maven-bundle-plugin`.)

Relates-To: elide-dev/jpms#1
Signed-off-by: Sam Gammon <sam@elide.dev>
RELNOTES=n/a
PiperOrigin-RevId: 725727215
@cpovirk
Copy link
Member

cpovirk commented Feb 12, 2025

(I should add that it was very valuable to have it in a single, end-to-end PR so that I could see how it all comes together and passes tests!)

@sgammon
Copy link
Contributor Author

sgammon commented Feb 12, 2025

@cpovirk Oh no I don't mind, I just want to provide a clean view of the changes. So far merging has been easy because I'm just (mostly) dropping things already merged in to master, rendering this PR smaller as we go.

Carry on :) very excited for this change

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
P3 no SLO
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add module-info.java
5 participants