Morning! I'm building a gradle plugin which is gen...
# multiplatform
e
Morning! I'm building a gradle plugin which is generating kotlin code using KotlinPoet. The plugin in it's current state is able to generate code for common, android, jvm and js, iosArm64 and iosSimulatorArm64 targets. I register a task for each target that the multiplatform plugin reports:
Copy code
kotlinMultiplatformExtension.targets.configureEach {
  configureKotlinMultiplatformTarget(project, this)
}
Task configuration adds the outputDirectory to the target.compilation like this:
Copy code
val task = project.tasks.register<CodeGenerationTask>(kotlinTarget.name) {
  kotlinTarget.compilations.configureEach {
    defaultSourceSet.kotlin.srcDir(outputDirectory)
  }
}
and the task dependency wired up like
Copy code
kotlinTarget.compilations.configureEach {
  compileTaskProvider.configure {
    dependsOn(task)
  }
}
My hope was to be able to support intermediate levels in the default hierarchy as outlined in the developer documentation - "apple", "native", "ios" etc. Problem is I have no idea how to add code for those source sets. I haven't event been able to locate them while debugging. Does anyone have tips on how I can get started playing around with this? Also general feedback on my approach are more than welcome.
t
Do you want to add a generator task that generates Kotlin source code for the specific
KotlinSourceSet
?
e
That sounds like what I want to do. 🙂 At some point I mange to locate sources for those intermediate sourcesets. I made extension methods to print them out.
Copy code
@Suppress("unused")
internal fun KotlinTarget.sourceSetsString(): String {
    val stringBuilder: StringBuilder = StringBuilder()

    this.compilations.forEach { compilation ->
        compilation.kotlinSourceSets.forEach { sourceSet ->
            stringBuilder.append("Target: ${this.targetName} of type ${this.platformType}\n")

            stringBuilder.append("\t\t> SourceSet dependencies: \n")
            sourceSetDeps(
                sourceSet = sourceSet,
                stringBuilder = stringBuilder,
                level = 3
            )
        }
    }
    return stringBuilder.toString()
}

fun sourceSetDeps(sourceSet: KotlinSourceSet, stringBuilder: StringBuilder, level: Int) {
    stringBuilder.append("${"\t".repeat(level)} > ${sourceSet}\n")
    sourceSet.dependsOn.forEach { dependentSourceSet ->
        sourceSetDeps(
            sourceSet = dependentSourceSet,
            stringBuilder = stringBuilder,
            level = level + 1
        )
    }
}
For some reason it's no longer printing out those intermediate sourcesets. But it's been much trial and error. I may be simply misremembering things. :) As an aside: While it works now my IDE complains about duplicate content roots after sync which I consider an indication I'm doing something wrong: Path [/myproject/library/build/generated/myproject/src/android] of module [library.main] was removed from modules [library.unitTest, library.androidTest]
t
I would just iterate over
kolinSourceSet
and add generator task as a dependency:
Copy code
kotlin.sourceSets.configureEach { 
    kotlin.srcDir(generatorTask.map { it.outputDirectory })
}
e
hmm.. so replace
Copy code
val task = project.tasks.register<CodeGenerationTask>(kotlinTarget.name) {
  kotlinTarget.compilations.configureEach {
    defaultSourceSet.kotlin.srcDir(outputDirectory)
  }
}
with
Copy code
kotlinTarget.compilations.configureEach {
  kotlinSourceSets.forEach { sourceSet ->
    sourceSet.kotlin.srcDir(outDir)
  }
}
? Or what is "kotlin" in your sniplet?
As I understand the docs this has the potential of allowing me to wire task dependencies manually which has been a massive pain. Reason: Task ':iosArm64SourcesJar' uses this output of task ':mylibraryIosArm64' without declaring an explicit or implicit dependency. This can lead to incorrect results being produced, depending on what order the tasks are executed. Build is running on CI now. Thanks for the help so far. If you know of talks / blogs where I can read more on this topic that'd be much appreciated. :)
t
first
kotlin
is
KotlinMultiplatformExtension
and second one is
SourceDirectorySet
👍 1
you don't need to use
compilations
- better to just iterate over all source sets and decide if you want to generate for it based on the source set name
e
thanks! I'll give it a try after lunch. 🙂
Hmm.. Getting a host of new warnings and errors which I need to tackle - I'll spend some time with it to see where it ends up. Your way of doing it is less code = easier to read so I'm liking it so far. 😂 Curious if you have any tips regarding task wiring. I'm often ending up with "XXXX declaring an explicit or implicit dependency." type errors. Should I be doing
Copy code
kotlinTarget.compilations.configureEach {
  compileTaskProvider.configure {
    dependsOn(task)
  }
}
or should I be looking for some other way to wire tasks? My assumption is that I need a code generations task / sourceset that I want to add code to - I'm not sure if that holds true or not.
Looked into my errors and they were on me. My only gripe is not reaching out sooner ❤️ Thanks for the fantastic tip @tapchicoma
👍 1
Literally everything works with your tips applied - even the duplicate sourcesets are fixed.