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

Properly fix Java 8 API compatibility (#2218) #2350

Merged
merged 1 commit into from
Aug 2, 2023

Conversation

Vampire
Copy link
Contributor

@Vampire Vampire commented Jun 26, 2023

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.

buildSrc/src/main/kotlin/Java9Modularity.kt Outdated Show resolved Hide resolved
// 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)
Copy link
Member

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.

Copy link
Contributor Author

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?

Copy link
Member

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.

Copy link
Contributor Author

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

Copy link
Member

@sandwwraith sandwwraith Jul 19, 2023

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.

Copy link
Member

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?

Copy link
Contributor Author

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.

Copy link
Member

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 )

Copy link
Contributor Author

Choose a reason for hiding this comment

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

// 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(
Copy link
Member

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

Copy link
Contributor Author

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.

Copy link
Member

@ALikhachev ALikhachev left a 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?

@Vampire
Copy link
Contributor Author

Vampire commented Aug 1, 2023

Would you mind making a minor adjustment by adding a description

Not at all, done :-)

@sandwwraith
Copy link
Member

@Vampire Thanks for all your efforts! I'm terribly sorry for not noticing it sooner, but your PR is based against master, while development is usually done in dev. At this point, dev is already updated to Kotlin 1.9.0. I've tried to put your commit on top of dev and I got a failed build:

> Task :kotlinx-serialization-core:compileKotlinJs FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':kotlinx-serialization-core:compileKotlinJs'.
> Failed to query the value of task ':kotlinx-serialization-core:compileKotlinJs' property 'incrementalModuleInfoProvider'.
   > Could not isolate value org.jetbrains.kotlin.gradle.incremental.IncrementalModuleInfoBuildService$Parameters_Decorated@757090b2 of type IncrementalModuleInfoBuildService.Parameters
      > Cannot query the value of task ':kotlinx-serialization-cbor:verifyKotlinJvmModule' property 'ownModuleName' because it has no value available.
        The value of this property is derived from:
          - task ':kotlinx-serialization-cbor:compileKotlinJvm' property 'moduleName'

I'm not sure why it happens, as you're setting ownModuleName explicitly. Can you rebase your branch on dev, please? Maybe @Tapchicoma could help us again.

@Vampire Vampire changed the base branch from master to dev August 1, 2023 15:43
@Vampire
Copy link
Contributor Author

Vampire commented Aug 1, 2023

No problem, fixed.
I set ownModuleName explicitly, yes.
But I set it to compileTask.moduleName which is now deprecated and replaced by compileTask.kotlinOptions.moduleName.
The deprecated one seems to stay unset and so the ownModuleName also stayed unset.
I replaced that deprecated usage in both places and verified the verification still works as intended, so should be fine now hopefully.

@sandwwraith
Copy link
Member

Thanks again!

@sandwwraith sandwwraith merged commit 8b231ff into Kotlin:dev Aug 2, 2023
@Vampire Vampire deleted the issue-2218 branch August 2, 2023 13:21
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.

java.lang.NoSuchMethodError when parsing a JSON stream on Java 8
3 participants