Skip to content

Make Eclipse and AspectJ compilers run on JDK 11 again #358

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

Closed
wants to merge 2 commits into from

Conversation

kriegaex
Copy link
Contributor

@kriegaex kriegaex commented Feb 2, 2024

AspectJ can compile many projects on JDK 11, because it is not always using Java 17 classes from JDT Core. If it does not work, there will be a runtime error, and the user can figure out what is wrong.

For ECJ, the case is more complicated, because it directly imports Java 17 classes from JDT Core. There were isolated in new class EclipseJavaCompilerDelegate, which is accessed using Class::forName and method handles from EclipseJavaCompiler.

I.e., Sisu Inject can scan the annotations on EclipseJavaCompiler on JDK 11, and there is no more confusing "No such compiler 'eclipse'" error, but rather:

Error injecting constructor, ... EcjFailureException:
Failed to run the ecj compiler: ECJ only works on Java 17+
  at org.codehaus.plexus.compiler.eclipse.EclipseJavaCompiler.<init>

This explicitly tells the user what is wrong.

A multi-release JAR solution was tested and worked well in Maven, but was dismissed due to the terrible developer experience in IDEs.

Fixes #347.


Update: The error message for ECJ on JDK 11 in more detail:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.12.1:compile (default-compile) on project eclipse-compiler-mapstruct: Execution default-compile of goal org.apache.maven.plugins:maven-compiler-plugin:3.12.1:compile failed: Unable to provision, see the following errors:
[ERROR]
[ERROR] 1) [Guice/ErrorInjectingConstructor]: EcjFailureException: Failed to run the ecj compiler: ECJ only works on Java 17+
[ERROR]   at EclipseJavaCompiler.<init>(EclipseJavaCompiler.java:66)
[ERROR]   at ClassRealm[plugin>org.apache.maven.plugins:maven-compiler-plugin:3.12.1, parent: ClassLoaders$AppClassLoader@2cdf8d8a]
[ERROR]       \_ installed by: WireModule -> PlexusBindingModule
[ERROR]   while locating EclipseJavaCompiler
[ERROR]   while locating Object annotated with *
[ERROR]
[ERROR] Learn more:
[ERROR]   https://github.com/google/guice/wiki/ERROR_INJECTING_CONSTRUCTOR
[ERROR]
[ERROR] 1 error
[ERROR]
[ERROR] ======================
[ERROR] Full classname legend:
[ERROR] ======================
[ERROR] ClassLoaders$AppClassLoader: "jdk.internal.loader.ClassLoaders$AppClassLoader"
[ERROR] EcjFailureException:         "org.codehaus.plexus.compiler.eclipse.EcjFailureException"
[ERROR] EclipseJavaCompiler:         "org.codehaus.plexus.compiler.eclipse.EclipseJavaCompiler"
[ERROR] PlexusBindingModule:         "org.eclipse.sisu.plexus.PlexusBindingModule"
[ERROR] WireModule:                  "org.eclipse.sisu.wire.WireModule"
[ERROR] ========================
[ERROR] End of classname legend:
[ERROR] ========================

An alternative to the injection exception would be to actually add guards to the method handle calls and throw exceptions there, i.e. not during wiring the plugin dependencies but when actually executing it. If the code reviewers would prefer that, I could certainly change it upon request.

AspectJ can compile many projects on JDK 11, because it is not always
using Java 17 classes from JDT Core. If it does not work, there will be
a runtime error, and the user can figure out what is wrong.

For ECJ, the case is more complicated, because it directly imports Java
17 classes from JDT Core. There were isolated in new class
EclipseJavaCompilerDelegate, which is accessed using Class::forName and
method handles from EclipseJavaCompiler.

I.e., Sisu Inject can scan the annotations on EclipseJavaCompiler on
JDK 11, and there is no more confusing "No such compiler 'eclipse'"
error, but rather:

Error injecting constructor, ... EcjFailureException:
Failed to run the ecj compiler: ECJ only works on Java 17+
  at org.codehaus.plexus.compiler.eclipse.EclipseJavaCompiler.<init>

This explicitly tells the user what is wrong.

A multi-release JAR solution was tested and worked well in Maven, but
was dismissed due to the terrible developer experience in IDEs.

Fixes codehaus-plexus#347.
@kriegaex
Copy link
Contributor Author

kriegaex commented Feb 2, 2024

Actually, it would be nice to run most of the unit and integration tests on JDK 11 again, too, but we would need toolchains for that, which is out of scope for this PR. The advantage would be, that we could add tests for the new error message on JDK 11 when trying to use ECJ. For a unit test, we might be able to mock the JVM's java version, but I do not want to introduce Mockito or so just for this PR. That should be something the commiters ought to decide. But I manually tested with some ECJ and AsectJ Maven projects and also manually ran some of the ITs directly from plexus-compiler-its/target/it on JDK 11 to verify that the error message occurs as expected for ECJ and that AspectJ compilation still works on JDK 11.

@laeubi
Copy link
Contributor

laeubi commented Feb 2, 2024

A multi-release JAR solution was tested and worked well in Maven, but was dismissed due to the terrible developer experience in IDEs.

I don't think this is something to consider here as there is literally not much to "develop" her and this Method Handle stuff is terrible as well ( sorry :-D ) I even wonder why we need MH at all and can't use reflection?

Thinking even more, I wonder if not maven could even be made to check the minimal maven version like it does for plugins already?

@kriegaex
Copy link
Contributor Author

kriegaex commented Feb 2, 2024

@laeubi, method handles are more efficient and type-safe than reflection. If you think, that is terrible, you should try my other local branch with the multi-release JAR not from Maven, but from your IDE, not finding the right source directory per JDK. That is even more horrible. And it definitely is to consider, because it does not compile the right thing and hence not yield the right test results in the IDE.

@kriegaex
Copy link
Contributor Author

kriegaex commented Feb 2, 2024

Please note my second commit:

Throw CompilerException in performCompile|getEcj, not in constructor

This shifts the focus from a supposed Maven component wiring error to the root cause, expressed in the error message:

Fatal error compiling: ECJ needs JRE 17+

We need to add safeguards throwing the exception in both methods accessing class EclipseJavaCompilerDelegate, because we cannot predict who calls which one first. Omitting one safeguard might lead to the return of the infamous "No such compiler 'eclipse'" error.

The new static boolean field EclipseJavaCompiler.isJdkSupported also enables us to write a simple unit test, asserting on the error message, if the field value is false.

Now, the corresponding Maven build error is much shorter:

[ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.12.1:compile (default-compile) on project eclipse-compiler-mapstruct: Fatal error compiling: ECJ needs JRE 17+

This shifts the focus from a supposed Maven component wiring error to
the root cause, expressed in the error message:

  Fatal error compiling: ECJ needs JRE 17+

We need to add safeguards throwing the exception in both methods
accessing class EclipseJavaCompilerDelegate, because we cannot predict
who calls which one first. Omitting one safeguard might lead to the
return of the infamous "No such compiler 'eclipse'" error.

The new static boolean field EclipseJavaCompiler.isJdkSupported also
enables us to write a simple unit test, asserting on the error message,
if the field value is false.

Relates to codehaus-plexus#347.
@cstamas
Copy link
Member

cstamas commented Feb 2, 2024

Please see #359 as well

@kriegaex
Copy link
Contributor Author

kriegaex commented Feb 3, 2024

Superseded by #361.

@kriegaex kriegaex closed this Feb 3, 2024
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.

ECJ usage broken in 2.14.x
3 participants