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

Support Toolchains for JVM projects #4120

Closed
3flex opened this issue Sep 21, 2021 · 7 comments · Fixed by #5269
Closed

Support Toolchains for JVM projects #4120

3flex opened this issue Sep 21, 2021 · 7 comments · Fixed by #5269

Comments

@3flex
Copy link
Member

3flex commented Sep 21, 2021

Expected Behavior

If I am using Gradle's toolchain support for my Kotlin project, detekt should, by default, use the version of the JDK specified by the toolchain during execution. set the JDK target and JDK path as defined by the toolchain. The values can be overridden as needed.

I.e. if this configuration is in place:

java {
    toolchain {
        languageVersion.set(JavaLanguageVersion.of(11))
    }
}

Then detekt should setup its Kotlin compilation environment and perform its analysis against JDK 11, regardless of which JDK version was used to run Gradle itself.

Current Behavior

detekt runs using the same version of Java that is used to run Gradle.
detekt sets its own default for the JDK target, which can be manually overridden on individual tasks. It currently does not accept a custom JDK path as an input parameter.

Context

As an example, if Gradle is run with JDK 11, but toolchain support is enabled and configured with JDK version 16, there will be unresolved reference errors if the codebase uses any new APIs from Java 12-16. It would also prevent rules that are configured with references to Java 12+ APIs, like ForbiddenMethodCall, from working correctly.

Adding support mirrors the toolchain support added to Kotlin in 1.5.30.

I'm very happy to work on this one, but it will mean removing the classloader cache, or at least bypassing it, as detekt will have to execute outside of Gradle's process using Gradle's Worker API with process isolation or using JavaExec, so I am asking for feedback first.

My preference is removing the classloader cache for simplicity, and I also believe this will reduce Gradle daemon memory pressure.

Edit: Toolchains will help with #3396 as it would allow running the build on Java 8 while running detekt with Java 11.

@BraisGabin
Copy link
Member

I just read about the Gradle¡s Worker API and I never use it directly. But I understood that we could use it and cache the class loader in it. I remember that it was like we could create our own daemon. Am I right? If we can do that, go ahead.

But I would vote against just removing the cache and going back to the old performance. It was really bad.

@3flex
Copy link
Member Author

3flex commented Sep 21, 2021

I'll start with a PR to switch to worker API, then if that goes well I'll follow up with a PR introducing toolchain support.

I believe worker API with process isolation will have better performance than using JavaExec like it did previously, though it might be a bit slower than the current implementation.

@BraisGabin
Copy link
Member

That sounds good. We can make some benchmarks and compare the implementations. I'm not worried about loosing a bit of performance if we improve the correctness.

@cortinico
Copy link
Member

execute outside of Gradle's process using Gradle's Worker API with process isolation or using JavaExec, so I am asking for feedback first.

A nice benefit of using the Workers API is that will mitigate the issues arising from misalignment of Kotlin version between our internal version and the version embedded with Gradle.

So ideally is something we should look into, even regardless of the toolchain support.

@chao2zhang
Copy link
Member

chao2zhang commented Sep 28, 2021

There is known de/serialization overhead of Worker API - It sounds like we are using Worker API only for the process isolation rather than granular task parallelization.

@3flex
Copy link
Member Author

3flex commented Sep 28, 2021

Worker API starts daemons which are reused between tasks and builds, so the advantage is the JVM startup cost is only experienced when new daemons launch. This is favorable to JavaExec which starts a new JVM instance for every single task and doesn't reuse them.

On projects with many modules, or heavy use of KMM, there could be many detekt tasks so the total cost of starting new JVMs for each task can be significant. That's one of the reasons the custom classloader approach was introduced in the first place.

Both achieve process isolation. Worker API issues I mentioned should only be a factor if someone is actively working on the Gradle plugin.

@github-actions github-actions bot added the stale label Dec 30, 2021
@BraisGabin BraisGabin removed the stale label Dec 30, 2021
@github-actions github-actions bot added the stale label Mar 31, 2022
@3flex 3flex added never gets stale Mark Issues/PRs that should not be closed as they won't get stale and removed stale labels Mar 31, 2022
@detekt detekt deleted a comment from github-actions bot Aug 1, 2022
@detekt detekt deleted a comment from github-actions bot Aug 1, 2022
@3flex 3flex self-assigned this Aug 1, 2022
@3flex
Copy link
Member Author

3flex commented Sep 2, 2022

I've updated the description given what I found out while implementing #5269.

@3flex 3flex removed the never gets stale Mark Issues/PRs that should not be closed as they won't get stale label Sep 4, 2022
@3flex 3flex removed their assignment Jun 13, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants