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

[BUG] [JSR-199] ECJ cannot resolve JPMS modules if using a user-provided file manager #3446

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from

Conversation

stephan-herrmann
Copy link
Contributor

ClasspathJsr199:

  • various drafty additions for handling modules

ModuleFinder:

  • find automatic modules, too
  • allow invocation without a parser for binary-only scans

EclipseFileManager:

  • allow scanning all packages (by specifying an empty package name)
  • establish symmetry between setLocation() and setLocationFromPaths()
  • catch disruptive IAE from ModuleFinder.scanForModule()

Fixes #958

@eclipse-jdt-bot
Copy link
Contributor

This pull request changes some projects for the first time in this development cycle.
Therefore the following files need a version increment:

org.eclipse.jdt.compiler.tool.tests/META-INF/MANIFEST.MF
org.eclipse.jdt.compiler.tool.tests/pom.xml

An additional commit containing all the necessary changes was pushed to the top of this PR's branch. To obtain these changes (for example if you want to push more changes) either fetch from your fork or apply the git patch.

Git patch
From 8640bece093293dc5591978e712fc6e30dffc551 Mon Sep 17 00:00:00 2001
From: Eclipse JDT Bot <jdt-bot@eclipse.org>
Date: Thu, 12 Dec 2024 18:38:57 +0000
Subject: [PATCH] Version bump(s) for 4.35 stream


diff --git a/org.eclipse.jdt.compiler.tool.tests/META-INF/MANIFEST.MF b/org.eclipse.jdt.compiler.tool.tests/META-INF/MANIFEST.MF
index 935a43ecfd..c5f3056e24 100644
--- a/org.eclipse.jdt.compiler.tool.tests/META-INF/MANIFEST.MF
+++ b/org.eclipse.jdt.compiler.tool.tests/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Bundle-ManifestVersion: 2
 Bundle-Name: %pluginName
 Bundle-SymbolicName: org.eclipse.jdt.compiler.tool.tests
-Bundle-Version: 1.4.600.qualifier
+Bundle-Version: 1.4.700.qualifier
 Bundle-Vendor: %providerName
 Bundle-Localization: plugin
 Bundle-RequiredExecutionEnvironment: JavaSE-17
diff --git a/org.eclipse.jdt.compiler.tool.tests/pom.xml b/org.eclipse.jdt.compiler.tool.tests/pom.xml
index 9f094788a4..06a6878a01 100644
--- a/org.eclipse.jdt.compiler.tool.tests/pom.xml
+++ b/org.eclipse.jdt.compiler.tool.tests/pom.xml
@@ -19,7 +19,7 @@
     <relativePath>../tests-pom/</relativePath>
   </parent>
   <artifactId>org.eclipse.jdt.compiler.tool.tests</artifactId>
-  <version>1.4.600-SNAPSHOT</version>
+  <version>1.4.700-SNAPSHOT</version>
   <packaging>eclipse-test-plugin</packaging>
   <properties>
   	<testSuite>${project.artifactId}</testSuite>
-- 
2.47.1

Further information are available in Common Build Issues - Missing version increments.

