I'm having some issues with having IntelliJ play n...
# intellij
j
I'm having some issues with having IntelliJ play nice with Java Comment Preprocessor. Since I'm using a multi-module project I need to set the kotlin/scala sources to the preprocessed folder so other modules use the right sources. However, when (re)importing the project intelliJ continuously sets the preprocessed folder in build as sources root instead of the normal
src/main/kotlin
, making IntelliJ stop giving hints. Is there a way to change the sourcesets in gradle while keeping the source root markings of IntelliJ as they were? I have a sample project (actually built for something else) which has this issue. I marked it in TODOs for instance here. I did an attempt with
idea.module {}
in
build.gradle.kts
but that didn't seem to do anything except from giving me duplicate content roots... Any tips? I was personally thinking I could maybe set the source folders back after building/deploying or something , but I also couldn't figure out how to do that...
o
Seems like you are setting all source directories to the preprocessed folder via
setSrcDirs
, erasing the main source directory. Would it work if you'd just add a source directory like so?
Copy code
kotlin {
    sourceSets {
        main {
            kotlin.srcDir(preprocessMain.target.get())
        }
    }
}
j
Thanks for the help! I tried that before, then you get overloads because classes and functions occur twice in the module according to other modules. The preprocessed folder needs to be the only source folder when compiling the project
o
So you'd want IntelliJ to see a different set of sources than your compilation? In that case, wouldn't IntelliJ suffer from multiply defined classes and functions as well?
j
yes. Well apparently not. If I manually unmark the preprocessed folders and mark the normal onces, all works fine on the intellij side. When running stuff it uses the preprocessed ones and when editing it thinks of the normal folders as source root. It's just every time the project gets imported or reloaded, the source folders get unmarked again and the preprocessed ones get marked
o
Interesting, although I have no idea on how to solve this. Maybe there is some way to stop IntelliJ from re-importing (parts of) the Gradle configuration, but I don't know, unfortunately.
j
I think that's where
Copy code
idea.module {}
should be able to help, but I didn't manage to configure that to solve my issue
o
Maybe there is one hack you could try: What if you'd use a run configuration for compilations, set an environment variable there, then in the build script specify different sets of source directories depending on whether that environment variable is set (compilation=yes, import=no).
j
Don't think that works, because to run a file from IntelliJ it also uses gradle, which is needed to make sure it runs preprocessed files. I just need to find a way to make what I do manually now (marking the directories) be done automatically after each run
o
While I have no experience with the
idea
plugin, from what I've read, would the
whenMerged
hook help in this case?
j
trying that now!
👍 1
looks like no iml is being generated at all, even when specifying generateTo
isn't that a maven thing actually?
o
I'm just trying to figure out where the IDE stores parts of the imported Gradle configuration in one of my projects.
Seems like the IDE does not store module configurations in any file, just in main memory.
j
awesome...
makes debugging a lot harder
o
Seems like those
beforeMerged
and
whenMerged
hooks don't get called. blob shrug
j
explains why my printlns don't do anything
o
Yeah. Seems like we have to take this seriously:
The techniques we discuss in this section don’t work with IDEA’s import facility
j
lol
yes, apparently indeed
I got it!! It works if you only temporarily change the source sets for the compilation 🙂
Copy code
val kotlinMainSources = kotlin.sourceSets.main.get().kotlin.sourceDirectories

val preprocessMain by tasks.creating(JcpTask::class) {
    sources.set(kotlinMainSources)
    clearTarget.set(true)
    fileExtensions.set(listOf("kt"))
    vars.set(Versions.versionMap)
    outputs.upToDateWhen { false }
}

tasks.compileKotlin {
    dependsOn(preprocessMain)

    doFirst {
        kotlin {
            sourceSets {
                main {
                    kotlin.setSrcDirs(listOf(preprocessMain.target.get()))
                }
            }
        }
    }

    doLast {
        kotlin {
            sourceSets {
                main {
                    kotlin.setSrcDirs(kotlinMainSources)
                }
            }
        }
    }
}
nvm, when I now run something from intellij it skips the preprocessing... obviously
o
With a toolchain that's unaware of preprocessing, I'd guess that there is no way to make it work without shooting oneself in the foot now and then. I wonder how the final goal could be achieved without preprocessing?
j
the only way to do it without preprocessing would be to maintain 14+ source versions...
o
Do you have a small example of what you'd like to achieve with preprocessing? Did you consider using KSP for code generation?
j
KSP can only generate code, not modify it right? Plus, my project has both scala and kotlin. Here is an example for why code generation is necessary. On some versions you need an override, on others not. It could only work with ksp if you'd generate this entire file in it. Other compatibility issues I have are like this, where
DayTimeIntervalType()
just doesn't yet exist before spark 3.2, so I cannot reference it using a normal if-statement, it needs to be actually removed from the code. Same story if you'd want to autogenerate it, it's a 1400+ lines file...
Copy code
tasks.compileKotlin {
    dependsOn(preprocessMain)
    outputs.upToDateWhen { false }
}
seems do do something!
obviously it becomes slow, but at least it works
o
You are right in that KSP cannot modify code. You'd need a compiler plugin for that. Also, while KSP cannot process Scala, it could generate Scala code, although it would currently lack a proper output directory for that, as far as I remember. I'll take a look at your code examples...
j
the outputs.upToDateWhen seems to do the trick for now. I'm making it depend on
Copy code
preprocessMain.outputs.upToDateSpec(this)
and in preprocessedMain I'm trying to create a file listener or something, so that the preprocessing detects correctly it's time to run
o
Seems a bit on the edge but pragmatism sometimes is, fingers crossed. It's probably hard to support a multitude of (Spark) library versions from a single code base. 👍
j
actually that is super easy, barely an inconvenience. Mostly it's just changing the versions in gradle to get bytecode compatibilty. After that it's just removing a couple of encoders. Same with supporting multiple scala versions
o
Yeah, that's the easy part but then your toolchain has no idea about that preprocessing step and thus cannot reason about its implications.
j
that's why I need to keep the preprocessing to a minimum and have loads of tests 🙂
1
we also test for all version on GH actions
Copy code
tasks.compileKotlin {
    dependsOn(preprocessMain)
    outputs.upToDateWhen { preprocessMain.outcomingFiles.files.isEmpty() }
}
seems to do the trick. JCP has outcoming files when there were changes anywhere in your given source folders. IntelliJ also always listens to upToDateWhen 🙂
🎉 1