-
Notifications
You must be signed in to change notification settings - Fork 624
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
Properly fix Java 8 API compatibility (#2218) #2350
Conversation
// Also add the module-info.java source file to the Kotlin compile task; | ||
// the Kotlin compiler will parse and check module dependencies, | ||
// but it currently won't compile to a module-info.class file. | ||
compileTask.source(sourceFile) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you remove this line, please provide an alternative way. It was here to perform validation of the sources in accordance to the module-info
file, and now nothing performs this validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you tell me what exactly that did / checked?
What kind of bad change did that capture, so what would make the build fail with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm sorry we lack documentation about this. Actually, Kotlin compiler is able to read module-info.java
file and verify it (the comment says 'the Kotlin compiler will parse and check module dependencies'). It verifies that all package names in exports
are correct and all requires
dependencies are present (not so sure about latter, @ALikhachev ?).
What happens here is we add module-info.java
location to the inputs of KotlinCompile
task so Kotlin compiler can perform this verification.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, I see.
But I'm not sure whether there is a good solution, besides having the normal compile task compile with Java 8 toolchain and having a second set of compile tasks with Java 11 toolchain and jdk-release 9 / jvm-target 9 that is hooked into the check
task dependencies. :-/
If I just add -Xjdk-release=1.8
as free compiler arg into the existing (pre-my-PR) setup, there come a looot of errors about classes being in modules that are not depended upon.
If with the new (with-my-PR) setup I use JVM toolchain 17 (to reproduce the problematic bytecode eventually) and add jdk-release 1.8 / jvm-target 1.8, the correct bytecode is produced but still those errors are not verified as the module infos seem to be ignored.
If I additionally add the module-info.java
to the KotlinCompile
task again, I get the same wall of errors I get if I just add "jdk-release 1.8" to the existing setup.
If I remove the "jdk-release" and only keep the "jvm-target", the verifications work again, but the wrong byte-code is produced. grml
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me try to sum this up: the problem is, when module-info.java
is in the KotlinCompile
task inputs AND jdk-release=1.8
specified to use correct Java API, then there are a lot of errors. What this errors are, exactly? I think this scenario is popular for multi-release JARs and should be somewhat supported without much setup, but I may be wrong.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
However, after giving it a second thought, I'm not so sure we need this verification at all. We have JavaCompile
task for module-info anyway — it will tell us if some of dependencies are missing and/or exported incorrectly, won't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, it wont actually.
The JavaCompile
task just compiles the module-info.java
file.
It will complain if there is an error within, like requiring a module that is not present, or exporting a package that does not exist.
But the intended verification happens when compiling the Kotlin files JPMS aware.
So if you for example remove exports kotlinx.serialization;
from core
, formats/json
compilation will fail with the verification for using classes in non-exported packages.
And if you for example remove requires transitive kotlinx.serialization.core;
from formats/json
, its compilation will fail with the verification for using classes without requiring the module.
This verification still has some bugs like https://youtrack.jetbrains.com/issue/KT-60583 and https://youtrack.jetbrains.com/issue/KT-60582 for example.
I've added the alternative means for the validation that @ALikhachev requested now.
It needed some work-arounds due to shortcomings in the Kotlin Gradle Plugin and in the module-info
handling.
But all are reported and documented with code comments.
And from my tests hopefully the same verifications done before are now again done.
Thank you very much @Tapchicoma for your thorough help in getting this set up.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. Thank you for you careful study.
Can you please also file this bug
when module-info.java is in the KotlinCompile task inputs AND jdk-release=1.8 specified to use correct Java API, then there are a lot of errors.
so a compiler team can look properly at it (cc @udalov )
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ed9fcf0
to
ba682a6
Compare
// the default source set depends on this new source set | ||
defaultSourceSet.dependsOn(this) | ||
// register and wire a task to verify module-info.java content | ||
val verifyModuleTask = registerVerifyModuleTask( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Am I right that this actually will cause the full compilation to happen twice (you've mentioned it somewhere)?
I believe it should be mentioned in the comments then
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, when running with check
this is correct.
During pure building it should not happen.
I added that info to the comment now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thank you.
Would you mind making a minor adjustment by adding a description (the description
property of the task) to the verification task in addition to setting the group?
Not at all, done :-) |
@Vampire Thanks for all your efforts! I'm terribly sorry for not noticing it sooner, but your PR is based against
I'm not sure why it happens, as you're setting ownModuleName explicitly. Can you rebase your branch on |
No problem, fixed. |
Thanks again! |
Fixes #2218
There are 7 methods in
ByteBuffer
that have the problem described in #2218 and was attempted to be fixed in #2219.Unfortunately, the fix in #2219 was incomplete as another of these 7 methods is used in the same class.
Also it could happen anytime and with similar cases, that this happens again and is only recognized very late.
With this PR, the JVM toolchains feature is used to ensure the proper API being compiled against.
As a side-effect I also improved the
module-info.java
compilation that was done "sub-optimally"by changing configuration of the task during its execution phase, and compiling into the same
output directory as the main compile task and then moving it out in a
doLast
action,and having a manual task dependency, ...
With the new setup, no explicit task dependencies are necessary, which are bad-practice,
the output directories are right from the start separated, and no manual task dependencies
are necessary anymore as the jar configuration and the compile task arguments both have the
necessary implicit task dependencies automatically and by using a
CommandLineArgumentProvider
which also has the necessary implicit dependencies, there is also no configuration change at
execution time necessary anymore.