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

2

7

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.

Borough answered 19/12, 2018 at 17:32 Comment(3)
Your question is pretty broad as it stands. You'd probably need to 1) process the classes in the input jar, probably using reflection, 2) create an intermediate model based on step 1, 3) generate code based on the model using KotlinPoet, 4) compile generated code, 5) package compiled code into a jar. Start researching on those and ask more specific questions if any. As for KotlinPoet, it'll definitely help with code generation, but other steps are out of scope of the library.Twerp
Thanks for the reply, but if my project is only a library and it has no main class, how do I execute the code generation? For example, if you had this code in a GenerateCode.kt class "FileSpec.builder (" "," HelloWorld "). AddType (TypeSpec.classBuilder (" Greeter ") .......build()" How can I execute that class when do "gradle build" or is this something that can not be done?Borough
What comes to my mind is to create a class with a main method that executes the creation of classes (GenerateCode.kt), but I would like to optimize that step when building my application with 'gradle build'. The creation of classes, methods and everything is not complicated, since there is a lot of documentation about Kotlinpoet. What causes me problems is to know in which part I run the class generation (GenerateCode.kt) for a project that is just a library like the previous example.Borough
A
7

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.

Antiphonary answered 14/5, 2020 at 6:24 Comment(1)
Any solution for projects using android application plugin? Gradle building stops when it tries to access runtimeClasspathAgglutinogen
S
0

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).

Skell answered 23/2 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.