jspecify
jspecify copied to clipboard
Write advice on using a javac that can (a) read type-use annotations from class files and (b) understand `MODULE`
Very rough notes for later cleanup:
Things you want (each of which should come with a justification for why you want it):
- You really want a javac with the fix for JDK-8225377. Currently, that means JDK 22 or higher. The fix may someday be backported, but we would have to convince OpenJDK that the benefits to users outweigh the compatibility impact.
- Ideally, you also want fixes for JDK-8043226 and JDK-8291643. Both are reasonably likely (not guaranteed) to be part of JDK 23. I don't know if backports are likely.
- You normally want a javac that recognizes
@Target(MODULE, ...)
, which we use for@NullMarked
. That means JDK 9 or higher, and probably everyone in practice wants JDK 11+, given that 9 and 10 don't seem to have been widely adopted.- Lack of this is not typically catastrophic. Is the only problem
-Werror
? It's been a while since I looked.
- Lack of this is not typically catastrophic. Is the only problem
- There might have been other fixes for type-use annotations (or other JSpecify-relevant features) between 11 and 22. If anyone knows any, please share.
How to get them:
- Use the newest version of Java you can for your build tools.
- This can be good advice independent of nullness, since newer versions pick up other fixes (and features?) and since you can absorb any incompatibilities gradually.
- But running whole build systems like Gradle under new versions might not work well.
- So use "toolchains" to use the newest JDK to compile while still potentially running under the build system under older versions. This is probably nicer for your users in additional ways, since the build tool can automatically download the appropriate JDK.
- Presumably you need for your runtime toolchain (for running tests and maybe other things?) to still be the version that you're targeting. Hopefully there's a way to set this up?
- To ensure compatibility with the version you're targeting, you have various options. From simplest to most complex:
- Use
--release
. - If that doesn't work because you rely on
Unsafe
, maybe ideally perform multiple builds and use multi-release jars or something, or maybe hack around it? Maybe don't bother with this and instead just try the options below? - If that doesn't work because you rely on APIs hidden by JDK modules, then use
--system
. Build-system integration for this seems to not be great.
- Use
- Gradle and Kotlin and probably other people tend recommend setting your toolchain to the version that you target. There is appealing simplicity in this advice (as you can see from my point about using a different runtime toolchain above), and it saves you from some problems that arise from lack of support for new JDKs in other build tools. But it fundamentally prevents you from picking up javac fixes.