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

Add toolchains support to Gradle plugin #5269

Merged
merged 3 commits into from Sep 10, 2022
Merged

Conversation

3flex
Copy link
Collaborator

@3flex 3flex commented Sep 2, 2022

This will need some tweaking to make sure it works properly with incremental builds and build cache.

This toolchain support does what the Kotlin Gradle Plugin does, but for detekt. If a toolchain is configured for the project (either using java or kotlin) that will then set both jdkTarget and jdkHome defaults on any Detekt or DetektCreateBaseline tasks.

See here for more on the KGP design goals.

In Kotlin, toolchain support affects only the Kotlin/JVM -jdk-home option’s value and additionally sets the -jvm-target value if it was not set explicitly by the user.

Notably this means that detekt itself will still run on the JDK that Gradle runs on - if Gradle is running on Java 8, then so will detekt. This means that despite what I said, I expect this PR will have no effect on #3396.

Closes #4120

@BraisGabin
Copy link
Collaborator

I'm not sure what this PR will provide to our users. Let me explain what I think it will provide and you tell me if I'm wrong.

If we run detekt-cli defining --jdk-home the creation of BindingContext will have the information related with the classes inside the JDK. For example: java.time.Instant. But, weren't we adding those classes already?

And then, if we run detekt using the plugin the --jdk-home will be informed automatically taking that information from the configuration of the kotlin gradle plugin. For that reason we don't need to add any new configuration to out plugin, right?

And your last sentence means that the --jdk-home only works to provide classes information. That jdk will not be used to execute anything, right?

@3flex
Copy link
Collaborator Author

3flex commented Sep 7, 2022

weren't we adding those classes already?

There's special handling in the Kotlin compiler that takes care of it. detekt calls this to make sure it's configured: https://github.com/JetBrains/kotlin/blob/v1.7.10/compiler/cli/src/org/jetbrains/kotlin/cli/jvm/config/JvmContentRoots.kt#L91-L111

The problem is, if JDK home isn't provided, the compiler will use the JRE it's currently running in to populate the JDK class roots. When detekt is run from Gradle, that will be the JDK Gradle is running under. If the Kotlin compilation is using a different JDK version set through a toolchain, there's a mismatch between detekt's JDK home and the Kotlin compilation JDK home, which affects detekt's ability to analyse the project correctly.

if we run detekt using the plugin the --jdk-home will be informed automatically taking that information from the configuration of the kotlin gradle plugin. For that reason we don't need to add any new configuration to out plugin, right?

Are you asking if the value will be set by default? Yes, it will be - if there's a toolchain setup. If not, detekt will run using the JDK that Gradle is running under.

That jdk will not be used to execute anything, right?
Correct. detekt will still run using the JDK that Gradle runs.


For an example, you can run Gradle with Java 8, add java.util.Optional.stream (added in Java 9) to ForbiddenMethodCall, and setup a toolchain with:

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

Then call the forbidden method somewhere in the project. assemble works, because Java 11 is applied to the compilation using the toolchain. detektMain works, but analysis is not correct as it uses Java 8 to setup the JDK roots, so it throws these warnings:

\C:\Users\3flex\IdeaProjects\toolchains-demo\src\main\kotlin\Main.kt:7:18: error: type mismatch: inferred type is Unit but Stream<out TypeVariable(R)!>! was expected
        .flatMap { it.stream() }
                 ^
\C:\Users\3flex\IdeaProjects\toolchains-demo\src\main\kotlin\Main.kt:7:23: error: unresolved reference: stream
        .flatMap { it.stream() }
                      ^
The BindingContext was created with 2 issues. Run detekt with --debug to see the error messages.

The forbidden method call is not picked up as an error.

When using this PR, and the toolchain's JDK path passed to detekt, it works correctly:

C:\Users\3flex\IdeaProjects\toolchains-demo\src\main\kotlin\Main.kt:7:23: The method `java.util.Optional.stream` has been forbidden in the detekt config. [ForbiddenMethodCall]

Gradle is still running Java 8, so detekt itself also runs on Java 8 but now performs analysis with JDK 11 passed as jdkHome, so the issue is flagged as expected.

@BraisGabin
Copy link
Collaborator

Thanks for the explanation. All clear now :)

@cortinico cortinico added this to the 1.22.0 milestone Sep 7, 2022
@cortinico cortinico added the notable changes Marker for notable changes in the changelog label Sep 7, 2022
This aligns to the behaviour of toolchains in the Kotlin Gradle Plugin.
KGP will set jvmTarget and jdkHome based on the enabled toolchain,
assuming it hasn't been set elsewhere in the build script.
Copy link
Member

@cortinico cortinico left a comment

Choose a reason for hiding this comment

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

Code looks great to me 👍

@Parameter(
names = ["--jdk-home"],
description = "EXPERIMENTAL: Use a custom JDK home directory to include into the classpath",
converter = PathConverter::class
)
Copy link
Member

Choose a reason for hiding this comment

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

This needs an update to the CLI page on the website as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

On this page https://detekt.dev/docs/gettingstarted/cli? Isn't that auto generated now? Or have I misunderstood...

That said, it needs to be added to the Grade docs since they're manually maintained, will do that before merging.

Copy link
Member

Choose a reason for hiding this comment

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

Isn't that auto generated now?

Yes you're right 👍 I forgot we autogenerate that 🚀

@BraisGabin BraisGabin merged commit c626f1e into detekt:main Sep 10, 2022
21 checks passed
@3flex 3flex mentioned this pull request Sep 11, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
api build cli core gradle-plugin notable changes Marker for notable changes in the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Toolchains for JVM projects
3 participants