diff --git a/variant_sources_precedence.md b/variant_sources_precedence.md --- a/variant_sources_precedence.md +++ b/variant_sources_precedence.md @@ -164,6 +164,8 @@ Policy rules: - once chosen, the policy cannot be changed later - the policy is single-valued; it is not intended to be switched during further configuration +- the enforcement point is the first selector registration itself; finalization + of `variants` alone does not freeze this policy Operationally: @@ -176,6 +178,70 @@ Operationally: --- +## Compile-Unit Naming Policy + +`variantSources` also exposes a policy for projecting compile units to symbolic +source-set names: + +```groovy +variantSources { + namingPolicy { + failOnNameCollision() + } +} +``` + +Base projected name: + +- `sourceSetName = variantName + capitalize(layerName)` + +Example: + +- `(browser, main)` -> `browserMain` +- `(browser, rjs)` -> `browserRjs` + +Available modes: + +- `failOnNameCollision()` rejects finalized compile-unit models that project the + same base name for different compile units +- `resolveNameCollision()` resolves such collisions deterministically + +### `resolveNameCollision()` semantics + +Conflicting compile units are ordered canonically by: + +```text +(variant.name, layer.name) +``` + +Name assignment in a conflicting group is: + +- the first compile unit keeps the base name +- the second gets suffix `2` +- the third gets suffix `3` +- and so on + +Example: + +- `(foo, variantBar)` and `(fooVariant, bar)` both project to `fooVariantBar` +- after canonical ordering: + - `(foo, variantBar)` -> `fooVariantBar` + - `(fooVariant, bar)` -> `fooVariantBar2` + +### Fixation Point + +Naming policy is fixed when the finalized `VariantSourcesContext` is created. + +Operationally this means: + +- policy selection must happen before `variantSources.whenFinalized(...)` + becomes observable +- compile-unit names are projected and validated before queued + `whenFinalized(...)` callbacks are replayed +- changing naming policy from inside a `whenFinalized(...)` callback is too late + +--- + ## Example ```groovy @@ -265,5 +331,7 @@ variant < layer < unit - already materialized targets are handled by `lateConfigurationPolicy(...)` - the late-configuration policy must be selected before the first selector rule and cannot be changed later +- compile-unit naming is governed by `namingPolicy(...)` +- by default, name collisions fail fast during finalized context creation - `variants` defines what exists - `variantSources` defines how those compile units are materialized as source sets diff --git a/variants/src/main/java/org/implab/gradle/variants/sources/SourceSetMaterializer.java b/variants/src/main/java/org/implab/gradle/variants/sources/SourceSetMaterializer.java --- a/variants/src/main/java/org/implab/gradle/variants/sources/SourceSetMaterializer.java +++ b/variants/src/main/java/org/implab/gradle/variants/sources/SourceSetMaterializer.java @@ -4,14 +4,18 @@ import org.gradle.api.NamedDomainObjectP import org.implab.gradle.common.sources.GenericSourceSet; /** - * Materializes symbolic source set names into actual GenericSourceSet + * Materializes symbolic source set names into actual {@link GenericSourceSet} * instances. + * + *
Symbolic names are assigned from the finalized compile-unit model using + * the selected + * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}. */ public interface SourceSetMaterializer { /** * Returns a lazy provider for the source set corresponding to the compile unit. * - * The provider is stable and cached per compile unit. + *
The provider is stable and cached per compile unit.
*/
NamedDomainObjectProvider Identity in this registry is the {@link GenericSourceSet} name assigned
+ * by the finalized
+ * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
*/
public interface VariantSourcesContext {
diff --git a/variants/src/main/java/org/implab/gradle/variants/sources/VariantSourcesExtension.java b/variants/src/main/java/org/implab/gradle/variants/sources/VariantSourcesExtension.java
--- a/variants/src/main/java/org/implab/gradle/variants/sources/VariantSourcesExtension.java
+++ b/variants/src/main/java/org/implab/gradle/variants/sources/VariantSourcesExtension.java
@@ -21,6 +21,9 @@ public interface VariantSourcesExtension
* If not selected explicitly, the default is
+ * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
*/
void lateConfigurationPolicy(Action super LateConfigurationPolicySpec> action);
@@ -28,24 +31,63 @@ public interface VariantSourcesExtension
lateConfigurationPolicy(Closures.action(closure));
}
+ /**
+ * Selects how compile-unit name collisions are handled when the finalized
+ * source context is created.
+ *
+ * This policy is single-valued:
+ * If not selected explicitly, the default is
+ * {@link NamingPolicySpec#failOnNameCollision()}.
+ */
void namingPolicy(Action super NamingPolicySpec> action);
default void namingPolicy(Closure> closure) {
namingPolicy(Closures.action(closure));
}
+ /**
+ * Registers a selector rule for all compile units of the given layer.
+ *
+ * Registering the first selector rule fixes the selected
+ * {@link #lateConfigurationPolicy(Action)} for the remaining extension
+ * lifecycle.
+ */
void layer(String layerName, Action super GenericSourceSet> action);
default void layer(String layerName, Closure> closure) {
layer(layerName, Closures.action(closure));
}
+ /**
+ * Registers a selector rule for all compile units of the given variant.
+ *
+ * Registering the first selector rule fixes the selected
+ * {@link #lateConfigurationPolicy(Action)} for the remaining extension
+ * lifecycle.
+ */
void variant(String variantName, Action super GenericSourceSet> action);
default void variant(String variantName, Closure> closure) {
variant(variantName, Closures.action(closure));
}
+ /**
+ * Registers a selector rule for one exact compile unit.
+ *
+ * Registering the first selector rule fixes the selected
+ * {@link #lateConfigurationPolicy(Action)} for the remaining extension
+ * lifecycle.
+ */
void unit(String variantName, String layerName, Action super GenericSourceSet> action);
/**
@@ -56,6 +98,10 @@ public interface VariantSourcesExtension
* By the time this callback becomes observable, compile-unit naming
+ * policy has already been fixed and symbolic source-set names for finalized
+ * compile units are determined.
*/
void whenFinalized(Action super VariantSourcesContext> action);
@@ -98,8 +144,21 @@ public interface VariantSourcesExtension
}
interface NamingPolicySpec {
+ /**
+ * Rejects finalized compile-unit models that project the same source-set
+ * name for different compile units.
+ */
void failOnNameCollision();
+ /**
+ * Resolves name collisions deterministically for the finalized
+ * compile-unit model.
+ *
+ * Conflicting compile units are ordered canonically by
+ * {@code (variant.name, layer.name)}. The first unit keeps the base
+ * projected name, and each next unit receives a numeric suffix
+ * ({@code 2}, {@code 3}, ...).
+ */
void resolveNameCollision();
}
}
diff --git a/variants_variant_sources.md b/variants_variant_sources.md
--- a/variants_variant_sources.md
+++ b/variants_variant_sources.md
@@ -453,7 +453,7 @@ Reasons:
* the DSL is internal to source materialization
* the source of truth for unit existence is already finalized in `VariantsView`
-* `GenericSourceSetMaterializer` returns `NamedDomainObjectProvider
+ *
+ *
+ *