KSP Compiler plugin for protobuf generation from POJO

ProtobufGenerator

KSP Compiler plugin for protobuf generation from POJO.

To implement protobuf, we use Wire to serialize/deserialize, which takes .proto files and generate its data class and builders. To follow this approach for the existing data classes, we have to manually generate the proto files. Which is a very teadious job. This plugin aims to remove that overhead by generating the proto files for the annotated classes.

Installing

Maven Central

Add KSP plugin, gradle dependency, packing options and include generated ksp directory to your sourceSets

plugins {
    id("com.google.devtools.ksp") version "<version>"
}

android {
    ....
    packagingOptions {
      resources.pickFirsts.add("**.proto")
    }

    sourceSets {
        getByName("debug") {
          resources.srcDirs("build/generated/ksp/debug/resources")
        }
        getByName("release") {
          resources.srcDirs("build/generated/ksp/release/resources")
        }
    }
    ...
}

dependencies {
    implementation("io.github.aashitshah26:protobufgenerator-core:1.0.4")
    ksp("io.github.aashitshah26:protobufgenerator-protogen:1.0.4")
}

Usage

Just add the @AutoProtoGenerator annotation to your data/POJO class and wait for the magic 🪄.

@AutoProtoGenerator(javaPackage = "com.proto.animal", javaMultipleFile = true)
data class Animal(
    val name: String,
    val canStayOnLand: Boolean,
    val canStayInWater: Boolean?,
    val canFly: Boolean
)

If there is a class which is inherited by some other classes and the parent class is referenced in your data class. We will have to use @OneOfParent and @OneOfChild annotations to support the generation of porto messages for child as well. See Test 2 for better understanding.

@OneOfParent(shouldGenerateSelf = true)
open class AnimalAttribute {
    val isMammal: Boolean = false
}

@OneOfChild(AnimalAttribute::class)
data class LandAttribute(
    val runningSpeed: Float = 0F
)

If there is some parameter that you don’t want to show up in your proto file, you can use @IgnoreProtoProperty annotation. See Test 3 for better understanding.

@AutoProtoGenerator(javaPackage = "com.proto.test3")
data class Test3(
    val name: ArrayList<ArrayList<String>>? = null,
    @IgnoreProtoProperty
    val ignoredProp: String? = null
)

We have also added support for Gson. All the properties annotated with SerializedName will use the annontation value as name. See Test 1 for better understanding.

@AutoProtoGenerator(javaPackage = "com.proto.animal")
data class Animal(
    @SerializedName("nm") val name: String,
    @SerializedName("canStayOnLand") val canStayOnLand: ArrayList<String>,
    @SerializedName("canStayInWater") val canStayInWater: Boolean?,
    @SerializedName("canFly") val canFly: Boolean
)

If you are using wire to generate the classes from proto, include the following code snippet to run wire after your proto is generated and set the wire source to the generated location.

wire {
    sourcePath {
        srcDir("build/generated/ksp/debug/resources/com/protogen")
    }
    kotlin {
        // to implement android.os.Parcelable
        android = true
    }
}

afterEvaluate {
    android.applicationVariants.forEach {
        val buildType = it.buildType.name
        val kspTask = "ksp${buildType.capitalize()}Kotlin"
        val wireTask = "generate${buildType.capitalize()}Protos"
        tasks.named(wireTask) {
            dependsOn(kspTask)
        }
    }
}

License

Copyright 2023 Aashit Shah

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

GitHub

View Github

Leave a Reply

Your email address will not be published. Required fields are marked *