| 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<VariantArtifacts |
|
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 Outgoing |
|
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 |
|
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 Outgoing |
|
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 |
|
|
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 Outgoing |
|
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 |
|
|
22 | void slot(String name, Action<? super ArtifactAssemblySpec> action); | |
| 23 |
|
23 | |||
| 24 |
default |
|
24 | default void slot(String name, Closure<?> closure) { | |
| 25 |
|
|
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 |
|
|
35 | void primarySlot(String name, Action<? super ArtifactAssemblySpec> action); | |
| 36 |
|
36 | |||
| 37 |
default |
|
37 | default void primarySlot(String name, Closure<?> closure) { | |
| 38 |
|
|
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 | } | |||
|
|
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 | 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
