How can I run a task BEFORE Kotlin compiles the so...
# gradle
m
How can I run a task BEFORE Kotlin compiles the source classes? I'm generating classes based on
.yml
files for localization files, if there is a key
commands.hello: "Hello {user}!"
, a class + function is generated named
I18nKeysData.Commands.Hello(name: Any)
Currently what I'm doing is this
Copy code
// HACKY WORKAROUND!!!
            // This makes the generateI18nKeys task to always be ran before the compileKotlin step
            // We need to do this (instead of using withType) because, for some reason, it doesn't work and the task isn't executed.
            //
            // We need to keep it within the "afterEvaluate" block because, if it isn't, the compile*InsertStuffHere* tasks won't exist!
            // <https://stackoverflow.com/a/58763804/7271796>
            project.afterEvaluate {
                project.tasks.filter { it.name.startsWith("compileKotlin") }.forEach {
                    it.dependsOn(task)
                }
            }
But I think it is very hacky and it broke in Kotlin 1.6.20-M1 because they added a new
compileCommonMainKotlinMetadata
task, so I wonder if there is a better solution for this
m
Use
srcDir
:
Copy code
(project.extensions.findByName("kotlin") as? KotlinProjectExtension).sourceSets.getByName("main").kotlin.srcDir(outputDir)
If
outputDir
is a Gradle
Property
from another task, it will setup the task dependency automatically
For this, take your codegenTask and use
flatMap
:
Copy code
val outputDir = codegenTaskProvider.flatMap { it.outputDir }
Full example here
v
Actually that snippet is bad in at least 4 different ways. Basically what Martin said. Though you can even use the task itself as argument to
srcDir
if that is the only output of it.
today i learned 3
πŸ‘€ 2
m
Thanks everyone, I will try that later πŸ™‚ The snippet is pretty bad because that was the only solution that I could find after countless hours searching on Google, but I always thought that maybe there was a better way to do it because that just feels so much hacky
v
Just if you are curious, the 4 things immediately coming in my mind are: β€’ Don't use
afterEvaluate
(no, it is not necessary, whatever SO says, you just need a proper lazy construct instead of looking what tasks are available right now) β€’ Don't iterate through all tasks, or all have to be realized, totally destroying task configuration avoidance β€’ Don't match the tasks by name when you actually want to match by type β€’ Don't generate into the checked in sources directory, and add a task dependency but instead properly register your generation task as Martin showed πŸ™‚
πŸ™ 1
Despite the last point, it would better be something like
tasks.withType<KotlinCompile>().configureEach { dependsOn(task) }
instead of the whole snippet, or if name is actually important too, then
tasks.withType<KotlinCompile>().matching { name.startsWith("compileKotlin") }.configureEach { dependsOn(task) }
(still using
withType
to not destroy task configuration avoidance completely)
Btw. @mbonnin why the uglified version with strings and casts? This should work just fine:
Copy code
kotlin {
    sourceSets {
        main {
            kotlin.srcDir(generatorTask)
        }
    }
}
or this:
Copy code
kotlin.sourceSets.main.get().kotlin.srcDir(generatorTask)
m
I'm on the "Programmatic over DSL" team. Programmatic works always. DSL requires the
plugin{}
block
v
Which is the recommended way anyway, except if you are writing a binary plugin. πŸ˜„ I'm just concerned because then people discard Kotlin DSL as too ugly to handle. πŸ™‚
m
I like the generated accessors now that I understand what they're doing under the hood. But when discovering all of this, I liked the "ugly" version better because it's explicit... I even made a talk about this πŸ˜…

https://www.youtube.com/watch?v=UGtYPRBCtEgβ–Ύ

πŸ‘€ 1
m
I implemented the changes and now the
generateI18nKeys
task is ran before
transformCommonMainDependenciesMetadata
and
compileCommonMainKotlinMetadata
! And now my project compiles without any compilation errors when using Kotlin 1.6.20-M1 :D I ended up going with the
kotlin { sourceSets {} }
version because I was already doing that before, but I was "hardcoding" the path (
kotlin.srcDir("build/generated/languages")
) instead of referencing the property (
kotlin.srcDir(generateI18nKeys.get().languageTargetFolder)
) Thanks for helping @Vampire @mbonnin :)
v
Does the task have more outputs? If not, as I said, just use the task / task provider as argument. πŸ™‚ And whenever you configure some path manually or add an explicit
dependsOn
, thanks actually a code smell. πŸ™‚
m
Yes, there is only one directory output, I've now replaced the
srcDir
to be just
generateI18nKeys
and it still works, thanks!
πŸ‘Œ 2
601 Views