--- name: add-compiler-option description: Adds a new compiler option to Metro. --- ## Files to Update When adding a new option, you need to update these files in order: ### 1. `metro-common/src/main/kotlin/dev/zacsweers/metro/compiler/MetroOptions.kt` Add the option in five places. These shared definitions serve both the compiler and the IDE plugin: #### a. Add enum entry in `MetroOption` ```kotlin MY_NEW_OPTION( RawMetroOption.boolean( // or RawMetroOption() for non-boolean types name = "my-new-option", // kebab-case name used in CLI defaultValue = false, valueDescription = "", description = "Description of what this option does.", required = false, allowMultipleOccurrences = false, ) ), ``` For non-boolean options, use the full `RawMetroOption` constructor with a `valueMapper`: ```kotlin MY_INT_OPTION( RawMetroOption( name = "my-int-option", defaultValue = 10, valueDescription = "", description = "Description here", required = false, allowMultipleOccurrences = false, valueMapper = { it.toInt() }, ) ), ``` #### b. Add property in the `MetroOptions` constructor ```kotlin public val myNewOption: Boolean = MetroOption.MY_NEW_OPTION.raw.defaultValue.expectAs(), ``` #### c. Add property in `MetroOptions.Builder` ```kotlin public var myNewOption: Boolean = base.myNewOption ``` #### d. Add to `Builder.build()` function ```kotlin myNewOption = myNewOption, ``` #### e. Add to `MetroOptions.Builder.applyOptionValue()` ```kotlin MetroOption.MY_NEW_OPTION -> myNewOption = value.expectAs() ``` `value` has already been converted by `RawMetroOption.valueMapper`. Use `value.expectAs()` for its mapped type and apply any additional conversion needed by the builder property. Compiler loading in `compiler/src/main/kotlin/dev/zacsweers/metro/compiler/MetroCompilerOptions.kt` and IDE loading through `MetroOptions.Builder.applyRawOptions()` both delegate to this shared mapping. Do not add a second per-option loading switch to either caller. ### 2. `gradle-plugin/src/main/kotlin/dev/zacsweers/metro/gradle/MetroPluginExtension.kt` Add a Gradle DSL property: ```kotlin /** * KDoc description of what this option does. * * Disabled by default. */ public val myNewOption: Property = objects.booleanProperty("metro.myNewOption", false) ``` For options that should support Gradle properties: ```kotlin objects.booleanProperty("metro.propertyName", defaultValue) ``` For options without Gradle property support: ```kotlin objects.property(Boolean::class.java).convention(false) ``` ### 3. `gradle-plugin/src/main/kotlin/dev/zacsweers/metro/gradle/MetroGradleSubplugin.kt` In `applyToCompilation`, add to the options list: ```kotlin add(lazyOption("my-new-option", extension.myNewOption)) ``` Note: The option name here must match the `name` in `RawMetroOption`. ### 4. `compiler/src/test/kotlin/dev/zacsweers/metro/compiler/MetroCompilerTest.kt` In `toPluginOptions()`, add handling for the new option: ```kotlin MetroOption.MY_NEW_OPTION -> { processor.option(entry.raw.cliOption, myNewOption) } ``` ### 5. (Optional) `compiler-tests/src/test/kotlin/dev/zacsweers/metro/compiler/MetroDirectives.kt` Only needed if the option should be controllable from test directives: ```kotlin val MY_NEW_OPTION by valueDirective("Description of the directive.") { it.toBoolean() } ``` Or for simple on/off directives: ```kotlin val MY_NEW_OPTION by directive("Description of the directive.") ``` Wire the directive into `MetroOptions.buildOptions` in `compiler-tests/src/test/kotlin/dev/zacsweers/metro/compiler/MetroExtensionRegistrarConfigurator.kt` so it affects the test configuration. Preserve the option's default when the directive is absent. ## Option Types - **Boolean**: Use `RawMetroOption.boolean()` helper - **Int**: Use `RawMetroOption()` with `valueMapper = { it.toInt() }` - **String**: Use `RawMetroOption()` with `valueMapper = { it }` - **Enum**: Use `RawMetroOption()` with `valueMapper = { it }` and parse in `Builder.applyOptionValue()` - **Set**: Use `RawMetroOption()` with `valueMapper = { it.splitToSequence(':').mapToSet { ClassId.fromString(it, false) } }` ## Naming Conventions - Enum entry: `SCREAMING_SNAKE_CASE` - CLI option name: `kebab-case` - Kotlin property: `camelCase` - Gradle property: `metro.camelCase` ## Annotations - Use `@DelicateMetroGradleApi` for experimental options - Use `@Deprecated` for options that will be removed