Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reimplement code generation using KotlinPoet #143

Merged
merged 76 commits into from
Oct 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
af9d62a
fixup broken test, common protos grpc
andrewparmet Oct 6, 2021
1575b6d
add protoc install step
andrewparmet Oct 6, 2021
215d900
try protoc again
andrewparmet Oct 6, 2021
589bf53
try again
andrewparmet Oct 6, 2021
b45cf74
add job dependency
andrewparmet Oct 6, 2021
0f590be
install curl?
andrewparmet Oct 6, 2021
c13a3c8
alpine
andrewparmet Oct 6, 2021
f464d24
no sudo
andrewparmet Oct 6, 2021
fcdd01a
call protoc
andrewparmet Oct 6, 2021
2b11b21
try finding protoc
andrewparmet Oct 6, 2021
4a4f49c
try caching protoc
andrewparmet Oct 6, 2021
0e694de
install protoc as command
andrewparmet Oct 6, 2021
ac5639a
give command a name
andrewparmet Oct 6, 2021
664fa81
include buildSrc classes in codegen
andrewparmet Oct 6, 2021
edecb8f
use sourceSets for common gradle code rather than copying class files…
andrewparmet Oct 6, 2021
b51b711
clean up test
andrewparmet Oct 6, 2021
3a1b449
remove extra line
andrewparmet Oct 6, 2021
2035a86
begin kotlinpoet migration
andrewparmet Oct 9, 2021
2d0d303
wip
andrewparmet Oct 9, 2021
a37d0ec
map entry converted
andrewparmet Oct 9, 2021
694b309
message constructor
andrewparmet Oct 9, 2021
39973b7
serialize method
andrewparmet Oct 10, 2021
39cd413
serialize method
andrewparmet Oct 10, 2021
a690265
messageSize
andrewparmet Oct 10, 2021
25f7bea
equals and hashCode
andrewparmet Oct 10, 2021
bcc85f8
toString
andrewparmet Oct 10, 2021
a915e40
dsl copy
andrewparmet Oct 10, 2021
ae9e5fa
dsl short of build method
andrewparmet Oct 10, 2021
bafef27
implement build
andrewparmet Oct 10, 2021
8b09548
begin work on deserializer
andrewparmet Oct 10, 2021
0c9c460
deserialize method
andrewparmet Oct 10, 2021
c6fe6c3
adjust indentation
andrewparmet Oct 10, 2021
702066b
dsl invoke
andrewparmet Oct 10, 2021
f3a09ab
handle oneofs and fix message size
andrewparmet Oct 10, 2021
d030649
enum
andrewparmet Oct 10, 2021
412ad2b
fix annotations
andrewparmet Oct 10, 2021
448490d
core compiles
andrewparmet Oct 10, 2021
d0329d4
conformance tests compile; one fails
andrewparmet Oct 10, 2021
aadf949
fixup some compilation
andrewparmet Oct 10, 2021
4d285c6
fix conformance tests
andrewparmet Oct 10, 2021
ad14239
implement service generation
andrewparmet Oct 10, 2021
f47be19
fixup some whitespace
andrewparmet Oct 10, 2021
15dd6e9
descriptor generation
andrewparmet Oct 11, 2021
5419022
fixup
andrewparmet Oct 11, 2021
f7036f8
fix message implementation; some unwrapping/wrapping is left
andrewparmet Oct 11, 2021
efafb5a
fix wrapper types in map entries
andrewparmet Oct 11, 2021
bd6a8d7
everything compiles now
andrewparmet Oct 11, 2021
340aacd
fix enum deprecation
andrewparmet Oct 11, 2021
02c5bb7
all tests pass
andrewparmet Oct 11, 2021
132a8f5
lint
andrewparmet Oct 11, 2021
6a254d1
undo some changes
andrewparmet Oct 11, 2021
1685b5a
clean up enum generation
andrewparmet Oct 12, 2021
f01d6af
finish cleaning up enums
andrewparmet Oct 12, 2021
d6b04ab
begin cleaning up map entry
andrewparmet Oct 12, 2021
e237a23
extract deserializer construction
andrewparmet Oct 12, 2021
36ca69e
Remove dead code, start to add some ktdoc (#2)
pranjalvachaspati-toast Oct 13, 2021
6186573
lint
andrewparmet Oct 13, 2021
b6f6f35
start using TypeName in properties
andrewparmet Oct 15, 2021
8a03480
clean up some deprecation handling and indentation
andrewparmet Oct 15, 2021
419f556
clean up some message annotation logic
andrewparmet Oct 16, 2021
cabd657
clean up interface implementation
andrewparmet Oct 16, 2021
f8774b6
start passing enum type name around
andrewparmet Oct 16, 2021
12fe227
file descriptor resolution resolves imports through kotlinpoet
andrewparmet Oct 16, 2021
d8a2658
the absolute minimum to get latest kotlinpoet
andrewparmet Oct 16, 2021
d4fcbc5
1.5.21
andrewparmet Oct 16, 2021
a8b955b
use code block with named parameters
andrewparmet Oct 16, 2021
eb9b619
start using type names for all properties
andrewparmet Oct 16, 2021
3f8bc46
remove all uses of typevariablename
andrewparmet Oct 17, 2021
702ddd2
add failing tests that are now passing
andrewparmet Oct 17, 2021
d92e6d5
Merge branch 'main' into kotlinpoet
andrewparmet Oct 18, 2021
f9afebb
Remove importresolver package (#5)
pranjalvachaspati-toast Oct 21, 2021
dea9213
Merge branch 'main' into kotlinpoet
andrewparmet Oct 21, 2021
703fde2
clean up a bit
andrewparmet Oct 21, 2021
bb5d784
lint and some cleanup
andrewparmet Oct 21, 2021
db2a4e4
remove more dead code
andrewparmet Oct 21, 2021
b1e1921
remove most usages of enclosing messages
andrewparmet Oct 21, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,8 @@ subprojects {
allWarningsAsErrors = true
jvmTarget = "1.8"
freeCompilerArgs = listOf("-Xinline-classes")
languageVersion = "1.4"
apiVersion = "1.4"
}
}

Expand Down
4 changes: 2 additions & 2 deletions buildSrc/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ repositories {
}

dependencies {
implementation("com.diffplug.spotless:spotless-plugin-gradle:5.15.0")
implementation("com.diffplug.spotless:spotless-plugin-gradle:5.15.1")
implementation("com.google.protobuf:protobuf-gradle-plugin:0.8.17")
implementation("io.codearte.gradle.nexus:gradle-nexus-staging-plugin:0.30.0")
implementation("com.google.guava:guava:30.1.1-jre")
implementation("ru.vyarus:gradle-animalsniffer-plugin:1.5.0")
implementation("org.jetbrains.kotlinx:binary-compatibility-validator:0.5.0")
implementation("org.jetbrains.kotlinx:binary-compatibility-validator:0.7.0")
implementation(kotlin("gradle-plugin-api"))
}
11 changes: 5 additions & 6 deletions buildSrc/src/main/kotlin/Dependencies.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,22 @@ object versions {
const val grpc = "1.41.0"
const val grpcKotlin = "1.1.0"
const val kollection = "0.7"
const val kotlin = "1.4.32"
const val kotlin = "1.5.21"
const val kotlinPoet = "1.10.1"
const val kotlinxCoroutines = "1.3.9"
const val protobuf = DEFAULT_PROTOBUF_VERSION
const val protobufPlugin = "0.8.17"
const val stringTemplate = "4.3.1"

// Test
const val jackson = "2.12.3"
const val jackson = "2.13.0"
const val junit = "5.7.1"
const val truth = "1.1.3"

// Benchmarks
const val datasets = "0.1.0"
const val gradleDownload = "4.1.1"
const val jmh = "1.26"
const val wire = "3.5.0"
const val wire = "4.0.0-alpha.12"

// Third Party
const val protoGoogleCommonProtos = "2.5.0"
Expand All @@ -57,6 +57,7 @@ object libraries {

const val kollection = "com.github.andrewoma.dexx:kollection:${versions.kollection}"

const val kotlinPoet = "com.squareup:kotlinpoet:${versions.kotlinPoet}"
const val kotlinReflect = "org.jetbrains.kotlin:kotlin-reflect:${versions.kotlin}"
const val kotlinStdlib = "org.jetbrains.kotlin:kotlin-stdlib:${versions.kotlin}"
const val kotlinxCoroutinesCore = "org.jetbrains.kotlinx:kotlinx-coroutines-core:${versions.kotlinxCoroutines}"
Expand All @@ -66,8 +67,6 @@ object libraries {
const val protobufLite = "com.google.protobuf:protobuf-javalite:${versions.protobuf}"
const val protoc = "com.google.protobuf:protoc:${versions.protobuf}"

const val stringTemplate = "org.antlr:ST4:${versions.stringTemplate}"

// Test
const val jackson = "com.fasterxml.jackson.module:jackson-module-kotlin:${versions.jackson}"
const val junit = "org.junit.jupiter:junit-jupiter:${versions.junit}"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import com.fasterxml.jackson.module.kotlin.readValue
object Database {
fun features(): List<Feature> {
return javaClass.getResourceAsStream("route_guide_db.json").use {
ObjectMapper().registerModule(KotlinModule()).readValue<FeatureDatabase>(it!!.reader())
ObjectMapper()
.registerModule(KotlinModule.Builder().build())
.readValue<FeatureDatabase>(it!!.reader())
}.feature
}
}
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
2 changes: 1 addition & 1 deletion protokt-codegen/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ dependencies {
implementation(libraries.arrow)
implementation(libraries.grpcStub)
implementation(libraries.kollection)
implementation(libraries.kotlinPoet)
implementation(libraries.kotlinReflect)
implementation(libraries.kotlinxCoroutinesCore)
implementation(libraries.protobufJava)
implementation(libraries.stringTemplate)

testImplementation(project(":testing:testing-util"))

Expand Down
71 changes: 28 additions & 43 deletions protokt-codegen/src/main/kotlin/com/toasttab/protokt/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,9 @@ import com.google.protobuf.ExtensionRegistry
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse
import com.google.protobuf.compiler.PluginProtos.CodeGeneratorResponse.Feature
import com.toasttab.protokt.codegen.impl.Accumulator
import com.toasttab.protokt.codegen.impl.Annotator
import com.toasttab.protokt.codegen.impl.FileDescriptorResolver
import com.toasttab.protokt.codegen.impl.ImportResolver
import com.toasttab.protokt.codegen.impl.resolvePackage
import com.toasttab.protokt.codegen.protoc.AnnotatedType
import com.squareup.kotlinpoet.FileSpec
import com.toasttab.protokt.codegen.impl.FileBuilder
import com.toasttab.protokt.codegen.protoc.ProtocolContext
import com.toasttab.protokt.codegen.protoc.TypeDesc
import com.toasttab.protokt.codegen.protoc.fileName
import com.toasttab.protokt.codegen.protoc.respectJavaPackage
import com.toasttab.protokt.codegen.protoc.toProtocol
import com.toasttab.protokt.ext.Protokt
import java.io.OutputStream
Expand All @@ -51,11 +44,8 @@ internal fun main(bytes: ByteArray, out: OutputStream) {
val files = req.protoFileList
.filter { filesToGenerate.contains(it.name) }
.mapNotNull {
response(
it,
generate(it, req.protoFileList, filesToGenerate, params),
params
)
val fileSpec = generate(it, req.protoFileList, filesToGenerate, params)
fileSpec?.let(::response)
}

if (files.isNotEmpty()) {
Expand All @@ -72,9 +62,8 @@ private fun generate(
protoFileList: List<FileDescriptorProto>,
filesToGenerate: Set<String>,
params: Map<String, String>
): String {
val code = StringBuilder()
val protocol =
): FileSpec? =
FileBuilder.buildFile(
toProtocol(
ProtocolContext(
fdp,
Expand All @@ -83,35 +72,31 @@ private fun generate(
protoFileList
)
)

val descs = protocol.types.map { TypeDesc(protocol.desc, AnnotatedType(it)) }

Accumulator.apply(
descs.map(Annotator::apply),
ImportResolver.resolveImports(descs),
FileDescriptorResolver.resolveFileDescriptor(descs),
code::append
)

return code.toString()
}
private fun response(fileSpec: FileSpec) =
CodeGeneratorResponse.File
.newBuilder()
.setContent(fileSpec.toString().let(::tidy))
.setName(fileSpec.name)
.build()

private fun response(
fdp: FileDescriptorProto,
code: String,
params: Map<String, String>
) =
code.takeIf { it.isNotBlank() }?.let {
CodeGeneratorResponse.File
.newBuilder()
.setContent(code)
.setName(
fileName(
resolvePackage(fdp, respectJavaPackage(params)),
fdp.name
)
).build()
}
// strips Explicit API mode declarations
// https://kotlinlang.org/docs/whatsnew14.html#explicit-api-mode-for-library-authors
private fun tidy(code: String) =
code
// https://stackoverflow.com/a/64970734
.replace("public class ", "class ")
.replace("public val ", "val ")
.replace("public var ", "var ")
.replace("public fun ", "fun ")
.replace("public object ", "object ")
.replace("public companion ", "companion ")
.replace("public override ", "override ")
.replace("public sealed ", "sealed ")
.replace("public data ", "data ")
// https://github.com/square/kotlinpoet/pull/932
.replace("): Unit {", ") {")

private fun parseParams(req: CodeGeneratorRequest) =
if (req.parameter == null || req.parameter.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,27 +13,25 @@
* limitations under the License.
*/

package com.toasttab.protokt.codegen.impl
package com.toasttab.protokt.codegen.annotators

import arrow.core.Some
import com.github.andrewoma.dexx.kollection.immutableListOf
import com.toasttab.protokt.codegen.impl.EnumAnnotator.Companion.annotateEnum
import com.toasttab.protokt.codegen.impl.MessageAnnotator.Companion.annotateMessage
import com.toasttab.protokt.codegen.impl.ServiceAnnotator.annotateService
import com.toasttab.protokt.codegen.model.PPackage
import com.squareup.kotlinpoet.TypeSpec
import com.toasttab.protokt.codegen.annotators.MessageAnnotator.Companion.annotateMessage
import com.toasttab.protokt.codegen.annotators.ServiceAnnotator.annotateService
import com.toasttab.protokt.codegen.impl.EnumBuilder
import com.toasttab.protokt.codegen.protoc.AnnotatedType
import com.toasttab.protokt.codegen.protoc.Enum
import com.toasttab.protokt.codegen.protoc.FileDesc
import com.toasttab.protokt.codegen.protoc.Message
import com.toasttab.protokt.codegen.protoc.Protocol
import com.toasttab.protokt.codegen.protoc.ProtocolContext
import com.toasttab.protokt.codegen.protoc.Service
import com.toasttab.protokt.codegen.protoc.TopLevelType
import com.toasttab.protokt.codegen.protoc.TypeDesc

/**
* STAnnotator is an implementation of a side effect free function.
* The input is an AST<TypeDesc> and its output is a NEW AST<TypeDesc> that has
* a NEW fully constructed AnnotatedType attached to each AST node.
* Annotates an unannotated AST. This effectively converts the protobuf AST to a Kotlin AST.
*/
object Annotator {
const val rootGoogleProto = "google.protobuf"
Expand All @@ -45,43 +43,42 @@ object Annotator {

data class Context(
val enclosing: List<Message>,
val pkg: PPackage,
val desc: FileDesc
)

fun apply(data: TypeDesc) =
TypeDesc(
data.desc,
AnnotatedType(
data.type.rawType,
Some(
annotate(
data.type.rawType,
Context(
immutableListOf(),
kotlinPackage(data),
data.desc
)
fun apply(protocol: Protocol) =
protocol.types.flatMap {
val annotated =
annotate(
it,
Context(
immutableListOf(),
protocol.desc
)
)
)
)

fun annotate(type: TopLevelType, ctx: Context): String =
annotated.map { type ->
TypeDesc(protocol.desc, AnnotatedType(it, type))
}
}

fun annotate(type: TopLevelType, ctx: Context): Iterable<TypeSpec> =
when (type) {
is Message ->
nonGrpc(ctx) {
nonDescriptors(ctx) {
annotateMessage(
type,
ctx.copy(enclosing = ctx.enclosing + type)
listOf(
annotateMessage(
type,
ctx.copy(enclosing = ctx.enclosing + type)
)
)
}
}
is Enum ->
nonGrpc(ctx) {
nonDescriptors(ctx) {
annotateEnum(type, ctx)
listOf(EnumBuilder(type, ctx).build())
}
}
is Service ->
Expand All @@ -93,14 +90,14 @@ object Annotator {
)
}

private fun nonDescriptors(ctx: Context, gen: () -> String) =
nonDescriptors(ctx.desc.context, "", gen)
private fun <T> nonDescriptors(ctx: Context, gen: () -> Iterable<T>) =
nonDescriptors(ctx.desc.context, emptyList(), gen)

fun <T> nonDescriptors(ctx: ProtocolContext, default: T, gen: () -> T) =
boolGen(!ctx.onlyGenerateDescriptors, default, gen)

private fun nonGrpc(ctx: Context, gen: () -> String) =
nonGrpc(ctx.desc.context, "", gen)
private fun <T> nonGrpc(ctx: Context, gen: () -> Iterable<T>) =
nonGrpc(ctx.desc.context, emptyList(), gen)

fun <T> nonGrpc(ctx: ProtocolContext, default: T, gen: () -> T) =
boolGen(!ctx.onlyGenerateGrpc, default, gen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,16 @@
* limitations under the License.
*/

package com.toasttab.protokt.codegen.impl
package com.toasttab.protokt.codegen.annotators

import arrow.core.None
import arrow.core.Some
import com.toasttab.protokt.codegen.impl.Annotator.Context
import com.squareup.kotlinpoet.TypeName
import com.toasttab.protokt.codegen.annotators.Annotator.Context
import com.toasttab.protokt.codegen.impl.Wrapper.interceptMapKeyTypeName
import com.toasttab.protokt.codegen.impl.Wrapper.interceptMapValueTypeName
import com.toasttab.protokt.codegen.protoc.MapEntry
import com.toasttab.protokt.codegen.protoc.Message
import com.toasttab.protokt.codegen.protoc.Oneof
import com.toasttab.protokt.codegen.protoc.StandardField
import com.toasttab.protokt.codegen.protoc.TypeDesc
import com.toasttab.protokt.codegen.template.Renderers.ConcatWithScope

fun resolveMapEntry(m: Message) =
MapEntry(
Expand All @@ -36,36 +33,15 @@ fun resolveMapEntry(m: Message) =
fun resolveMapEntryTypes(f: StandardField, ctx: Context) =
f.mapEntry!!.let {
MapTypeParams(
interceptMapKeyTypeName(f, it.key.unqualifiedTypeName, ctx)!!,
interceptMapValueTypeName(f, it.value.typePClass.renderName(ctx.pkg), ctx)!!
interceptMapKeyTypeName(f, it.key.typePClass.toTypeName(), ctx)!!,
interceptMapValueTypeName(f, it.value.typePClass.toTypeName(), ctx)!!
)
}

class MapTypeParams(
val kType: String,
val vType: String
val kType: TypeName,
val vType: TypeName
)

fun oneOfScope(f: Oneof, type: String, ctx: Context) =
ctx.stripEnclosingMessageNamePrefix(
ctx.stripRootMessageNamePrefix(
ConcatWithScope.render(
scope = type,
value = f.name
)
)
)

fun String.emptyToNone() =
if (isEmpty()) {
None
} else {
Some(this)
}

fun kotlinPackage(data: TypeDesc) =
resolvePackage(
data.desc.options,
data.desc.packageName,
data.desc.context.respectJavaPackage
)
fun oneOfScope(f: Oneof, type: String) =
"$type.${f.name}"
Loading