1

I have to generate kotlin code based on types from an external module.

Example project:
|-Base
| |-interface ContentProvider
|
|-First
| |-interface FirstProvider : ContentProvider
|
|-Second
| |-interface SecondProvider : ContentProvider
|
|-Main app
  |-class providers (generated)
    |- object : FirstProvider
    |- object : SecondProvider

So, I have to generate class providers which contains implementation of each descendant of ContentProvider

I have tried to use KSP to achieve that, but KSP has not an access to content of an other module.

I need some idea how it could be implemented.

2 Answers 2

1

I would:

Write a new classpath scanning program

  • In a new Gradle module, write a Kotlin JVM program with a JVM classpath scanner as dependency (eg Reflections (a bit easier to use, but not in active development) or ClassGraph (more modern and supposedly faster)).
  • Have that program accept arguments specifying (i) a destination directory for new code (this would normally be somewhere in target project's build directory) and (ii) the path of module(s) to be scanned.
  • Have that program scan the classpath of those modules and generate whatever code files you desire into the code directory. Some use Kotlin Poet to write the code, but I prefer just to generate it by hand1.

Write a new Gradle task

  • Write a new Gradle task, which has running that JVM program with those arguments as its task action. You can use Project.javaexec2 in the task action, or extend the JavaExec class.
  • The new task should declare as inputs the classpath scanning program JAR and the module(s) to be scanned in FileCollection objects; and declare as an output directory the generated source directory.
  • To get your hands on the JAR with your new classpath scanner in, set up a new configuration in the target project and put the classpath scanning project as its dependency. You can then resolve that configuration to get the JAR and pass it to your new task.
  • You can similarly use a configuration to capture the module(s) you are scanning.
  • Declare your output directory as part of the Kotlin source so it's included in the compilation.

Write a Gradle plugin

  • All of that build code is probably best written outside of a build script, so package it in a plugin if you can and apply it in your target project(s).
  • Have that plugin declare an extension which facilitates you capturing the module(s) to be scanned in target project build scripts, if appropriate.

1 If generating by hand, I recommend using the org.intellij.lang.annotations.Language annotation on functions returning a code string as this provides some basic formatting in your target language

2 If using the configuration cache, this needs to called from ExecOperations (documentation)

1
  • Thank you, I'll investigate this way. It seems as an option to solve my task. Commented Mar 14 at 11:20
0

I tested many different approaches to solve this problem and came back to using KSP. The solution was split between two KSP plugins.

KSP scanning plugin

Added to all modules in which scanning must be performed. In modules, this plugin collects information about the types necessary for final generation. The collected type information is used to generate types with metadata about the types required for final generation. The metadata types placed into package with a predefined name, as example: com.viacheslav.synt.

KSP generating plugin

The second plugin, using Resolver.getDeclarationsFromPackage, gets all metadata types from package with predefined name. The metadata types contains the types information needed at this stage. Using this metadata, I gain access to information about types with all the capabilities for their analysis. Based on this data, the plugin generates the final code, which handles all the necessary types from all modules.

Precompiled script plugins

I use Precompiled script plugins in projects, this made it easy to connect the KSP scanning plugin to all project modules.

Not the answer you're looking for? Browse other questions tagged or ask your own question.