Hi everyone, I'm currently trying to develop a Gra...
# plugin-development
j
Hi everyone, I'm currently trying to develop a Gradle Plugin that will generate Kotlin Code. I'm struggling with properly setting up the source sets and output directory. While this is a plugin for Kotlin in general, it should also work for Android. So far I got this:
Copy code
class MyPlugin : Plugin<Project> {
  override fun apply(project: Project) {
    val myPlugin = project.extensions.create("myPlugin", MyPluginExtension::class.java)
    val kotlin = project.extensions.getByType(KotlinProjectExtension::class.java)
	
	kotlin.sourceSets.forEach { sourceSet ->
	  val destination = project.layout.buildDirectory.dir("generated/myPlugin/${sourceSet.name}") // TODO: this feels wrong
	  sourceSet.kotlin.srcDir(destination)
	  project.tasks.register(
	    "generateMyCode${sourceSet.name}", // TODO: this feels wrong
		GenerateCodeTask::class.java
	  ) {
	    it.destination.set(destination)
	  }
	}
  }
}
The source set does get detected but registering all the tasks doesn't feel right. I thought of this because in Android you get all those tasks for each build variant, but like this many more tasks are registered (e.g. for *AndroidTest) Follow up question: what's the correct way to attach this to e.g. the assemble task?
v
The
assemble
task should not get these attached. And you should not register the plain directory as source dir. You should instead make sure that the
GenerateCodeTask
task properly declare its inputs and outputs as always, and then set the instances of this task as
srcDir
this will then automatically add task dependencies where needed automatically, so that every consumer of source files - be it a compile task, as static code analysis task, a sources jar task, ... - automatically gets the necessary task dependency and sees all sources. At least that's how it works with "normal" projects. Not sure how to do it properly for Android as Android is always special and I'm not into android development. 🙂
j
Thanks for the quick reply! My task looks somewhat like this:
Copy code
class GenerateMyCodeTask : DefaultTask() {
 @get:OutputDirectory val destination: DirectoryProperty = project.objects.directoryProperty() 
}
And I changed the plugin to
Copy code
val task = project.tasks.register(...) { it.destination.set(destination) }
sourceSet.kotlin.srcDir(task.get().outputs)
Is that the intended way?
v
Even
sourceSet.kotlin.srcDir(task)
should do if you do not have other outputs in it. If you do, using
get()
is bad as you break task-configuration avoidance, but should use
flatMap
in that case.
1
m
You can also have a single code generating task and wire its output to the
main
(or
commonMain
) Kotlin source set
You could register one codegen task for every Kotlin compilation but my guess is that register just one task and wiring that to
main
/`commonMain` is a lot simpler
(same goes for Android BTW, most of the code you find out there runs a separate codegen instance for each Android variant when most of the time you only need a single codegen instance that is wired to the
main
source set)
j
Thanks @Vampire, that works too!
Thanks @Martin, I did it like that:
Copy code
try { kotlin.sourceSets.getByName("commonMain").kotlin.srcDir(task)
} catch (e: UnknownDomainObjectException) { kotlin.sourceSets.getByName("main").kotlin.srcDir(task)
}
Now that seems to work in Android project, which is fine for now. Haven't tested it in KMP yet. I did find some extension for KMP source sets but they were internal. So is my way the intended way (or at least acceptable 😛 )?
m
You probably need only one of those lines in each block. 👍
j
Yeah, Slack does weird things...
m
Besides that, I prefer detecting if AGP is applied using
pluginManager.withId("com.android.library"), ....
instead of a big try/catch but I guess the try/catch should work too
This is overall what we do in Apollo.
kotlin.sourceSets.getByName("main").kotlin.srcDir(task)
works on Android with some limitations around tooling (IDE and lint IIRC)
Latest versions of AGP have dedicated methods but I find that it’s not worth the added complexity and
srcDir()
is “good enough”, especially I’m not sure I’m getting by using the newer methods
j
Thanks for all that info! Haven't come across that GitHub repo yet for some reason. I'll look into it and the Apollo plugin too. I'm certain I'll find a good setup with all that 🙏
👍 1
m
@Fredrick Eisele was that supposed to land in that thread?
It looks like a different problem from the initial one?
f
Nope, wrong place.