##// END OF EJS Templates
WIP working on outgoing variant artifacts
cin -
r47:6084dc61f02a default
parent child
Show More
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
NO CONTENT: file renamed from variants_variant_sources.md to variant_sources.md
@@ -1,9 +1,16
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Plugin;
4 import org.gradle.api.Plugin;
4 import org.gradle.api.Project;
5 import org.gradle.api.Project;
5 import org.implab.gradle.common.core.lang.Deferred;
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 import org.implab.gradle.variants.artifacts.VariantArtifactsContext;
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 import org.implab.gradle.variants.core.VariantsExtension;
14 import org.implab.gradle.variants.core.VariantsExtension;
8
15
9 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
16 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
@@ -11,18 +18,46 public abstract class VariantArtifactsPl
11 @Override
18 @Override
12 public void apply(Project target) {
19 public void apply(Project target) {
13 var extensions = target.getExtensions();
20 var extensions = target.getExtensions();
21 var objects = target.getObjects();
14
22
15 // Apply the main VariantsPlugin to ensure the core variant model is available.
23 // Apply the main VariantsPlugin to ensure the core variant model is available.
16 target.getPlugins().apply(VariantsPlugin.class);
24 target.getPlugins().apply(VariantsPlugin.class);
17 // Access the VariantsExtension to configure variant sources.
25 // Access the VariantsExtension to configure variant sources.
18 var variantsExtension = extensions.getByType(VariantsExtension.class);
26 var variantsExtension = extensions.getByType(VariantsExtension.class);
19
27
20 var deferred = new Deferred<VariantArtifactsContext>();
28 var deferred = new Deferred<VariantArtifactsRegistry>();
21
29
22 variantsExtension.whenFinalized(variants -> {
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 * Materialized outgoing publication state of a single slot.
10 * Materialized outgoing publication state of a single slot.
11 *
11 *
12 * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific
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 OutgoingVariantSpec}, which
13 * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which
14 * is limited to the root outgoing configuration of the variant.
14 * is limited to the root outgoing configuration of the variant.
15 */
15 */
16 public interface OutgoingArtifactSlotSpec {
16 public interface OutgoingArtifactSlotSpec {
@@ -3,10 +3,11 package org.implab.gradle.variants.artif
3 import org.gradle.api.NamedDomainObjectContainer;
3 import org.gradle.api.NamedDomainObjectContainer;
4 import org.gradle.api.NamedDomainObjectProvider;
4 import org.gradle.api.NamedDomainObjectProvider;
5 import org.gradle.api.artifacts.Configuration;
5 import org.gradle.api.artifacts.Configuration;
6 import org.gradle.api.provider.Property;
6 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.Variant;
7
8
8 /** Описывает конфигурация варианта исходящей конфигурации */
9 /** Описывает исходящую конфигурацию варианта */
9 public interface VariantArtifactsConfiguration {
10 public interface OutgoingConfiguration {
10 /**
11 /**
11 * Исходный вариант для которого строится Outgoing конфигурация
12 * Исходный вариант для которого строится Outgoing конфигурация
12 */
13 */
@@ -25,4 +26,13 public interface VariantArtifactsConfigu
25 * @see {@link ArtifactSlot}
26 * @see {@link ArtifactSlot}
26 */
27 */
27 NamedDomainObjectContainer<Slot> getSlots();
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 * <p>This is a variant-level publication hook. Slot-specific publication state is exposed separately via
13 * <p>This is a variant-level publication hook. Slot-specific publication state is exposed separately via
14 * {@link OutgoingArtifactSlotSpec}.
14 * {@link OutgoingArtifactSlotSpec}.
15 */
15 */
16 public interface OutgoingVariantSpec {
16 public interface OutgoingConfigurationSpec {
17 /**
17 /**
18 * Returns the variant whose outgoing configuration is represented here.
18 * Returns the variant whose outgoing configuration is represented here.
19 *
19 *
@@ -18,10 +18,17 public interface VariantArtifactsContext
18 */
18 */
19 VariantsView getVariants();
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 VariantArtifactsConfiguration requireArtifacts(Variant variant);
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 * @param action variant-level outgoing configuration callback
41 * @param action variant-level outgoing configuration callback
42 */
42 */
43 void whenOutgoingVariant(Action<? super OutgoingVariantSpec> action);
43 void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action);
44
44
45 default void whenOutgoingVariant(Closure<?> closure) {
45 default void whenOutgoingVariant(Closure<?> closure) {
46 whenOutgoingVariant(Closures.action(closure));
46 whenOutgoingVariant(Closures.action(closure));
@@ -19,10 +19,10 public interface VariantArtifactsSpec {
19 * @param action slot declaration
19 * @param action slot declaration
20 * @return slot identity
20 * @return slot identity
21 */
21 */
22 Slot slot(String name, Action<? super ArtifactAssemblySpec> action);
22 void slot(String name, Action<? super ArtifactAssemblySpec> action);
23
23
24 default Slot slot(String name, Closure<?> closure) {
24 default void slot(String name, Closure<?> closure) {
25 return slot(name, Closures.action(closure));
25 slot(name, Closures.action(closure));
26 }
26 }
27
27
28 /**
28 /**
@@ -32,9 +32,9 public interface VariantArtifactsSpec {
32 * @param action slot declaration
32 * @param action slot declaration
33 * @return slot identity
33 * @return slot identity
34 */
34 */
35 Slot primarySlot(String name, Action<? super ArtifactAssemblySpec> action);
35 void primarySlot(String name, Action<? super ArtifactAssemblySpec> action);
36
36
37 default Slot primarySlot(String name, Closure<?> closure) {
37 default void primarySlot(String name, Closure<?> closure) {
38 return primarySlot(name, Closures.action(closure));
38 primarySlot(name, Closures.action(closure));
39 }
39 }
40 }
40 }
@@ -1,11 +1,105
1 package org.implab.gradle.variants.artifacts.internal;
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 import org.gradle.api.Action;
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 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
19 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
5 import org.implab.gradle.variants.core.Variant;
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 {
24 @NonNullByDefault
8 public void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action) {
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 }
9
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 }
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() + "'");
10 }
69 }
11 }
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 }
104 }
105 }
@@ -60,6 +60,13 public class VariantsView {
60 return roles;
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 * Returns all declared variants included in this view.
71 * Returns all declared variants included in this view.
65 */
72 */
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now