variant_sources_precedence.md
269 lines
| 6.4 KiB
| text/x-minidsrc
|
MarkdownLexer
|
|
r41 | # `variantSources`: selectors and precedence | ||
| `variantSources` configures source-set materialization over the compile-unit space. | ||||
| A compile unit is defined as: | ||||
| - `(variant, layer)` | ||||
| This means: | ||||
| - `variant` defines compilation semantics | ||||
| - `layer` defines compilation partitioning | ||||
| The `variantSources` DSL does not introduce a separate source model. | ||||
| Instead, it provides configuration selectors over the existing compile-unit space. | ||||
| ## Selectors | ||||
| Three selectors are available: | ||||
| - `variant(...)` | ||||
| - `layer(...)` | ||||
| - `unit(...)` | ||||
| They all target the same set of compile units, but at different levels of specificity. | ||||
| ### `variant(...)` | ||||
| `variant(...)` applies configuration to all compile units that belong to the given variant. | ||||
| Example: | ||||
| ```groovy | ||||
| variantSources { | ||||
| variant("browser") { | ||||
| declareOutputs("js", "dts") | ||||
| } | ||||
| } | ||||
| ``` | ||||
| This affects all compile units of `browser`, for example: | ||||
| - `(browser, main)` | ||||
| - `(browser, rjs)` | ||||
| - `(browser, test)` | ||||
| Use this selector for variant-wide conventions. | ||||
| --- | ||||
| ### `layer(...)` | ||||
| `layer(...)` applies configuration to all compile units that use the given layer. | ||||
| Example: | ||||
| ```groovy | ||||
| variantSources { | ||||
| layer("main") { | ||||
| set("ts") { | ||||
| srcDir("src/main/ts") | ||||
| } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| This affects all compile units with layer `main`, for example: | ||||
| - `(browser, main)` | ||||
| - `(nodejs, main)` | ||||
| - `(electron, main)` | ||||
| Use this selector for cross-variant layer conventions. | ||||
| --- | ||||
| ### `unit(...)` | ||||
| `unit(...)` applies configuration to one exact compile unit. | ||||
| Example: | ||||
| ```groovy | ||||
| variantSources { | ||||
| unit("browser", "main") { | ||||
| set("resources") { | ||||
| srcDir("src/browserMain/resources") | ||||
| } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| This affects only: | ||||
| - `(browser, main)` | ||||
| Use this selector for the most specific adjustments. | ||||
| --- | ||||
| ## Precedence | ||||
| For each compile unit, source-set configuration is applied in the following order: | ||||
| ```text | ||||
| variant < layer < unit | ||||
| ``` | ||||
| This means: | ||||
| 1. `variant(...)` actions are applied first | ||||
| 2. `layer(...)` actions are applied next | ||||
| 3. `unit(...)` actions are applied last | ||||
| Each next level is allowed to refine or override the previous one. | ||||
| ### Within the same level | ||||
| Within the same selector level, actions are applied in registration order. | ||||
| For example, if two plugins both configure `layer("main")`, their actions are applied in the same order in which they were registered. | ||||
|
|
r43 | ### Scope of this guarantee | ||
| This precedence describes the normal materialization order used by the source-set | ||||
| materializer. | ||||
| It is stable for source sets that are configured before they are materialized. | ||||
| If a selector rule is added after a target source set has already been | ||||
| materialized, the behavior depends on the selected late-configuration policy. | ||||
| - in `fail` mode, such late configuration is rejected | ||||
| - in `warn` and `allow` modes, the late action is applied as an imperative | ||||
| follow-up step | ||||
| - in `warn` and `allow` modes, selector precedence is not reconstructed | ||||
| retroactively for already materialized targets | ||||
| --- | ||||
| ## Late Configuration Policy | ||||
| `variantSources` exposes a policy switch for selector rules that target already | ||||
| materialized source sets: | ||||
| ```groovy | ||||
| variantSources { | ||||
| lateConfigurationPolicy { | ||||
| failOnLateConfiguration() | ||||
| } | ||||
| } | ||||
| ``` | ||||
| Available modes: | ||||
| - `failOnLateConfiguration()` rejects such rules | ||||
| - `warnOnLateConfiguration()` allows them and emits a warning | ||||
| - `allowLateConfiguration()` allows them silently | ||||
| Policy rules: | ||||
| - the policy must be chosen before the first selector rule is added | ||||
| - selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)` | ||||
| - once chosen, the policy cannot be changed later | ||||
| - the policy is single-valued; it is not intended to be switched during further | ||||
| configuration | ||||
| Operationally: | ||||
| - `fail` preserves the strict precedence contract by rejecting late mutation of | ||||
| already materialized targets | ||||
| - `warn` and `allow` keep compatibility with imperative late mutation | ||||
| - in `warn` and `allow`, already materialized targets observe the late action in | ||||
| actual registration order, not in reconstructed `variant < layer < unit` | ||||
| order | ||||
|
|
r41 | --- | ||
| ## Example | ||||
| ```groovy | ||||
| variantSources { | ||||
| variant("browser") { | ||||
| declareOutputs("js", "dts") | ||||
| } | ||||
| layer("main") { | ||||
| set("ts") { | ||||
| srcDir("src/main/ts") | ||||
| } | ||||
| } | ||||
| unit("browser", "main") { | ||||
| set("resources") { | ||||
| srcDir("src/browserMain/resources") | ||||
| } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| For compile unit `(browser, main)` the effective configuration is built in this order: | ||||
| 1. `variant("browser")` | ||||
| 2. `layer("main")` | ||||
| 3. `unit("browser", "main")` | ||||
| For compile unit `(browser, rjs)` the effective configuration is built in this order: | ||||
| 1. `variant("browser")` | ||||
| 2. `layer("rjs")` if present | ||||
| 3. `unit("browser", "rjs")` if present | ||||
| For compile unit `(nodejs, main)` the effective configuration is built in this order: | ||||
| 1. `variant("nodejs")` if present | ||||
| 2. `layer("main")` | ||||
| 3. `unit("nodejs", "main")` if present | ||||
| --- | ||||
| ## Model boundary | ||||
| These selectors do not define compile units. | ||||
| Compile units are derived from finalized `variants`. | ||||
| `variantSources` only configures how source sets are materialized for those units. | ||||
| This means: | ||||
| - `variants` is the source of truth for compile-unit existence | ||||
| - `variantSources` is the source of truth for compile-unit source-set configuration | ||||
| --- | ||||
| ## Operational semantics | ||||
| The `variantSources` API is exposed through a finalized context. | ||||
| Conceptually, configuration is registered against finalized model objects, while DSL sugar may still use names for convenience. | ||||
|
|
r43 | Internally, selector-based configuration is accumulated and later applied by the | ||
| source-set materializer when a `GenericSourceSet` is created for a compile unit. | ||||
|
|
r41 | |||
| This guarantees that: | ||||
|
|
r43 | - selector precedence is stable before materialization | ||
|
|
r41 | - registration order is preserved | ||
|
|
r43 | - configuration of already materialized targets is governed by the selected | ||
| late-configuration policy | ||||
|
|
r41 | - adapters do not need to depend on raw Gradle lifecycle timing | ||
| --- | ||||
| ## Summary | ||||
| - compile unit space is `(variant, layer)` | ||||
| - `variant(...)`, `layer(...)`, and `unit(...)` are selectors over that space | ||||
| - precedence is: | ||||
| ```text | ||||
| variant < layer < unit | ||||
| ``` | ||||
| - registration order is preserved within the same selector level | ||||
|
|
r43 | - already materialized targets are handled by `lateConfigurationPolicy(...)` | ||
| - the late-configuration policy must be selected before the first selector rule | ||||
| and cannot be changed later | ||||
|
|
r41 | - `variants` defines what exists | ||
| - `variantSources` defines how those compile units are materialized as source sets | ||||
