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

Add module-info.java #2970

Open
hrchu opened this issue Oct 18, 2017 · 76 comments · May be fixed by #7094 or #7673
Open

Add module-info.java #2970

hrchu opened this issue Oct 18, 2017 · 76 comments · May be fixed by #7094 or #7673
Assignees

Comments

@hrchu
Copy link

hrchu commented Oct 18, 2017

So that projects depend on this can be published to a public artifact repository.
Note that this is not breaking backward compatibility. All codes except this file can be still compiled in Java 6.

@jbduncan
Copy link
Contributor

jbduncan commented Oct 18, 2017

Guava has an Automatic-Module-Name in its MANIFEST.MF now, so I believe this is not quite as important as it may seem. But if I'm mistaken, then I'd be more than happy to be proven wrong.

(BTW, I think I might have misunderstood something, because module-info.java is Java 9 specific, right? Would a Java 8 compiler (which I believe is what is used to compile guava-jre) or an Android compiler (for guava-android) happily process it or otherwise ignore it?)

@cpovirk
Copy link
Member

cpovirk commented Oct 18, 2017

Our current thinking is that we'll look into this next quarter. We have seen some problems from module-info files in third-party jars, since they're Java 9 .class files and not everyone uses Java 9 yet. So, if we can get away with Automatic-Module-Name, we'll continue to do so, possibly even beyond next quarter. But if there are cases in which Automatic-Module-Name isn't good enough, that would be good to know.

@hrchu
Copy link
Author

hrchu commented Oct 18, 2017

It is possible to only compile modile-info.java in Java 9, so the jar file is still compatible to users who uses earlier Java version.

A maven example:
https://github.com/twonote/radosgw-admin4j/blob/java9/pom.xml#L127

@cpovirk
Copy link
Member

cpovirk commented Oct 18, 2017

Right, thanks. We've seen problems even when the main .class files are compiled for an older version but the module-info.class is present. As I understand it, various tools try to process all .class files, and they need to be updated to ignore module-info.class.

@hrchu
Copy link
Author

hrchu commented Oct 19, 2017

@cpovirk could you tell me more about this problem?

@cpovirk
Copy link
Member

cpovirk commented Oct 19, 2017

I wasn't personally involved in fixing the problems, but the basic idea seems to be that people scan the whole classpath (using something like ClassPath.getTopLevelClasses -- which might be an example of something that needs updated to ignore module-info.class :)) and then try to examine/load the classes with a tool that understands only, say, Java 8.

@orionll
Copy link

orionll commented Feb 20, 2018

It's worth mentioning that if we add module-info.java, all packages will not be open anymore.

@jbduncan
Copy link
Contributor

@orionll Am I right to think/remember that in the JPMS, open packages are packages whose internal classes can be inspected with reflection?

@orionll
Copy link

orionll commented Feb 20, 2018

@jbduncat Yes, exactly. And also private members of public classes.

@jbduncan
Copy link
Contributor

@orionll Cool, thanks for confirming things for me. :)

I personally wonder how important it would be for Guava's packages to be open when used on the module path. I struggle to imagine that reflectively calling Guava's internals is a common thing to do, especially considering Guava's (IMO) pretty durn good API. 🤔

@HoldYourWaffle
Copy link

Are there any reasons for it not being open? Even if it's uncommon it might still be done by some people.

@jbduncan
Copy link
Contributor

@HoldYourWaffle I think the main reason is it prevents people from using reflection to depend on internals which may change or disappear in future releases of Guava without warning.

@jbduncan
Copy link
Contributor

...which by my understanding makes things easier for everyone in the long-run.

@jbduncan
Copy link
Contributor

The only reason I can currently think of to have Guava's packages open in the module-info.java is if frameworks like Spring need to reflectively access its internals to do important stuff, but I don't know how important or common that is.

@orionll
Copy link

orionll commented Feb 20, 2018

All Guava packages should be closed because as @jbduncan said the dependence on class internals is a bad practice. If someone really wants to access the internals, they can use --add-opens command line option which forces the specified package to be open.

@HoldYourWaffle
Copy link

