How to generate code with KotlinPoet when I am building my application? (Gradle)

2.7k Views Asked by At

I'm new using kotlinpoet and I've been reading the documentation and it seems like a great library, but I could not find an example to solve my problem.

I have a dependency lib-domain-0.1.jar in which I have business objects for example:

package pe.com.business.domain

data class Person(val id: Int? = null, val name: String? = null)
...
..
package pe.com.business.domain

data class Departament(val id: Int? = null, val direction: String? = null)
...
..
.

And I want to build a new dependency called lib-domain-fx-0-1.jar where it has the same domains but with JavaFx properties (With tornadofx) for example:

package pe.com.business.domainfx
import tornadofx.*

class Person {
  val idProperty = SimpleIntegerProperty()
  var id by idProperty

  val nameProperty = SimpleStringProperty()
  var name by nameProperty
}
...
..
package pe.com.business.domainfx
import tornadofx.*

class Departament {
  val idProperty = SimpleIntegerProperty()
  var id by idProperty

  val directionProperty = SimpleStringProperty()
  var direction by directionProperty
}
...
..
.

My question is, how can I generate these files in lib-domain-fx-0-1.jar by simply compiling my application with a gradle build? My project "lib-domain-fx-0-1.jar" is just a library, so it has no main class, so I do not know where to start the generation of code?. I have seen several examples in which they use @Annotations and two different modules in the same project, but that is not what I need :(. I need to convert all classes of lib-domain-0.1.jar to the JavaFx version with TornadoFX in another project (lib-domain-fx-0.1.jar)

Thanks and regards.

2

There are 2 best solutions below

1
On

In my opinion KotlinPoet lacks in its documentation of any example of how to integrate it into a project.

As @Egor mentioned, the question itself is pretty broad, so I will answer only the core part: How to generate code with KotlinPoet when I am building my application with Gradle?

I did it with custom Gradle tasks.

There is an application/library/sub-project somewhere in src/main/java/com/business/package/GenerateCode.kt:

package com.business.package

import com.squareup.kotlinpoet.*

fun main() {
    // using kotlinpoet here

    // in the end wrap everything into FileSpec
    val kotlinFile: FileSpec = ...
    // and output result to stdout
    kotlinFile.writeTo(System.out)
}

Now make Gradle to create a file with produced output. Add to build.gradle:

task runGenerator(type: JavaExec) {
    group = 'kotlinpoet'
    classpath = sourceSets.main.runtimeClasspath
    main = 'com.business.package.GenerateCodeKt'
    // store the output instead of printing to the console:
    standardOutput = new ByteArrayOutputStream()
    // extension method genSource.output() can be used to obtain the output:
    doLast {
        ext.generated = standardOutput.toString()
    }
}

task saveGeneratedSources(dependsOn: runRatioGenerator) {
    group = 'kotlinpoet'
    // use build directory
    //def outputDir = new File("/${buildDir}/generated-sources")
    // or add to existing source files
    def outputDir = new File(sourceSets.main.java.srcDirs.first(), "com/business/package")
    def outputFile = new File(outputDir, "Generated.kt")
    doLast {
        if(!outputDir.exists()) {
            outputDir.mkdirs()
        }
        outputFile.text = tasks.runGenerator.generated
    }
}

In Android Studio / Intellij IDEA open the Gradle tool window, find new group kotlinpoet (without group the tasks will be in the others section), and execute task saveGeneratedSources.

0
On

You can use KotlinPoet directly in your Gradle scripts as below:

buildscript {
    dependencies {
        classpath 'com.squareup:kotlinpoet:1.16.0'
    }
}

tasks.register('generateHelloWorld') {
    doLast {
        def funSpec = FunSpec.builder("helloWorld")
                .addStatement("println(\"Hello, World!\")")
                .build()

        def fileSpec = FileSpec.builder("com.example", "HelloWorld")
                .addFunction(funSpec)
                .build()

        fileSpec.writeTo(file('build/generated/source/HelloWorld.kt'))
    }
}

From here, just add the generated sources folder to your source set and bind generateHelloWorld into your task graph where you want it (before compilation).