Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Advanced techniques for building Kotlin DSL(s) ...

Advanced techniques for building Kotlin DSL(s) - droidcon NYC 2022

How to create Kotlin DSL that looks like the syntax of a programming language?
In this talk, I would like to talk about advanced techniques for creating Kotlin DSL.
As an example, we will see how to build a DSL that looks like a Starlark programming language, a Python dialect used for writing build scripts for Bazel build system. This DSL is a declarative code generator that builds an abstract syntax tree of a Starlark language and then generates a formatted code based on it.
However, all the concepts described in this talk are applicable to any kind of Kotlin DSL(s) including ones for Android projects.
The example project was presented at BazelCon 2021, a Google conference about Bazel build system: https://youtu.be/dz-CFEwJuko
In addition, there is a blog post that shows the capabilities of this DSL: https://proandroiddev.com/304fa8b3680c
The source code is available on GitHub: https://github.com/Morfly/airin

Pavlo Stavytskyi

September 02, 2022
Tweet

More Decks by Pavlo Stavytskyi

Other Decks in Programming

Transcript

  1. Concrete DSL example • Starlark code generator (Python dialect) •

    Replicate Starlark syntax in Kotlin • https://github.com/Morfly/airin → airin-starlark 9
  2. // Starlark/Python SRC_FILES = ["MyClass.java"] android_binary( name = "app", srcs

    = ["MainActivity.java"] + SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 11
  3. // Kotlin DSL val starlarkFile = BUILD.bazel { val SRC_FILES

    by list["MyClass.java"] android_binary( name = "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) } fileWriter.write(starlarkFile) Starlark code generator 12
  4. // Kotlin DSL val starlarkFile = BUILD.bazel { val SRC_FILES

    by list["MyClass.java"] android_binary( name = "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) } fileWriter.write(starlarkFile) // Creates BUILD.bazel file Starlark code generator 13
  5. Starlark code generator 14 android_binary function call Starlark file name

    argument srcs argument manifest argument + binary expression list expression SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … MainActivity.java string literal
  6. MainActivity.java string literal android_binary function call Starlark file name argument

    srcs argument manifest argument + binary expression list expression SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … MainActivity.java string literal // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name = "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 15 android_binary function call Starlark file name argument manifest argument + binary expression SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES …
  7. Starlark file name argument srcs argument manifest argument + binary

    expression SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … MainActivity.java string literal android_binary function call // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name = "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 16 android_binary function call list expression MainActivity.java string literal
  8. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 17 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … name argument app string literal list expression MainActivity.java string literal
  9. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 18 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … manifest argument AndroidManifest.xml string literal list expression MainActivity.java string literal
  10. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 19 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … srcs argument list expression MainActivity.java string literal
  11. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 20 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … + binary expression list expression MainActivity.java string literal
  12. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 21 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … list expression SRC_FILES reference MainActivity.java string literal
  13. // Kotlin DSL val SRC_FILES by list["MyClass.java"] android_binary( name =

    "app", srcs = list["MainActivity.java"] `+` SRCS_FILES, manifest = "AndroidManifest.xml", ) Starlark code generator 22 android_binary function call Starlark file name argument srcs argument manifest argument + binary expression MainActivity.java string literal SRC_FILES reference app string literal AndroidManifest.xml string literal SRC_FILES … list expression SRC_FILES reference MainActivity.java string literal
  14. class StarlarkFile(val statements: List<Node>): Node class FunctionCall(val name: String, val

    arguments: List<Node>): Node class Argument(val name: String, param: Node) Starlark code generator 25
  15. class StarlarkFile(val statements: List<Node>): Node class FunctionCall(val name: String, val

    arguments: List<Node>): Node class Argument(val name: String, param: Node) class StringLiteral(val value: String): Node class ListExpression(val items: List<Node>): Node class ListReference(val name: String): Node class BinaryPlus(val left: Node, val right: Node): Node Starlark code generator 26
  16. // Kotlin DSL val starlarkFile = BUILD.bazel { ... }

    fileWriter.write(starlarkFile) Entering DSL context 28
  17. // Kotlin DSL val starlarkFile = BUILD.bazel { ... }

    fileWriter.write(starlarkFile) Entering DSL context 29
  18. // Implementation object BUILD inline fun BUILD.bazel(body: StarlarkFileContext.() -> Unit):

    StarlarkFile { val context = StarlarkFileContext() context.body() val nodes = context.nodes return StarlarkFile(path, nodes) } Lambdas with receiver 31
  19. // Implementation object BUILD inline fun BUILD.bazel(body: StarlarkFileContext.() -> Unit):

    StarlarkFile { val context = StarlarkFileContext() context.body() val nodes = context.nodes return StarlarkFile(path, nodes) } Lambdas with receiver 32
  20. // Implementation object BUILD inline fun BUILD.bazel(body: StarlarkFileContext.() -> Unit):

    StarlarkFile { val context = StarlarkFileContext() context.body() val nodes = context.nodes return StarlarkFile(path, nodes) } Lambdas with receiver 33
  21. // Kotlin DSL val buildFile = BUILD.bazel { this: StarlarkFileContext

    -> android_binary( name = "app", srcs = list["MainActivity.java"], manifest = "AndroidManifest.xml", ) } Lambdas with receiver 34
  22. // Kotlin DSL class StarlarkFileContext { fun android_binary(...) {...} }

    val buildFile = BUILD.bazel { this: StarlarkFileContext -> android_binary( name = "app", srcs = list["MainActivity.java"], manifest = "AndroidManifest.xml", ) } Lambdas with receiver 35
  23. // Kotlin DSL class StarlarkFileContext { val statements = mutableListOf<Node>()

    fun android_binary(...) {...} } Lambdas with receiver 36
  24. // Starlark/Python android_binary( name = "app", srcs = ["MainActivity.java"], manifest

    = "AndroidManifest.xml", ) // Kotlin DSL android_binary( name = "app", srcs = list["MainActivity.java"], manifest = "AndroidManifest.xml", ) Function calls 39
  25. // Implementation fun StarlarkFileContext.android_binary( val name: String, val srcs: List<String?>?

    = null, val manifest: String? = null, ) { ... } Function calls 40
  26. // Implementation fun StarlarkFileContext.android_binary( val name: String, val srcs: List<String?>?

    = null, val manifest: String? = null, ) { val args = listOf( Argument("name", StringLiteral(name)), Argument("srcs", ListExpression(srcs)), Argument("manifest", StringLiteral(manifest)) ) statements += FunctionCall("android_binary", args) } Function calls 41
  27. // Implementation object _ListExpressionBuilder val list get() = _ListExpressionBuilder operator

    fun <T> _ListExpressionBuilder.get(vararg args: T): List<T> { return ListExpression(listOf(*args)) } Indexed access operator 46
  28. // Implementation object _ListExpressionBuilder val list get() = _ListExpressionBuilder operator

    fun <T> _ListExpressionBuilder.get(vararg args: T): List<T> { return ListExpression(listOf(*args)) } // Kotlin DSL list["MainActivity.java"] Indexed access operator 47
  29. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} Property delegation 49
  30. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} // Kotlin DSL val MY_STRING by "value" val MY_LIST by list[1, 2, 3] val MY_DICT by {"key" to "value"} Property delegation 50
  31. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} // Kotlin DSL val MY_STRING by "value" val MY_LIST by list[1, 2, 3] val MY_DICT by {"key" to "value"} Property delegation 51
  32. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} // Kotlin DSL val MY_STRING by "value" val MY_LIST by list[1, 2, 3] val MY_DICT by {"key" to "value"} Property delegation 52
  33. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} // Kotlin DSL val MY_STRING by "value" val MY_STRING = "value" val MY_LIST by list[1, 2, 3] val MY_DICT by {"key" to "value"} Property delegation 53
  34. // Starlark/Python MY_STRING = "value" MY_LIST = [1, 2, 3]

    MY_DICT = {"key": "value"} // Kotlin DSL val MY_STRING: StringReference by "value" val MY_LIST: ListReference by list[1, 2, 3] val MY_DICT: DictionaryReference by {"key" to "value"} Property delegation 54
  35. // Implementation class ListReference<out T>(val name: String) : List<T>, Node

    class DictionaryReference<K, V: Any>(val name: String) : Map<K, V>, Node Property delegation 56
  36. // Implementation class ListReference<out T>(val name: String) : List<T>, Node

    class DictionaryReference<K, V: Any>(val name: String) : Map<K, V>, Node class StringReference(val name: String) : String, Node Property delegation 57
  37. // Implementation class ListReference<out T>(val name: String) : List<T>, Node

    class DictionaryReference<K, V: Any>(val name: String) : Map<K, V>, Node class StringReference(val name: String) : CharSequence, Node Property delegation 58
  38. // Kotlin DSL val NAME by "app" val SRCS by

    list["MainActivity.java"] val MANIFEST_VALUES by dict {"minSdkVersion" to "21"} android_binary( name = NAME, // String srcs = SRCS, // List manifest_values = MANIFEST_VALUES // Dictionary ) Property delegation 59
  39. // Implementation fun StarlarkFileContext.android_binary( val name: CharSequence, val srcs: List<CharSequence?>?

    = null, val manifest_values: Map<CharSequence?, CharSequence?>? = null, ) {...} Property delegation 60
  40. Dynamic function args 62 // Starlark/Python android_binary( name = "app",

    srcs = ["MainActivity.java"], some_new_argument = {"key": "value"} )
  41. Dynamic function args 63 // Starlark android_binary( name = "app",

    srcs = ["MainActivity.java"], some_new_argument = {"key": "value"} ) // Kotlin DSL android_binary { name = "app" srcs = list["MainActivity.java"] "some_new_argument" `=` {"key" to "value"} }
  42. Dynamic function args 64 // Starlark android_binary( name = "app",

    srcs = ["MainActivity.java"], some_new_argument = {"key": "value"} ) // Kotlin DSL android_binary { name = "app" srcs = list["MainActivity.java"] "some_new_argument" `=` {"key" to "value"} }
  43. Dynamic function args 65 // Starlark android_binary( name = "app",

    srcs = ["MainActivity.java"], some_new_argument = {"key": "value"} ) // Kotlin DSL android_binary { name = "app" srcs = list["MainActivity.java"] "some_new_argument" `=` {"key" to "value"} }
  44. Dynamic function args 66 // Starlark android_binary( name = "app",

    srcs = ["MainActivity.java"], some_new_argument = {"key": "value"} ) // Kotlin DSL android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] "some_new_argument" `=` {"key" to "value"} }
  45. Dynamic function args 67 // Implementation class AndroidBinaryContext { val

    args = linkedSetOf<Argument>() var name: CharSequence by args var srcs: ListType<CharSequence?>? by args var manifest: CharSequence? by args }
  46. Dynamic function args 68 // Implementation class AndroidBinaryContext { val

    args = linkedSetOf<Argument>() var name: CharSequence by args var srcs: ListType<CharSequence?>? by args var manifest: CharSequence? by args infix fun CharSequence.`=`(value: CharSequence) { args = Argument(id = this, value = Expression(value, ::StringLiteral)) } }
  47. Dynamic function args 69 // Implementation class AndroidBinaryContext { val

    args = linkedSetOf<Argument>() var name: CharSequence by args var srcs: ListType<CharSequence?>? by args var manifest: CharSequence? by args infix fun CharSequence.`=`(value: CharSequence) { args = Argument(id = this, value = Expression(value, ::StringLiteral)) } }
  48. Dynamic function calls 71 // Starlark/Python some_new_function( name = "app",

    ) // Kotlin DSL "some_new_function" { "name" `=` "app" }
  49. Dynamic function calls 72 // Implementation operator fun String.invoke(body: FunctionCallContext.()

    -> Unit) { val args = FunctionCallContext().apply(body).args statements += VoidFunctionCall(name, args) }
  50. Dynamic function calls 73 // Implementation operator fun String.invoke(body: FunctionCallContext.()

    -> Unit) { val args = FunctionCallContext().apply(body).args statements += VoidFunctionCall(name, args) }
  51. Dynamic function calls 74 // Implementation operator fun String.invoke(body: FunctionCallContext.()

    -> Unit) { val args = FunctionCallContext().apply(body).args statements += VoidFunctionCall(name, args) }
  52. Concatenations 77 // Starlark/Python SOURCE_FILES = ["MainViewModel.java"] android_binary( name =

    "app", srcs = SOURCE_FILES + ["MainActivity.java"], ) // Kotlin DSL val SOURCE_FILES by list["MainViewModel.java"] android_binary( name = "app", srcs = SOURCE_FILES `+` list["MainActivity.java"], )
  53. Concatenations 78 // Starlark/Python SOURCE_FILES = ["MainViewModel.java"] android_binary( name =

    "app", srcs = SOURCE_FILES + ["MainActivity.java"], ) // Kotlin DSL val SOURCE_FILES by list["MainViewModel.java"] android_binary( name = "app", srcs = SOURCE_FILES `+` list["MainActivity.java"], srcs = SOURCE_FILES + list["MainActivity.java"], )
  54. Concatenations 79 // Implementation infix fun <T> List<T>?.`+`(other: List<T>?): List<T>

    = ListBinaryOperation( left = Expression(this, ::ListExpression), operator = BinaryOperator.PLUS, right = Expression(other, ::ListExpression) )
  55. Concatenations 80 // Implementation infix fun <T> List<T>?.`+`(other: List<T>?): List<T>

    = ListBinaryOperation( left = Expression(this, ::ListExpression), operator = BinaryOperator.PLUS, right = Expression(other, ::ListExpression) ) class ListBinaryOperation<T>( val left: Node, val operator: BinaryOperator, val right: Node ) : Node, List<T>
  56. Concatenations 81 // Implementation infix fun <T> List<T>?.`+`(other: List<T>?): List<T>

    = ListBinaryOperation( left = Expression(this, ::ListExpression), operator = BinaryOperator.PLUS, right = Expression(other, ::ListExpression) ) class ListBinaryOperation<T>( val left: Node, val operator: BinaryOperator, val right: Node ) : Node, List<T>
  57. Context conflicts 83 // Kotlin DSL BUILD.bazel { this: StarlarkFileContext

    -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] } }
  58. Context conflicts 84 // Kotlin DSL BUILD.bazel { this: StarlarkFileContext

    -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] android_binary {...} } }
  59. Context conflicts 86 // Kotlin DSL @DslMarker annotation class LanguageScope

    @LanguageScope class StarlarkFileContext @LanguageScope class AndroidBinaryContext
  60. Context conflicts 87 // Kotlin DSL BUILD.bazel { this: StarlarkFileContext

    -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] android_binary {...} } }
  61. Context conflicts 88 // Kotlin DSL BUILD.bazel { this: StarlarkFileContext

    -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] `+` list["MainViewModel.java"] } }
  62. Context conflicts 89 // Kotlin DSL BUILD.bazel { this: StarlarkFileContext

    -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] `+` list["MainViewModel.java"] } }
  63. Restructuring the DSL 92 // Kotlin DSL sealed interface ConcatenationsFeature

    { // implement `+` operators } @LanguageScope class StarlarkFileContext: ConcatenationsFeature {...} @LanguageScope class AndroidBinaryContext: ConcatenationsFeature {...}
  64. Restructuring the DSL 93 // Kotlin DSL BUILD.bazel { this:

    StarlarkFileContext -> android_binary { this: AndroidBinaryContext -> name = "app" srcs = list["MainActivity.java"] `+` list["MainViewModel.java"] } }
  65. Restructuring the DSL 94 // Kotlin DSL BUILD.bazel { this:

    StarlarkFileContext -> android_binary { this: AndroidBinaryContext -> "manifest_values" `=` { this: DictionaryContext -> "key_" `+` "1" to "value_" `+` "1" } } }
  66. Restructuring the DSL 95 // Kotlin DSL BUILD.bazel { android_binary

    { "manifest_values" `=` { this: DictionaryContext -> "key_" `+` "1" to "value_" `+` "1" } } }
  67. List comprehensions 98 // Starlark/Python SRCS = [file + ".java"

    for file in ["MainActivity"]] // Kotlin DSL val SRCS by "file" `in` list["MainActivity"] take { it `+` ".java" }
  68. List comprehensions 99 // Starlark/Python SRCS = [file + ".java"

    for file in ["MainActivity"]] // Kotlin DSL val SRCS by "file" `in` list["MainActivity"] take { it `+` ".java" }
  69. List comprehensions 100 // Starlark/Python SRCS = [file + ".java"

    for file in ["MainActivity"]] // Kotlin DSL val SRCS by "file" `in` list["MainActivity"] take { it `+` ".java" }
  70. List comprehensions 101 // Starlark/Python SRCS = [file + ".java"

    for file in ["MainActivity"]] // Kotlin DSL val SRCS by "file" `in` list["MainActivity"] take { it `+` ".java" }
  71. List comprehensions 102 // Starlark/Python SRCS = [file + ".java"

    for file in ["MainActivity"]] // Kotlin DSL val SRCS by "file" `in` list["MainActivity"] take { it: CharSequence -> it `+` ".java" }
  72. class AndroidBinaryContext : FunctionCallContext() { var name: Name by fargs

    var custom_package: StringType? by fargs var manifest: Label? by fargs var manifest_values: DictionaryType<Key?, Value?>? by fargs var debug_key: Label? by fargs var enable_data_binding: BooleanType? by fargs var multidex: StringType? by fargs var incremental_dexing: NumberType? by fargs var crunch_png: BooleanType? by fargs var dex_shards: NumberType? by fargs var resource_files: ListType<Label?>? by fargs var srcs: ListType<Label?>? by fargs var plugins: ListType<Label?>? by fargs var deps: ListType<Label?>? by fargs var visibility: ListType<Label?>? by fargs var args: ListType<StringType?>? by fargs var env: DictionaryType<Key?, Value?>? by fargs var output_licenses: ListType<StringType?>? by fargs } fun BuildStatementsLibrary.`android_binary`(body: AndroidBinaryContext.() -> Unit): Unit = registerFunctionCallStatement("android_binary", AndroidBinaryContext(), body) Applying KSP 105
  73. class AndroidBinaryContext : FunctionCallContext() { var name: Name by fargs

    var custom_package: StringType? by fargs var manifest: Label? by fargs var manifest_values: DictionaryType<Key?, Value?>? by fargs var debug_key: Label? by fargs var enable_data_binding: BooleanType? by fargs var multidex: StringType? by fargs var incremental_dexing: NumberType? by fargs var crunch_png: BooleanType? by fargs var dex_shards: NumberType? by fargs var resource_files: ListType<Label?>? by fargs var srcs: ListType<Label?>? by fargs var plugins: ListType<Label?>? by fargs var deps: ListType<Label?>? by fargs var visibility: ListType<Label?>? by fargs var args: ListType<StringType?>? by fargs var env: DictionaryType<Key?, Value?>? by fargs var output_licenses: ListType<StringType?>? by fargs } fun BuildStatementsLibrary.`android_binary`(body: AndroidBinaryContext.() -> Unit): Unit = registerFunctionCallStatement("android_binary", AndroidBinaryContext(), body) Applying KSP 106 fun BuildStatementsLibrary.`android_binary`( name: Name, custom_package: StringType? = UnspecifiedString, manifest: Label? = UnspecifiedString, manifest_values: DictionaryType<Key?, Value?>? = UnspecifiedDictionary, debug_key: Label? = UnspecifiedString, enable_data_binding: BooleanType? = UnspecifiedBoolean, multidex: StringType? = UnspecifiedString, incremental_dexing: NumberType? = UnspecifiedNumber, crunch_png: BooleanType? = UnspecifiedBoolean, dex_shards: NumberType? = UnspecifiedNumber, resource_files: ListType<Label?>? = UnspecifiedList, srcs: ListType<Label?>? = UnspecifiedList, plugins: ListType<Label?>? = UnspecifiedList, deps: ListType<Label?>? = UnspecifiedList, visibility: ListType<Label?>? = UnspecifiedList, args: ListType<StringType?>? = UnspecifiedList, env: DictionaryType<Key?, Value?>? = UnspecifiedDictionary, output_licenses: ListType<StringType?>? = UnspecifiedList ): Unit { val _args = linkedSetOf<Argument>().also { it += Argument("name", Expression(name, ::StringLiteral)) if (custom_package !== UnspecifiedString) it += Argument("custom_package", Expression(custom_package, ::StringLiteral)) if (manifest !== UnspecifiedString) it += Argument("manifest", Expression(manifest, ::StringLiteral)) if (manifest_values !== UnspecifiedDictionary) it += Argument("manifest_values", Expression(manifest_values, ::DictionaryExpression)) if (debug_key !== UnspecifiedString) it += Argument("debug_key", Expression(debug_key, ::StringLiteral)) if (enable_data_binding !== UnspecifiedBoolean) it += Argument("enable_data_binding", Expression(enable_data_binding, ::BooleanLiteral)) if (multidex !== UnspecifiedString) it += Argument("multidex", Expression(multidex, ::StringLiteral)) if (incremental_dexing !== UnspecifiedNumber) it += Argument("incremental_dexing", Expression(incremental_dexing, ::NumberLiteral)) if (crunch_png !== UnspecifiedBoolean) it += Argument("crunch_png", Expression(crunch_png, ::BooleanLiteral)) if (dex_shards !== UnspecifiedNumber) it += Argument("dex_shards", Expression(dex_shards, ::NumberLiteral)) if (resource_files !== UnspecifiedList) it += Argument("resource_files", Expression(resource_files, ::ListExpression)) if (srcs !== UnspecifiedList) it += Argument("srcs", Expression(srcs, ::ListExpression)) if (plugins !== UnspecifiedList) it += Argument("plugins", Expression(plugins, ::ListExpression)) if (deps !== UnspecifiedList) it += Argument("deps", Expression(deps, ::ListExpression)) if (visibility !== UnspecifiedList) it += Argument("visibility", Expression(visibility, ::ListExpression)) if (args !== UnspecifiedList) it += Argument("args", Expression(args, ::ListExpression)) if (env !== UnspecifiedDictionary) it += Argument("env", Expression(env, ::DictionaryExpression)) if (output_licenses !== UnspecifiedList) it += Argument("output_licenses", Expression(output_licenses, ::ListExpression)) } return registerFunctionCallStatement("android_binary", _args) }
  74. // Kotlin DSL @LibraryFunction(name = "android_binary") private interface AndroidBinary {

    @Argument(required = true) val name: Name val manifest: CharSequence? val srcs: List<CharSequence?>? val deps: List<CharSequence?>? ... } Applying KSP 107
  75. // Kotlin DSL @LibraryFunction(name = "android_binary") private interface AndroidBinary {

    @Argument(required = true) val name: Name val manifest: CharSequence? val srcs: List<CharSequence?>? val deps: List<CharSequence?>? ... } Applying KSP 108
  76. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 110
  77. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 111
  78. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 112
  79. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 113
  80. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 114
  81. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 115
  82. // Starlark/Python VIEW_MODELS_WITH_RES_IMPORTS = ["MyViewModel.kt", ...] [ genrule( name =

    "modify_imports_in_" + file[0:-3], srcs = [file], outs = [file[0:-3] + "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import org.morfly.airin.sample.app.R/import org.morfly.airin.sample.app.viewmodels.R/g' > $(OUTS) """, ) for file in VIEW_MODELS_WITH_RES_IMPORTS ] Usage example 116
  83. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 117
  84. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 118
  85. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 119
  86. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 120
  87. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 121
  88. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 122
  89. val VIEW_MODELS_WITH_RES_IMPORTS by list["MyViewModel.kt", ...] "file" `in` VIEW_MODELS_WITH_RES_IMPORTS take {

    file -> genrule( name = "modify_imports_in_" `+` file[0..-3], srcs = list[file], outs = list[file[0..-3] `+` "_synthetic.kt"], cmd = """ cat $(SRCS) | sed 's/import $packageName.R/import $packageName.viewmodels.R/g' > $(OUTS) """.trimIndent() ) } Usage example 123