File file = locPath.toFile();
IModule mod = ModuleFinder.scanForModule(this, file, null, true, null);
if (mod != null) {
return;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Several locations now assume that each ClasspathJsr199 is either JRT or a single module classpath location. See in particular new uses of the inherited field module. At a second look this is probably wrong, and each ClasspathJsr199 must be able to handle multiple modules.

Is this initialization the place that should actually create a Map<Location,IModule>? I'm a bit confused by the multi-level multiplicities: an Iterable of Sets of Locations where each Location has multiple Paths. At which of these levels can we assume a unique module? Is a LocationWrapper the right level? So if a module is in a jar, can we assume that it is represented by one LocationWrapper containing exactly one path?

// do nothing
if (this.jrt != null)
return; // do nothing
this.module = mod;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

If we need to handle multiple modules, then we probably need to let initialize() remember the module, since only there we know the corresponding LocationWrapper.

public char[][] listPackages() {
Set<String> packageNames = new HashSet<>();
try {
for (JavaFileObject fileObject : this.fileManager.list(this.location, "", fileTypes, true)) { //$NON-NLS-1$
Copy link
Contributor Author

@stephan-herrmann stephan-herrmann Dec 12, 2024

Choose a reason for hiding this comment

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

Listing all class files in all packages is surely a performance issue. Also passing "" to signal no filtering is a bit crude (needed an adjustment inside ModuleFinder, too). Unfortunately I found no better we to list all packages, which, however, the compiler needs particularly for auto modules.

Also, can we assume that 3rd-party file managers will handle "" in the same way?

}
return result;
} catch (IOException e) {
// ??
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't know what's the best strategy to handle exceptions in this code area.

Comment on lines 298 to 307
Set<String> moduleNames = new HashSet<>();
try {
for (Set<Location> locs : this.fileManager.listLocationsForModules(this.location)) {
for (Location loc : locs) {
String moduleName = this.fileManager.inferModuleName(loc);
if (moduleName != null)
moduleNames.add(moduleName);
}
}
return moduleNames;
Copy link
Contributor Author

@stephan-herrmann stephan-herrmann Dec 12, 2024

Choose a reason for hiding this comment

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

If the implementation is good, it could perhaps benefit from caching.

But even the sequence of invocations looks funny: we list module locations, so those locations are already known to contain a module. Is it really necessary to then "infer" the module name? Wait, it seems we could use if (loc instanceof ModuleLocationWrapper wrapper) .. wrapper.getModuleName().

To do so we'd need to

  • make that class public
  • provide a getModuleName() accessor

Next, I'm not even sure if it is safe to use our classes like LocationWrapper. Wouldn't that break if a custom file manager is used??

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wait, it seems we could use ... ModuleLocationWrapper ...

Never mind, inferModuleName() is implemented in that exact way I was considering for the client side, so this part looks OK :)

Still caching the set of module names might be a good idea.

@@ -269,6 +329,11 @@ public IModule getModule(char[] name) {
return super.getModule(name);
}

@Override
public IModule getModule() {
return this.module;
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Called by the compiler. If we handle multiple modules, how should the compiler ask for a specific module?

At this point I wonder if the ClasspathJsr199 with Location MODULE_PATH should actually be resolved to a set of classpaths, just like ClasspathContainers in the IDE?

Copy link
Member

Choose a reason for hiding this comment

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

Yep, looks like it's not adequate for such scenarios. ClasspathJsr199 is more of a version of ClasspathLocation that knows a bit about Location but not doing a good job of morphing into a Location.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Should I try to move the loops from ClasspathJsr199.initialize() into EclipseCompilerImpl.handleLocations() so that we can create individual ClasspathJsr199 per module?

}
} catch (IllegalArgumentException iae) { // e.g., from ModuleFinder.scanForModule(Classpath, File, Parser, boolean, String)
// FIXME ignore for now
Copy link
Contributor Author

Choose a reason for hiding this comment

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

CompilerToolJava9Tests.testBug566749() expects a regular compile error, which now additionally surfaces as the IllegalArgumentException caught here.

Iterable<Set<Location>> locationsForModules = this.fileManager.listLocationsForModules(this.location);
for (Set<Location> locs: locationsForModules) {
for (Location loc : locs) {
if (loc instanceof LocationWrapper wrapper) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This use of LocationWrapper leaks an abstraction, it will not work with other file managers.

But then, how to find the contained paths??

Copy link
Member

Choose a reason for hiding this comment

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

If it were a StandardJavaFileManager we could use getLocationAsPaths(Location)

@nettozahler
Copy link

During this analysis and overhaul maybe you could also have a look at PR #3057 since it also touches ClasspathJsr199. Eventually #3057 becomes obsolet with the changes proposed here.

@stephan-herrmann
Copy link
Contributor Author

In 477cab7 I changed the strategy towards using one ClasspathJsr199 per module.

@jarthana from my p.o.v. this already looks much better. So perhaps you could take another look?

I have some local changes attempting to support also MODULE_SOURCE_PATH, but it seems, ClasspathJsr199 is not suitable for handling sources.

@stephan-herrmann
Copy link
Contributor Author

Test failed with the same bogus error as has been blocking #3388 for some time now.

@laeubi
Copy link
Contributor

laeubi commented Dec 15, 2024

Test failed with the same bogus error as has been blocking #3388 for some time now.

I would try to rebase the change on top of master as a new release-stream has started in the meanwhile.

@stephan-herrmann
Copy link
Contributor Author

Test failed with the same bogus error as has been blocking #3388 for some time now.

I would try to rebase the change on top of master as a new release-stream has started in the meanwhile.

The change is based on I20241211-1800. Are you saying a new stream has started just within the last three days?? It looks like the stream change happened on 2024-11-23.

file manager eclipse-jdt#958

ClasspathJsr199:
+ various drafty additions for handling modules
ModuleFinder:
+ find automatic modules, too
+ allow invocation without a parser for binary-only scans
EclipseFileManager:
+ allow scanning all packages (by specifying an empty package name)
+ establish symmetry between setLocation() and setLocationFromPaths()
+ catch disruptive IAE from ModuleFinder.scanForModule()
file manager

+ change to 1 ClasspathJsr199 per module
+ test with two module dependencies (one auto, one regular)

Fixes eclipse-jdt#958
@laeubi
Copy link
Contributor

laeubi commented Dec 15, 2024

At the time of my comment there where three commits and at least one of them looks quite old, the other was a merge commit and then a "recent" one, beside that github showed merge conflicts. All these can probably lead to the build being confused about the actual baseline in use.

Now you fixed that up, the build seem working fine (all checks are green).

@stephan-herrmann
Copy link
Contributor Author

At the time of my comment there where three commits and at least one of them looks quite old, the other was a merge commit and then a "recent" one,

I have no clue where you saw this, but of course after you made me rebase the branch, I'm no longer able to prove that the branch indeed started from HEAD of master only 4 days ago.

beside that github showed merge conflicts. All these can probably lead to the build being confused about the actual baseline in use.

Now you fixed that up, the build seem working fine (all checks are green).

I wouldn't call this strategy "fixing" but working around same intermittent bug. And of course none of this is of any help for #3388

@laeubi
Copy link
Contributor

laeubi commented Dec 15, 2024

I wouldn't call this strategy "fixing" but working around same intermittent bug

Contributions are always welcome to fix such "bugs", but as JDT is not a standalone project several things influence the outcome (e.g. changes to aggregator parent pom, I-Build repositories and so on) of a PR that depends on such setups.

@stephan-herrmann
Copy link
Contributor Author

I wouldn't call this strategy "fixing" but working around same intermittent bug

Contributions are always welcome to fix such "bugs", but as JDT is not a standalone project several things influence the outcome (e.g. changes to aggregator parent pom, I-Build repositories and so on) of a PR that depends on such setups.

This is not helpful.

@laeubi
Copy link
Contributor

laeubi commented Dec 16, 2024

This is not helpful.

I showed you how to resolve the issue and it works, I'm not sure how to be more helpful... instead the solution is claimed a "workaround" for a bug.

@stephan-herrmann
Copy link
Contributor Author

This is not helpful.

I showed you how to resolve the issue and it works, I'm not sure how to be more helpful... instead the solution is claimed a "workaround" for a bug.

I'm hoping for a solution that is applicable also for BETA_JAVA24 without requiring that branches need to be tightly in sync, which would be an unacceptable requirement. Also I'm hoping for a solution where I'm able to understand the connection between the error and the remedy.

@laeubi
Copy link
Contributor

laeubi commented Dec 17, 2024

I'm hoping for a solution that is applicable also for BETA_JAVA24 without requiring that branches need to be tightly in sync, which would be an unacceptable requirement

The build infrastructure for (feature) branches is currently to ensure they can be merged into master at some point in time what requires them to be at least close to the target branch. Why this is unacceptable requirement is beyond my knowledge it more feels like a personal preference than a technical requirement, at least most contributors have adapted to the process to keep branches up-to date.

Anyways there are some minimal requirements such a branch must fulfill e.g. it requires to include changes in version of bundles compared to the current integration build and it should not have any merge conflicts.

Also I'm hoping for a solution where I'm able to understand the connection between the error and the remedy.

I hoped to explain it here its a bit hard to keep track of what is said where, but to summarize for this particular issue.

Where it was failing before

Lets look at the last failing build logs here and we see it fails with:

[2024-12-15T09:58:04.486Z] [ERROR] Failed to execute goal org.eclipse.tycho.extras:tycho-p2-extras-plugin:4.0.10:compare-version-with-baselines (compare-attached-artifacts-with-release) on project org.eclipse.jdt.compiler.apt.tests: Baseline and reactor have the same fully qualified version, but different content
[2024-12-15T09:58:04.486Z] [ERROR] different
[2024-12-15T09:58:04.486Z] [ERROR]    META-INF/ECLIPSE_.RSA: present in baseline only
[2024-12-15T09:58:04.486Z] [ERROR]    META-INF/ECLIPSE_.SF: present in baseline only
[2024-12-15T09:58:04.486Z] [ERROR] -> [Help 1]

If we search up for the offending project we will see this (baseline-replace with latest ibuild):

[2024-12-15T09:56:33.139Z] [INFO] --- tycho-p2-plugin:4.0.10:p2-metadata-default (default-p2-metadata-default) @ org.eclipse.jdt.compiler.apt.tests --- 
[2024-12-15T09:56:33.139Z] [INFO] No baseline version org.eclipse.jdt:org.eclipse.jdt.compiler.apt.tests:eclipse-test-plugin:1.3.600-SNAPSHOT

We see that actually there is no baseline version found, because its version was changed on master here 5 days ago (as of today) to version 1.3.700 while your branch is still 1.3.600

Now the next check kicks in (baseline-compare with latest release):

[2024-12-15T09:56:59.730Z] [INFO] --- tycho-p2-extras:4.0.10:compare-version-with-baselines (compare-attached-artifacts-with-release) @ org.eclipse.jdt.compiler.apt.tests ---
[2024-12-15T09:56:59.730Z] [ERROR] Baseline and reactor have the same fully qualified version, but different content
[2024-12-15T09:56:59.730Z] different
[2024-12-15T09:56:59.730Z]    META-INF/ECLIPSE_.RSA: present in baseline only
[2024-12-15T09:56:59.730Z]    META-INF/ECLIPSE_.SF: present in baseline only

it complains that you have build the exact same version as in the previous release but the content slightly differs what is classified as an error for platform builds as it usually means you have a missing version increment or something else is badly wrong.

Please note that at this point there are no ignores! And of course this is not a bug...

How it was fixed

You have rebased your branch on top of master and thus now it includes the version change as well, you could have merged master but we don't like merge commits here... another option would have been a squash merge (and later rebase) but this would pollute your PR with unrelated diffs / code changes maybe.

Why it is working afterwards

Lets look at the last successful build logs here and we see:

[2024-12-15T15:13:27.543Z] [INFO] --- tycho-p2-plugin:4.0.10:p2-metadata-default (default-p2-metadata-default) @ org.eclipse.jdt.compiler.apt.tests ---
[2024-12-15T15:13:27.543Z] [INFO] MavenProject: org.eclipse.jdt:org.eclipse.jdt.compiler.apt.tests:1.3.700-SNAPSHOT @ /home/jenkins/agent/workspace/eclipse.jdt.core-Github_PR-3446/org.eclipse.jdt.compiler.apt.tests/pom.xml
[2024-12-15T15:13:27.543Z]     The main artifact has been replaced with the baseline version.
[2024-12-15T15:13:27.543Z]     The following attached artifacts have been replaced with the baseline version: [sources]

So we see the baseline artifact from the last ibuild was found and your currently build jar was compared to the baseline and it was found to be semantically equal (and this is where we taking ignores into account) and therefore replaced by the jar from the last ibuild.

Later on then we see

[2024-12-15T15:13:56.002Z] [INFO] --- tycho-p2-extras:4.0.10:compare-version-with-baselines (compare-attached-artifacts-with-release) @ org.eclipse.jdt.compiler.apt.tests ---
[2024-12-15T15:13:56.002Z] [INFO] 

so no error at all here, because both artifacts have different content (as the bundle was changed) but also different version that is higher than in the last release.

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

Successfully merging this pull request may close these issues.

[BUG] [JSR-199] ECJ cannot resolve JPMS modules if using a user-provided file manager
5 participants