| This diff has been collapsed as it changes many lines, (694 lines changed) Show them Hide them | |||
| @@ -0,0 +1,694 | |||
|
|
1 | # Variants and Variant Artifacts | |
|
|
2 | ||
|
|
3 | ## Overview | |
|
|
4 | ||
|
|
5 | This document describes the artifact model built on top of `variants` and | |
|
|
6 | `variantSources`. | |
|
|
7 | ||
|
|
8 | The goal is to define: | |
|
|
9 | ||
|
|
10 | - a stable artifact-facing model for outgoing contracts; | |
|
|
11 | - a DSL for declaring slots and their inputs; | |
|
|
12 | - a resolver bridge between `variantArtifacts` and `variantSources`; | |
|
|
13 | - extension points for deduplication and similar policies; | |
|
|
14 | - a model that remains live during configuration and does not require an | |
|
|
15 | artificial freeze of slot content. | |
|
|
16 | ||
|
|
17 | The design follows the same split already used elsewhere: | |
|
|
18 | ||
|
|
19 | - `variants` defines the closed domain topology; | |
|
|
20 | - `variantSources` defines source materialization semantics over that topology; | |
|
|
21 | - `variantArtifacts` defines outgoing artifact contracts over that topology. | |
|
|
22 | ||
|
|
23 | --- | |
|
|
24 | ||
|
|
25 | ## Core idea | |
|
|
26 | ||
|
|
27 | `variantArtifacts` is not the owner of the variant model and not the owner of | |
|
|
28 | source-set materialization. | |
|
|
29 | ||
|
|
30 | Its purpose is narrower: | |
|
|
31 | ||
|
|
32 | - decide which variants participate in outgoing publication; | |
|
|
33 | - define artifact slots for those outgoing variants; | |
|
|
34 | - declare how each slot gathers its inputs. | |
|
|
35 | ||
|
|
36 | This makes `variantArtifacts` an outgoing-contract layer, not a compilation or | |
|
|
37 | source-materialization layer. | |
|
|
38 | ||
|
|
39 | --- | |
|
|
40 | ||
|
|
41 | ## Model boundaries | |
|
|
42 | ||
|
|
43 | ### What belongs to `variants` | |
|
|
44 | ||
|
|
45 | - identities: `Variant`, `Role`, `Layer`; | |
|
|
46 | - normalized topology relation `(variant, role, layer)`; | |
|
|
47 | - finalization of the core domain model; | |
|
|
48 | - `VariantsView` and derived topology views. | |
|
|
49 | ||
|
|
50 | ### What belongs to `variantSources` | |
|
|
51 | ||
|
|
52 | - compile units and role projections derived from finalized `variants`; | |
|
|
53 | - source-set naming policy; | |
|
|
54 | - source-set materialization; | |
|
|
55 | - lazy access to `GenericSourceSet` providers and their named outputs. | |
|
|
56 | ||
|
|
57 | ### What belongs to `variantArtifacts` | |
|
|
58 | ||
|
|
59 | - selection of outgoing variants; | |
|
|
60 | - slot identities inside an outgoing variant; | |
|
|
61 | - slot assembly declarations; | |
|
|
62 | - root outgoing configurations for outgoing variants; | |
|
|
63 | - slot-level assembly bodies; | |
|
|
64 | - publication-facing hooks. | |
|
|
65 | ||
|
|
66 | ### What does not belong to `variantArtifacts` | |
|
|
67 | ||
|
|
68 | - ownership of variant existence; | |
|
|
69 | - ownership of source-set naming; | |
|
|
70 | - direct mutation of source materialization internals; | |
|
|
71 | - compiler- or toolchain-specific logic; | |
|
|
72 | - eager flattening of source inputs into files during DSL declaration. | |
|
|
73 | ||
|
|
74 | --- | |
|
|
75 | ||
|
|
76 | ## Outgoing subset | |
|
|
77 | ||
|
|
78 | Not every declared `Variant` must become outgoing. | |
|
|
79 | ||
|
|
80 | `variantArtifacts` defines an outgoing subset of variants. | |
|
|
81 | ||
|
|
82 | For each outgoing variant there is one root outgoing aggregate: | |
|
|
83 | ||
|
|
84 | - one root consumable `Configuration`; | |
|
|
85 | - one or more artifact slots; | |
|
|
86 | - one primary slot; | |
|
|
87 | - optional secondary slots. | |
|
|
88 | ||
|
|
89 | This is why `OutgoingConfiguration` is a real model object and not merely a | |
|
|
90 | publication event payload. | |
|
|
91 | ||
|
|
92 | It represents a live build-facing aggregate: | |
|
|
93 | ||
|
|
94 | - it has its own attributes; | |
|
|
95 | - it is visible to other build logic as soon as registered; | |
|
|
96 | - it contains lazy handles rather than eagerly materialized state; | |
|
|
97 | - it is distinct from slot assembly state. | |
|
|
98 | ||
|
|
99 | --- | |
|
|
100 | ||
|
|
101 | ## Identity-first split | |
|
|
102 | ||
|
|
103 | The artifact model should preserve the same identity-first principle used for | |
|
|
104 | the rest of the project. | |
|
|
105 | ||
|
|
106 | ### Identity objects | |
|
|
107 | ||
|
|
108 | - `Variant` | |
|
|
109 | - `Slot` | |
|
|
110 | - `ArtifactSlot = (variant, slot)` | |
|
|
111 | ||
|
|
112 | These objects are: | |
|
|
113 | ||
|
|
114 | - cheap; | |
|
|
115 | - replayable; | |
|
|
116 | - suitable for discovery and selection. | |
|
|
117 | ||
|
|
118 | ### Stateful objects | |
|
|
119 | ||
|
|
120 | - `OutgoingConfiguration` | |
|
|
121 | - `ArtifactAssembly` | |
|
|
122 | ||
|
|
123 | These objects are: | |
|
|
124 | ||
|
|
125 | - build-facing; | |
|
|
126 | - allowed to contain Gradle lazy handles; | |
|
|
127 | - obtained through dedicated APIs rather than embedded into identity objects. | |
|
|
128 | ||
|
|
129 | ### Rule of thumb | |
|
|
130 | ||
|
|
131 | - slot identity is cheap and replayable; | |
|
|
132 | - inside one `OutgoingConfiguration`, `Slot` is the natural local identity; | |
|
|
133 | - `ArtifactSlot` is useful when slot identity must be referenced outside the | |
|
|
134 | parent outgoing configuration; | |
|
|
135 | - slot body is stateful and resolved separately; | |
|
|
136 | - outgoing configuration is a live aggregate, not a mere snapshot. | |
|
|
137 | ||
|
|
138 | --- | |
|
|
139 | ||
|
|
140 | ## Artifact model | |
|
|
141 | ||
|
|
142 | Conceptually: | |
|
|
143 | ||
|
|
144 | ```java | |
|
|
145 | interface VariantArtifactsContext { | |
|
|
146 | VariantsView getVariants(); | |
|
|
147 | void all(Action<? super OutgoingConfiguration> action); | |
|
|
148 | Optional<OutgoingConfiguration> findArtifacts(Variant variant); | |
|
|
149 | OutgoingConfiguration requireArtifacts(Variant variant); | |
|
|
150 | ArtifactAssemblies getAssemblies(); | |
|
|
151 | } | |
|
|
152 | ``` | |
|
|
153 | ||
|
|
154 | ```java | |
|
|
155 | interface OutgoingConfiguration { | |
|
|
156 | Variant getVariant(); | |
|
|
157 | NamedDomainObjectProvider<Configuration> getOutgoingConfiguration(); | |
|
|
158 | NamedDomainObjectContainer<Slot> getSlots(); | |
|
|
159 | Property<Slot> getPrimarySlot(); | |
|
|
160 | } | |
|
|
161 | ``` | |
|
|
162 | ||
|
|
163 | ```java | |
|
|
164 | interface ArtifactAssemblies { | |
|
|
165 | ArtifactAssembly resolveSlot(ArtifactSlot slot); | |
|
|
166 | } | |
|
|
167 | ``` | |
|
|
168 | ||
|
|
169 | This intentionally uses a Gradle-style local slot container rather than a | |
|
|
170 | separate `ArtifactSlotsView`. | |
|
|
171 | ||
|
|
172 | The reason is simple: | |
|
|
173 | ||
|
|
174 | - inside one `OutgoingConfiguration`, slot identity is local and naturally | |
|
|
175 | expressed as `Slot`; | |
|
|
176 | - a dedicated `ArtifactSlotsView` would suggest a detached readonly projection | |
|
|
177 | without clearly owning mutation/configuration semantics; | |
|
|
178 | - `NamedDomainObjectContainer<Slot>` better matches the live configuration model | |
|
|
179 | and keeps the parent aggregate as the owner of slot structure. | |
|
|
180 | ||
|
|
181 | `ArtifactSlot` remains useful, but only as a fully qualified identity outside | |
|
|
182 | the parent aggregate: | |
|
|
183 | ||
|
|
184 | - resolver APIs; | |
|
|
185 | - global payloads; | |
|
|
186 | - cross-variant references. | |
|
|
187 | ||
|
|
188 | Important distinction: | |
|
|
189 | ||
|
|
190 | - `OutgoingConfiguration` describes outgoing publication structure; | |
|
|
191 | - `ArtifactAssembly` describes how one slot artifact is assembled. | |
|
|
192 | ||
|
|
193 | An `ArtifactAssembly` does not create the root outgoing configuration. It serves | |
|
|
194 | one already declared slot inside that configuration. | |
|
|
195 | ||
|
|
196 | ### Primary slot ownership | |
|
|
197 | ||
|
|
198 | Primary status belongs to the parent outgoing configuration, not to the slot | |
|
|
199 | itself. | |
|
|
200 | ||
|
|
201 | This is why the preferred container-level contract is: | |
|
|
202 | ||
|
|
203 | ```java | |
|
|
204 | Property<Slot> getPrimarySlot(); | |
|
|
205 | ``` | |
|
|
206 | ||
|
|
207 | and not a slot-local boolean or a derived `ArtifactSlot` reference. | |
|
|
208 | ||
|
|
209 | Reasons: | |
|
|
210 | ||
|
|
211 | - the primary role is assigned by the parent aggregate; | |
|
|
212 | - within one `OutgoingConfiguration`, the variant identity is already known, so | |
|
|
213 | `Slot` is sufficient and avoids redundant `(variant, slot)` duplication; | |
|
|
214 | - `Property` matches the rest of the Gradle-facing live model better than a | |
|
|
215 | custom `findPrimarySlot()` style API; | |
|
|
216 | - `Property` gives useful write/configure/finalize semantics without inventing a | |
|
|
217 | separate special lifecycle abstraction. | |
|
|
218 | ||
|
|
219 | Expected usage: | |
|
|
220 | ||
|
|
221 | - DSL or adapters may assign the primary slot through `set(...)`; | |
|
|
222 | - adapters that want to provide a default may use `convention(...)`; | |
|
|
223 | - the property may remain unset while the model is still incomplete; | |
|
|
224 | - at materialization time the property may be finalized; | |
|
|
225 | - if more than one slot exists and `primarySlot` is still unset at the | |
|
|
226 | materialization point, that is a model error. | |
|
|
227 | ||
|
|
228 | The model should still enforce that the selected primary slot belongs to the | |
|
|
229 | same `OutgoingConfiguration`. | |
|
|
230 | ||
|
|
231 | ### Proposed public shape | |
|
|
232 | ||
|
|
233 | With these constraints, the preferred public structure is: | |
|
|
234 | ||
|
|
235 | ```java | |
|
|
236 | interface VariantArtifactsContext { | |
|
|
237 | VariantsView getVariants(); | |
|
|
238 | void all(Action<? super OutgoingConfiguration> action); | |
|
|
239 | Optional<OutgoingConfiguration> findArtifacts(Variant variant); | |
|
|
240 | OutgoingConfiguration requireArtifacts(Variant variant); | |
|
|
241 | ArtifactAssemblies getAssemblies(); | |
|
|
242 | } | |
|
|
243 | ||
|
|
244 | interface OutgoingConfiguration { | |
|
|
245 | Variant getVariant(); | |
|
|
246 | NamedDomainObjectProvider<Configuration> getOutgoingConfiguration(); | |
|
|
247 | NamedDomainObjectContainer<Slot> getSlots(); | |
|
|
248 | Property<Slot> getPrimarySlot(); | |
|
|
249 | } | |
|
|
250 | ||
|
|
251 | interface ArtifactAssemblies { | |
|
|
252 | ArtifactAssembly resolveSlot(ArtifactSlot slot); | |
|
|
253 | } | |
|
|
254 | ||
|
|
255 | record ArtifactSlot(Variant variant, Slot slot) {} | |
|
|
256 | ``` | |
|
|
257 | ||
|
|
258 | This gives: | |
|
|
259 | ||
|
|
260 | - one global registry of outgoing variants; | |
|
|
261 | - one local slot container per outgoing variant; | |
|
|
262 | - one fully qualified slot identity for resolver and cross-aggregate use; | |
|
|
263 | - one explicit service for slot assembly materialization. | |
|
|
264 | ||
|
|
265 | ### Minimal internal shape | |
|
|
266 | ||
|
|
267 | The preferred minimal internal structure is: | |
|
|
268 | ||
|
|
269 | ```java | |
|
|
270 | final class VariantArtifactsRegistry implements VariantArtifactsContext { | |
|
|
271 | OutgoingConfiguration outgoingConfiguration(Variant variant); | |
|
|
272 | ArtifactAssemblyRules slotRules(ArtifactSlot slot); | |
|
|
273 | ArtifactAssemblies assemblies(); | |
|
|
274 | } | |
|
|
275 | ||
|
|
276 | interface ArtifactAssemblyRules { | |
|
|
277 | void from(Object input); | |
|
|
278 | void fromVariant(Action<? super OutputSelectionSpec> action); | |
|
|
279 | void fromRole(String roleName, Action<? super OutputSelectionSpec> action); | |
|
|
280 | void fromLayer(String layerName, Action<? super OutputSelectionSpec> action); | |
|
|
281 | } | |
|
|
282 | ``` | |
|
|
283 | ||
|
|
284 | Responsibility split: | |
|
|
285 | ||
|
|
286 | - `VariantArtifactsRegistry` owns the whole artifact model; | |
|
|
287 | - `OutgoingConfiguration` owns only the structural variant-local aggregate; | |
|
|
288 | - `ArtifactAssemblyRules` owns the content declaration of one slot; | |
|
|
289 | - `ArtifactAssemblies` materializes `ArtifactAssembly` from | |
|
|
290 | `ArtifactSlot -> ArtifactAssemblyRules`. | |
|
|
291 | ||
|
|
292 | This keeps `OutgoingConfiguration` focused on structure and avoids overloading it | |
|
|
293 | with slot-content APIs such as `rules(slot)`. | |
|
|
294 | ||
|
|
295 | ### DSL binding | |
|
|
296 | ||
|
|
297 | The DSL should be connected through the registry, not by making | |
|
|
298 | `OutgoingConfiguration` responsible for content rules. | |
|
|
299 | ||
|
|
300 | Conceptually: | |
|
|
301 | ||
|
|
302 | ```java | |
|
|
303 | variantArtifacts.variant("browser", spec -> { | |
|
|
304 | spec.slot("runtime", assembly -> { | |
|
|
305 | assembly.fromRole("production", out -> out.output("js")); | |
|
|
306 | }); | |
|
|
307 | }); | |
|
|
308 | ``` | |
|
|
309 | ||
|
|
310 | Operationally this means: | |
|
|
311 | ||
|
|
312 | 1. registry creates or returns `OutgoingConfiguration` for the variant; | |
|
|
313 | 2. slot declaration creates or returns `Slot` inside that outgoing configuration; | |
|
|
314 | 3. registry forms `ArtifactSlot(variant, slot)`; | |
|
|
315 | 4. registry resolves `slotRules(artifactSlot)`; | |
|
|
316 | 5. bound `ArtifactAssemblySpec` writes into those rules. | |
|
|
317 | ||
|
|
318 | So the DSL writes: | |
|
|
319 | ||
|
|
320 | - structure into `OutgoingConfiguration`; | |
|
|
321 | - slot content into registry-owned `ArtifactAssemblyRules`. | |
|
|
322 | ||
|
|
323 | This is the intended bridge point between the public DSL and the internal | |
|
|
324 | resolver/materialization model. | |
|
|
325 | ||
|
|
326 | --- | |
|
|
327 | ||
|
|
328 | ## Live model and monotonic structure | |
|
|
329 | ||
|
|
330 | `variantArtifacts` should be treated as a live configuration model during the | |
|
|
331 | whole configuration phase. | |
|
|
332 | ||
|
|
333 | This means: | |
|
|
334 | ||
|
|
335 | - slot inputs remain live; | |
|
|
336 | - `from(...)`, `fromVariant(...)`, `fromRole(...)`, `fromLayer(...)` may keep | |
|
|
337 | contributing inputs until task execution; | |
|
|
338 | - `ArtifactAssembly` may expose live `FileCollection`, `Provider`, and task | |
|
|
339 | wiring; | |
|
|
340 | - external task outputs remain outside the control of this model and must be | |
|
|
341 | accepted as live inputs. | |
|
|
342 | ||
|
|
343 | The model should therefore avoid a mandatory freeze phase for slot content. | |
|
|
344 | ||
|
|
345 | Instead, it should follow a monotonic rule: | |
|
|
346 | ||
|
|
347 | - outgoing variant existence may grow; | |
|
|
348 | - slot existence may grow; | |
|
|
349 | - slot content may grow; | |
|
|
350 | - publication-visible identity should not be retroactively redefined. | |
|
|
351 | ||
|
|
352 | In practice this means: | |
|
|
353 | ||
|
|
354 | - slot names are stable once declared; | |
|
|
355 | - primary slot designation is structural; | |
|
|
356 | - slot input content remains live. | |
|
|
357 | ||
|
|
358 | This also means that the model does not need a dedicated freeze phase for slot | |
|
|
359 | content merely because the root outgoing configuration was registered earlier. | |
|
|
360 | ||
|
|
361 | Early registration of the root `Configuration` and live evolution of slot input | |
|
|
362 | content are compatible concerns. | |
|
|
363 | ||
|
|
364 | --- | |
|
|
365 | ||
|
|
366 | ## DSL principles | |
|
|
367 | ||
|
|
368 | The DSL should remain declarative and symbolic. | |
|
|
369 | ||
|
|
370 | It should describe: | |
|
|
371 | ||
|
|
372 | - which variant is outgoing; | |
|
|
373 | - which slots exist; | |
|
|
374 | - which slot is primary; | |
|
|
375 | - which selectors contribute inputs to each slot. | |
|
|
376 | ||
|
|
377 | It should not directly expose: | |
|
|
378 | ||
|
|
379 | - `GenericSourceSet`; | |
|
|
380 | - `FileCollection`; | |
|
|
381 | - concrete resolved files from `variantSources`; | |
|
|
382 | - internal resolver state. | |
|
|
383 | ||
|
|
384 | ### DSL shape | |
|
|
385 | ||
|
|
386 | Conceptually: | |
|
|
387 | ||
|
|
388 | ```groovy | |
|
|
389 | variantArtifacts { | |
|
|
390 | variant("browser") { | |
|
|
391 | primarySlot("runtime") { | |
|
|
392 | fromRole("production") { | |
|
|
393 | output("js") | |
|
|
394 | output("resources") | |
|
|
395 | } | |
|
|
396 | } | |
|
|
397 | ||
|
|
398 | slot("types") { | |
|
|
399 | fromVariant { | |
|
|
400 | output("dts") | |
|
|
401 | } | |
|
|
402 | } | |
|
|
403 | ||
|
|
404 | slot("sources") { | |
|
|
405 | fromLayer("main") { | |
|
|
406 | output("sources") | |
|
|
407 | } | |
|
|
408 | } | |
|
|
409 | ||
|
|
410 | slot("bundleMetadata") { | |
|
|
411 | from(someTask) | |
|
|
412 | from(layout.buildDirectory.file("generated/meta.json")) | |
|
|
413 | } | |
|
|
414 | } | |
|
|
415 | } | |
|
|
416 | ``` | |
|
|
417 | ||
|
|
418 | ### Meaning of contribution forms | |
|
|
419 | ||
|
|
420 | - `from(Object)` adds a direct input independent from `variantSources`; | |
|
|
421 | - `fromVariant { output(...) }` selects named outputs from all compile units of | |
|
|
422 | the current variant; | |
|
|
423 | - `fromRole(role) { output(...) }` selects named outputs from compile units that | |
|
|
424 | belong to the given role projection; | |
|
|
425 | - `fromLayer(layer) { output(...) }` selects named outputs from the compile unit | |
|
|
426 | of the current variant and the given layer, if such unit exists. | |
|
|
427 | ||
|
|
428 | The DSL stores declarations, not resolved file collections. | |
|
|
429 | ||
|
|
430 | --- | |
|
|
431 | ||
|
|
432 | ## Contribution model | |
|
|
433 | ||
|
|
434 | Internally the DSL should compile to slot contributions. | |
|
|
435 | ||
|
|
436 | Conceptually: | |
|
|
437 | ||
|
|
438 | - `DirectContribution` | |
|
|
439 | - `VariantOutputContribution` | |
|
|
440 | - `RoleOutputContribution` | |
|
|
441 | - `LayerOutputContribution` | |
|
|
442 | ||
|
|
443 | These contributions should remain symbolic for as long as possible. | |
|
|
444 | ||
|
|
445 | They should not resolve source sets or files at declaration time. | |
|
|
446 | ||
|
|
447 | Each contribution is expected to provide: | |
|
|
448 | ||
|
|
449 | - its selection scope; | |
|
|
450 | - the requested output names; | |
|
|
451 | - enough symbolic identity for later validation and resolver policies. | |
|
|
452 | ||
|
|
453 | --- | |
|
|
454 | ||
|
|
455 | ## Resolver bridge between `variantSources` and `variantArtifacts` | |
|
|
456 | ||
|
|
457 | This is the central integration point. | |
|
|
458 | ||
|
|
459 | `variantArtifacts` should not access mutable internals of `variantSources`. | |
|
|
460 | ||
|
|
461 | Instead, it should resolve slot inputs through the public finalized | |
|
|
462 | `VariantSourcesContext`. | |
|
|
463 | ||
|
|
464 | ### Bridge responsibilities | |
|
|
465 | ||
|
|
466 | The bridge: | |
|
|
467 | ||
|
|
468 | - takes slot contribution declarations; | |
|
|
469 | - expands them against finalized variant topology; | |
|
|
470 | - maps logical selectors to compile units and role projections; | |
|
|
471 | - obtains source sets lazily through `VariantSourcesContext`; | |
|
|
472 | - resolves named outputs from those source sets; | |
|
|
473 | - builds the live input model for an `ArtifactAssembly`. | |
|
|
474 | ||
|
|
475 | ### Bridge input | |
|
|
476 | ||
|
|
477 | - current outgoing variant identity; | |
|
|
478 | - slot contribution declarations; | |
|
|
479 | - `VariantSourcesContext`. | |
|
|
480 | ||
|
|
481 | ### Bridge output | |
|
|
482 | ||
|
|
483 | - a live collection of logical slot inputs; | |
|
|
484 | - later adapted to `FileCollection` or other assembly-facing input models. | |
|
|
485 | ||
|
|
486 | ### Resolution semantics | |
|
|
487 | ||
|
|
488 | For one outgoing variant: | |
|
|
489 | ||
|
|
490 | - `fromVariant { output(x) }` | |
|
|
491 | - expands to all `CompileUnit` of that variant; | |
|
|
492 | - `fromRole(role) { output(x) }` | |
|
|
493 | - expands to `RoleProjection(variant, role)` and then to its compile units; | |
|
|
494 | - `fromLayer(layer) { output(x) }` | |
|
|
495 | - expands to one compile unit `(variant, layer)` when it exists; | |
|
|
496 | - `from(Object)` | |
|
|
497 | - bypasses `variantSources` completely. | |
|
|
498 | ||
|
|
499 | After compile units are known, the bridge asks | |
|
|
500 | `ctx.getSourceSets().getSourceSet(unit)` for each selected unit and resolves the | |
|
|
501 | requested named output. | |
|
|
502 | ||
|
|
503 | This keeps `variantArtifacts` independent from source-set naming internals and | |
|
|
504 | other materialization details. | |
|
|
505 | ||
|
|
506 | --- | |
|
|
507 | ||
|
|
508 | ## Validation | |
|
|
509 | ||
|
|
510 | Validation should be structural and symbolic. | |
|
|
511 | ||
|
|
512 | It should validate: | |
|
|
513 | ||
|
|
514 | - outgoing variant refers to an existing `Variant`; | |
|
|
515 | - referenced `Role` exists in that variant projection space; | |
|
|
516 | - referenced `Layer` exists in that variant compile-unit space; | |
|
|
517 | - primary slot is defined when needed; | |
|
|
518 | - primary slot refers to a slot declared in the same outgoing configuration. | |
|
|
519 | ||
|
|
520 | Validation should not require eager materialization of source sets or eager | |
|
|
521 | resolution of files. | |
|
|
522 | ||
|
|
523 | --- | |
|
|
524 | ||
|
|
525 | ## Deduplication and policy extension points | |
|
|
526 | ||
|
|
527 | Deduplication is important, but it should not be baked into the DSL itself. | |
|
|
528 | ||
|
|
529 | The correct place for it is the resolver bridge, after symbolic contributions | |
|
|
530 | have been expanded to logical inputs but before they are finally adapted to | |
|
|
531 | assembly-facing file collections. | |
|
|
532 | ||
|
|
533 | ### Why not in the DSL | |
|
|
534 | ||
|
|
535 | At declaration time it is still unknown whether selectors overlap: | |
|
|
536 | ||
|
|
537 | - `fromVariant` | |
|
|
538 | - `fromRole` | |
|
|
539 | - `fromLayer` | |
|
|
540 | ||
|
|
541 | may all describe the same logical source output. | |
|
|
542 | ||
|
|
543 | ### Why not rely only on `FileCollection` | |
|
|
544 | ||
|
|
545 | `FileCollection` may still provide useful physical deduplication, but it is too | |
|
|
546 | late and too file-oriented to serve as the only semantic mechanism. | |
|
|
547 | ||
|
|
548 | The artifact model should first deduplicate logical inputs, then let Gradle | |
|
|
549 | perform any additional physical deduplication. | |
|
|
550 | ||
|
|
551 | ### Default expectation | |
|
|
552 | ||
|
|
553 | The default resolver should support: | |
|
|
554 | ||
|
|
555 | - deduplication of topology-aware inputs by logical identity; | |
|
|
556 | - no implicit deduplication of direct `from(Object)` inputs. | |
|
|
557 | ||
|
|
558 | Logical identity should be based on domain meaning, for example: | |
|
|
559 | ||
|
|
560 | - `(CompileUnit, outputName)` | |
|
|
561 | ||
|
|
562 | and not on projected source-set names. | |
|
|
563 | ||
|
|
564 | This is important because source-set naming policy belongs to `variantSources` | |
|
|
565 | and must not silently redefine artifact semantics. | |
|
|
566 | ||
|
|
567 | ### Extension points | |
|
|
568 | ||
|
|
569 | The model should provide explicit internal extension points for: | |
|
|
570 | ||
|
|
571 | - deduplication policy; | |
|
|
572 | - logical input identity; | |
|
|
573 | - adaptation of resolved logical inputs to assembly-facing objects. | |
|
|
574 | ||
|
|
575 | Conceptually: | |
|
|
576 | ||
|
|
577 | ```java | |
|
|
578 | interface SlotInputDedupPolicy { ... } | |
|
|
579 | interface LogicalSlotInputIdentity { ... } | |
|
|
580 | interface SlotInputAdapter { ... } | |
|
|
581 | ``` | |
|
|
582 | ||
|
|
583 | The default implementation may remain simple, but these seams should exist from | |
|
|
584 | the start. | |
|
|
585 | ||
|
|
586 | --- | |
|
|
587 | ||
|
|
588 | ## Publication hooks | |
|
|
589 | ||
|
|
590 | Publication hooks remain useful, but they should observe the live structural | |
|
|
591 | model rather than define it. | |
|
|
592 | ||
|
|
593 | Examples: | |
|
|
594 | ||
|
|
595 | - `whenOutgoingVariant(...)` | |
|
|
596 | - `whenOutgoingSlot(...)` | |
|
|
597 | ||
|
|
598 | These hooks are adapter-facing customization points over already declared | |
|
|
599 | outgoing structure: | |
|
|
600 | ||
|
|
601 | - root configuration attributes; | |
|
|
602 | - slot artifact attributes; | |
|
|
603 | - assembly task tweaks. | |
|
|
604 | ||
|
|
605 | The recommended way to connect publication-facing `Spec` objects to the | |
|
|
606 | structural model is by backlink, not by moving slot rules into publication | |
|
|
607 | types. | |
|
|
608 | ||
|
|
609 | Conceptually: | |
|
|
610 | ||
|
|
611 | ```java | |
|
|
612 | interface OutgoingConfigurationSpec { | |
|
|
613 | OutgoingConfiguration getOutgoingArtifacts(); | |
|
|
614 | Variant getVariant(); | |
|
|
615 | Configuration getConfiguration(); | |
|
|
616 | } | |
|
|
617 | ||
|
|
618 | interface OutgoingArtifactSlotSpec { | |
|
|
619 | ArtifactSlot getArtifactSlot(); | |
|
|
620 | ArtifactAssembly getAssembly(); | |
|
|
621 | boolean isPrimary(); | |
|
|
622 | } | |
|
|
623 | ``` | |
|
|
624 | ||
|
|
625 | In this arrangement: | |
|
|
626 | ||
|
|
627 | - `OutgoingConfigurationSpec` remains a publication-facing facade; | |
|
|
628 | - `OutgoingConfigurationSpec` may expose the structural aggregate when an | |
|
|
629 | adapter needs it; | |
|
|
630 | - slot rules still belong to `VariantArtifactsRegistry`; | |
|
|
631 | - publication specs do not become owners of declaration or resolver state. | |
|
|
632 | ||
|
|
633 | They should not become the primary structural API for the artifact model. | |
|
|
634 | ||
|
|
635 | This is why a separate phase-oriented `OutgoingPublicationsContext` is not | |
|
|
636 | required. | |
|
|
637 | ||
|
|
638 | The live `OutgoingConfiguration` aggregate is sufficient. | |
|
|
639 | ||
|
|
640 | --- | |
|
|
641 | ||
|
|
642 | ## Design principles | |
|
|
643 | ||
|
|
644 | ### 1. Keep topology ownership in `variants` | |
|
|
645 | ||
|
|
646 | `variantArtifacts` selects from the topology model. It does not own it. | |
|
|
647 | ||
|
|
648 | ### 2. Keep source ownership in `variantSources` | |
|
|
649 | ||
|
|
650 | `variantArtifacts` consumes source materialization through a resolver bridge. It | |
|
|
651 | does not own source-set semantics. | |
|
|
652 | ||
|
|
653 | ### 3. Keep the DSL symbolic | |
|
|
654 | ||
|
|
655 | The DSL declares intent and selection rules, not materialized files. | |
|
|
656 | ||
|
|
657 | ### 4. Keep slot content live | |
|
|
658 | ||
|
|
659 | Do not introduce an artificial finalize phase for slot content unless a real | |
|
|
660 | semantic need appears. | |
|
|
661 | ||
|
|
662 | ### 5. Fix only structural identity | |
|
|
663 | ||
|
|
664 | Slot name, primary designation, and outgoing shape are structural. Slot inputs | |
|
|
665 | remain live. | |
|
|
666 | ||
|
|
667 | ### 6. Resolve through dedicated bridges | |
|
|
668 | ||
|
|
669 | Cross-model integration belongs in a resolver service, not in DSL classes. | |
|
|
670 | ||
|
|
671 | ### 7. Add policy seams early | |
|
|
672 | ||
|
|
673 | Deduplication and similar concerns should have extension points from the start, | |
|
|
674 | even if the initial implementation is conservative. | |
|
|
675 | ||
|
|
676 | --- | |
|
|
677 | ||
|
|
678 | ## Summary | |
|
|
679 | ||
|
|
680 | `variantArtifacts` should be modeled as a live outgoing-contract layer over | |
|
|
681 | `variants`, with source input resolution delegated to a dedicated bridge over | |
|
|
682 | `variantSources`. | |
|
|
683 | ||
|
|
684 | The resulting shape is: | |
|
|
685 | ||
|
|
686 | - `variants` owns topology; | |
|
|
687 | - `variantSources` owns source materialization; | |
|
|
688 | - `variantArtifacts` owns outgoing contract structure; | |
|
|
689 | - a resolver bridge connects symbolic slot declarations to live source-derived | |
|
|
690 | inputs; | |
|
|
691 | - deduplication and similar concerns are policies of that bridge, not of the | |
|
|
692 | DSL itself; | |
|
|
693 | - slot content stays live during configuration; | |
|
|
694 | - only publication-visible structure is treated as stable identity. | |
| @@ -0,0 +1,24 | |||
|
|
1 | package org.implab.gradle.internal; | |
|
|
2 | ||
|
|
3 | import java.util.LinkedList; | |
|
|
4 | import java.util.List; | |
|
|
5 | import java.util.function.Consumer; | |
|
|
6 | ||
|
|
7 | public class ReplayableQueue<T> { | |
|
|
8 | private final List<Consumer<? super T>> consumers = new LinkedList<>(); | |
|
|
9 | private final List<T> values = new LinkedList<>(); | |
|
|
10 | ||
|
|
11 | public void add(T value) { | |
|
|
12 | consumers.forEach(consumer -> consumer.accept(value)); | |
|
|
13 | values.add(value); | |
|
|
14 | } | |
|
|
15 | ||
|
|
16 | List<T> values() { | |
|
|
17 | return List.copyOf(values); | |
|
|
18 | } | |
|
|
19 | ||
|
|
20 | public void forEach(Consumer<? super T> consumer) { | |
|
|
21 | values.forEach(consumer); | |
|
|
22 | consumers.add(consumer); | |
|
|
23 | } | |
|
|
24 | } No newline at end of file | |
| @@ -0,0 +1,19 | |||
|
|
1 | package org.implab.gradle.variants.artifacts; | |
|
|
2 | ||
|
|
3 | import java.util.Set; | |
|
|
4 | ||
|
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
6 | import org.implab.gradle.variants.core.Layer; | |
|
|
7 | import org.implab.gradle.variants.core.Role; | |
|
|
8 | ||
|
|
9 | @NonNullByDefault | |
|
|
10 | public interface ArtifactAssemblyRules { | |
|
|
11 | void addDirectInput(Object input); | |
|
|
12 | ||
|
|
13 | void addVariantOutputs(Set<String> outputs); | |
|
|
14 | ||
|
|
15 | void addRoleOutputs(Role role, Set<String> outputs); | |
|
|
16 | ||
|
|
17 | void addLayerOutputs(Layer layer, Set<String> outputs); | |
|
|
18 | ||
|
|
19 | } | |
| @@ -0,0 +1,10 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | import org.gradle.api.Action; | |
|
|
5 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
6 | ||
|
|
7 | @NonNullByDefault | |
|
|
8 | interface ArtifactInputsResolver { | |
|
|
9 | void observeInputs(ArtifactSlot slot, Action<? super ResolvedSlotInput> action); | |
|
|
10 | } No newline at end of file | |
| @@ -0,0 +1,52 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.Set; | |
|
|
4 | ||
|
|
5 | import org.gradle.api.Action; | |
|
|
6 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRules; | |
|
|
7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
|
|
8 | import org.implab.gradle.variants.core.Variant; | |
|
|
9 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; | |
|
|
10 | ||
|
|
11 | final class BoundArtifactAssemblySpec implements ArtifactAssemblySpec { | |
|
|
12 | private final VariantArtifactsRegistry registry; | |
|
|
13 | private final Variant variant; | |
|
|
14 | private final ArtifactAssemblyRules rules; | |
|
|
15 | ||
|
|
16 | BoundArtifactAssemblySpec( | |
|
|
17 | VariantArtifactsRegistry registry, | |
|
|
18 | Variant variant, | |
|
|
19 | ArtifactAssemblyRules rules) { | |
|
|
20 | this.registry = registry; | |
|
|
21 | this.variant = variant; | |
|
|
22 | this.rules = rules; | |
|
|
23 | } | |
|
|
24 | ||
|
|
25 | @Override | |
|
|
26 | public void from(Object artifact) { | |
|
|
27 | rules.addDirectInput(artifact); | |
|
|
28 | } | |
|
|
29 | ||
|
|
30 | @Override | |
|
|
31 | public void fromVariant(Action<? super OutputSelectionSpec> action) { | |
|
|
32 | rules.addVariantOutputs(outputs(action)); | |
|
|
33 | } | |
|
|
34 | ||
|
|
35 | @Override | |
|
|
36 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { | |
|
|
37 | var role = registry.requireRole(variant, roleName); | |
|
|
38 | rules.addRoleOutputs(role, outputs(action)); | |
|
|
39 | } | |
|
|
40 | ||
|
|
41 | @Override | |
|
|
42 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { | |
|
|
43 | var layer = registry.requireLayer(variant, layerName); | |
|
|
44 | rules.addLayerOutputs(layer, outputs(action)); | |
|
|
45 | } | |
|
|
46 | ||
|
|
47 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { | |
|
|
48 | var spec = new DefaultOutputSelectionSpec(); | |
|
|
49 | action.execute(spec); | |
|
|
50 | return spec.outputs(); | |
|
|
51 | } | |
|
|
52 | } | |
| @@ -0,0 +1,39 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.gradle.api.Action; | |
|
|
4 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |
|
|
5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
|
|
6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
7 | ||
|
|
8 | /** | |
|
|
9 | * DSL фасад для описания исходящей конфигурации варианта | |
|
|
10 | */ | |
|
|
11 | final class BoundVariantArtifactsSpec implements VariantArtifactsSpec { | |
|
|
12 | private final VariantArtifactsRegistry registry; | |
|
|
13 | private final DefaultOutgoingConfiguration outgoing; | |
|
|
14 | ||
|
|
15 | BoundVariantArtifactsSpec(VariantArtifactsRegistry registry, DefaultOutgoingConfiguration outgoing) { | |
|
|
16 | this.registry = registry; | |
|
|
17 | this.outgoing = outgoing; | |
|
|
18 | } | |
|
|
19 | ||
|
|
20 | @Override | |
|
|
21 | public void slot(String name, Action<? super ArtifactAssemblySpec> action) { | |
|
|
22 | var slot = outgoing.getSlots().maybeCreate(name); | |
|
|
23 | var artifactSlot = new ArtifactSlot(outgoing.getVariant(), slot); | |
|
|
24 | var rules = registry.slotRules(artifactSlot); | |
|
|
25 | ||
|
|
26 | action.execute(new BoundArtifactAssemblySpec(registry, outgoing.getVariant(), rules)); | |
|
|
27 | } | |
|
|
28 | ||
|
|
29 | @Override | |
|
|
30 | public void primarySlot(String name, Action<? super ArtifactAssemblySpec> action) { | |
|
|
31 | var slot = outgoing.getSlots().maybeCreate(name); | |
|
|
32 | outgoing.getPrimarySlot().set(slot); | |
|
|
33 | ||
|
|
34 | var artifactSlot = new ArtifactSlot(outgoing.getVariant(), slot); | |
|
|
35 | var rules = registry.slotRules(artifactSlot); | |
|
|
36 | ||
|
|
37 | action.execute(new BoundArtifactAssemblySpec(registry, outgoing.getVariant(), rules)); | |
|
|
38 | } | |
|
|
39 | } | |
| @@ -0,0 +1,8 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | import org.implab.gradle.variants.sources.CompileUnit; | |
|
|
5 | ||
|
|
6 | @NonNullByDefault | |
|
|
7 | record CompileUnitOutputKey(CompileUnit unit, String outputName) implements SlotInputKey { | |
|
|
8 | } | |
| @@ -0,0 +1,51 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.LinkedHashMap; | |
|
|
4 | import java.util.Map; | |
|
|
5 | ||
|
|
6 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
7 | import org.gradle.api.file.ProjectLayout; | |
|
|
8 | import org.gradle.api.model.ObjectFactory; | |
|
|
9 | import org.gradle.api.tasks.TaskContainer; | |
|
|
10 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
|
|
11 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; | |
|
|
12 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
13 | import org.implab.gradle.variants.sources.VariantSourcesContext; | |
|
|
14 | ||
|
|
15 | @NonNullByDefault | |
|
|
16 | final class DefaultArtifactAssemblies implements ArtifactAssemblies { | |
|
|
17 | private final ArtifactInputsResolver inputs; | |
|
|
18 | private final ObjectFactory objects; | |
|
|
19 | ||
|
|
20 | private final VariantArtifactsRegistry registry; | |
|
|
21 | private final VariantSourcesContext sources; | |
|
|
22 | private final TaskContainer tasks; | |
|
|
23 | private final ProjectLayout layout; | |
|
|
24 | ||
|
|
25 | private final Map<ArtifactSlot, ArtifactAssembly> assemblies = new LinkedHashMap<>(); | |
|
|
26 | ||
|
|
27 | DefaultArtifactAssemblies( | |
|
|
28 | VariantArtifactsRegistry registry, | |
|
|
29 | VariantSourcesContext sources, | |
|
|
30 | TaskContainer tasks, | |
|
|
31 | ProjectLayout layout) { | |
|
|
32 | this.registry = registry; | |
|
|
33 | this.sources = sources; | |
|
|
34 | this.tasks = tasks; | |
|
|
35 | this.layout = layout; | |
|
|
36 | } | |
|
|
37 | ||
|
|
38 | @Override | |
|
|
39 | public ArtifactAssembly resolveSlot(ArtifactSlot slot) { | |
|
|
40 | return assemblies.computeIfAbsent(slot, this::createAssembly); | |
|
|
41 | } | |
|
|
42 | ||
|
|
43 | private ArtifactAssembly createAssembly(ArtifactSlot slot) { | |
|
|
44 | var files = objects.fileCollection(); | |
|
|
45 | ||
|
|
46 | inputs.observeInputs(slot, input -> files.from(input.input())); | |
|
|
47 | ||
|
|
48 | // register task + wire live inputs | |
|
|
49 | // return DefaultArtifactAssembly(...) | |
|
|
50 | } | |
|
|
51 | } | |
| @@ -0,0 +1,42 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.ArrayList; | |
|
|
4 | import java.util.List; | |
|
|
5 | import java.util.Objects; | |
|
|
6 | import java.util.Set; | |
|
|
7 | ||
|
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
9 | import org.gradle.api.Action; | |
|
|
10 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRules; | |
|
|
11 | import org.implab.gradle.variants.core.Layer; | |
|
|
12 | import org.implab.gradle.variants.core.Role; | |
|
|
13 | ||
|
|
14 | @NonNullByDefault | |
|
|
15 | final class DefaultArtifactAssemblyRules implements ArtifactAssemblyRules { | |
|
|
16 | private final List<SlotContribution> contributions = new ArrayList<>(); | |
|
|
17 | ||
|
|
18 | @Override | |
|
|
19 | public void addDirectInput(Object input) { | |
|
|
20 | contributions.add(new DirectContribution(Objects.requireNonNull(input, "input"))); | |
|
|
21 | } | |
|
|
22 | ||
|
|
23 | @Override | |
|
|
24 | public void addVariantOutputs(Set<String> outputs) { | |
|
|
25 | contributions.add(new VariantOutputsContribution(Set.copyOf(outputs))); | |
|
|
26 | } | |
|
|
27 | ||
|
|
28 | @Override | |
|
|
29 | public void addRoleOutputs(Role role, Set<String> outputs) { | |
|
|
30 | contributions.add(new RoleOutputsContribution(role, Set.copyOf(outputs))); | |
|
|
31 | } | |
|
|
32 | ||
|
|
33 | @Override | |
|
|
34 | public void addLayerOutputs(Layer layer, Set<String> outputs) { | |
|
|
35 | contributions.add(new LayerOutputsContribution(layer, Set.copyOf(outputs))); | |
|
|
36 | } | |
|
|
37 | ||
|
|
38 | @Override | |
|
|
39 | public void all(Action<? super SlotContribution> action) { | |
|
|
40 | contributions.forEach(action::execute); | |
|
|
41 | } | |
|
|
42 | } | |
| @@ -0,0 +1,116 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.LinkedHashMap; | |
|
|
4 | import java.util.Map; | |
|
|
5 | import java.util.Set; | |
|
|
6 | import java.util.function.Consumer; | |
|
|
7 | ||
|
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
9 | import org.gradle.api.Action; | |
|
|
10 | import org.implab.gradle.internal.ReplayableQueue; | |
|
|
11 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
12 | import org.implab.gradle.variants.sources.CompileUnit; | |
|
|
13 | import org.implab.gradle.variants.sources.VariantSourcesContext; | |
|
|
14 | ||
|
|
15 | @NonNullByDefault | |
|
|
16 | final class DefaultArtifactInputsResolver implements ArtifactInputsResolver { | |
|
|
17 | private final VariantArtifactsRegistry registry; | |
|
|
18 | private final VariantSourcesContext sources; | |
|
|
19 | private final SlotInputDedupPolicy dedupPolicy; | |
|
|
20 | ||
|
|
21 | private final Map<ArtifactSlot, ReplayableQueue<ResolvedSlotInput>> resolved = new LinkedHashMap<>(); | |
|
|
22 | ||
|
|
23 | DefaultArtifactInputsResolver( | |
|
|
24 | VariantArtifactsRegistry registry, | |
|
|
25 | VariantSourcesContext sources, | |
|
|
26 | SlotInputDedupPolicy dedupPolicy) { | |
|
|
27 | this.registry = registry; | |
|
|
28 | this.sources = sources; | |
|
|
29 | this.dedupPolicy = dedupPolicy; | |
|
|
30 | } | |
|
|
31 | ||
|
|
32 | /** | |
|
|
33 | * ставит replayble-hook на registry.slotRules(slot).all(action) | |
|
|
34 | */ | |
|
|
35 | @Override | |
|
|
36 | public void observeInputs(ArtifactSlot slot, Action<? super ResolvedSlotInput> action) { | |
|
|
37 | resolvedInputs(slot).forEach(action::execute); | |
|
|
38 | } | |
|
|
39 | ||
|
|
40 | private ReplayableQueue<ResolvedSlotInput> resolvedInputs(ArtifactSlot slot) { | |
|
|
41 | return resolved.computeIfAbsent(slot, this::bindSlot); | |
|
|
42 | } | |
|
|
43 | ||
|
|
44 | private ReplayableQueue<ResolvedSlotInput> bindSlot(ArtifactSlot slot) { | |
|
|
45 | var queue = new ReplayableQueue<ResolvedSlotInput>(); | |
|
|
46 | var filter = dedupPolicy.newFilter(slot); | |
|
|
47 | ||
|
|
48 | // TODO: жесть какая-то нужно переписать | |
|
|
49 | registry.slotRules(slot).all(contribution -> { | |
|
|
50 | var processor = new ContributionProcessor(slot, input -> { | |
|
|
51 | if (filter.accept(input)) { | |
|
|
52 | queue.add(input); | |
|
|
53 | } | |
|
|
54 | }); | |
|
|
55 | contribution.accept(processor); | |
|
|
56 | }); | |
|
|
57 | ||
|
|
58 | return queue; | |
|
|
59 | } | |
|
|
60 | ||
|
|
61 | private void emitUnitOutputs( | |
|
|
62 | ArtifactSlot slot, | |
|
|
63 | Set<CompileUnit> units, | |
|
|
64 | Set<String> outputs, | |
|
|
65 | Consumer<? super ResolvedSlotInput> sink) { | |
|
|
66 | for (var unit : units) { | |
|
|
67 | var sourceSet = sources.getSourceSets().getSourceSet(unit); | |
|
|
68 | for (var output : outputs) { | |
|
|
69 | sink.accept(new ResolvedSlotInput( | |
|
|
70 | slot, | |
|
|
71 | new CompileUnitOutputKey(unit, output), | |
|
|
72 | sourceSet.map(ss -> ss.output(output)))); | |
|
|
73 | } | |
|
|
74 | } | |
|
|
75 | } | |
|
|
76 | ||
|
|
77 | ||
|
|
78 | ||
|
|
79 | class ContributionProcessor implements SlotContributionVisitor { | |
|
|
80 | private final ArtifactSlot slot; | |
|
|
81 | private final Consumer<? super ResolvedSlotInput> sink; | |
|
|
82 | ||
|
|
83 | ContributionProcessor(ArtifactSlot slot, Consumer<? super ResolvedSlotInput> sink) { | |
|
|
84 | this.slot = slot; | |
|
|
85 | this.sink = sink; | |
|
|
86 | } | |
|
|
87 | ||
|
|
88 | @Override | |
|
|
89 | public void visit(DirectContribution contribution) { | |
|
|
90 | sink.accept(new ResolvedSlotInput( | |
|
|
91 | slot, | |
|
|
92 | SlotInputKey.newUniqueKey("Direct slot for " + slot), | |
|
|
93 | contribution.input())); | |
|
|
94 | } | |
|
|
95 | ||
|
|
96 | @Override | |
|
|
97 | public void visit(VariantOutputsContribution contribution) { | |
|
|
98 | var units = sources.getCompileUnits().getUnitsForVariant(slot.variant()); | |
|
|
99 | emitUnitOutputs(slot, units, contribution.outputs(), sink); | |
|
|
100 | } | |
|
|
101 | ||
|
|
102 | @Override | |
|
|
103 | public void visit(RoleOutputsContribution contribution) { | |
|
|
104 | var projection = sources.getRoleProjections().getProjection(slot.variant(), contribution.role()); | |
|
|
105 | var units = sources.getRoleProjections().getUnits(projection); | |
|
|
106 | emitUnitOutputs(slot, units, contribution.outputs(), sink); | |
|
|
107 | } | |
|
|
108 | ||
|
|
109 | @Override | |
|
|
110 | public void visit(LayerOutputsContribution contribution) { | |
|
|
111 | var unit = sources.getCompileUnits().requireUnit(slot.variant(), contribution.layer()); | |
|
|
112 | emitUnitOutputs(slot, Set.of(unit), contribution.outputs(), sink); | |
|
|
113 | } | |
|
|
114 | ||
|
|
115 | } | |
|
|
116 | } | |
| @@ -0,0 +1,62 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.gradle.api.NamedDomainObjectContainer; | |
|
|
4 | import org.gradle.api.NamedDomainObjectProvider; | |
|
|
5 | import org.gradle.api.artifacts.Configuration; | |
|
|
6 | import org.gradle.api.model.ObjectFactory; | |
|
|
7 | import org.gradle.api.provider.Property; | |
|
|
8 | import org.gradle.api.provider.ProviderFactory; | |
|
|
9 | import org.gradle.api.provider.Provider; | |
|
|
10 | import org.implab.gradle.variants.artifacts.OutgoingConfiguration; | |
|
|
11 | import org.implab.gradle.variants.artifacts.Slot; | |
|
|
12 | import org.implab.gradle.variants.core.Variant; | |
|
|
13 | ||
|
|
14 | class DefaultOutgoingConfiguration implements OutgoingConfiguration { | |
|
|
15 | ||
|
|
16 | private final Variant variant; | |
|
|
17 | ||
|
|
18 | private final NamedDomainObjectProvider<Configuration> configurationProvider; | |
|
|
19 | ||
|
|
20 | private final NamedDomainObjectContainer<Slot> slots; | |
|
|
21 | ||
|
|
22 | private final Property<Slot> primarySlot; | |
|
|
23 | ||
|
|
24 | public DefaultOutgoingConfiguration( | |
|
|
25 | Variant variant, | |
|
|
26 | NamedDomainObjectProvider<Configuration> configurationProvider, | |
|
|
27 | ObjectFactory objectFactory, | |
|
|
28 | ProviderFactory providerFactory) { | |
|
|
29 | this.variant = variant; | |
|
|
30 | this.configurationProvider = configurationProvider; | |
|
|
31 | this.slots = objectFactory.domainObjectContainer(Slot.class); | |
|
|
32 | this.primarySlot = objectFactory.property(Slot.class) | |
|
|
33 | .convention(providerFactory | |
|
|
34 | .provider(this::primarySlotConvention) | |
|
|
35 | .flatMap(x -> x)); | |
|
|
36 | } | |
|
|
37 | ||
|
|
38 | @Override | |
|
|
39 | public Property<Slot> getPrimarySlot() { | |
|
|
40 | return primarySlot; | |
|
|
41 | } | |
|
|
42 | ||
|
|
43 | @Override | |
|
|
44 | public Variant getVariant() { | |
|
|
45 | return variant; | |
|
|
46 | } | |
|
|
47 | ||
|
|
48 | @Override | |
|
|
49 | public NamedDomainObjectProvider<Configuration> getOutgoingConfiguration() { | |
|
|
50 | return configurationProvider; | |
|
|
51 | } | |
|
|
52 | ||
|
|
53 | @Override | |
|
|
54 | public NamedDomainObjectContainer<Slot> getSlots() { | |
|
|
55 | return slots; | |
|
|
56 | } | |
|
|
57 | ||
|
|
58 | private Provider<Slot> primarySlotConvention() { | |
|
|
59 | return slots.size() == 1 ? slots.named(slots.getNames().first()) : null; | |
|
|
60 | } | |
|
|
61 | ||
|
|
62 | } | |
| @@ -0,0 +1,11 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | ||
|
|
5 | @NonNullByDefault | |
|
|
6 | record DirectContribution(Object input) implements SlotContribution { | |
|
|
7 | @Override | |
|
|
8 | public void accept(SlotContributionVisitor visitor) { | |
|
|
9 | visitor.visit(this); | |
|
|
10 | } | |
|
|
11 | } | |
| @@ -0,0 +1,14 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.Set; | |
|
|
4 | ||
|
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
6 | import org.implab.gradle.variants.core.Layer; | |
|
|
7 | ||
|
|
8 | @NonNullByDefault | |
|
|
9 | record LayerOutputsContribution(Layer layer, Set<String> outputs) implements SlotContribution { | |
|
|
10 | @Override | |
|
|
11 | public void accept(SlotContributionVisitor visitor) { | |
|
|
12 | visitor.visit(this); | |
|
|
13 | } | |
|
|
14 | } | |
| @@ -0,0 +1,11 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
5 | ||
|
|
6 | @NonNullByDefault | |
|
|
7 | record ResolvedSlotInput( | |
|
|
8 | ArtifactSlot slot, | |
|
|
9 | SlotInputKey key, | |
|
|
10 | Object input) { | |
|
|
11 | } | |
| @@ -0,0 +1,14 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.Set; | |
|
|
4 | ||
|
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
6 | import org.implab.gradle.variants.core.Role; | |
|
|
7 | ||
|
|
8 | @NonNullByDefault | |
|
|
9 | record RoleOutputsContribution(Role role, Set<String> outputs) implements SlotContribution { | |
|
|
10 | @Override | |
|
|
11 | public void accept(SlotContributionVisitor visitor) { | |
|
|
12 | visitor.visit(this); | |
|
|
13 | } | |
|
|
14 | } | |
| @@ -0,0 +1,9 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | ||
|
|
5 | @NonNullByDefault | |
|
|
6 | public interface SlotContribution { | |
|
|
7 | ||
|
|
8 | void accept(SlotContributionVisitor visitor); | |
|
|
9 | } | |
| @@ -0,0 +1,11 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | public interface SlotContributionVisitor { | |
|
|
4 | void visit(DirectContribution contribution); | |
|
|
5 | ||
|
|
6 | void visit(VariantOutputsContribution contribution); | |
|
|
7 | ||
|
|
8 | void visit(RoleOutputsContribution contribution); | |
|
|
9 | ||
|
|
10 | void visit(LayerOutputsContribution contribution); | |
|
|
11 | } | |
| @@ -0,0 +1,9 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
5 | ||
|
|
6 | @NonNullByDefault | |
|
|
7 | interface SlotInputDedupPolicy { | |
|
|
8 | SlotInputFilter newFilter(ArtifactSlot slot); | |
|
|
9 | } | |
| @@ -0,0 +1,8 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | ||
|
|
5 | @NonNullByDefault | |
|
|
6 | interface SlotInputFilter { | |
|
|
7 | boolean accept(ResolvedSlotInput input); | |
|
|
8 | } | |
| @@ -0,0 +1,20 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | ||
|
|
5 | @NonNullByDefault | |
|
|
6 | interface SlotInputKey { | |
|
|
7 | ||
|
|
8 | static SlotInputKey newUniqueKey(String hint) { | |
|
|
9 | return new SlotInputKey() { | |
|
|
10 | @Override | |
|
|
11 | public String toString() { | |
|
|
12 | return hint; | |
|
|
13 | } | |
|
|
14 | }; | |
|
|
15 | } | |
|
|
16 | ||
|
|
17 | static SlotInputKey newUniqueKey() { | |
|
|
18 | return newUniqueKey("unnamed"); | |
|
|
19 | } | |
|
|
20 | } | |
| @@ -0,0 +1,13 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.Set; | |
|
|
4 | ||
|
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
6 | ||
|
|
7 | @NonNullByDefault | |
|
|
8 | record VariantOutputsContribution(Set<String> outputs) implements SlotContribution { | |
|
|
9 | @Override | |
|
|
10 | public void accept(SlotContributionVisitor visitor) { | |
|
|
11 | visitor.visit(this); | |
|
|
12 | } | |
|
|
13 | } | |
|
|
1 | NO CONTENT: file renamed from variants_variant_sources.md to variant_sources.md |
| @@ -1,9 +1,16 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | import org.gradle.api.Action; | |
|
|
3 | 4 | import org.gradle.api.Plugin; |
|
|
4 | 5 | import org.gradle.api.Project; |
|
|
5 | 6 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
7 | import org.implab.gradle.variants.artifacts.OutgoingArtifactSlotSpec; | |
|
|
8 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; | |
|
|
6 | 9 | import org.implab.gradle.variants.artifacts.VariantArtifactsContext; |
|
|
10 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; | |
|
|
11 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |
|
|
12 | import org.implab.gradle.variants.artifacts.internal.VariantArtifactsRegistry; | |
|
|
13 | import org.implab.gradle.variants.core.Variant; | |
|
|
7 | 14 | import org.implab.gradle.variants.core.VariantsExtension; |
|
|
8 | 15 | |
|
|
9 | 16 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
| @@ -11,18 +18,46 public abstract class VariantArtifactsPl | |||
|
|
11 | 18 | @Override |
|
|
12 | 19 | public void apply(Project target) { |
|
|
13 | 20 | var extensions = target.getExtensions(); |
|
|
21 | var objects = target.getObjects(); | |
|
|
14 | 22 | |
|
|
15 | 23 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
|
16 | 24 | target.getPlugins().apply(VariantsPlugin.class); |
|
|
17 | 25 | // Access the VariantsExtension to configure variant sources. |
|
|
18 | 26 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
|
19 | 27 | |
|
|
20 |
var deferred = new Deferred<VariantArtifacts |
|
|
|
28 | var deferred = new Deferred<VariantArtifactsRegistry>(); | |
|
|
21 | 29 | |
|
|
22 | 30 | variantsExtension.whenFinalized(variants -> { |
|
|
23 | ||
|
|
31 | deferred.resolve(new VariantArtifactsRegistry(variants)); | |
|
|
24 | 32 | }); |
|
|
25 | 33 | |
|
|
34 | var variantArtifacts = new VariantArtifactsExtension() { | |
|
|
35 | ||
|
|
36 | @Override | |
|
|
37 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { | |
|
|
38 | deferred.whenResolved(registry -> registry.configureVariant( | |
|
|
39 | objects.named(Variant.class, variantName), action)); | |
|
|
40 | } | |
|
|
41 | ||
|
|
42 | @Override | |
|
|
43 | public void whenFinalized(Action<? super VariantArtifactsContext> action) { | |
|
|
44 | deferred.whenResolved(registry -> action.execute(registry.variantsContext())); | |
|
|
45 | } | |
|
|
46 | ||
|
|
47 | @Override | |
|
|
48 | public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) { | |
|
|
49 | deferred.whenResolved(registry -> registry.configureOutgoing(action)); | |
|
|
50 | ||
|
|
51 | } | |
|
|
52 | ||
|
|
53 | @Override | |
|
|
54 | public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) { | |
|
|
55 | deferred.whenResolved(registry -> registry.configureOutgoingSlot(action)); | |
|
|
56 | } | |
|
|
57 | ||
|
|
58 | }; | |
|
|
59 | ||
|
|
60 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); | |
|
|
26 | 61 | |
|
|
27 | 62 | } |
|
|
28 | 63 | |
| @@ -10,7 +10,7 import org.implab.gradle.common.core.lan | |||
|
|
10 | 10 | * Materialized outgoing publication state of a single slot. |
|
|
11 | 11 | * |
|
|
12 | 12 | * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific |
|
|
13 |
* publication tweaks should be applied here rather than through {@link Outgoing |
|
|
|
13 | * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which | |
|
|
14 | 14 | * is limited to the root outgoing configuration of the variant. |
|
|
15 | 15 | */ |
|
|
16 | 16 | public interface OutgoingArtifactSlotSpec { |
| @@ -3,10 +3,11 package org.implab.gradle.variants.artif | |||
|
|
3 | 3 | import org.gradle.api.NamedDomainObjectContainer; |
|
|
4 | 4 | import org.gradle.api.NamedDomainObjectProvider; |
|
|
5 | 5 | import org.gradle.api.artifacts.Configuration; |
|
|
6 | import org.gradle.api.provider.Property; | |
|
|
6 | 7 | import org.implab.gradle.variants.core.Variant; |
|
|
7 | 8 | |
|
|
8 |
/** Описывает конфигураци |
|
|
|
9 |
public interface |
|
|
|
9 | /** Описывает исходящую конфигурацию варианта */ | |
|
|
10 | public interface OutgoingConfiguration { | |
|
|
10 | 11 | /** |
|
|
11 | 12 | * Исходный вариант для которого строится Outgoing конфигурация |
|
|
12 | 13 | */ |
| @@ -25,4 +26,13 public interface VariantArtifactsConfigu | |||
|
|
25 | 26 | * @see {@link ArtifactSlot} |
|
|
26 | 27 | */ |
|
|
27 | 28 | NamedDomainObjectContainer<Slot> getSlots(); |
|
|
29 | ||
|
|
30 | /** | |
|
|
31 | * Основной набор артефактов (primary variant) для исходящей конфигурации | |
|
|
32 | * | |
|
|
33 | * <p> | |
|
|
34 | * Если в свойстве {@link #getSlots()} есть только один слой, то по конвенции он | |
|
|
35 | * считается также основным. | |
|
|
36 | */ | |
|
|
37 | Property<Slot> getPrimarySlot(); | |
|
|
28 | 38 | } |
| @@ -13,7 +13,7 import groovy.lang.Closure; | |||
|
|
13 | 13 | * <p>This is a variant-level publication hook. Slot-specific publication state is exposed separately via |
|
|
14 | 14 | * {@link OutgoingArtifactSlotSpec}. |
|
|
15 | 15 | */ |
|
|
16 |
public interface Outgoing |
|
|
|
16 | public interface OutgoingConfigurationSpec { | |
|
|
17 | 17 | /** |
|
|
18 | 18 | * Returns the variant whose outgoing configuration is represented here. |
|
|
19 | 19 | * |
| @@ -18,10 +18,17 public interface VariantArtifactsContext | |||
|
|
18 | 18 | */ |
|
|
19 | 19 | VariantsView getVariants(); |
|
|
20 | 20 | |
|
|
21 | void all(Action<? super VariantArtifactsConfiguration> action); | |
|
|
21 | ArtifactAssemblies getAssemblies(); | |
|
|
22 | 22 | |
|
|
23 | Optional<VariantArtifactsConfiguration> findArtifacts(Variant variant); | |
|
|
23 | /** | |
|
|
24 | * Replayable hook для всех объявленных конфигураций | |
|
|
25 | */ | |
|
|
26 | void all(Action<? super OutgoingConfiguration> action); | |
|
|
24 | 27 | |
|
|
25 |
|
|
|
|
28 | Optional<OutgoingConfiguration> findArtifacts(Variant variant); | |
|
|
29 | ||
|
|
30 | OutgoingConfiguration requireArtifacts(Variant variant); | |
|
|
31 | ||
|
|
32 | ArtifactAssemblyRules slotRules(ArtifactSlot slot); | |
|
|
26 | 33 | |
|
|
27 | 34 | } |
| @@ -40,7 +40,7 public interface VariantArtifactsExtensi | |||
|
|
40 | 40 | * |
|
|
41 | 41 | * @param action variant-level outgoing configuration callback |
|
|
42 | 42 | */ |
|
|
43 |
void whenOutgoingVariant(Action<? super Outgoing |
|
|
|
43 | void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action); | |
|
|
44 | 44 | |
|
|
45 | 45 | default void whenOutgoingVariant(Closure<?> closure) { |
|
|
46 | 46 | whenOutgoingVariant(Closures.action(closure)); |
| @@ -19,10 +19,10 public interface VariantArtifactsSpec { | |||
|
|
19 | 19 | * @param action slot declaration |
|
|
20 | 20 | * @return slot identity |
|
|
21 | 21 | */ |
|
|
22 |
|
|
|
|
22 | void slot(String name, Action<? super ArtifactAssemblySpec> action); | |
|
|
23 | 23 | |
|
|
24 |
default |
|
|
|
25 |
|
|
|
|
24 | default void slot(String name, Closure<?> closure) { | |
|
|
25 | slot(name, Closures.action(closure)); | |
|
|
26 | 26 | } |
|
|
27 | 27 | |
|
|
28 | 28 | /** |
| @@ -32,9 +32,9 public interface VariantArtifactsSpec { | |||
|
|
32 | 32 | * @param action slot declaration |
|
|
33 | 33 | * @return slot identity |
|
|
34 | 34 | */ |
|
|
35 |
|
|
|
|
35 | void primarySlot(String name, Action<? super ArtifactAssemblySpec> action); | |
|
|
36 | 36 | |
|
|
37 |
default |
|
|
|
38 |
|
|
|
|
37 | default void primarySlot(String name, Closure<?> closure) { | |
|
|
38 | primarySlot(name, Closures.action(closure)); | |
|
|
39 | 39 | } |
|
|
40 | 40 | } |
| @@ -1,11 +1,105 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | import java.util.LinkedHashMap; | |
|
|
4 | import java.util.Map; | |
|
|
5 | import java.util.Optional; | |
|
|
6 | ||
|
|
7 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
3 | 8 | import org.gradle.api.Action; |
|
|
9 | import org.gradle.api.InvalidUserDataException; | |
|
|
10 | import org.gradle.api.file.ProjectLayout; | |
|
|
11 | import org.gradle.api.model.ObjectFactory; | |
|
|
12 | import org.gradle.api.tasks.TaskContainer; | |
|
|
13 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
|
|
14 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRules; | |
|
|
15 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
16 | import org.implab.gradle.variants.artifacts.Slot; | |
|
|
17 | import org.implab.gradle.variants.artifacts.OutgoingConfiguration; | |
|
|
18 | import org.implab.gradle.variants.artifacts.VariantArtifactsContext; | |
|
|
4 | 19 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
|
5 | 20 | import org.implab.gradle.variants.core.Variant; |
|
|
21 | import org.implab.gradle.variants.core.VariantsView; | |
|
|
22 | import org.implab.gradle.variants.sources.VariantSourcesContext; | |
|
|
6 | 23 | |
|
|
7 | public class VariantArtifactsRegistry { | |
|
|
8 | public void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action) { | |
|
|
24 | @NonNullByDefault | |
|
|
25 | public final class VariantArtifactsRegistry { | |
|
|
26 | private final VariantsView variants; | |
|
|
27 | private final ArtifactAssemblies assemblies; | |
|
|
28 | ||
|
|
29 | private final Map<Variant, DefaultOutgoingConfiguration> outgoingByVariant = new LinkedHashMap<>(); | |
|
|
30 | private final Map<ArtifactSlot, DefaultArtifactAssemblyRules> rulesBySlot = new LinkedHashMap<>(); | |
|
|
31 | ||
|
|
32 | private final VariantArtifactsContext context = new ContextView(); | |
|
|
33 | ||
|
|
34 | ||
|
|
35 | VariantArtifactsRegistry( | |
|
|
36 | ObjectFactory objects, | |
|
|
37 | VariantsView variants, | |
|
|
38 | VariantSourcesContext sources, | |
|
|
39 | TaskContainer tasks, | |
|
|
40 | ProjectLayout layout) { | |
|
|
41 | this.variants = variants; | |
|
|
42 | this.assemblies = new DefaultArtifactAssemblies(this, sources, tasks, layout); | |
|
|
43 | } | |
|
|
44 | ||
|
|
45 | public VariantArtifactsContext context() { | |
|
|
46 | return context; | |
|
|
47 | } | |
|
|
48 | ||
|
|
49 | ||
|
|
50 | ArtifactAssemblyRules slotRules(ArtifactSlot slot) { | |
|
|
51 | assertDeclaredSlot(slot); | |
|
|
52 | return rulesBySlot.computeIfAbsent(slot, key -> new DefaultArtifactAssemblyRules()); | |
|
|
53 | } | |
|
|
54 | ||
|
|
55 | void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action) { | |
|
|
56 | var outgoing = outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration); | |
|
|
57 | action.execute(new BoundVariantArtifactsSpec(this, outgoing)); | |
|
|
58 | } | |
|
|
59 | ||
|
|
60 | private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) { | |
|
|
61 | // register <variant>Elements eagerly | |
|
|
62 | } | |
|
|
9 | 63 | |
|
|
64 | private void assertDeclaredSlot(ArtifactSlot slot) { | |
|
|
65 | var outgoing = context.requireArtifacts(slot.variant()); | |
|
|
66 | if (outgoing.getSlots().findByName(slot.slot().getName()) == null) { | |
|
|
67 | throw new InvalidUserDataException( | |
|
|
68 | "Slot '" + slot.slot().getName() + "' isn't declared for variant '" + slot.variant().getName() + "'"); | |
|
|
69 | } | |
|
|
70 | } | |
|
|
71 | ||
|
|
72 | private final class ContextView implements VariantArtifactsContext { | |
|
|
73 | @Override | |
|
|
74 | public VariantsView getVariants() { | |
|
|
75 | return variants; | |
|
|
76 | } | |
|
|
77 | ||
|
|
78 | @Override | |
|
|
79 | public ArtifactAssemblies getAssemblies() { | |
|
|
80 | return assemblies; | |
|
|
81 | } | |
|
|
82 | ||
|
|
83 | @Override | |
|
|
84 | public void all(Action<? super OutgoingConfiguration> action) { | |
|
|
85 | outgoingByVariant.values().forEach(action::execute); | |
|
|
86 | } | |
|
|
87 | ||
|
|
88 | @Override | |
|
|
89 | public Optional<OutgoingConfiguration> findArtifacts(Variant variant) { | |
|
|
90 | return Optional.ofNullable(outgoingByVariant.get(variant)); | |
|
|
91 | } | |
|
|
92 | ||
|
|
93 | @Override | |
|
|
94 | public OutgoingConfiguration requireArtifacts(Variant variant) { | |
|
|
95 | return findArtifacts(variant) | |
|
|
96 | .orElseThrow(() -> new InvalidUserDataException( | |
|
|
97 | "Outgoing configuration for variant '" + variant.getName() + "' isn't declared")); | |
|
|
98 | } | |
|
|
99 | ||
|
|
100 | @Override | |
|
|
101 | public ArtifactAssemblyRules slotRules(ArtifactSlot slot) { | |
|
|
102 | return VariantArtifactsRegistry.this.slotRules(slot); | |
|
|
103 | } | |
|
|
10 | 104 | } |
|
|
11 | 105 | } |
| @@ -60,6 +60,13 public class VariantsView { | |||
|
|
60 | 60 | return roles; |
|
|
61 | 61 | } |
|
|
62 | 62 | |
|
|
63 | public void assertRole(Role role) { | |
|
|
64 | Objects.requireNonNull(role, "The role can't be null"); | |
|
|
65 | ||
|
|
66 | if (!roles.contains(role)) | |
|
|
67 | throw new InvalidUserDataException("The specified role '" + role.getName() + "' isn't declared"); | |
|
|
68 | } | |
|
|
69 | ||
|
|
63 | 70 | /** |
|
|
64 | 71 | * Returns all declared variants included in this view. |
|
|
65 | 72 | */ |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