Good point I forgot about --add-opens. Imho you should always be able to hack into internals (maybe you want to do something the developers didn't think of but it's too 'personal' that it's not worthy of a PR), with the risk of it breaking in new versions. --add-opens allows for this so it'd indeed be better to close the guava packages.

@SamCarlberg
Copy link

Guava has an Automatic-Module-Name in its MANIFEST.MF now, so I believe this is not quite as important as it may seem. But if I'm mistaken, then I'd be more than happy to be proven wrong.

It does mean that jlink will not work, since the tool requires a module name to be specified in the module-info.java file; automatic module names will not be accepted.

@hannes-transentials
Copy link

As much as I love Guava and appreciate Google's efforts, it is somehow embarrassing that a company like Google is not able to adopt Java modules within one year.

Either Google does not use Guava internally or they keep using JDK 8 and won't adopt Jigsaw.

@jbduncan
Copy link
Contributor

jbduncan commented Dec 9, 2018

@hannes-transentials I think it's most likely that Google have not migrated to JDK 11 and adopted modules yet simply because their internal codebase is so mind-bogglingly humongous. ;)

I say this because I remember reading somewhere (or I inferred) that they use Guava or an superset internally, and I also remember they announced a few years ago that they'd finally migrated to JDK 8 after a lot of effort. So I'm sure that they'll announce support for JDK 11 or a later LTS version (and, by extension, modules) when they fully migrate away from Java 8 and when they feel that most of us non-Googlers have moved away from Java 8 too. (I know that my company hasn't done so yet simply because Java 9 was such a freaking big, backwards-incompatible change!)

@orionll
Copy link

orionll commented Dec 9, 2018

It's worth mentioning that adding module-info.java can break some existing tools. For example, I know that IDEA's Osmorc plugin does not work when both module-info.java exists and the library is an OSGi bundle (Guava is). So, while the tools are not ready yet, I would abstain from adding module-info.java to Guava.

@hannes-transentials
Copy link

So I either do without modularized applications or stay away from Guava (and many other popular applications)? I somehow hoped that there was some middle ground.

@jbduncan
Copy link
Contributor

jbduncan commented Dec 9, 2018

Well... you can use Guava as a module in a vanilla-Java modular application. But since Guava only includes an Automatic-Module-Name in its manifest, rather than going further and including amodule.info (out of necessity to stay Java-8-compatible), you won't be able to use it with jlink to create minimised modular Java applications.

Furthermore, frameworks built on top of Java that have their own programming models, like Spring, may have not fully migrated to be Java-11-compatible yet, so if you use such frameworks a lot, you may have to wait a bit longer.

That being said, if you do use a framework such a Spring, please check for yourself if Java 11 and modules work with it, since my knowledge of Spring and other frameworks is limited. :)

@overheadhunter
Copy link

out of necessity to stay Java-8-compatible

Well you can create multi-release jars, where the module-info.class file only gets included inside META-INF/versions/9 and is therefore invisible for old¹ class loaders.


¹ As long as no fancy custom class loaders eagerly load everything they find in a jar without reading the manifest entries.

@talios
Copy link

talios commented Dec 11, 2018

@hannes-transentials You could make use of something like https://github.com/moditect/moditect to adapt guava and add a module-info.java/.class at your applications side of things as a transitionary work around.

If module-info.java was to be added to guava, hopefully it'd be done so as a modular jar so we don't break java 8< versions.

@cowwoc
Copy link

cowwoc commented Feb 6, 2025

I can't think of any problems with you depending on jdk.unsupported and removing it later. Go for it.

@sgammon
Copy link
Contributor

sgammon commented Feb 7, 2025

@cpovirk I've been reworking this PR over the past 48 hours, and it is apparent to me that there are multiple approaches we could take with regard to JPMS compilation steps:

(1) Modify JPMS-enabled libs only. Additional compiler executions could be added to JPMS-enabled modules (non-Android guava, failureaccess, testlib), with defaults left alone in the top-level POM and elsewhere.

  • This repeats some configuration for sure, but is probably less invasive for Android and GWT use cases
  • The PR takes this approach now, but I wanted to try it against 2 for comparison

(2) Modify defaults and then non-JPMS libs. More targets are JPMS-enabled in this PR than not (the odd ones out are Android Guava, guava-gwt, and guava-test). So, maybe we set new defaults and override modules which do not need or cannot support JPMS.

  • Initial investigation reveals that this might not reduce the size of the change by much
  • We would have to get overrides exactly-right in the more exotic leaf libraries
  • There are some complications down this route related to compile source roots -- JPMS wants them, but they preclude use of <exclude> etc., as I know you are aware from inline comments ;)

Any advice one way or the other? After investigating 2, I'm inclined to stick with 1, with extensive inline comments justifying why compiler flags are put where they are.

Current draft of these changes is here, although it is undergoing cleanup now and will trim down. Case 1 and 2 are both on the order of hundreds of lines, less than 500 for sure. We're talking about, like, 350 vs. 250 or somewhere in there in terms of lines added, mostly of POM XML:

