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
MY_NEW_OPTION(
RawMetroOption.boolean( // or RawMetroOption() for non-boolean types
name = "my-new-option", // kebab-case name used in CLI
defaultValue = false,
valueDescription = "<true | false>",
description = "Description of what this option does.",
required = false,
allowMultipleOccurrences = false,
)
),
For non-boolean options, use the full RawMetroOption constructor with a valueMapper:
MY_INT_OPTION(
RawMetroOption(
name = "my-int-option",
defaultValue = 10,
valueDescription = "<count>",
description = "Description here",
required = false,
allowMultipleOccurrences = false,
valueMapper = { it.toInt() },
)
),
b. Add property in the MetroOptions constructor
public val myNewOption: Boolean = MetroOption.MY_NEW_OPTION.raw.defaultValue.expectAs(),
c. Add property in MetroOptions.Builder
public var myNewOption: Boolean = base.myNewOption
d. Add to Builder.build() function
myNewOption = myNewOption,
e. Add to MetroOptions.Builder.applyOptionValue()
MetroOption.MY_NEW_OPTION -> myNewOption = value.expectAs()
value has already been converted by RawMetroOption.valueMapper. Use value.expectAs<T>() 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:
/**
* KDoc description of what this option does.
*
* Disabled by default.
*/
public val myNewOption: Property<Boolean> =
objects.booleanProperty("metro.myNewOption", false)
For options that should support Gradle properties:
objects.booleanProperty("metro.propertyName", defaultValue)
For options without Gradle property support:
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:
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:
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:
val MY_NEW_OPTION by
valueDirective("Description of the directive.") { it.toBoolean() }
Or for simple on/off directives:
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()withvalueMapper = { it.toInt() } - String: Use
RawMetroOption()withvalueMapper = { it } - Enum: Use
RawMetroOption()withvalueMapper = { it }and parse inBuilder.applyOptionValue() - Set: Use
RawMetroOption()withvalueMapper = { 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
@DelicateMetroGradleApifor experimental options - Use
@Deprecatedfor options that will be removed