IDE-only error: Cannot inline bytecode built with JVM target 11
jasonsparc opened this issue · 11 comments
I'm at JVM 21 (LTS), but Android Studio still compiles Kotlin DSL build scripts at Java 8. Because of that, and the fact that this plugin now targets JVM 11, Android Studio now displays the following error:
Cannot inline bytecode built with JVM target 11 into bytecode that is being built with JVM target 1.8. Please specify proper '-jvm-target' option
This error appears only for inline
methods. Other methods work fine.
Do note that, this an IDE-only error. The build works fine (when run via CLI).
Now, I haven't tried the above for "IntelliJ IDEA Community Edition", but I believe it should produce the same error (unless the latest versions no longer produce such errors).
Environment
From my project's gradlew -v
:
------------------------------------------------------------
Gradle 8.5
------------------------------------------------------------
Build time: 2023-11-29 14:08:57 UTC
Revision: 28aca86a7180baa17117e0e5ba01d8ea9feca598
Kotlin: 1.9.20
Groovy: 3.0.17
Ant: Apache Ant(TM) version 1.10.13 compiled on January 4 2023
JVM: 21.0.1 (Oracle Corporation 21.0.1+12-LTS-29)
OS: Windows 10 10.0 amd64
And my IDE:
Android Studio Hedgehog | 2023.1.1 Patch 1
Build #AI-231.9392.1.2311.11255304, built on December 27, 2023
Runtime version: 17.0.7+0-b2043.56-10550314 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o.
Note that, my IDE is currently from the latest stable release (from the stable release channel) that most people developing in KMM with Android would likely be using at the moment.
Suggestions
I believe I've alluded this problem before in #59, which is why I (subtly) suggested in that, that Java 8 should still be the output bytecode (regardless of the JVM toolchain used).
And, I'm currently aware of a few ways of fixing this issue:
Option 1:
Removed all inline
methods, and do not expose any inline
methods at all.
Of course, this also means that you can't have reified
inline
functions.
Option 2:
Simply set up something like the following in your build script(s):
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
// ...
tasks.withType<KotlinCompile>().configureEach {
compilerOptions.jvmTarget.set(JvmTarget.JVM_1_8)
}
tasks.withType<JavaCompile>().configureEach {
options.compilerArgs.add("--release=8")
}
I believe you could use JavaCompile.setTargetCompatibility(…)
instead of the options.compilerArgs.add("--release=8")
line above. I simply gave the above code snippet because that's how I usually set up my custom Gradle plugins in order to avoid the described problem above and be able to use inline
methods freely in my build. Feel free to solve the problem a different way.
Note that, even with the above, you are still free to use whatever JVM toolchain version you want, even JVM 11 – the above snippet simply sets the output bytecode to Java 8 compatibility.
Edit: Clarifications.
Option 3:
Advice users to use @Suppress("INLINE_FROM_HIGHER_PLATFORM")
annotation when using this plugin with Gradle Kotlin DSL in Android Studio. Example usage:
@Suppress("INLINE_FROM_HIGHER_PLATFORM")
buildConfig {
// ...
buildConfigField("APP_NAME", project.name)
}
Then, simply wait for Google and JetBrains to fix their IDE.
Relevant ticket: https://youtrack.jetbrains.com/issue/KTIJ-20816
Hi @jasonsparc, let me research if there is any impact to downgrade do JVM 8.
Hi @jasonsparc, I give it a try to build under JVM8, but the main blocker will be the lack of Map.of
that was added on JVM9.
Even this is not strictly related, I'd have to increase the complexity of the of the BuildConfigJavaGenerator
to use Map.of
when targeting JVM9 or above, or have a custom builder method for <=JVM8. This is, of course, when using Map buildconfigfields.
Given that AGP now requires JVM17 to run, and other libraries like mockk
have already bumpted to JVM11, I think having support for JVM8 (with the extra complexity) is not worth.
So, going back to your problem:
I'm at JVM 21 (LTS), but Android Studio still compiles Kotlin DSL build scripts at Java 8.
Not sure this is accurate to be honest, which version of AGP are you using, is it an old project?
If you are having that issue, I'm more willing to think it's a problem on how your buildscript is configured, probably your AS is running under JVM8.
Have you tried to set it explicitly in the build script?
java.toolchain.languageVersion = JavaLanguageVersion.of(11)
android {
The AGP plugin also adds the java
closure.
If you can provide a sample repo where this problem exits, I may help you workaround it.
I'm not saying no to downgrade to JVM8, I just want to make sure the effort is worth it, as it's right now an obsolete JVM
Disregard my previous comment. It doesn't work.
I'll research a bit, I figure out a solution. Worst case, I'll have to increase the complexity of the Java generator
Edit: I just saw your new comment now, a few seconds after I've posted this comment.
Actually, the actual issue is from IDEA. It doesn't matter if your users set their JVM toolchain to be higher than Java 8, and Android Studio already comes with its own embedded JVM. In fact, I don't even have Java 8 installed. Android Studio simply thinks that all Gradle Kotlin DSL build scripts are Java 8, even if in fact, Gradle itself would think otherwise. Perhaps that's one reason why plugins targeting JVM higher than 8 doesn't expose inline
methods. The issue at JetBrains is at this ticket, https://youtrack.jetbrains.com/issue/KTIJ-20816
…but they closed that ticket just 3 months ago, in favor of fixing something else, that could, in theory, also fix the issue. That other ticket is at, https://youtrack.jetbrains.com/issue/KTIJ-18375
…and that ticket has been marked fixed for IntelliJ IDEA 2023.3; however, I'm not really sure if that really fixed the issue.
It seems that the issue has something to do with how the IDE analyzes the build scripts. Even though Gradle is the one actually building the build scripts, the IDE insists that the build scripts are Java 8.
Now, that's just silly, don't you think? Since the IDE is even able to inquire from Gradle the current JVM toolchain it is using and set the project settings automatically, say for example, Java 21, when Gradle is set to use Java 21 toolchain. But the analysis of the IDE, that's what's broken.
I really couldn't blame you if there's really no way to downgrade to Java 8 now. However, I just wanted to point out the issue that your inline
methods won't be useable now, except perhaps if you advice your users to use @Suppress("INLINE_FROM_HIGHER_PLATFORM")
annotation, which is a simple fix and the IDE is happy about it anyway.
I would say, given the complexity, let's just wait for Google's Android Studio to catch up with the new IntelliJ IDEA 2023.3 where the fix has already been implemented.
Also, you've said that,
Given that AGP now requires JVM17 to run, and other libraries like mockk have already bumpted to JVM11, I think having support for JVM8 (with the extra complexity) is not worth.
Well you don't have to downgrade and support Java 8 environments. You could simply set the the Kotlin compiler to output Java 8 bytecode, while still targeting JVM 11 or higher runtime environments. Java 8 bytecode would be happy to use any JVM higher than it actually.
In fact, given that your project simply only have kotlin sources, the following lines should be enough to resolve the issue:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8)
}
Forget my tip about having to set up JavaCompile
with setTargetCompatibility
or compilerArgs.add("--release=8")
, as that would break your build if you are now using JVM9 APIs (as you've said about using Map.of
). Simply instruct the kotlin compiler to output Java 8 bytecode.
Otherwise, I'm happy with just using @Suppress("INLINE_FROM_HIGHER_PLATFORM")
annotation in my projects, but your users might not be. Although, it's really IDEA that's to blame here, given that the issue has even persisted for 2 years, and it was only 3 months ago (or 2 months?) that the issue was fixed.
Regarding my last comment, if you don't like duplicating lines of code, perhaps the following would work:
In your root project's build script, perhaps add something like the following:
allprojects {
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
compilerOptions.jvmTarget.set(org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_1_8)
}
}
I'm not free at the moment to clone and experiment with your repo, but if I find the time, I might try and fix the issue, and also try if my suggestion above would work.
I've just implemented something simillar by just targeting JVM 8 toolchain for the plugin
project.
I could change the target in the Compile task on both Java and Kotlin, but this is easier and less complex.
Feel free to challenge anything. So far I managed to get rid of the IDE issue.
So using JVM 8 toolchain didn't fail your build? How about your tests that rely on say, Map.of
?
I also think that dependency resolution would fail if you have dependencies that are available only for higher JVMs (i.e., those that configure some Gradle metadata saying that they should only be available for, say, JVM 11, such as, AGP for example). So far, your plugin doesn't seem to have such dependencies. So I guess that would be okay.
So using JVM 8 toolchain didn't fail your build? How about your tests that rely on say,
Map.of
?
If you look at the PR, on the test task I'm switching the toolchain to JVM11:
https://github.com/gmazzo/gradle-buildconfig-plugin/pull/123/files#diff-126180cb2041a63aa5868495a1fda5bff4cb7fde6e324c8d11b164a63127af0eR67
I also think that dependency resolution would fail if you have dependencies that are available only for higher JVMs (i.e., those that configure some Gradle metadata saying that they should only be available for, say, JVM 11, such as, AGP for example). So far, your plugin doesn't seem to have such dependencies. So I guess that would be okay.
It's true the plugin module metadata now sets "org.gradle.jvm.version": 8
attribute, but by the compatibility rule, it will be picked up by any other attribute version that is higher. So it's fine.
The only dependency of the plugin is the kotlin-stdlib
but is not declarin any, so it will be fine too:
https://repo1.maven.org/maven2/org/jetbrains/kotlin/kotlin-stdlib/1.9.22/kotlin-stdlib-1.9.22.module
Also the tests has passed (that are still targeting JVM11) and I also tried it on other projects I have
Thanks again for reporting this 💪