##// END OF EJS Templates
more documentation
cin -
r44:ae7ec3f08ac3 default
parent child
Show More
@@ -1,269 +1,337
1 # `variantSources`: selectors and precedence
1 # `variantSources`: selectors and precedence
2
2
3 `variantSources` configures source-set materialization over the compile-unit space.
3 `variantSources` configures source-set materialization over the compile-unit space.
4
4
5 A compile unit is defined as:
5 A compile unit is defined as:
6
6
7 - `(variant, layer)`
7 - `(variant, layer)`
8
8
9 This means:
9 This means:
10
10
11 - `variant` defines compilation semantics
11 - `variant` defines compilation semantics
12 - `layer` defines compilation partitioning
12 - `layer` defines compilation partitioning
13
13
14 The `variantSources` DSL does not introduce a separate source model.
14 The `variantSources` DSL does not introduce a separate source model.
15 Instead, it provides configuration selectors over the existing compile-unit space.
15 Instead, it provides configuration selectors over the existing compile-unit space.
16
16
17 ## Selectors
17 ## Selectors
18
18
19 Three selectors are available:
19 Three selectors are available:
20
20
21 - `variant(...)`
21 - `variant(...)`
22 - `layer(...)`
22 - `layer(...)`
23 - `unit(...)`
23 - `unit(...)`
24
24
25 They all target the same set of compile units, but at different levels of specificity.
25 They all target the same set of compile units, but at different levels of specificity.
26
26
27 ### `variant(...)`
27 ### `variant(...)`
28
28
29 `variant(...)` applies configuration to all compile units that belong to the given variant.
29 `variant(...)` applies configuration to all compile units that belong to the given variant.
30
30
31 Example:
31 Example:
32
32
33 ```groovy
33 ```groovy
34 variantSources {
34 variantSources {
35 variant("browser") {
35 variant("browser") {
36 declareOutputs("js", "dts")
36 declareOutputs("js", "dts")
37 }
37 }
38 }
38 }
39 ```
39 ```
40
40
41 This affects all compile units of `browser`, for example:
41 This affects all compile units of `browser`, for example:
42
42
43 - `(browser, main)`
43 - `(browser, main)`
44 - `(browser, rjs)`
44 - `(browser, rjs)`
45 - `(browser, test)`
45 - `(browser, test)`
46
46
47 Use this selector for variant-wide conventions.
47 Use this selector for variant-wide conventions.
48
48
49 ---
49 ---
50
50
51 ### `layer(...)`
51 ### `layer(...)`
52
52
53 `layer(...)` applies configuration to all compile units that use the given layer.
53 `layer(...)` applies configuration to all compile units that use the given layer.
54
54
55 Example:
55 Example:
56
56
57 ```groovy
57 ```groovy
58 variantSources {
58 variantSources {
59 layer("main") {
59 layer("main") {
60 set("ts") {
60 set("ts") {
61 srcDir("src/main/ts")
61 srcDir("src/main/ts")
62 }
62 }
63 }
63 }
64 }
64 }
65 ```
65 ```
66
66
67 This affects all compile units with layer `main`, for example:
67 This affects all compile units with layer `main`, for example:
68
68
69 - `(browser, main)`
69 - `(browser, main)`
70 - `(nodejs, main)`
70 - `(nodejs, main)`
71 - `(electron, main)`
71 - `(electron, main)`
72
72
73 Use this selector for cross-variant layer conventions.
73 Use this selector for cross-variant layer conventions.
74
74
75 ---
75 ---
76
76
77 ### `unit(...)`
77 ### `unit(...)`
78
78
79 `unit(...)` applies configuration to one exact compile unit.
79 `unit(...)` applies configuration to one exact compile unit.
80
80
81 Example:
81 Example:
82
82
83 ```groovy
83 ```groovy
84 variantSources {
84 variantSources {
85 unit("browser", "main") {
85 unit("browser", "main") {
86 set("resources") {
86 set("resources") {
87 srcDir("src/browserMain/resources")
87 srcDir("src/browserMain/resources")
88 }
88 }
89 }
89 }
90 }
90 }
91 ```
91 ```
92
92
93 This affects only:
93 This affects only:
94
94
95 - `(browser, main)`
95 - `(browser, main)`
96
96
97 Use this selector for the most specific adjustments.
97 Use this selector for the most specific adjustments.
98
98
99 ---
99 ---
100
100
101 ## Precedence
101 ## Precedence
102
102
103 For each compile unit, source-set configuration is applied in the following order:
103 For each compile unit, source-set configuration is applied in the following order:
104
104
105 ```text
105 ```text
106 variant < layer < unit
106 variant < layer < unit
107 ```
107 ```
108
108
109 This means:
109 This means:
110
110
111 1. `variant(...)` actions are applied first
111 1. `variant(...)` actions are applied first
112 2. `layer(...)` actions are applied next
112 2. `layer(...)` actions are applied next
113 3. `unit(...)` actions are applied last
113 3. `unit(...)` actions are applied last
114
114
115 Each next level is allowed to refine or override the previous one.
115 Each next level is allowed to refine or override the previous one.
116
116
117 ### Within the same level
117 ### Within the same level
118
118
119 Within the same selector level, actions are applied in registration order.
119 Within the same selector level, actions are applied in registration order.
120
120
121 For example, if two plugins both configure `layer("main")`, their actions are applied in the same order in which they were registered.
121 For example, if two plugins both configure `layer("main")`, their actions are applied in the same order in which they were registered.
122
122
123 ### Scope of this guarantee
123 ### Scope of this guarantee
124
124
125 This precedence describes the normal materialization order used by the source-set
125 This precedence describes the normal materialization order used by the source-set
126 materializer.
126 materializer.
127
127
128 It is stable for source sets that are configured before they are materialized.
128 It is stable for source sets that are configured before they are materialized.
129
129
130 If a selector rule is added after a target source set has already been
130 If a selector rule is added after a target source set has already been
131 materialized, the behavior depends on the selected late-configuration policy.
131 materialized, the behavior depends on the selected late-configuration policy.
132
132
133 - in `fail` mode, such late configuration is rejected
133 - in `fail` mode, such late configuration is rejected
134 - in `warn` and `allow` modes, the late action is applied as an imperative
134 - in `warn` and `allow` modes, the late action is applied as an imperative
135 follow-up step
135 follow-up step
136 - in `warn` and `allow` modes, selector precedence is not reconstructed
136 - in `warn` and `allow` modes, selector precedence is not reconstructed
137 retroactively for already materialized targets
137 retroactively for already materialized targets
138
138
139 ---
139 ---
140
140
141 ## Late Configuration Policy
141 ## Late Configuration Policy
142
142
143 `variantSources` exposes a policy switch for selector rules that target already
143 `variantSources` exposes a policy switch for selector rules that target already
144 materialized source sets:
144 materialized source sets:
145
145
146 ```groovy
146 ```groovy
147 variantSources {
147 variantSources {
148 lateConfigurationPolicy {
148 lateConfigurationPolicy {
149 failOnLateConfiguration()
149 failOnLateConfiguration()
150 }
150 }
151 }
151 }
152 ```
152 ```
153
153
154 Available modes:
154 Available modes:
155
155
156 - `failOnLateConfiguration()` rejects such rules
156 - `failOnLateConfiguration()` rejects such rules
157 - `warnOnLateConfiguration()` allows them and emits a warning
157 - `warnOnLateConfiguration()` allows them and emits a warning
158 - `allowLateConfiguration()` allows them silently
158 - `allowLateConfiguration()` allows them silently
159
159
160 Policy rules:
160 Policy rules:
161
161
162 - the policy must be chosen before the first selector rule is added
162 - the policy must be chosen before the first selector rule is added
163 - selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
163 - selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
164 - once chosen, the policy cannot be changed later
164 - once chosen, the policy cannot be changed later
165 - the policy is single-valued; it is not intended to be switched during further
165 - the policy is single-valued; it is not intended to be switched during further
166 configuration
166 configuration
167 - the enforcement point is the first selector registration itself; finalization
168 of `variants` alone does not freeze this policy
167
169
168 Operationally:
170 Operationally:
169
171
170 - `fail` preserves the strict precedence contract by rejecting late mutation of
172 - `fail` preserves the strict precedence contract by rejecting late mutation of
171 already materialized targets
173 already materialized targets
172 - `warn` and `allow` keep compatibility with imperative late mutation
174 - `warn` and `allow` keep compatibility with imperative late mutation
173 - in `warn` and `allow`, already materialized targets observe the late action in
175 - in `warn` and `allow`, already materialized targets observe the late action in
174 actual registration order, not in reconstructed `variant < layer < unit`
176 actual registration order, not in reconstructed `variant < layer < unit`
175 order
177 order
176
178
177 ---
179 ---
178
180
181 ## Compile-Unit Naming Policy
182
183 `variantSources` also exposes a policy for projecting compile units to symbolic
184 source-set names:
185
186 ```groovy
187 variantSources {
188 namingPolicy {
189 failOnNameCollision()
190 }
191 }
192 ```
193
194 Base projected name:
195
196 - `sourceSetName = variantName + capitalize(layerName)`
197
198 Example:
199
200 - `(browser, main)` -> `browserMain`
201 - `(browser, rjs)` -> `browserRjs`
202
203 Available modes:
204
205 - `failOnNameCollision()` rejects finalized compile-unit models that project the
206 same base name for different compile units
207 - `resolveNameCollision()` resolves such collisions deterministically
208
209 ### `resolveNameCollision()` semantics
210
211 Conflicting compile units are ordered canonically by:
212
213 ```text
214 (variant.name, layer.name)
215 ```
216
217 Name assignment in a conflicting group is:
218
219 - the first compile unit keeps the base name
220 - the second gets suffix `2`
221 - the third gets suffix `3`
222 - and so on
223
224 Example:
225
226 - `(foo, variantBar)` and `(fooVariant, bar)` both project to `fooVariantBar`
227 - after canonical ordering:
228 - `(foo, variantBar)` -> `fooVariantBar`
229 - `(fooVariant, bar)` -> `fooVariantBar2`
230
231 ### Fixation Point
232
233 Naming policy is fixed when the finalized `VariantSourcesContext` is created.
234
235 Operationally this means:
236
237 - policy selection must happen before `variantSources.whenFinalized(...)`
238 becomes observable
239 - compile-unit names are projected and validated before queued
240 `whenFinalized(...)` callbacks are replayed
241 - changing naming policy from inside a `whenFinalized(...)` callback is too late
242
243 ---
244
179 ## Example
245 ## Example
180
246
181 ```groovy
247 ```groovy
182 variantSources {
248 variantSources {
183 variant("browser") {
249 variant("browser") {
184 declareOutputs("js", "dts")
250 declareOutputs("js", "dts")
185 }
251 }
186
252
187 layer("main") {
253 layer("main") {
188 set("ts") {
254 set("ts") {
189 srcDir("src/main/ts")
255 srcDir("src/main/ts")
190 }
256 }
191 }
257 }
192
258
193 unit("browser", "main") {
259 unit("browser", "main") {
194 set("resources") {
260 set("resources") {
195 srcDir("src/browserMain/resources")
261 srcDir("src/browserMain/resources")
196 }
262 }
197 }
263 }
198 }
264 }
199 ```
265 ```
200
266
201 For compile unit `(browser, main)` the effective configuration is built in this order:
267 For compile unit `(browser, main)` the effective configuration is built in this order:
202
268
203 1. `variant("browser")`
269 1. `variant("browser")`
204 2. `layer("main")`
270 2. `layer("main")`
205 3. `unit("browser", "main")`
271 3. `unit("browser", "main")`
206
272
207 For compile unit `(browser, rjs)` the effective configuration is built in this order:
273 For compile unit `(browser, rjs)` the effective configuration is built in this order:
208
274
209 1. `variant("browser")`
275 1. `variant("browser")`
210 2. `layer("rjs")` if present
276 2. `layer("rjs")` if present
211 3. `unit("browser", "rjs")` if present
277 3. `unit("browser", "rjs")` if present
212
278
213 For compile unit `(nodejs, main)` the effective configuration is built in this order:
279 For compile unit `(nodejs, main)` the effective configuration is built in this order:
214
280
215 1. `variant("nodejs")` if present
281 1. `variant("nodejs")` if present
216 2. `layer("main")`
282 2. `layer("main")`
217 3. `unit("nodejs", "main")` if present
283 3. `unit("nodejs", "main")` if present
218
284
219 ---
285 ---
220
286
221 ## Model boundary
287 ## Model boundary
222
288
223 These selectors do not define compile units.
289 These selectors do not define compile units.
224 Compile units are derived from finalized `variants`.
290 Compile units are derived from finalized `variants`.
225
291
226 `variantSources` only configures how source sets are materialized for those units.
292 `variantSources` only configures how source sets are materialized for those units.
227
293
228 This means:
294 This means:
229
295
230 - `variants` is the source of truth for compile-unit existence
296 - `variants` is the source of truth for compile-unit existence
231 - `variantSources` is the source of truth for compile-unit source-set configuration
297 - `variantSources` is the source of truth for compile-unit source-set configuration
232
298
233 ---
299 ---
234
300
235 ## Operational semantics
301 ## Operational semantics
236
302
237 The `variantSources` API is exposed through a finalized context.
303 The `variantSources` API is exposed through a finalized context.
238
304
239 Conceptually, configuration is registered against finalized model objects, while DSL sugar may still use names for convenience.
305 Conceptually, configuration is registered against finalized model objects, while DSL sugar may still use names for convenience.
240
306
241 Internally, selector-based configuration is accumulated and later applied by the
307 Internally, selector-based configuration is accumulated and later applied by the
242 source-set materializer when a `GenericSourceSet` is created for a compile unit.
308 source-set materializer when a `GenericSourceSet` is created for a compile unit.
243
309
244 This guarantees that:
310 This guarantees that:
245
311
246 - selector precedence is stable before materialization
312 - selector precedence is stable before materialization
247 - registration order is preserved
313 - registration order is preserved
248 - configuration of already materialized targets is governed by the selected
314 - configuration of already materialized targets is governed by the selected
249 late-configuration policy
315 late-configuration policy
250 - adapters do not need to depend on raw Gradle lifecycle timing
316 - adapters do not need to depend on raw Gradle lifecycle timing
251
317
252 ---
318 ---
253
319
254 ## Summary
320 ## Summary
255
321
256 - compile unit space is `(variant, layer)`
322 - compile unit space is `(variant, layer)`
257 - `variant(...)`, `layer(...)`, and `unit(...)` are selectors over that space
323 - `variant(...)`, `layer(...)`, and `unit(...)` are selectors over that space
258 - precedence is:
324 - precedence is:
259
325
260 ```text
326 ```text
261 variant < layer < unit
327 variant < layer < unit
262 ```
328 ```
263
329
264 - registration order is preserved within the same selector level
330 - registration order is preserved within the same selector level
265 - already materialized targets are handled by `lateConfigurationPolicy(...)`
331 - already materialized targets are handled by `lateConfigurationPolicy(...)`
266 - the late-configuration policy must be selected before the first selector rule
332 - the late-configuration policy must be selected before the first selector rule
267 and cannot be changed later
333 and cannot be changed later
334 - compile-unit naming is governed by `namingPolicy(...)`
335 - by default, name collisions fail fast during finalized context creation
268 - `variants` defines what exists
336 - `variants` defines what exists
269 - `variantSources` defines how those compile units are materialized as source sets
337 - `variantSources` defines how those compile units are materialized as source sets
@@ -1,17 +1,21
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.gradle.api.NamedDomainObjectProvider;
3 import org.gradle.api.NamedDomainObjectProvider;
4 import org.implab.gradle.common.sources.GenericSourceSet;
4 import org.implab.gradle.common.sources.GenericSourceSet;
5
5
6 /**
6 /**
7 * Materializes symbolic source set names into actual GenericSourceSet
7 * Materializes symbolic source set names into actual {@link GenericSourceSet}
8 * instances.
8 * instances.
9 *
10 * <p>Symbolic names are assigned from the finalized compile-unit model using
11 * the selected
12 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
9 */
13 */
10 public interface SourceSetMaterializer {
14 public interface SourceSetMaterializer {
11 /**
15 /**
12 * Returns a lazy provider for the source set corresponding to the compile unit.
16 * Returns a lazy provider for the source set corresponding to the compile unit.
13 *
17 *
14 * The provider is stable and cached per compile unit.
18 * <p>The provider is stable and cached per compile unit.
15 */
19 */
16 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
20 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
17 } No newline at end of file
21 }
@@ -1,71 +1,73
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.implab.gradle.common.sources.GenericSourceSet;
4 import org.implab.gradle.common.sources.GenericSourceSet;
5 import org.implab.gradle.variants.core.Layer;
5 import org.implab.gradle.variants.core.Layer;
6 import org.implab.gradle.variants.core.Variant;
6 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.VariantsView;
7 import org.implab.gradle.variants.core.VariantsView;
8
8
9 /**
9 /**
10 * Registry of symbolic source set names produced by sources projection.
10 * Registry of symbolic source set names produced by sources projection.
11 *
11 *
12 * Identity in this registry is the GenericSourceSet name.
12 * <p>Identity in this registry is the {@link GenericSourceSet} name assigned
13 * by the finalized
14 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
13 */
15 */
14 public interface VariantSourcesContext {
16 public interface VariantSourcesContext {
15
17
16 /**
18 /**
17 * Finalized core model.
19 * Finalized core model.
18 */
20 */
19 VariantsView getVariants();
21 VariantsView getVariants();
20
22
21 /**
23 /**
22 * Derived compile-side view.
24 * Derived compile-side view.
23 */
25 */
24 CompileUnitsView getCompileUnits();
26 CompileUnitsView getCompileUnits();
25
27
26 /**
28 /**
27 * Derived role-side view.
29 * Derived role-side view.
28 */
30 */
29 RoleProjectionsView getRoleProjections();
31 RoleProjectionsView getRoleProjections();
30
32
31 /**
33 /**
32 * Lazy source set provider service.
34 * Lazy source set provider service.
33 */
35 */
34 SourceSetMaterializer getSourceSets();
36 SourceSetMaterializer getSourceSets();
35
37
36 /**
38 /**
37 * Configures all GenericSourceSets produced from the given layer.
39 * Configures all GenericSourceSets produced from the given layer.
38 *
40 *
39 * The action is applied:
41 * The action is applied:
40 * - to already materialized source sets of this layer
42 * - to already materialized source sets of this layer
41 * - to all future source sets of this layer
43 * - to all future source sets of this layer
42 *
44 *
43 * <p>For future source sets, selector precedence and registration order are
45 * <p>For future source sets, selector precedence and registration order are
44 * preserved by the materializer.
46 * preserved by the materializer.
45 *
47 *
46 * <p>For already materialized source sets, behavior is governed by
48 * <p>For already materialized source sets, behavior is governed by
47 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
49 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
48 * In warn/allow modes the action is applied as a late imperative step and does
50 * In warn/allow modes the action is applied as a late imperative step and does
49 * not retroactively restore selector precedence.
51 * not retroactively restore selector precedence.
50 */
52 */
51 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
53 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
52
54
53 /**
55 /**
54 * Configures all GenericSourceSets produced from the given variant.
56 * Configures all GenericSourceSets produced from the given variant.
55 *
57 *
56 * <p>Late application semantics for already materialized source sets are
58 * <p>Late application semantics for already materialized source sets are
57 * governed by
59 * governed by
58 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
60 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
59 */
61 */
60 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
62 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
61
63
62 /**
64 /**
63 * Configures the GenericSourceSet produced from the given compile unit.
65 * Configures the GenericSourceSet produced from the given compile unit.
64 *
66 *
65 * <p>Late application semantics for already materialized source sets are
67 * <p>Late application semantics for already materialized source sets are
66 * governed by
68 * governed by
67 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
69 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
68 */
70 */
69 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
71 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
70
72
71 }
73 }
@@ -1,105 +1,164
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 import org.gradle.api.Action;
4 import org.gradle.api.Action;
5 import org.implab.gradle.common.core.lang.Closures;
5 import org.implab.gradle.common.core.lang.Closures;
6 import org.implab.gradle.common.sources.GenericSourceSet;
6 import org.implab.gradle.common.sources.GenericSourceSet;
7 import groovy.lang.Closure;
7 import groovy.lang.Closure;
8
8
9 @NonNullByDefault
9 @NonNullByDefault
10 public interface VariantSourcesExtension {
10 public interface VariantSourcesExtension {
11
11
12 /**
12 /**
13 * Selects how selector rules behave when they target an already materialized
13 * Selects how selector rules behave when they target an already materialized
14 * {@link GenericSourceSet}.
14 * {@link GenericSourceSet}.
15 *
15 *
16 * <p>This policy is single-valued:
16 * <p>This policy is single-valued:
17 * <ul>
17 * <ul>
18 * <li>it must be selected before the first selector rule is registered via
18 * <li>it must be selected before the first selector rule is registered via
19 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
19 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
20 * {@link #unit(String, String, Action)};</li>
20 * {@link #unit(String, String, Action)};</li>
21 * <li>once selected, it cannot be changed later;</li>
21 * <li>once selected, it cannot be changed later;</li>
22 * <li>the policy controls both diagnostics and late-application semantics.</li>
22 * <li>the policy controls both diagnostics and late-application semantics.</li>
23 * </ul>
23 * </ul>
24 *
25 * <p>If not selected explicitly, the default is
26 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
24 */
27 */
25 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
28 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
26
29
27 default void lateConfigurationPolicy(Closure<?> closure) {
30 default void lateConfigurationPolicy(Closure<?> closure) {
28 lateConfigurationPolicy(Closures.action(closure));
31 lateConfigurationPolicy(Closures.action(closure));
29 }
32 }
30
33
34 /**
35 * Selects how compile-unit name collisions are handled when the finalized
36 * source context is created.
37 *
38 * <p>This policy is single-valued:
39 * <ul>
40 * <li>it must be selected before the finalized
41 * {@link VariantSourcesContext} becomes observable through
42 * {@link #whenFinalized(Action)};</li>
43 * <li>once the context is being created, the policy is fixed and cannot be
44 * changed later;</li>
45 * <li>the policy governs validation of compile-unit names produced by the
46 * source-set materializer.</li>
47 * </ul>
48 *
49 * <p>If not selected explicitly, the default is
50 * {@link NamingPolicySpec#failOnNameCollision()}.
51 */
31 void namingPolicy(Action<? super NamingPolicySpec> action);
52 void namingPolicy(Action<? super NamingPolicySpec> action);
32
53
33 default void namingPolicy(Closure<?> closure) {
54 default void namingPolicy(Closure<?> closure) {
34 namingPolicy(Closures.action(closure));
55 namingPolicy(Closures.action(closure));
35 }
56 }
36
57
58 /**
59 * Registers a selector rule for all compile units of the given layer.
60 *
61 * <p>Registering the first selector rule fixes the selected
62 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
63 * lifecycle.
64 */
37 void layer(String layerName, Action<? super GenericSourceSet> action);
65 void layer(String layerName, Action<? super GenericSourceSet> action);
38
66
39 default void layer(String layerName, Closure<?> closure) {
67 default void layer(String layerName, Closure<?> closure) {
40 layer(layerName, Closures.action(closure));
68 layer(layerName, Closures.action(closure));
41 }
69 }
42
70
71 /**
72 * Registers a selector rule for all compile units of the given variant.
73 *
74 * <p>Registering the first selector rule fixes the selected
75 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
76 * lifecycle.
77 */
43 void variant(String variantName, Action<? super GenericSourceSet> action);
78 void variant(String variantName, Action<? super GenericSourceSet> action);
44
79
45 default void variant(String variantName, Closure<?> closure) {
80 default void variant(String variantName, Closure<?> closure) {
46 variant(variantName, Closures.action(closure));
81 variant(variantName, Closures.action(closure));
47 }
82 }
48
83
84 /**
85 * Registers a selector rule for one exact compile unit.
86 *
87 * <p>Registering the first selector rule fixes the selected
88 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
89 * lifecycle.
90 */
49 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
91 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
50
92
51 /**
93 /**
52 * Invoked when finalized variants-derived source context becomes available.
94 * Invoked when finalized variants-derived source context becomes available.
53 *
95 *
54 * Replayable:
96 * Replayable:
55 * <ul>
97 * <ul>
56 * <li>if called before variants finalization, action is queued
98 * <li>if called before variants finalization, action is queued
57 * <li>if called after variants finalization, action is invoked immediately
99 * <li>if called after variants finalization, action is invoked immediately
58 * </ul>
100 * </ul>
101 *
102 * <p>By the time this callback becomes observable, compile-unit naming
103 * policy has already been fixed and symbolic source-set names for finalized
104 * compile units are determined.
59 */
105 */
60 void whenFinalized(Action<? super VariantSourcesContext> action);
106 void whenFinalized(Action<? super VariantSourcesContext> action);
61
107
62 default void whenFinalized(Closure<?> closure) {
108 default void whenFinalized(Closure<?> closure) {
63 whenFinalized(Closures.action(closure));
109 whenFinalized(Closures.action(closure));
64 }
110 }
65
111
66
112
67 /**
113 /**
68 * Imperative selector for the late-configuration mode.
114 * Imperative selector for the late-configuration mode.
69 *
115 *
70 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
116 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
71 */
117 */
72 interface LateConfigurationPolicySpec {
118 interface LateConfigurationPolicySpec {
73 /**
119 /**
74 * Rejects selector registration if it targets any already materialized
120 * Rejects selector registration if it targets any already materialized
75 * source set.
121 * source set.
76 */
122 */
77 void failOnLateConfiguration();
123 void failOnLateConfiguration();
78
124
79 /**
125 /**
80 * Allows late selector registration, but emits a warning when it targets an
126 * Allows late selector registration, but emits a warning when it targets an
81 * already materialized source set.
127 * already materialized source set.
82 *
128 *
83 * <p>For such targets, selector precedence is not re-established
129 * <p>For such targets, selector precedence is not re-established
84 * retroactively. The action is applied as a late imperative step, after the
130 * retroactively. The action is applied as a late imperative step, after the
85 * state already produced at the materialization moment.
131 * state already produced at the materialization moment.
86 */
132 */
87 void warnOnLateConfiguration();
133 void warnOnLateConfiguration();
88
134
89 /**
135 /**
90 * Allows late selector registration without a warning when it targets an
136 * Allows late selector registration without a warning when it targets an
91 * already materialized source set.
137 * already materialized source set.
92 *
138 *
93 * <p>For such targets, selector precedence is not re-established
139 * <p>For such targets, selector precedence is not re-established
94 * retroactively. The action is applied as a late imperative step, after the
140 * retroactively. The action is applied as a late imperative step, after the
95 * state already produced at the materialization moment.
141 * state already produced at the materialization moment.
96 */
142 */
97 void allowLateConfiguration();
143 void allowLateConfiguration();
98 }
144 }
99
145
100 interface NamingPolicySpec {
146 interface NamingPolicySpec {
147 /**
148 * Rejects finalized compile-unit models that project the same source-set
149 * name for different compile units.
150 */
101 void failOnNameCollision();
151 void failOnNameCollision();
102
152
153 /**
154 * Resolves name collisions deterministically for the finalized
155 * compile-unit model.
156 *
157 * <p>Conflicting compile units are ordered canonically by
158 * {@code (variant.name, layer.name)}. The first unit keeps the base
159 * projected name, and each next unit receives a numeric suffix
160 * ({@code 2}, {@code 3}, ...).
161 */
103 void resolveNameCollision();
162 void resolveNameCollision();
104 }
163 }
105 }
164 }
@@ -1,776 +1,872
1 # Variants and Variant Sources
1 # Variants and Variant Sources
2
2
3 ## Overview
3 ## Overview
4
4
5 This document describes a two-layer model for build variants:
5 This document describes a two-layer model for build variants:
6
6
7 - `variants` defines the **core domain model**
7 - `variants` defines the **core domain model**
8 - `variantSources` defines **source materialization semantics** for that model
8 - `variantSources` defines **source materialization semantics** for that model
9
9
10 The main goal is to keep the core model small, explicit, and stable, while allowing source-related behavior to remain flexible and adapter-friendly.
10 The main goal is to keep the core model small, explicit, and stable, while allowing source-related behavior to remain flexible and adapter-friendly.
11
11
12 The model is intentionally split into:
12 The model is intentionally split into:
13
13
14 1. a **closed, finalized domain model**
14 1. a **closed, finalized domain model**
15 2. an **open, runtime-oriented source materialization model**
15 2. an **open, runtime-oriented source materialization model**
16
16
17 This separation is important because compilation, source aggregation, publication, and adapter-specific behavior do not belong to the same abstraction layer.
17 This separation is important because compilation, source aggregation, publication, and adapter-specific behavior do not belong to the same abstraction layer.
18
18
19 ---
19 ---
20
20
21 ## Core idea
21 ## Core idea
22
22
23 The `variants` model is based on three independent domains:
23 The `variants` model is based on three independent domains:
24
24
25 - `Layer`
25 - `Layer`
26 - `Role`
26 - `Role`
27 - `Variant`
27 - `Variant`
28
28
29 A finalized `VariantsView` contains the normalized relation:
29 A finalized `VariantsView` contains the normalized relation:
30
30
31 - `(variant, role, layer)`
31 - `(variant, role, layer)`
32
32
33 This relation is the source of truth.
33 This relation is the source of truth.
34
34
35 Everything else is derived from it.
35 Everything else is derived from it.
36
36
37 ---
37 ---
38
38
39 ## `variants`: the core domain model
39 ## `variants`: the core domain model
40
40
41 ### Purpose
41 ### Purpose
42
42
43 `variants` describes:
43 `variants` describes:
44
44
45 - what layers exist
45 - what layers exist
46 - what roles exist
46 - what roles exist
47 - what variants exist
47 - what variants exist
48 - which `(variant, role, layer)` combinations are valid
48 - which `(variant, role, layer)` combinations are valid
49
49
50 It does **not** describe:
50 It does **not** describe:
51
51
52 - source directories
52 - source directories
53 - source roots
53 - source roots
54 - source set materialization
54 - source set materialization
55 - compilation tasks
55 - compilation tasks
56 - publication mechanics
56 - publication mechanics
57 - source set inheritance
57 - source set inheritance
58 - layer merge behavior for a concrete toolchain
58 - layer merge behavior for a concrete toolchain
59
59
60 Those concerns are intentionally outside the core model.
60 Those concerns are intentionally outside the core model.
61
61
62 ---
62 ---
63
63
64 ## Core DSL example
64 ## Core DSL example
65
65
66 ```groovy
66 ```groovy
67 variants {
67 variants {
68 layers {
68 layers {
69 main()
69 main()
70 test()
70 test()
71 generated()
71 generated()
72 rjs()
72 rjs()
73 cjs()
73 cjs()
74 }
74 }
75
75
76 roles {
76 roles {
77 production()
77 production()
78 test()
78 test()
79 tool()
79 tool()
80 }
80 }
81
81
82 variant("browser") {
82 variant("browser") {
83 role("production") {
83 role("production") {
84 layers("main", "generated", "rjs")
84 layers("main", "generated", "rjs")
85 }
85 }
86 role("test") {
86 role("test") {
87 layers("main", "test", "generated", "rjs")
87 layers("main", "test", "generated", "rjs")
88 }
88 }
89 }
89 }
90
90
91 variant("nodejs") {
91 variant("nodejs") {
92 role("production") {
92 role("production") {
93 layers("main", "generated", "cjs")
93 layers("main", "generated", "cjs")
94 }
94 }
95 role("test") {
95 role("test") {
96 layers("main", "test", "generated", "cjs")
96 layers("main", "test", "generated", "cjs")
97 }
97 }
98 role("tool") {
98 role("tool") {
99 layers("main", "generated", "cjs")
99 layers("main", "generated", "cjs")
100 }
100 }
101 }
101 }
102 }
102 }
103 ```
103 ```
104
104
105 ### Interpretation
105 ### Interpretation
106
106
107 This example means:
107 This example means:
108
108
109 * `browser` production uses `main`, `generated`, `rjs`
109 * `browser` production uses `main`, `generated`, `rjs`
110 * `browser` test uses `main`, `test`, `generated`, `rjs`
110 * `browser` test uses `main`, `test`, `generated`, `rjs`
111 * `nodejs` production uses `main`, `generated`, `cjs`
111 * `nodejs` production uses `main`, `generated`, `cjs`
112 * `nodejs` test uses `main`, `test`, `generated`, `cjs`
112 * `nodejs` test uses `main`, `test`, `generated`, `cjs`
113 * `nodejs` tool uses `main`, `generated`, `cjs`
113 * `nodejs` tool uses `main`, `generated`, `cjs`
114
114
115 The model is purely declarative.
115 The model is purely declarative.
116
116
117 ---
117 ---
118
118
119 ## Identity and references
119 ## Identity and references
120
120
121 `Layer`, `Role`, and `Variant` are identity objects.
121 `Layer`, `Role`, and `Variant` are identity objects.
122
122
123 They exist as declared domain values.
123 They exist as declared domain values.
124
124
125 References between model elements are symbolic:
125 References between model elements are symbolic:
126
126
127 * layers are referenced by layer name
127 * layers are referenced by layer name
128 * roles are referenced by role name
128 * roles are referenced by role name
129 * variants are referenced by variant name
129 * variants are referenced by variant name
130
130
131 This is intentional.
131 This is intentional.
132
132
133 The core model is declarative, not navigation-oriented.
133 The core model is declarative, not navigation-oriented.
134
134
135 It is acceptable for aggregates to hold symbolic references to foreign domain values, as long as identity is clearly defined and validated later.
135 It is acceptable for aggregates to hold symbolic references to foreign domain values, as long as identity is clearly defined and validated later.
136
136
137 ---
137 ---
138
138
139 ## Finalization
139 ## Finalization
140
140
141 The `variants` model is finalized once.
141 The `variants` model is finalized once.
142
142
143 Finalization is an internal lifecycle transition. It is typically triggered privately, for example from `afterEvaluate`, but that mechanism is not part of the public API contract.
143 Finalization is an internal lifecycle transition. It is typically triggered privately, for example from `afterEvaluate`, but that mechanism is not part of the public API contract.
144
144
145 The public contract is:
145 The public contract is:
146
146
147 * `variants.whenFinalized(...)`
147 * `variants.whenFinalized(...)`
148
148
149 This callback is **replayable**:
149 This callback is **replayable**:
150
150
151 * if called before finalization, the action is queued
151 * if called before finalization, the action is queued
152 * if called after finalization, the action is invoked immediately
152 * if called after finalization, the action is invoked immediately
153
153
154 The callback receives a finalized, read-only view of the model.
154 The callback receives a finalized, read-only view of the model.
155
155
156 Example:
156 Example:
157
157
158 ```java
158 ```java
159 variants.whenFinalized(view -> {
159 variants.whenFinalized(view -> {
160 // use finalized VariantsView here
160 // use finalized VariantsView here
161 });
161 });
162 ```
162 ```
163
163
164 ---
164 ---
165
165
166 ## `VariantsView`
166 ## `VariantsView`
167
167
168 `VariantsView` is the finalized representation of the core model.
168 `VariantsView` is the finalized representation of the core model.
169
169
170 It contains:
170 It contains:
171
171
172 * all declared `Layer`
172 * all declared `Layer`
173 * all declared `Role`
173 * all declared `Role`
174 * all declared `Variant`
174 * all declared `Variant`
175 * all normalized entries `(variant, role, layer)`
175 * all normalized entries `(variant, role, layer)`
176
176
177 Conceptually:
177 Conceptually:
178
178
179 ```java
179 ```java
180 interface VariantsView {
180 interface VariantsView {
181 Set<Layer> getLayers();
181 Set<Layer> getLayers();
182 Set<Role> getRoles();
182 Set<Role> getRoles();
183 Set<Variant> getVariants();
183 Set<Variant> getVariants();
184 Set<VariantRoleLayer> getEntries();
184 Set<VariantRoleLayer> getEntries();
185 }
185 }
186 ```
186 ```
187
187
188 Where:
188 Where:
189
189
190 ```java
190 ```java
191 record VariantRoleLayer(Variant variant, Role role, Layer layer) {}
191 record VariantRoleLayer(Variant variant, Role role, Layer layer) {}
192 ```
192 ```
193
193
194 This view is:
194 This view is:
195
195
196 * immutable
196 * immutable
197 * normalized
197 * normalized
198 * validated
198 * validated
199 * independent from DSL internals
199 * independent from DSL internals
200
200
201 ---
201 ---
202
202
203 ## Derived views
203 ## Derived views
204
204
205 Two important views can be derived from `VariantsView`:
205 Two important views can be derived from `VariantsView`:
206
206
207 * `CompileUnitsView`
207 * `CompileUnitsView`
208 * `RoleProjectionsView`
208 * `RoleProjectionsView`
209
209
210 These views are not part of the raw core model itself, but they are naturally derived from it.
210 These views are not part of the raw core model itself, but they are naturally derived from it.
211
211
212 ---
212 ---
213
213
214 ## `CompileUnitsView`
214 ## `CompileUnitsView`
215
215
216 ### Purpose
216 ### Purpose
217
217
218 A compile unit is defined as:
218 A compile unit is defined as:
219
219
220 * `(variant, layer)`
220 * `(variant, layer)`
221
221
222 This is based on the following rationale:
222 This is based on the following rationale:
223
223
224 * `variant` defines compilation semantics
224 * `variant` defines compilation semantics
225 * `layer` partitions a variant into separate compilation units
225 * `layer` partitions a variant into separate compilation units
226 * `role` is not a compilation boundary
226 * `role` is not a compilation boundary
227
227
228 This is especially useful for toolchains such as TypeScript, where compilation is often more practical or more correct per layer than for the whole variant at once.
228 This is especially useful for toolchains such as TypeScript, where compilation is often more practical or more correct per layer than for the whole variant at once.
229
229
230 ### Example
230 ### Example
231
231
232 From:
232 From:
233
233
234 * `(browser, production, main)`
234 * `(browser, production, main)`
235 * `(browser, production, rjs)`
235 * `(browser, production, rjs)`
236 * `(browser, test, main)`
236 * `(browser, test, main)`
237 * `(browser, test, test)`
237 * `(browser, test, test)`
238 * `(browser, test, rjs)`
238 * `(browser, test, rjs)`
239
239
240 we derive compile units:
240 we derive compile units:
241
241
242 * `(browser, main)`
242 * `(browser, main)`
243 * `(browser, rjs)`
243 * `(browser, rjs)`
244 * `(browser, test)`
244 * `(browser, test)`
245
245
246 ### Conceptual API
246 ### Conceptual API
247
247
248 ```java
248 ```java
249 interface CompileUnitsView {
249 interface CompileUnitsView {
250 Set<CompileUnit> getUnits();
250 Set<CompileUnit> getUnits();
251 Set<CompileUnit> getUnitsForVariant(Variant variant);
251 Set<CompileUnit> getUnitsForVariant(Variant variant);
252 boolean contains(Variant variant, Layer layer);
252 boolean contains(Variant variant, Layer layer);
253 Set<Role> getRoles(CompileUnit unit);
253 Set<Role> getRoles(CompileUnit unit);
254 }
254 }
255
255
256 record CompileUnit(Variant variant, Layer layer) {}
256 record CompileUnit(Variant variant, Layer layer) {}
257 ```
257 ```
258
258
259 ### Meaning
259 ### Meaning
260
260
261 `CompileUnitsView` answers:
261 `CompileUnitsView` answers:
262
262
263 * what can be compiled
263 * what can be compiled
264 * how a variant is partitioned into compile units
264 * how a variant is partitioned into compile units
265 * which logical roles include a given compile unit
265 * which logical roles include a given compile unit
266
266
267 ---
267 ---
268
268
269 ## `RoleProjectionsView`
269 ## `RoleProjectionsView`
270
270
271 ### Purpose
271 ### Purpose
272
272
273 A role projection is defined as:
273 A role projection is defined as:
274
274
275 * `(variant, role)`
275 * `(variant, role)`
276
276
277 This is based on the following rationale:
277 This is based on the following rationale:
278
278
279 * `role` is not about compilation
279 * `role` is not about compilation
280 * `role` groups compile units by purpose
280 * `role` groups compile units by purpose
281 * roles are more closely related to publication, aggregation, assembly, or result grouping
281 * roles are more closely related to publication, aggregation, assembly, or result grouping
282
282
283 ### Example
283 ### Example
284
284
285 For `browser`:
285 For `browser`:
286
286
287 * `production` includes compile units:
287 * `production` includes compile units:
288
288
289 * `(browser, main)`
289 * `(browser, main)`
290 * `(browser, rjs)`
290 * `(browser, rjs)`
291
291
292 * `test` includes compile units:
292 * `test` includes compile units:
293
293
294 * `(browser, main)`
294 * `(browser, main)`
295 * `(browser, test)`
295 * `(browser, test)`
296 * `(browser, rjs)`
296 * `(browser, rjs)`
297
297
298 ### Conceptual API
298 ### Conceptual API
299
299
300 ```java
300 ```java
301 interface RoleProjectionsView {
301 interface RoleProjectionsView {
302 Set<RoleProjection> getProjections();
302 Set<RoleProjection> getProjections();
303 Set<RoleProjection> getProjectionsForVariant(Variant variant);
303 Set<RoleProjection> getProjectionsForVariant(Variant variant);
304 Set<RoleProjection> getProjectionsForRole(Role role);
304 Set<RoleProjection> getProjectionsForRole(Role role);
305 Set<CompileUnit> getUnits(RoleProjection projection);
305 Set<CompileUnit> getUnits(RoleProjection projection);
306 }
306 }
307
307
308 record RoleProjection(Variant variant, Role role) {}
308 record RoleProjection(Variant variant, Role role) {}
309 ```
309 ```
310
310
311 ### Meaning
311 ### Meaning
312
312
313 `RoleProjectionsView` answers:
313 `RoleProjectionsView` answers:
314
314
315 * how compile units are grouped by purpose
315 * how compile units are grouped by purpose
316 * what belongs to `production`, `test`, `tool`, etc.
316 * what belongs to `production`, `test`, `tool`, etc.
317 * what should be aggregated or published together
317 * what should be aggregated or published together
318
318
319 ---
319 ---
320
320
321 ## Why `CompileUnitsView` and `RoleProjectionsView` are not part of `VariantsView`
321 ## Why `CompileUnitsView` and `RoleProjectionsView` are not part of `VariantsView`
322
322
323 `VariantsView` is intentionally minimal.
323 `VariantsView` is intentionally minimal.
324
324
325 It expresses the domain relation:
325 It expresses the domain relation:
326
326
327 * `(variant, role, layer)`
327 * `(variant, role, layer)`
328
328
329 `CompileUnitsView` and `RoleProjectionsView` are **derived interpretations** of that relation.
329 `CompileUnitsView` and `RoleProjectionsView` are **derived interpretations** of that relation.
330
330
331 They are natural and useful, but they are still interpretations:
331 They are natural and useful, but they are still interpretations:
332
332
333 * `CompileUnit = (variant, layer)`
333 * `CompileUnit = (variant, layer)`
334 * `RoleProjection = (variant, role)`
334 * `RoleProjection = (variant, role)`
335
335
336 This is why they are better treated as derived views rather than direct core model primitives.
336 This is why they are better treated as derived views rather than direct core model primitives.
337
337
338 ---
338 ---
339
339
340 ## `variantSources`: source semantics for layers
340 ## `variantSources`: source semantics for layers
341
341
342 ### Purpose
342 ### Purpose
343
343
344 `variantSources` does **not** define variants.
344 `variantSources` does **not** define variants.
345
345
346 It defines how a declared `Layer` contributes sources.
346 It defines how a declared `Layer` contributes sources.
347
347
348 In other words:
348 In other words:
349
349
350 * `variants` defines **what exists**
350 * `variants` defines **what exists**
351 * `variantSources` defines **how layers become source inputs**
351 * `variantSources` defines **how layers become source inputs**
352
352
353 This distinction is important.
353 This distinction is important.
354
354
355 `variantSources` does not own the variant model. It interprets it.
355 `variantSources` does not own the variant model. It interprets it.
356
356
357 ---
357 ---
358
358
359 ## Main idea
359 ## Main idea
360
360
361 A layer source rule describes the source contribution of a layer.
361 A layer source rule describes the source contribution of a layer.
362
362
363 This is independent of any concrete variant or role.
363 This is independent of any concrete variant or role.
364
364
365 Conceptually:
365 Conceptually:
366
366
367 * `Layer -> source contribution rule`
367 * `Layer -> source contribution rule`
368
368
369 Examples of source contribution semantics:
369 Examples of source contribution semantics:
370
370
371 * base directory
371 * base directory
372 * source directories
372 * source directories
373 * logical source kinds (`ts`, `js`, `resources`)
373 * logical source kinds (`ts`, `js`, `resources`)
374 * declared outputs (`js`, `dts`, `resources`)
374 * declared outputs (`js`, `dts`, `resources`)
375
375
376 ---
376 ---
377
377
378 ## `variantSources` DSL example
378 ## `variantSources` DSL example
379
379
380 ```groovy
380 ```groovy
381 variantSources {
381 variantSources {
382 layerRule("main") {
382 layerRule("main") {
383 from("src/main")
383 from("src/main")
384
384
385 set("ts") {
385 set("ts") {
386 srcDir("ts")
386 srcDir("ts")
387 }
387 }
388 set("js") {
388 set("js") {
389 srcDir("js")
389 srcDir("js")
390 }
390 }
391 set("resources") {
391 set("resources") {
392 srcDir("resources")
392 srcDir("resources")
393 }
393 }
394
394
395 outputs("js", "dts", "resources")
395 outputs("js", "dts", "resources")
396 }
396 }
397
397
398 layerRule("test") {
398 layerRule("test") {
399 from("src/test")
399 from("src/test")
400
400
401 set("ts") {
401 set("ts") {
402 srcDir("ts")
402 srcDir("ts")
403 }
403 }
404 set("resources") {
404 set("resources") {
405 srcDir("resources")
405 srcDir("resources")
406 }
406 }
407
407
408 outputs("js", "dts", "resources")
408 outputs("js", "dts", "resources")
409 }
409 }
410
410
411 layerRule("rjs") {
411 layerRule("rjs") {
412 from("src/rjs")
412 from("src/rjs")
413
413
414 set("ts") {
414 set("ts") {
415 srcDir("ts")
415 srcDir("ts")
416 }
416 }
417
417
418 outputs("js", "dts")
418 outputs("js", "dts")
419 }
419 }
420
420
421 layerRule("cjs") {
421 layerRule("cjs") {
422 from("src/cjs")
422 from("src/cjs")
423
423
424 set("ts") {
424 set("ts") {
425 srcDir("ts")
425 srcDir("ts")
426 }
426 }
427
427
428 outputs("js", "dts")
428 outputs("js", "dts")
429 }
429 }
430 }
430 }
431 ```
431 ```
432
432
433 ### Interpretation
433 ### Interpretation
434
434
435 This means:
435 This means:
436
436
437 * `main` contributes `ts`, `js`, and `resources`
437 * `main` contributes `ts`, `js`, and `resources`
438 * `test` contributes `ts` and `resources`
438 * `test` contributes `ts` and `resources`
439 * `rjs` contributes `ts`
439 * `rjs` contributes `ts`
440 * `cjs` contributes `ts`
440 * `cjs` contributes `ts`
441
441
442 These are layer rules only.
442 These are layer rules only.
443
443
444 They do not yet say which variant consumes them.
444 They do not yet say which variant consumes them.
445
445
446 ---
446 ---
447
447
448 ## Why `variantSources` remains open
448 ## Why `variantSources` remains open
449
449
450 Unlike `variants`, `variantSources` does not need to be closed in the same way.
450 Unlike `variants`, `variantSources` does not need to be closed in the same way.
451
451
452 Reasons:
452 Reasons:
453
453
454 * the DSL is internal to source materialization
454 * the DSL is internal to source materialization
455 * the source of truth for unit existence is already finalized in `VariantsView`
455 * the source of truth for unit existence is already finalized in `VariantsView`
456 * `GenericSourceSetMaterializer` returns `NamedDomainObjectProvider<GenericSourceSet>`
456 * `SourceSetMaterializer` returns `NamedDomainObjectProvider<GenericSourceSet>`
457 * adapters may need to refine source-related behavior after `variants` is finalized
457 * adapters may need to refine source-related behavior after `variants` is finalized
458
458
459 Therefore:
459 Therefore:
460
460
461 * `variants` is finalized
461 * `variants` is finalized
462 * `variantSources` may remain open
462 * `variantSources` may remain open
463
463
464 This is not a contradiction.
464 This is not a contradiction.
465
465
466 It reflects the difference between:
466 It reflects the difference between:
467
467
468 * a closed domain model
468 * a closed domain model
469 * an open infrastructure/materialization model
469 * an open infrastructure/materialization model
470
470
471 This openness is still constrained by explicit policy fixation points:
472
473 * late-configuration policy is fixed when the first selector rule is registered
474 * naming policy is fixed when the finalized `VariantSourcesContext` is created
475
471 ---
476 ---
472
477
473 ## Late configuration policy
478 ## Late configuration policy
474
479
475 Openness of `variantSources` does not mean that late configuration is
480 Openness of `variantSources` does not mean that late configuration is
476 semantically neutral.
481 semantically neutral.
477
482
478 Selector rules may be added after the finalized context becomes available, but
483 Selector rules may be added after the finalized context becomes available, but
479 their behavior against already materialized `GenericSourceSet` objects must be
484 their behavior against already materialized `GenericSourceSet` objects must be
480 controlled explicitly.
485 controlled explicitly.
481
486
482 Conceptually, `variantSources` exposes a policy choice such as:
487 Conceptually, `variantSources` exposes a policy choice such as:
483
488
484 ```groovy
489 ```groovy
485 variantSources {
490 variantSources {
486 lateConfigurationPolicy {
491 lateConfigurationPolicy {
487 failOnLateConfiguration()
492 failOnLateConfiguration()
488 }
493 }
489 }
494 }
490 ```
495 ```
491
496
492 Available modes are:
497 Available modes are:
493
498
494 * `failOnLateConfiguration()`
499 * `failOnLateConfiguration()`
495 * `warnOnLateConfiguration()`
500 * `warnOnLateConfiguration()`
496 * `allowLateConfiguration()`
501 * `allowLateConfiguration()`
497
502
498 Meaning:
503 Meaning:
499
504
500 * `fail` rejects selector rules that target already materialized source sets
505 * `fail` rejects selector rules that target already materialized source sets
501 * `warn` allows them but emits a warning
506 * `warn` allows them but emits a warning
502 * `allow` allows them silently
507 * `allow` allows them silently
503
508
504 This policy is intentionally modeled as an imperative choice, not as a mutable
509 This policy is intentionally modeled as an imperative choice, not as a mutable
505 property:
510 property:
506
511
507 * it must be chosen before the first selector rule is added
512 * it must be chosen before the first selector rule is added
508 * selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
513 * selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
509 * once chosen, it cannot be changed later
514 * once chosen, it cannot be changed later
510 * it controls runtime behavior, not just a stored value
515 * it controls runtime behavior, not just a stored value
516 * the enforcement point is the first selector registration itself, not variants
517 finalization in isolation
511
518
512 For source sets configured before materialization, selector precedence remains:
519 For source sets configured before materialization, selector precedence remains:
513
520
514 ```text
521 ```text
515 variant < layer < unit
522 variant < layer < unit
516 ```
523 ```
517
524
518 For already materialized source sets in `warn` and `allow` modes:
525 For already materialized source sets in `warn` and `allow` modes:
519
526
520 * the late action is applied as an imperative follow-up step
527 * the late action is applied as an imperative follow-up step
521 * selector precedence is not reconstructed retroactively
528 * selector precedence is not reconstructed retroactively
522 * actual observation order is the order in which late actions are registered
529 * actual observation order is the order in which late actions are registered
523
530
524 ---
531 ---
525
532
533 ## Compile-unit naming policy
534
535 Source-set naming is treated as a separate policy concern from selector
536 registration.
537
538 Conceptually, `variantSources` exposes:
539
540 ```groovy
541 variantSources {
542 namingPolicy {
543 failOnNameCollision()
544 }
545 }
546 ```
547
548 The base projected name of a compile unit is:
549
550 ```text
551 variantName + capitalize(layerName)
552 ```
553
554 Examples:
555
556 * `(browser, main)` -> `browserMain`
557 * `(browser, rjs)` -> `browserRjs`
558
559 Available modes are:
560
561 * `failOnNameCollision()` - reject finalized compile-unit models that project
562 the same source-set name for different compile units
563 * `resolveNameCollision()` - resolve such conflicts deterministically
564
565 ### `resolveNameCollision()` semantics
566
567 Conflicting compile units are ordered canonically by:
568
569 ```text
570 (variant.name, layer.name)
571 ```
572
573 Within one conflicting group:
574
575 * the first compile unit keeps the base name
576 * the second gets suffix `2`
577 * the third gets suffix `3`
578 * and so on
579
580 For example, if:
581
582 * `(foo, variantBar)` projects to `fooVariantBar`
583 * `(fooVariant, bar)` also projects to `fooVariantBar`
584
585 then canonical ordering yields:
586
587 * `(foo, variantBar)` -> `fooVariantBar`
588 * `(fooVariant, bar)` -> `fooVariantBar2`
589
590 ### Fixation point
591
592 Naming policy is fixed when the finalized `VariantSourcesContext` is created.
593
594 Operationally this means:
595
596 * naming policy must be selected before `variantSources.whenFinalized(...)`
597 becomes observable
598 * compile-unit names are projected and validated before queued
599 `whenFinalized(...)` callbacks are replayed
600 * changing naming policy from inside a `whenFinalized(...)` callback is too late
601
602 This differs intentionally from late-configuration policy:
603
604 * late-configuration policy is fixed by the first selector rule
605 * naming policy is fixed by finalized-context creation
606
607 ---
608
526 ## `VariantSourcesContext`
609 ## `VariantSourcesContext`
527
610
528 `variantSources.whenFinalized(...)` remains useful, but not because `variantSources` itself is frozen.
611 `variantSources.whenFinalized(...)` remains useful, but not because `variantSources` itself is frozen.
529
612
530 Its purpose is to provide access to a finalized context derived from `variants`.
613 Its purpose is to provide access to a finalized context derived from `variants`.
531
614
532 This context contains:
615 This context contains:
533
616
534 * `CompileUnitsView`
617 * `CompileUnitsView`
535 * `RoleProjectionsView`
618 * `RoleProjectionsView`
536 * `GenericSourceSetMaterializer`
619 * `SourceSetMaterializer`
620
621 By the time the context becomes observable:
622
623 * compile-unit naming policy is already fixed
624 * symbolic source-set names for finalized compile units are already determined
537
625
538 Conceptually:
626 Conceptually:
539
627
540 ```java
628 ```java
541 interface VariantSourcesContext {
629 interface VariantSourcesContext {
542 CompileUnitsView getCompileUnits();
630 CompileUnitsView getCompileUnits();
543 RoleProjectionsView getRoleProjections();
631 RoleProjectionsView getRoleProjections();
544 GenericSourceSetMaterializer getSourceSets();
632 SourceSetMaterializer getSourceSets();
545 }
633 }
546 ```
634 ```
547
635
548 This callback is also replayable.
636 This callback is also replayable.
549
637
550 Example:
638 Example:
551
639
552 ```java
640 ```java
553 variantSources.whenFinalized(ctx -> {
641 variantSources.whenFinalized(ctx -> {
554 var units = ctx.getCompileUnits();
642 var units = ctx.getCompileUnits();
555 var roles = ctx.getRoleProjections();
643 var roles = ctx.getRoleProjections();
556 var sourceSets = ctx.getSourceSets();
644 var sourceSets = ctx.getSourceSets();
557 });
645 });
558 ```
646 ```
559
647
560 ---
648 ---
561
649
562 ## `GenericSourceSetMaterializer`
650 ## `SourceSetMaterializer`
563
651
564 ### Purpose
652 ### Purpose
565
653
566 `GenericSourceSetMaterializer` is the official source of truth for materialized source sets.
654 `SourceSetMaterializer` is the official source of truth for materialized source sets.
567
655
568 It is responsible for:
656 It is responsible for:
569
657
570 * lazy creation of `GenericSourceSet`
658 * lazy creation of `GenericSourceSet`
659 * projecting finalized compile units to symbolic source-set names
660 * validating or resolving name collisions according to naming policy
571 * applying `layerRule`
661 * applying `layerRule`
572 * connecting a compile unit to a source set provider
662 * connecting a compile unit to a source set provider
573 * exposing source sets to adapters
663 * exposing source sets to adapters
574
664
575 This is the correct place to apply `layerRule`.
665 This is the correct place to apply `layerRule`.
576
666
577 Adapters should not apply layer rules themselves.
667 Adapters should not apply layer rules themselves.
578
668
579 ### Conceptual API
669 ### Conceptual API
580
670
581 ```java
671 ```java
582 interface GenericSourceSetMaterializer {
672 interface SourceSetMaterializer {
583 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
673 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
584 }
674 }
585 ```
675 ```
586
676
587 ---
677 ---
588
678
589 ## Why `GenericSourceSetMaterializer` should own `layerRule` application
679 ## Why `SourceSetMaterializer` should own `layerRule` application
590
680
591 If adapters applied `layerRule` directly, responsibility would leak across multiple layers:
681 If adapters applied `layerRule` directly, responsibility would leak across multiple layers:
592
682
593 * one component would know compile units
683 * one component would know compile units
594 * another would know source semantics
684 * another would know source semantics
595 * another would know how to configure `GenericSourceSet`
685 * another would know how to configure `GenericSourceSet`
596
686
597 This would make the model harder to reason about.
687 This would make the model harder to reason about.
598
688
599 Instead:
689 Instead:
600
690
601 * `layerRule` is DSL/spec-level
691 * `layerRule` is DSL/spec-level
602 * `GenericSourceSetMaterializer` is execution/materialization-level
692 * `SourceSetMaterializer` is execution/materialization-level
603 * adapters are consumption-level
693 * adapters are consumption-level
604
694
605 This gives a much cleaner separation.
695 This gives a much cleaner separation.
606
696
607 ---
697 ---
608
698
609 ## `GenericSourceSet` as materialization target
699 ## `GenericSourceSet` as materialization target
610
700
611 `GenericSourceSet` is the materialized source aggregation object.
701 `GenericSourceSet` is the materialized source aggregation object.
612
702
613 It is a good fit because it can represent:
703 It is a good fit because it can represent:
614
704
615 * multiple logical source sets
705 * multiple logical source sets
616 * aggregated source directories
706 * aggregated source directories
617 * declared outputs
707 * declared outputs
618 * lazy registration through providers
708 * lazy registration through providers
619
709
620 The materializer is therefore the owner of:
710 The materializer is therefore the owner of:
621
711
622 * creating `GenericSourceSet`
712 * creating `GenericSourceSet`
623 * populating its source sets
713 * populating its source sets
624 * declaring outputs
714 * declaring outputs
625 * returning a provider for later use
715 * returning a provider for later use
626
716
627 ---
717 ---
628
718
629 ## How an adapter should use the model
719 ## How an adapter should use the model
630
720
631 Example:
721 Example:
632
722
633 ```java
723 ```java
634 variantSources.whenFinalized(ctx -> {
724 variantSources.whenFinalized(ctx -> {
635 for (CompileUnit unit : ctx.getCompileUnits().getUnits()) {
725 for (CompileUnit unit : ctx.getCompileUnits().getUnits()) {
636 var sourceSetProvider = ctx.getSourceSets().getSourceSet(unit);
726 var sourceSetProvider = ctx.getSourceSets().getSourceSet(unit);
637
727
638 var variant = unit.variant();
728 var variant = unit.variant();
639 var layer = unit.layer();
729 var layer = unit.layer();
640
730
641 // create compile task for this compile unit
731 // create compile task for this compile unit
642 // configure compiler options from variant semantics
732 // configure compiler options from variant semantics
643 // use sourceSetProvider as task input
733 // use sourceSetProvider as task input
644 }
734 }
645
735
646 for (RoleProjection projection : ctx.getRoleProjections().getProjections()) {
736 for (RoleProjection projection : ctx.getRoleProjections().getProjections()) {
647 var units = ctx.getRoleProjections().getUnits(projection);
737 var units = ctx.getRoleProjections().getUnits(projection);
648
738
649 // aggregate outputs of included compile units
739 // aggregate outputs of included compile units
650 // use for publication or assembly
740 // use for publication or assembly
651 }
741 }
652 });
742 });
653 ```
743 ```
654
744
655 ---
745 ---
656
746
657 ## Why compile unit is `(variant, layer)` and not `(variant, role)` or `(variant, role, layer)`
747 ## Why compile unit is `(variant, layer)` and not `(variant, role)` or `(variant, role, layer)`
658
748
659 ### Not `(variant, role)`
749 ### Not `(variant, role)`
660
750
661 Because role is not a compilation boundary.
751 Because role is not a compilation boundary.
662
752
663 Role is a logical grouping of results.
753 Role is a logical grouping of results.
664
754
665 ### Not `(variant, role, layer)`
755 ### Not `(variant, role, layer)`
666
756
667 Because role does not define the compile unit itself.
757 Because role does not define the compile unit itself.
668
758
669 A compile unit is a unit of compilation, not a unit of publication grouping.
759 A compile unit is a unit of compilation, not a unit of publication grouping.
670
760
671 ### Correct interpretation
761 ### Correct interpretation
672
762
673 * `(variant, layer)` = compile unit
763 * `(variant, layer)` = compile unit
674 * `(variant, role)` = logical result group
764 * `(variant, role)` = logical result group
675 * `(variant, role, layer)` = membership relation between them
765 * `(variant, role, layer)` = membership relation between them
676
766
677 This is the most coherent separation.
767 This is the most coherent separation.
678
768
679 ---
769 ---
680
770
681 ## Model boundaries
771 ## Model boundaries
682
772
683 ### What belongs to `variants`
773 ### What belongs to `variants`
684
774
685 * declared domains: `Layer`, `Role`, `Variant`
775 * declared domains: `Layer`, `Role`, `Variant`
686 * normalized relation `(variant, role, layer)`
776 * normalized relation `(variant, role, layer)`
687 * finalization lifecycle
777 * finalization lifecycle
688 * finalized `VariantsView`
778 * finalized `VariantsView`
689
779
690 ### What belongs to derived views
780 ### What belongs to derived views
691
781
692 * compile units: `(variant, layer)`
782 * compile units: `(variant, layer)`
693 * role projections: `(variant, role)`
783 * role projections: `(variant, role)`
694
784
695 ### What belongs to `variantSources`
785 ### What belongs to `variantSources`
696
786
697 * source semantics of layers
787 * source semantics of layers
698 * source materialization rules
788 * source materialization rules
699 * lazy `GenericSourceSet` provisioning
789 * lazy `GenericSourceSet` provisioning
700 * source adapter integration
790 * source adapter integration
701
791
702 ### What does not belong to `variants`
792 ### What does not belong to `variants`
703
793
704 * source directories
794 * source directories
705 * base paths
795 * base paths
706 * output declarations
796 * output declarations
707 * source set layout
797 * source set layout
708 * task registration
798 * task registration
709 * compiler-specific assumptions
799 * compiler-specific assumptions
710
800
711 ---
801 ---
712
802
713 ## Design principles
803 ## Design principles
714
804
715 ### 1. Keep the core model small
805 ### 1. Keep the core model small
716
806
717 The core model should only contain domain facts.
807 The core model should only contain domain facts.
718
808
719 ### 2. Separate domain truth from materialization
809 ### 2. Separate domain truth from materialization
720
810
721 The existence of compile units comes from `VariantsView`, not from source rules.
811 The existence of compile units comes from `VariantsView`, not from source rules.
722
812
723 ### 3. Treat source materialization as infrastructure
813 ### 3. Treat source materialization as infrastructure
724
814
725 `variantSources` is an interpretation layer, not the source of truth.
815 `variantSources` is an interpretation layer, not the source of truth.
726
816
727 ### 4. Prefer replayable finalized hooks
817 ### 4. Prefer replayable finalized hooks
728
818
729 Adapters should not depend on raw Gradle lifecycle callbacks such as `afterEvaluate`.
819 Adapters should not depend on raw Gradle lifecycle callbacks such as `afterEvaluate`.
730
820
731 ### 5. Make late behavior explicit
821 ### 5. Make late behavior explicit
732
822
733 Late configuration after materialization is a policy decision, not an implicit
823 Late configuration after materialization is a policy decision, not an implicit
734 guarantee.
824 guarantee.
735
825
736 ### 6. Keep heavy runtime objects behind providers
826 ### 6. Keep heavy runtime objects behind providers
737
827
738 Materialized `GenericSourceSet` objects should remain behind a lazy API.
828 Materialized `GenericSourceSet` objects should remain behind a lazy API.
739
829
830 ### 7. Make name-collision behavior explicit
831
832 Compile-unit naming must be governed by an explicit policy, not by incidental
833 materialization order.
834
740 ---
835 ---
741
836
742 ## Summary
837 ## Summary
743
838
744 The model is intentionally split into two layers.
839 The model is intentionally split into two layers.
745
840
746 ### `variants`
841 ### `variants`
747
842
748 A closed, finalized domain model:
843 A closed, finalized domain model:
749
844
750 * `Layer`
845 * `Layer`
751 * `Role`
846 * `Role`
752 * `Variant`
847 * `Variant`
753 * `(variant, role, layer)`
848 * `(variant, role, layer)`
754
849
755 ### `variantSources`
850 ### `variantSources`
756
851
757 An open, source-materialization layer:
852 An open, source-materialization layer:
758
853
759 * layer source rules
854 * layer source rules
760 * compile-unit source set materialization
855 * compile-unit source set materialization
856 * compile-unit naming policy
761 * adapter-facing `GenericSourceSet` providers
857 * adapter-facing `GenericSourceSet` providers
762
858
763 ### Derived views
859 ### Derived views
764
860
765 From the finalized variant model:
861 From the finalized variant model:
766
862
767 * `CompileUnitsView`: `(variant, layer)`
863 * `CompileUnitsView`: `(variant, layer)`
768 * `RoleProjectionsView`: `(variant, role)`
864 * `RoleProjectionsView`: `(variant, role)`
769
865
770 ### Operational interpretation
866 ### Operational interpretation
771
867
772 * `variant` defines compilation semantics
868 * `variant` defines compilation semantics
773 * `layer` partitions compilation
869 * `layer` partitions compilation
774 * `role` groups results by purpose
870 * `role` groups results by purpose
775
871
776 This keeps the core model stable and minimal, while allowing source handling and adapter integration to remain flexible.
872 This keeps the core model stable and minimal, while allowing source handling and adapter integration to remain flexible.
General Comments 0
You need to be logged in to leave comments. Login now