Image

The original PR weighs in much heavier:

Image

So I think we are well on our way to a trim, potentially mergeable change.

cc / @cowwoc in case you want to weigh in

@sgammon
Copy link
Contributor

sgammon commented Feb 7, 2025

Update: You can try the latest JPMS-enabled Guava here, in the JPMS attic repository. Add it to your Gradle or Maven build according to the directions, and then use the JPMS version of Guava with:

<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</groupId>
  <version>33.4.0-jre-jpms</version>
</dependency>
dependencies {
  implementation("com.google.guava:guava:33.4.0-jre-jpms")
}

To force overrides transitively in Gradle:

configurations.all {
  resolutionStrategy.eachDependency {
    if (it.requested.group == "com.google.guava") {
      useVersion("33.4.0-jre-jpms")
      because("jpms support")
    }
  }
}

Several projects are passing integration tests, including Caffeine, GSON, Checkstyle, and others. I am using it already in downstream projects via Gradle with no issues.

sgammon added a commit to sgammon/guava that referenced this issue Feb 7, 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.

- feat: add `module-info.java` to `guava` module
- 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 <[email protected]>
Signed-off-by: Sam Gammon <[email protected]>
@cpovirk
Copy link
Member

cpovirk commented Feb 7, 2025

I don't want to act like I would have foreseen this, but now that you say it, I find it very believable that (1) would turn out simpler. I skimmed the draft PR, and nothing in it immediately offended my delicate sensibilities, so that also seems like a good sign. I'm all ears if you or others end up with reasons to prefer (2) in the future, but (1) sounds fine to me.

@sgammon
Copy link
Contributor

sgammon commented Feb 7, 2025

Cool :) once it is cleaned up and my mess of commits are squashed, I will update the attached PR (#7094) and push it again to the JPMS attic for testing.

sgammon added a commit to sgammon/guava that referenced this issue Feb 7, 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.

- feat: add `module-info.java` to `guava` module
- 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 <[email protected]>
Signed-off-by: Sam Gammon <[email protected]>
sgammon added a commit to sgammon/guava that referenced this issue Feb 8, 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.

- feat: add `module-info.java` to `guava` module
- 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 <[email protected]>
Signed-off-by: Sam Gammon <[email protected]>
sgammon added a commit to sgammon/guava that referenced this issue Feb 8, 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.

- 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 <[email protected]>
sgammon added a commit to sgammon/guava that referenced this issue Feb 8, 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.

- 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 <[email protected]>
@sgammon
Copy link
Contributor

sgammon commented Feb 8, 2025

@cpovirk and others: PR #7094 is ready for review.

sgammon added a commit to sgammon/guava that referenced this issue Feb 8, 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.

- 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 <[email protected]>
sgammon added a commit to sgammon/guava that referenced this issue Feb 8, 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.

- 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 <[email protected]>
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=n/a
PiperOrigin-RevId: 725320956
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=n/a
PiperOrigin-RevId: 725320956
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=n/a
PiperOrigin-RevId: 725625326
@cpovirk cpovirk self-assigned this Feb 11, 2025
@cpovirk cpovirk added P2 and removed P3 no SLO labels Feb 11, 2025
sgammon added a commit to sgammon/guava that referenced this issue 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.

- 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 <[email protected]>
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=Changed the `failureaccess` jar to be a modular jar.
PiperOrigin-RevId: 725708714
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=Changed the `failureaccess` jar to be a modular jar.
PiperOrigin-RevId: 726100871
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=Changed the Guava jar (and guava-testlib jar) to be a modular jar.
PiperOrigin-RevId: 725281837
@copybara-service copybara-service bot linked a pull request Feb 12, 2025 that will close this issue
copybara-service bot pushed a commit that referenced this issue 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 <[email protected]>
RELNOTES=n/a
PiperOrigin-RevId: 725727215
copybara-service bot pushed a commit that referenced this issue Feb 13, 2025
In pre-[modularization](#2970) cl/725625326, I had pledged not to bikeshed over this. But it turns out that the number matters to some part of our JDiff pipeline, which has been broken since that CL. (Then I broke it _even more_ by introducing usages of `java.io.Serial` (cl/726154745), which I've since rolled back externally (cl/726521329).)

We still really should [finish setting up japicmp](google/error-prone#4311 (comment)) to replace JDiff, but again, I just want to unbreak publishing documentation for releases and snapshots.

(I will still view this as a _small_ bit of evidence that biggest version number is best version number for snapshot purposes :))

RELNOTES=n/a
PiperOrigin-RevId: 726570524
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.