{"id":16465045,"url":"https://github.com/morfly/pendant","last_synced_at":"2026-03-05T09:04:12.623Z","repository":{"id":206770483,"uuid":"699622261","full_name":"Morfly/pendant","owner":"Morfly","description":"Declarative Starlark code generator written in Kotlin","archived":false,"fork":false,"pushed_at":"2024-05-08T20:56:48.000Z","size":533,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-03-03T13:27:01.952Z","etag":null,"topics":["bazel","code-generation","kotlin","starlark"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Morfly.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-03T02:05:32.000Z","updated_at":"2024-05-08T20:56:52.000Z","dependencies_parsed_at":"2024-10-11T11:42:04.090Z","dependency_job_id":null,"html_url":"https://github.com/Morfly/pendant","commit_stats":null,"previous_names":["morfly/pendant"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/Morfly/pendant","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morfly%2Fpendant","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morfly%2Fpendant/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morfly%2Fpendant/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morfly%2Fpendant/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Morfly","download_url":"https://codeload.github.com/Morfly/pendant/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Morfly%2Fpendant/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30117498,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-05T08:19:04.902Z","status":"ssl_error","status_checked_at":"2026-03-05T08:17:37.148Z","response_time":93,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bazel","code-generation","kotlin","starlark"],"created_at":"2024-10-11T11:31:49.857Z","updated_at":"2026-03-05T09:04:12.582Z","avatar_url":"https://github.com/Morfly.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Pendant 💠\n\nPendant — is a declarative Starlark code generator written in Kotlin.\nUse Kotlin DSL that looks and feels like Starlark syntax, for generating Bazel scripts in an intuitive and type-safe fashion.\n\n- [Materials](#materials)\n- [Installation](#installation)\n- [Overview](#overview)\n- [Generate Starlark files](#generate-starlark-files)\n- [Starlark syntax elements](#starlark-syntax-elements)\n- [Modifiers](#modifiers)\n- [Generate Kotlin DSL for custom Starlark functions](#generate-kotlin-dsl-for-custom-starlark-functions)\n\n## Materials\n\n- Learn more about how Pendant is built internally\n  at [droidcon New York 2022](https://www.droidcon.com/2022/09/29/advanced-techniques-for-building-kotlin-dsls/)\n  and [Android Worldwide](https://youtu.be/p03NJxUCID0?si=6WMSoyn-G38GavqB).\n- Pendant is a key component of [Airin](https://github.com/Morfly/airin), an open-source tool for automated migration\n  from Gradle to Bazel build systems.\n\n## Installation\n\n### Gradle\n\nAdd the following dependencies to your Gradle module to start using Pendant.\n\n```kotlin\ndependencies {\n    // Starlark code generator.\n    implementation(\"io.morfly.pendant:pendant-starlark:x.y.z\")\n    // Kotlin DSL extension with Bazel functions.\n    implementation(\"io.morfly.pendant:pendant-library-bazel:x.y.z\")\n\n    // Optional. Generator for custom Starlark functions.\n    ksp(\"io.morfly.pendant:pendant-library-compiler:x.y.z\")\n}\n```\n\n[![Maven Central](https://maven-badges.herokuapp.com/maven-central/io.morfly.pendant/pendant-starlark/badge.svg)](https://maven-badges.herokuapp.com/maven-central/io.morfly.pendant/pendant-starlark)\n\n## Overview\nTo generate Starlark files Pendant provides a Kotlin DSL that replicates Starlark syntax as close as possible.\nHere is how you can generate a `BUILD.bazel` file with Pendant.\n\n```kotlin\n// Kotlin\nval builder = BUILD.bazel {\n    load(\"@io_bazel_rules_kotlin//kotlin:android.bzl\", \"kt_android_library\")\n\n    kt_android_library(\n        name = \"my-library\",\n        srcs = glob(\"src/main/kotlin/**/*.kt\"),\n        custom_package = \"io.morfly.mylibrary\",\n        manifest = \"src/main/AndroidManifest.xml\",\n        resource_files = glob([\"src/main/res/**\"]),\n    )\n}\nval file = builder.build()\nfile.write(\"path/in/file/system\")\n```\n\nAs a result, a `BUILD` file with the following content is generated. Pendant takes care of the code formatting, so you\ndon't have to do it yourself.\n\n```python\n# Generated Starlark\nload(\"@io_bazel_rules_kotlin//kotlin:android.bzl\", \"kt_android_library\")\n\nkt_android_library(\n    name = \"my-library\",\n    srcs = glob(\"src/main/kotlin/**/*.kt\"),\n    custom_package = \"io.morfly.mylibrary\",\n    manifest = \"src/main/AndroidManifest.xml\",\n    resource_files = glob([\"src/main/res/**\"]),\n)\n```\n\n## Generate Starlark files\n\nPendant provides an API for generating different types of Starlark files. Once entered the file context, you can use the\nKotlin DSL to generate corresponding Starlark statements.\nDepending on the file type, a different set of Starlark syntax features and functions is available.\n\n### Building Starlark files\n\nTo generate `BUILD.bazel` files, the following expression must be used.\n\n```kotlin\nval builder = BUILD.bazel { ... }\n```\n\nOr a shorter form to produce `BUILD` files.\n\n```kotlin\nval builder = BUILD { ... }\n```\n\nSimilarly, Pendant provides an API for generating `WORKSPACE.bazel` files.\n\n```kotlin\nval builder = WORKSPACE.bazel { ... }\n```\n\nOr a shorter form to produce `WORKSPACE` files.\n\n```kotlin\nval builder = WORKSPACE { ... }\n```\n\nAdditionally, it is possible to generate files with `.bzl` extension.\n\n```kotlin\nval builder = \"starlark_file\".bzl { ... }\n```\n\n### Write files to file system\n\nEach function demonstrated above returns a `FileContext` instance that serves as a file builder. In order to write it to\nfile it must be first built using `build()` function.\n\n```kotlin\nval file = builder.build()\n```\n\nFinally, it could be written to file using the API below.\n\n```kotlin\nStarlarkFileWriter.write(\"path/in/file/system\", file)\n// or\nfile.write(\"path/in/file/system\")\n```\n\n### Get file contents as string\n\nAlternatively, the formatted contents of a generated file could be returned as a string.\n\n```kotlin\nval starlarkCode: String = StarlarkFileFormatter.format(file)\n// or\nval starlarkCode: String = file.format()\n```\n\n## Starlark syntax elements\n\nNow, the most important part. In this section we will take a closer look at Kotlin DSL components that represent\ncorresponding Starlark syntax elements, for code generation.\n\n### Variable assignments\n\nVariable declaration and assignment is an essential feature of Starlark language. Use `by` operator to do it.\n\u003e You might ask, why aren't we using `=` operator in this case?\n\u003e The latter operator will perform a variable assignment operation while compiling the Kotlin code. However, what we\n\u003e need is generating the statement in Starlark rather than executing it in Kotlin.\n\n```kotlin\n// Kotlin\nval NAME by \"app\"\n```\n\n```python\n# Generated Starlark\nNAME = \"app\"\n```\n\nWhat's important is that this operation is type safe, meaning `NAME` is a variable of string type on the Kotlin DSL\nlevel and could be used further accordingly.\n\n#### Dynamic variable assignments\nIn case the name of the variable is set dynamically, during runtime, but still needs to be referenced in further code, the following syntax is used.\n\n```kotlin\n// Kotlin\nval VARIABLE by \"VARIABLE_$i\" `=` \"value\"\n```\n\n```python\n# Generated Starlark\nVARIABLE_1 `=` \"value\"\n```\n\n### List expressions\n\nOne of the syntax elements of Starlark are list expressions. There is no equivalent for such an expression in Kotlin.\nHowever, the `list[]` function could be used to achieve the same result.\n\n```kotlin\n// Kotlin\nval SRCS by list[\"io/morfly/Main.kt\"]\n```\n\n```python\n# Generated Starlark\nSRCS = [\"io/morfly/Main.kt\"]\n```\n\nRegular `listOf` function from Kotlin standard library also works perfectly well. However, having `list[]` function is\nconvenient when you're copy-pasting actual Starlark code in your Kotlin file. This way you would need to do less editing\nto make it compilable in your code generator program.\n\n### Dictionary expressions\n\nSimilarly to list expressions, Kotlin does not have dictionary expressions in its syntax. However, the `dict` function\ncould be used to achieve the same result.\n\n```kotlin\n// Kotlin\nval MANIFEST_VALUES by dict { \"minSdkVersion\" to \"23\" }\n```\n\n```python\n# Generated Starlark\nMANIFEST_VALUES = { \"minSdkVersion\" : \"23\" }\n```\n\nYou might notice that `to` operator is used to map dictionary values. However, this is not a usual `to` function from\nKotlin standard library but a more powerful version.\n\nFor example, you could use composite keys and values with concatenation operation.\n\n```kotlin\n// Kotlin\nval MANIFEST_VALUES by dict { \"minSdk\" `+` \"Version\" to \"2\" `+` \"3\" }\n```\n\n```python\n# Generated Starlark\nMANIFEST_VALUES = { \"minSdk\" + \"Version\" : \"2\" + \"3\" }\n```\n\n#### Short form\n\nIn addition, in some cases it's possible to use a shorted form of Kotlin DSL for dictionary expressions. If followed\nby `` `=` `` or `` `+` `` operators (with backticks) `dict` keyword could be omitted.\n\n```kotlin\n// Kotlin\nval MANIFEST_VALUES by dict { } `+` { \"minSdkVersion\" to \"23\" }\n\nandroid_binary {\n    \"manifest_values\" `=` { \"minSdkVersion\" to \"23\" }\n}\n```\n\n```python\n# Generated Starlark\nMANIFEST_VALUES = {} + { \"minSdkVersion\": \"23\" }\n\nandroid_binary(\n    name = \"app\",\n    manifest_values = { \"minSdkVersion\": \"23\" },\n)\n```\n\n### Concatenations\n\nUsing `` `+` `` function with backticks you could generate concatenation expressions.\n\n\u003e You might ask, why aren't we using a regular `+` operator in this case?\n\u003e The latter operator will perform a variable assignment operation while compiling the Kotlin code. However, what we\n\u003e need is generating the statement in Starlark rather than executing it in Kotlin.\n\n```kotlin\n// Kotlin\nval ARTIFACTS by list[\"@maven//:androidx_compose_runtime_runtime\"]\n\nval DEPS by ARTIFACTS `+` list[\"//my-library\"]\n```\n\n```python\n# Generated Starlark\nARTIFACTS = [\"@maven//:androidx_compose_runtime_runtime\"]\n\nDEPS = ARTIFACTS + [\"//my-library\"]\n```\n\nAs you can see, you could use concatenations with varouus types of expressions, like list expressions, variable\nreferences, list comprehensions, etc.\nThis operation is type-safe in all these cases.\n\n### Function calls\n\nThere are different ways to generate a function call with Pendant.\n\nThe easiest way is to use Starlark or Bazel functions available directly in Pendant.\n\n```kotlin\n// Kotlin\nandroid_binary(\n    name = \"app\",\n    srcs = glob(\"src/main/kotlin/**/*.kt\"),\n    deps = ARTIFACTS `+` [\"//my-library\"],\n    manifest = \"src/main/AndroidManifest.xml\"\n)\n```\n\nEach function in the library is available in 2 variations: with round brackets `()`, and with curly brackets `{}`.\nThe latter is especially useful if you need more customization.\n\nFor example, you could use custom parameters which are not part of Pendant library.\n\n```kotlin\n// Kotlin\nandroid_binary {\n    name = \"app\"\n    \"manifest_values\" `=` { \"minSdkVersion\" to \"23\" }\n}\n```\n\nMoreover, you could declare calls of functions which are completely absent in Pendant library like shown below.\n\n```kotlin\n// Kotlin\n\"android_binary\" {\n    \"name\" `=` \"app\"\n    \"manifest_values\" `=` { \"minSdkVersion\" to \"23\" }\n}\n```\n\nOne more bonus of generating function calls using curly brackets `{}` is that it preserves the order of passed arguments\nthe way you specify them.\n\nAlternatively, dynamic function calls could be used for functions with no arguments.\n\n```kotlin\n\"glob\"()\n```\n\n#### Function call expressions\n\nSo far we've seen how to generate function calls as standalone statements, meaning they don't return any value.\n\nAdditionally, Pendant allows generating functions as expressions that return values.\n\n```kotlin\n// Kotlin\nval SRCS by glob(\"src/main/kotlin/**/*.kt\")\n```\n\n```python\n# Generated Starlark\nSRCS = glob([\"src/main/kotlin/**/*.kt\"])\n```\n\nYou could use dynamic API for these types of functions as well. Make sure to explicitly specify the return type.\n\n```kotlin\n// Kotlin\nimport io.morfly.pendant.starlark.lang.feature.invoke\n\nval SRCS by \"glob\"\u003cListType\u003cStringType\u003e\u003e(\"src/main/kotlin/**/*.kt\")\n```\n\n```python\n# Generated Starlark\nSRCS = glob([\"src/main/kotlin/**/*.kt\"])\n```\n\n\u003e You might need to manually import the `io.morfly.pendant.starlark.lang.feature.invoke`function from Pendant for\n\u003e dynamic function calls with return values.\n\nIf you need to generate a function call with named arguments, use curly brackets `{}`.\n\n```kotlin\n// Kotlin\nimport io.morfly.pendant.starlark.lang.feature.invoke\n\nval SRCS by \"glob\"\u003cListType\u003cStringType\u003e\u003e {\n    \"include\" `=` list[\"src/main/kotlin/**/*.kt\"]\n}\n```\n\n```python\n# Generated Starlark\nSRCS = glob(include = [\"src/main/kotlin/**/*.kt\"])\n```\n\n\u003e Dynamic function calls that return values rely on context receivers, a feature introduced in recent versions of\n\u003e Kotlin. If you need to use it as part of Gradle plugin or scripts, it might not be supported, as Gradle uses older\n\u003e Kotlin versions.\n\n#### Type-safe API for custom functions\n\nPendant also allows you to generate a Kotlin DSL for custom Starlark functions. Learn how\nto [generate Kotlin DSL for custom Starlark functions](#generate-kotlin-dsl-for-custom-starlark-functions) in a\ncorresponding section.\n\n### List comprehensions\n\nAnother powerful Starlark feature for building lists is list comprehensions. Use combination of `` `in` `` and `take`\noperators to generate them with Pendant.\n\n```kotlin\n// Kotlin\nval CLASSES by list[\"MainActivity\", \"MainViewModel\"]\n\nval SRCS by \"name\" `in` CLASSES take { name -\u003e name `+` \".kt\" }\n```\n\n```python\n# Generated Starlark\nCLASSES = [\"MainActivity\", \"MainViewModel\"]\n\nSRCS = [name + \".kt\" for name in classes]\n```\n\n#### Nested comprehensions\n\nAdditionally, you could use use `` `for `` operator for nested comprehensions.\n\n```kotlin\n// Kotlin\nval MATRIX by list[\n    list[1, 2],\n    list[3, 4]\n]\n\nval NUMBERS by \"list\" `in` MATRIX `for` { list -\u003e\n    \"number\" `in` list take { number -\u003e number }\n}\n```\n\n```python\n# Generated Starlark\nMATRIX = [\n    [1, 2],\n    [3, 4]\n]\n\nNUMBERS = [number for list in MATRIX for number in list]\n```\n\nAlternatively, Pendant supports another variation of nested comprehensions as shown below.\n\n```kotlin\nimport io.morfly.pendant.starlark.lang.feature.invoke\n\nval RANGE by \"range\"\u003cListType\u003cStringType\u003e\u003e(5)\nval SRCS by \"i\" `in` RANGE take { \"j\" `in` RANGE take { j -\u003e j } }\n```\n\n```python\n# Generated Starlark\nRANGE = range(5)\n\nSRCS = [\n    [j for j in RANGE]\n    for i in RANGE\n]\n```\n\n### Slices\n\nTo generate Starlark slices use the following syntax.\n\n```kotlin\n// Kotlin\n\"abc.kt\"[0..-3]\n```\n\n```python\n# Generated Starlark\n\"abc.kt\"[0:-3]\n```\n\n### Load statements\n\n#### Loading rules/functions\n\n```kotlin\n// Kotlin\nload(\"@rules_java//java:defs.bzl\", \"java_binary\")\n```\n\n```python\n# Generated Starlark\nload(\"@rules_java//java:defs.bzl\", \"java_binary\")\n```\n\n#### Loading values\n\nIf you need to reference the values impored with `load` you could use `of` function and specify the types of the\ncorresponding values.\n\n```kotlin\n// Kotlin\nval (DAGGER_ARTIFACTS, DAGGER_REPOSITORIES) = load(\n    \"@dagger//:workspace_defs.bzl\",\n    \"DAGGER_ARTIFACTS\", \"DAGGER_REPOSITORIES\"\n).of\u003cListType\u003cStringType\u003e, ListType\u003cStringType\u003e\u003e()\n\nmaven_install(\n    artifacts = DAGGER_ARTIFACTS,\n    repositories = DAGGER_REPOSITORIES\n)\n```\n\n```python\n# Generated Starlark\nload(\"@dagger//:workspace_defs.bzl\", \"DAGGER_ARTIFACTS\", \"DAGGER_REPOSITORIES\")\n\nmaven_install(\n    artifacts = DAGGER_ARTIFACTS,\n    repositories = DAGGER_REPOSITORIES\n)\n```\n\nAlternatively, you could manually instantiate the reference with the needed type and name.\n\n```kotlin\n// Kotlin\nload(\"@dagger//:workspace_defs.bzl\", \"DAGGER_ARTIFACTS\", \"DAGGER_REPOSITORIES\")\n\nmaven_install(\n    artifacts = ListReference\u003cStringType\u003e(\"DAGGER_ARTIFACTS\"),\n    repositories = ListReference\u003cStringType\u003e(\"DAGGER_REPOSITORIES\")\n)\n```\n\nYou can\nuse `StringReference`, `NumberReference`, `BooleanReference`, `ListReference`, `DicrionaryReference`, `TupleReference`\nor `AnyReference` to refer to imported values.\n\n### Raw code injection\n\nIf you need more freedom with code generation or formatting you could always inject raw strings as part of the generated\ncode.\n\nTo do this use `+` unary plus operator or `raw()` extension function on a string that represents a code.\n\n```kotlin\n// Kotlin\n+\"\"\"\nSRCS = glob([\"src/main/kotlin/**/*.kt\"])\n\"\"\".trimIndent()\n```\n\n```kotlin\n// Kotlin\n\"\"\"\nSRCS = glob([\"src/main/kotlin/**/*.kt\"])\n\"\"\".trimIndent().raw()\n```\n\n```python\n# Generated Starlark\nSRCS = glob([\"src/main/kotlin/**/*.kt\"])\n```\n\n## Modifiers\n\nModifiers is a flexible mechanism that allows you to externally modify a file builder outside of its body.\n\nEach Kotlin DSL element with a body surrounded by curly brackets `{}` could be modified.\nTo do so, you need to assign it a unique `_id`.\n\n```kotlin\n// Kotlin\nval builder = BUILD.bazel {\n    _id = \"build_file\"\n\n    android_library {\n        _id = \"android_library_target\"\n\n        name = \"my-library\"\n        deps = list[\"//another-library\"]\n    }\n}\n```\n\nThen, use `onContext` API to inject additional code in the corresponding DSL context.\n\n```kotlin\n// Kotlin\nbuilder.onContext\u003cBuildContext\u003e(id = \"build_file\") {\n    android_binary(\n        name = \"app\",\n        deps = list[\":my-library\"]\n    )\n}\n```\n\n```kotlin\n// Kotlin\nbuilder.onContext\u003cAndroidLibraryContext\u003e(id = \"android_library_target\") {\n    visibility = list[\"//visibility:public\"]\n    deps = list[\"@maven//:androidx_compose_runtime_runtime\"]\n}\n```\n\n```python\n# Generated Starlark\nandroid_library(\n    name = \"my-library\"\n    deps = [\"//another-library\"] + [\"@maven//:androidx_compose_runtime_runtime\"],\n)\n```\n\n### Checkpoints\n\nThe code added by modifiers is always added at the end of the context block. However, with checkpoints you can precisely\ncontrol where the code from modifiers is injected.\n\n```kotlin\n// Kotlin\nval builder = BUILD.bazel {\n    _id = \"build_file\"\n\n    val DEPS by list[\"@maven//:androidx_compose_runtime_runtime\"]\n\n    _checkpoint(\"middle\")\n\n    android_library(\n        name = \"my-library\",\n        deps = DEPS\n    )\n}\n```\n\n```kotlin\n// Kotlin\nbuilder.onContext\u003cBuildContext\u003e(id = \"build_file\", checkpoint = \"middle\") {\n    android_binary(\n        name = \"app\",\n        deps = list[\"my-library\"]\n    )\n}\n```\n\n```python\n# Generated Starlark\nDEPS = [\"@maven//:androidx_compose_runtime_runtime\"]\n\nandroid_binary(\n    name = \"app\",\n    deps = [\"my-library\"],\n)\n\nandroid_library(\n    name = \"my-library\",\n    deps = DEPS,\n)\n```\n\n## Generate Kotlin DSL for custom Starlark functions\n\nThe Kotlin DSL for Starlark/Bazel library functions in Pendant is generated using a library generation API.\nIn fact, you can generate an additional DSL for any custom function you like.\n\nTo do that, make sure you add Pendant symbol processor to your dependencies.\n\n```kotlin\ndependencies {\n    ksp(\"io.morfly.pendant:pendant-library-compiler:x.y.z\")\n}\n```\n\nDeclare an interface, where each its field will represent a corresponding argument of a function you generate. Then,\nannotate it with the `@LibraryFunction` as shown below.\n\n```kotlin\n// Kotlin\n@LibraryFunction(\n    name = \"custom_android_binary\",\n    scope = [FunctionScope.Build],\n    kind = FunctionKind.Statement\n)\ninterface CustomAndroidBinary {\n\n    @Argument(required = true)\n    val name: Name\n    val srcs: ListType\u003cLabel?\u003e?\n    val custom_package: StringType?\n}\n```\n\n```kotlin\n// Kotlin\nval builder = BUILD {\n    custom_android_binary(\n        name = \"app\",\n        custom_package = \"io.morfly.pendant\"\n    )\n}\n```\n\nWhen using `@LibraryFunction` annotation, you need to specify the following arguments:\n\n| Param      | Description                                                                                                                                                               |\n|------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `name`     | Name of a generated function both in Kotlin DSL and in Starlark                                                                                                           |\n| `scope`    | Configure in what types of files the Kotlin DSL function could be used. Use `FunctionScope.Build`, `FunctionScope.Workspace` or `FunctionScope.Starlark` for `.bzl` files |\n| `kind`     | Configure if the Kotlin DSL function generates a Starlark function call as `FunctionKind.Statement` or as `FunctionKind.Expression`                                       |\n| `brackets` | Optional. Configure what kind of Kotlin DSL functions will be generated. Either with `BracketsKind.Round` `()`, `BracketsKind.Curly` `{}` or both                         |\n\nOptionally you could annotate an argument with `@Argument` for additional configuration.\nWhen using `@Argument` annotation, you can specify the following arguments:\n\n| Param      | Description                                                                                                          |\n|------------|----------------------------------------------------------------------------------------------------------------------|\n| `name`     | Optional. Name of the generated Starlark function call                                                               |\n| `required` | Optional. Configure if the argument mandatory in the generated Kotlin DSL                                            |\n| `variadic` | Optional. Configure an argument of `ListType` to be a vararg in Kotlin DSL. Throws compilation error for other types |\n\n#### Function call expressions\n\nTo generate a function with return values, mark it with `FunctionKind.Expression`.\nTo specify a return type declare a property annotated with `@Returns`. The return type of the generated Kotlin function\nwill be derived from the annotated property type.\n\n```kotlin\n@LibraryFunction(\n    name = \"custom_glob\",\n    scope = [FunctionScope.Build],\n    kind = FunctionKind.Expression\n)\ninterface CustomGlob {\n\n    @Argument(variadic = true)\n    val include: ListType\u003cLabel?\u003e\n\n    @Returns\n    val returns: ListType\u003cLabel\u003e\n}\n```\n\n```kotlin\n// Kotlin\nval builder = BUILD {\n    val SRCS by custom_glob(\"src/main/kotlin/**/*.kt\")\n}\n```\n\n| Param  | Description                                                                                                                       |\n|--------|-----------------------------------------------------------------------------------------------------------------------------------|\n| `kind` | Optional. Configure if the function in Kotlin DSL has an explicit return type or if it is derived dynamically with type inference |\n\n#### Dynamic return type\n\nIn some cases the return type of a Kotlin DSL function is inferred based on the call site.\nA good example is `select` function in Starlark.\n\n```kotlin\n// Kotlin\n@LibraryFunction(\n    name = \"custom_select\",\n    scope = [FunctionScope.Build],\n    kind = FunctionKind.Expression\n)\ninterface CustomSelect {\n\n    @Argument(required = true, implicit = true)\n    val select: Map\u003cKey, Value\u003e\n\n    @Returns(kind = ReturnKind.Dynamic)\n    val returns: Any\n}\n```\n\nIn the example below a `custom_select` is being used as a `deps` argument, so that its return type is inferred from the\nfunction argument type.\n\n```kotlin\n// Kotlin\nval builder = BUILD {\n    android_binary(\n        name = \"app\",\n        deps = custom_select({\n            \":arm_build\": [\":arm_lib\"],\n            \":x86_debug_build\": [\":x86_dev_lib\"],\n            \"//conditions:default\": [\":generic_lib\"],\n        }),\n    )\n}\n```\n\n```python\n# Generated Starlark\nandroid_binary(\n    name = \"app\",\n    deps = custom_select({\n        \":arm_build\": [\":arm_lib\"],\n        \":x86_debug_build\": [\":x86_dev_lib\"],\n        \"//conditions:default\": [\":generic_lib\"],\n    }),\n)\n```\n\n## License\n\n    Copyright 2023 Pavlo Stavytskyi.\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       https://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorfly%2Fpendant","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorfly%2Fpendant","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorfly%2Fpendant/lists"}