| @@ -0,0 +1,29 | |||||
|
|
1 | package org.implab.gradle.common.core.lang; | |||
|
|
2 | ||||
|
|
3 | import java.util.LinkedList; | |||
|
|
4 | import java.util.List; | |||
|
|
5 | import java.util.function.Consumer; | |||
|
|
6 | ||||
|
|
7 | public final class Deferred<T> { | |||
|
|
8 | private final List<Consumer<T>> listeners = new LinkedList<>(); | |||
|
|
9 | private T value; | |||
|
|
10 | private boolean resolved = false; | |||
|
|
11 | ||||
|
|
12 | public void resolve(T value) { | |||
|
|
13 | if (resolved) { | |||
|
|
14 | throw new IllegalStateException("Already resolved"); | |||
|
|
15 | } | |||
|
|
16 | this.value = value; | |||
|
|
17 | this.resolved = true; | |||
|
|
18 | listeners.forEach(listener -> listener.accept(value)); | |||
|
|
19 | listeners.clear(); | |||
|
|
20 | } | |||
|
|
21 | ||||
|
|
22 | public void whenResolved(Consumer<T> listener) { | |||
|
|
23 | if (resolved) { | |||
|
|
24 | listener.accept(value); | |||
|
|
25 | } else { | |||
|
|
26 | listeners.add(listener); | |||
|
|
27 | } | |||
|
|
28 | } | |||
|
|
29 | } | |||
| @@ -0,0 +1,346 | |||||
|
|
1 | # Identity-First Model: Preserving Lazy Semantics Without Custom Events | |||
|
|
2 | ||||
|
|
3 | In build and configuration systems, the same tension appears again and again: | |||
|
|
4 | ||||
|
|
5 | * you want an **observable model** that consumers can subscribe to using something like `all(...)` | |||
|
|
6 | * you do **not** want that subscription to destroy laziness | |||
|
|
7 | * and you would prefer to avoid introducing a custom event bus, event ordering, and a separate lifecycle model | |||
|
|
8 | ||||
|
|
9 | A practical way to solve this is to **separate identity from computed state**. | |||
|
|
10 | ||||
|
|
11 | ## Core idea | |||
|
|
12 | ||||
|
|
13 | The idea is simple: | |||
|
|
14 | ||||
|
|
15 | * one object is responsible only for **identity** and minimal selection metadata | |||
|
|
16 | * the actual aggregate content is obtained through **separate API calls** | |||
|
|
17 | * those API calls may be lazy, expensive, cached, provider-based, or computed on demand | |||
|
|
18 | ||||
|
|
19 | So instead of one βfatβ object, we get two layers. | |||
|
|
20 | ||||
|
|
21 | ### 1. Identity layer | |||
|
|
22 | ||||
|
|
23 | Lightweight objects that: | |||
|
|
24 | ||||
|
|
25 | * are cheap to create | |||
|
|
26 | * are effectively immutable | |||
|
|
27 | * are safe to observe eagerly | |||
|
|
28 | * work well as keys and subscription points | |||
|
|
29 | ||||
|
|
30 | ### 2. State / aggregate access layer | |||
|
|
31 | ||||
|
|
32 | Separate APIs that: | |||
|
|
33 | ||||
|
|
34 | * resolve or compute content from identity | |||
|
|
35 | * do heavy work only when needed | |||
|
|
36 | * preserve lazy semantics where that actually matters | |||
|
|
37 | ||||
|
|
38 | This is especially useful when a collection must be replayable, but should not drag expensive materialization along with it. | |||
|
|
39 | ||||
|
|
40 | --- | |||
|
|
41 | ||||
|
|
42 | ## The problem this solves | |||
|
|
43 | ||||
|
|
44 | Consider a typical Gradle-like scenario. | |||
|
|
45 | ||||
|
|
46 | An adapter wants to subscribe to a collection: | |||
|
|
47 | ||||
|
|
48 | ```java | |||
|
|
49 | projections.getProjections().all(projection -> { | |||
|
|
50 | ... | |||
|
|
51 | }); | |||
|
|
52 | ``` | |||
|
|
53 | ||||
|
|
54 | If `projection` is a heavy object that already contains: | |||
|
|
55 | ||||
|
|
56 | * computed bindings | |||
|
|
57 | * providers of source sets | |||
|
|
58 | * derived state | |||
|
|
59 | * partially materialized objects | |||
|
|
60 | ||||
|
|
61 | then `all(...)` starts forcing things that were supposed to stay lazy. | |||
|
|
62 | ||||
|
|
63 | Typical symptoms: | |||
|
|
64 | ||||
|
|
65 | * lazy semantics are weakened or broken | |||
|
|
66 | * coupling increases | |||
|
|
67 | * plugin application order starts to matter | |||
|
|
68 | * custom events begin to look tempting: `onCreated`, `onResolved`, `onMaterialized` | |||
|
|
69 | ||||
|
|
70 | The problem is not that `all(...)` is bad. | |||
|
|
71 | The problem is that **too much meaning has been packed into the observable object**. | |||
|
|
72 | ||||
|
|
73 | --- | |||
|
|
74 | ||||
|
|
75 | ## The solution: observe identity, not state | |||
|
|
76 | ||||
|
|
77 | If the observable collection contains only identity objects, the picture changes. | |||
|
|
78 | ||||
|
|
79 | For example: | |||
|
|
80 | ||||
|
|
81 | ```java | |||
|
|
82 | public interface SourceSetProjection extends Named { | |||
|
|
83 | } | |||
|
|
84 | ``` | |||
|
|
85 | ||||
|
|
86 | This object contains only identity: | |||
|
|
87 | ||||
|
|
88 | * `name` | |||
|
|
89 | * perhaps a small amount of selection metadata | |||
|
|
90 | * but not heavy aggregate state | |||
|
|
91 | ||||
|
|
92 | Now this subscription: | |||
|
|
93 | ||||
|
|
94 | ```java | |||
|
|
95 | projections.getProjections().all(projection -> { | |||
|
|
96 | ... | |||
|
|
97 | }); | |||
|
|
98 | ``` | |||
|
|
99 | ||||
|
|
100 | is no longer dangerous. | |||
|
|
101 | It eagerly materializes only cheap keys, not the full aggregate graph. | |||
|
|
102 | ||||
|
|
103 | The actual content is requested separately: | |||
|
|
104 | ||||
|
|
105 | ```java | |||
|
|
106 | Set<VariantLayerBinding> bindings = projections.getBindings(projection); | |||
|
|
107 | NamedDomainObjectProvider<GenericSourceSet> sourceSet = | |||
|
|
108 | materializer.getSourceSet(projection.getName()); | |||
|
|
109 | ``` | |||
|
|
110 | ||||
|
|
111 | Those calls may remain: | |||
|
|
112 | ||||
|
|
113 | * lazy | |||
|
|
114 | * computed | |||
|
|
115 | * cached | |||
|
|
116 | * tied to runtime lifecycle | |||
|
|
117 | * delegated to a dedicated materializer | |||
|
|
118 | ||||
|
|
119 | --- | |||
|
|
120 | ||||
|
|
121 | ## Example | |||
|
|
122 | ||||
|
|
123 | ### A problematic design | |||
|
|
124 | ||||
|
|
125 | Suppose we define projection like this: | |||
|
|
126 | ||||
|
|
127 | ```java | |||
|
|
128 | public interface SourceSetProjection extends Named { | |||
|
|
129 | Set<VariantLayerBinding> getBindings(); | |||
|
|
130 | NamedDomainObjectProvider<GenericSourceSet> getSourceSet(); | |||
|
|
131 | } | |||
|
|
132 | ``` | |||
|
|
133 | ||||
|
|
134 | This is problematic because `SourceSetProjection` is no longer just identity. It is already close to an aggregate. | |||
|
|
135 | ||||
|
|
136 | It mixes: | |||
|
|
137 | ||||
|
|
138 | * symbolic identity | |||
|
|
139 | * relation data | |||
|
|
140 | * runtime references into a foreign domain | |||
|
|
141 | ||||
|
|
142 | Subscribing via `all(...)` now risks pulling in much more than intended. | |||
|
|
143 | ||||
|
|
144 | The type says βprojectionβ, but internally it already carries half the system. | |||
|
|
145 | ||||
|
|
146 | --- | |||
|
|
147 | ||||
|
|
148 | ### A cleaner design | |||
|
|
149 | ||||
|
|
150 | Split responsibilities instead: | |||
|
|
151 | ||||
|
|
152 | ```java | |||
|
|
153 | public interface SourceSetProjection extends Named { | |||
|
|
154 | } | |||
|
|
155 | ``` | |||
|
|
156 | ||||
|
|
157 | ```java | |||
|
|
158 | public interface SourceSetProjections { | |||
|
|
159 | NamedDomainObjectCollection<SourceSetProjection> getProjections(); | |||
|
|
160 | Set<VariantLayerBinding> getBindings(String sourceSetName); | |||
|
|
161 | } | |||
|
|
162 | ``` | |||
|
|
163 | ||||
|
|
164 | ```java | |||
|
|
165 | public interface SourceSetMaterializer { | |||
|
|
166 | NamedDomainObjectProvider<GenericSourceSet> getSourceSet(String sourceSetName); | |||
|
|
167 | } | |||
|
|
168 | ``` | |||
|
|
169 | ||||
|
|
170 | Now the adapter flow looks like this: | |||
|
|
171 | ||||
|
|
172 | ```java | |||
|
|
173 | projections.getProjections().all(projection -> { | |||
|
|
174 | Set<VariantLayerBinding> bindings = | |||
|
|
175 | projections.getBindings(projection.getName()); | |||
|
|
176 | ||||
|
|
177 | NamedDomainObjectProvider<GenericSourceSet> sourceSet = | |||
|
|
178 | materializer.getSourceSet(projection.getName()); | |||
|
|
179 | ||||
|
|
180 | // apply adapter-specific policy | |||
|
|
181 | }); | |||
|
|
182 | ``` | |||
|
|
183 | ||||
|
|
184 | What changed: | |||
|
|
185 | ||||
|
|
186 | * replayable subscription is preserved | |||
|
|
187 | * eager observation is acceptable because `SourceSetProjection` is cheap | |||
|
|
188 | * expensive and computed state has moved to separate APIs | |||
|
|
189 | * materialization remains under the control of a single owner | |||
|
|
190 | ||||
|
|
191 | --- | |||
|
|
192 | ||||
|
|
193 | ## Why this is often better than events | |||
|
|
194 | ||||
|
|
195 | When identity and state are mixed together, people quickly start inventing events: | |||
|
|
196 | ||||
|
|
197 | * `projectionCreated` | |||
|
|
198 | * `projectionResolved` | |||
|
|
199 | * `sourceSetAvailable` | |||
|
|
200 | * `sourceSetMaterialized` | |||
|
|
201 | ||||
|
|
202 | That usually happens because it becomes important to know **when exactly** an object is βready enoughβ. | |||
|
|
203 | ||||
|
|
204 | If the observable object contains only identity, and heavy state is obtained separately, then many of those events become unnecessary. | |||
|
|
205 | ||||
|
|
206 | The architecture becomes calmer: | |||
|
|
207 | ||||
|
|
208 | * an **identity registry** | |||
|
|
209 | * a **lookup API** for relations | |||
|
|
210 | * a **lazy materialization API** for heavy objects | |||
|
|
211 | ||||
|
|
212 | Instead of saying: | |||
|
|
213 | ||||
|
|
214 | > βWhen this object becomes sufficiently ready, I will react.β | |||
|
|
215 | ||||
|
|
216 | you can say: | |||
|
|
217 | ||||
|
|
218 | > βI can observe identity immediately, and ask for the expensive state only when I actually need it.β | |||
|
|
219 | ||||
|
|
220 | This is easier to reason about, easier to test, and usually easier to evolve. | |||
|
|
221 | ||||
|
|
222 | --- | |||
|
|
223 | ||||
|
|
224 | ## What should live inside an identity object | |||
|
|
225 | ||||
|
|
226 | An identity object does not have to be completely empty. | |||
|
|
227 | It may carry **selection metadata**, as long as that metadata is: | |||
|
|
228 | ||||
|
|
229 | * cheap | |||
|
|
230 | * stable | |||
|
|
231 | * not expensive to initialize | |||
|
|
232 | * not turning the object into an aggregate | |||
|
|
233 | ||||
|
|
234 | Typical examples: | |||
|
|
235 | ||||
|
|
236 | * `id` | |||
|
|
237 | * `name` | |||
|
|
238 | * `kind` | |||
|
|
239 | * `type` | |||
|
|
240 | * domain key | |||
|
|
241 | ||||
|
|
242 | What should usually stay out: | |||
|
|
243 | ||||
|
|
244 | * computed aggregate content | |||
|
|
245 | * runtime references to foreign domains | |||
|
|
246 | * lazy providers of heavy objects | |||
|
|
247 | * derived state that can trigger premature materialization | |||
|
|
248 | ||||
|
|
249 | A useful rule of thumb: | |||
|
|
250 | ||||
|
|
251 | **An identity object contains selection metadata; aggregate content is obtained separately.** | |||
|
|
252 | ||||
|
|
253 | --- | |||
|
|
254 | ||||
|
|
255 | ## Why this works well with `all(...)` | |||
|
|
256 | ||||
|
|
257 | `all(...)` weakens laziness only if the observed objects are themselves heavy or stateful. | |||
|
|
258 | ||||
|
|
259 | If the observed objects are: | |||
|
|
260 | ||||
|
|
261 | * cheap | |||
|
|
262 | * identity-only | |||
|
|
263 | * effectively immutable | |||
|
|
264 | ||||
|
|
265 | then eager observation is usually acceptable. | |||
|
|
266 | ||||
|
|
267 | So the real principle is: | |||
|
|
268 | ||||
|
|
269 | **Eager observation of identity is often harmless. | |||
|
|
270 | Eager observation of computed state is not.** | |||
|
|
271 | ||||
|
|
272 | That is why `all(...)` can be perfectly fine for collections of: | |||
|
|
273 | ||||
|
|
274 | * `Variant` | |||
|
|
275 | * `Layer` | |||
|
|
276 | * `Role` | |||
|
|
277 | * `SourceSetProjection` | |||
|
|
278 | ||||
|
|
279 | as long as those objects stay on the identity side of the boundary. | |||
|
|
280 | ||||
|
|
281 | --- | |||
|
|
282 | ||||
|
|
283 | ## Where this principle is especially useful | |||
|
|
284 | ||||
|
|
285 | This approach is particularly effective when: | |||
|
|
286 | ||||
|
|
287 | * there is replayable observation via `all(...)` | |||
|
|
288 | * identity objects are cheap and stable | |||
|
|
289 | * aggregate content may be expensive | |||
|
|
290 | * symbolic model and runtime model should remain separate | |||
|
|
291 | * you want to avoid building a custom event system | |||
|
|
292 | ||||
|
|
293 | For Gradle-like models, this is often a very natural fit. | |||
|
|
294 | ||||
|
|
295 | --- | |||
|
|
296 | ||||
|
|
297 | ## When it is unnecessary | |||
|
|
298 | ||||
|
|
299 | If an object is: | |||
|
|
300 | ||||
|
|
301 | * small | |||
|
|
302 | * cheap | |||
|
|
303 | * and already fully represents its useful content | |||
|
|
304 | ||||
|
|
305 | then splitting identity and state may be overengineering. | |||
|
|
306 | ||||
|
|
307 | So this is not a universal rule. It is a tool to use when there is real tension between: | |||
|
|
308 | ||||
|
|
309 | * key | |||
|
|
310 | * state | |||
|
|
311 | * computation | |||
|
|
312 | * runtime reference | |||
|
|
313 | ||||
|
|
314 | --- | |||
|
|
315 | ||||
|
|
316 | ## Practical conclusion | |||
|
|
317 | ||||
|
|
318 | The principle can be summarized like this: | |||
|
|
319 | ||||
|
|
320 | 1. **Identity objects** hold only identity and cheap selection metadata. | |||
|
|
321 | 2. **Aggregate content** is not stored inside them, but retrieved through separate API calls. | |||
|
|
322 | 3. Those API calls may perform: | |||
|
|
323 | ||||
|
|
324 | * lazy resolution | |||
|
|
325 | * caching | |||
|
|
326 | * heavy computation | |||
|
|
327 | * materialization on demand | |||
|
|
328 | 4. This makes it possible to use replayable mechanisms such as `all(...)` without destroying laziness where laziness actually matters. | |||
|
|
329 | ||||
|
|
330 | This is how you can combine: | |||
|
|
331 | ||||
|
|
332 | * simple observation | |||
|
|
333 | * a clean model | |||
|
|
334 | * no custom event bus | |||
|
|
335 | * lazy materialization of heavy state | |||
|
|
336 | ||||
|
|
337 | --- | |||
|
|
338 | ||||
|
|
339 | # Short design note version | |||
|
|
340 | ||||
|
|
341 | A concise version of the same principle: | |||
|
|
342 | ||||
|
|
343 | > Use identity objects as cheap, observable keys. | |||
|
|
344 | > Keep expensive or computed aggregate content out of them. | |||
|
|
345 | > Resolve that content through separate APIs on demand. | |||
|
|
346 | > This allows replayable observation (`all(...)`) without forcing premature materialization, and often removes the need for a custom event model. | |||
| @@ -0,0 +1,306 | |||||
|
|
1 | # Identity-first model: ΠΊΠ°ΠΊ ΡΠΎΡ ΡΠ°Π½ΠΈΡΡ lazy semantics Π±Π΅Π· ΡΠ²ΠΎΠΈΡ ΡΠΎΠ±ΡΡΠΈΠΉ | |||
|
|
2 | ||||
|
|
3 | Π ΡΠΈΡΡΠ΅ΠΌΠ°Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ ΠΈ ΡΠ±ΠΎΡΠΊΠΈ ΠΏΠΎΡΡΠΈ Π²ΡΠ΅Π³Π΄Π° Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ ΠΎΠ΄Π½ΠΎ ΠΈ ΡΠΎ ΠΆΠ΅ Π½Π°ΠΏΡΡΠΆΠ΅Π½ΠΈΠ΅: | |||
|
|
4 | ||||
|
|
5 | * Ρ ΠΎΡΠ΅ΡΡΡ ΠΈΠΌΠ΅ΡΡ **Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΡ ΠΌΠΎΠ΄Π΅Π»Ρ**, Π½Π° ΠΊΠΎΡΠΎΡΡΡ ΠΌΠΎΠΆΠ½ΠΎ ΠΏΠΎΠ΄ΠΏΠΈΡΠ°ΡΡΡΡ ΡΠ΅ΡΠ΅Π· ΡΡΠΎ-ΡΠΎ Π²ΡΠΎΠ΄Π΅ `all(...)` | |||
|
|
6 | * Π½ΠΎ Π½Π΅ Ρ ΠΎΡΠ΅ΡΡΡ, ΡΡΠΎΠ±Ρ ΡΠ°ΠΊΠ°Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠ° **Π»ΠΎΠΌΠ°Π»Π° Π»Π΅Π½ΠΈΠ²ΠΎΡΡΡ** | |||
|
|
7 | * Π΅ΡΡ ΠΌΠ΅Π½ΡΡΠ΅ Ρ ΠΎΡΠ΅ΡΡΡ ΡΠ°ΡΠΈΡΡ Π² Π°ΡΡ ΠΈΡΠ΅ΠΊΡΡΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΡ ΡΠΈΠ½Ρ ΡΠΎΠ±ΡΡΠΈΠΉ, ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ ΠΈ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ lifecycle | |||
|
|
8 | ||||
|
|
9 | ΠΠ΄ΠΈΠ½ ΠΈΠ· ΡΠ°Π±ΠΎΡΠΈΡ ΡΠΏΠΎΡΠΎΠ±ΠΎΠ² ΡΠ΅ΡΠΈΡΡ ΡΡΠΎ β **ΡΠ°Π·Π΄Π΅Π»ΠΈΡΡ identity ΠΈ Π²ΡΡΠΈΡΠ»ΡΠ΅ΠΌΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅**. | |||
|
|
10 | ||||
|
|
11 | ## Π‘ΡΡΡ ΠΏΡΠΈΠ½ΡΠΈΠΏΠ° | |||
|
|
12 | ||||
|
|
13 | ΠΠ΄Π΅Ρ ΠΏΡΠΎΡΡΠ°Ρ: | |||
|
|
14 | ||||
|
|
15 | * ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ ΡΠΎΠ»ΡΠΊΠΎ Π·Π° **identity** ΠΈ ΠΌΠΈΠ½ΠΈΠΌΠ°Π»ΡΠ½ΡΠ΅ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΠ΅ Π²ΡΠ±ΠΎΡΠ° | |||
|
|
16 | * Π²ΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΠ΅Π»ΡΠ½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π°Π³ΡΠ΅Π³Π°ΡΠ° ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΡΡ **ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌΠΈ Π²ΡΠ·ΠΎΠ²Π°ΠΌΠΈ API** | |||
|
|
17 | * ΡΡΠΈ Π²ΡΠ·ΠΎΠ²Ρ ΡΠΆΠ΅ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ lazy, Π΄ΠΎΡΠΎΠ³ΠΈΠΌΠΈ, Π²ΡΡΠΈΡΠ»ΡΠ΅ΠΌΡΠΌΠΈ, ΠΊΡΡΠΈΡΡΠ΅ΠΌΡΠΌΠΈ, ΠΏΡΠΎΠ²Π°ΠΉΠ΄Π΅ΡΠ½ΡΠΌΠΈ β ΠΊΠ°ΠΊΠΈΠΌΠΈ ΡΠ³ΠΎΠ΄Π½ΠΎ | |||
|
|
18 | ||||
|
|
19 | Π’ΠΎ Π΅ΡΡΡ Π²ΠΌΠ΅ΡΡΠΎ ΠΎΠ΄Π½ΠΎΠ³ΠΎ βΡΠΎΠ»ΡΡΠΎΠ³ΠΎβ ΠΎΠ±ΡΠ΅ΠΊΡΠ° ΠΌΡ ΠΏΠΎΠ»ΡΡΠ°Π΅ΠΌ Π΄Π²Π° ΡΠ»ΠΎΡ: | |||
|
|
20 | ||||
|
|
21 | ### 1. Identity layer | |||
|
|
22 | ||||
|
|
23 | ΠΡΠ³ΠΊΠΈΠ΅ ΠΎΠ±ΡΠ΅ΠΊΡΡ, ΠΊΠΎΡΠΎΡΡΠ΅: | |||
|
|
24 | ||||
|
|
25 | * Π΄ΡΡΠ΅Π²ΠΎ ΡΠΎΠ·Π΄Π°ΡΡΡΡ | |||
|
|
26 | * Π½Π΅ ΠΌΡΡΠΈΡΡΡΡ | |||
|
|
27 | * Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎ Π½Π°Π±Π»ΡΠ΄Π°ΡΡΡΡ eagerly | |||
|
|
28 | * Π³ΠΎΠ΄ΡΡΡΡ ΠΊΠ°ΠΊ ΠΊΠ»ΡΡΠΈ ΠΈ ΡΠΎΡΠΊΠΈ ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠΈ | |||
|
|
29 | ||||
|
|
30 | ### 2. State / aggregate access layer | |||
|
|
31 | ||||
|
|
32 | ΠΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ API, ΠΊΠΎΡΠΎΡΡΠ΅: | |||
|
|
33 | ||||
|
|
34 | * ΠΏΠΎ identity Π½Π°Ρ ΠΎΠ΄ΡΡ ΠΈΠ»ΠΈ Π²ΡΡΠΈΡΠ»ΡΡΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ | |||
|
|
35 | * Π΄Π΅Π»Π°ΡΡ heavy work ΡΠΎΠ»ΡΠΊΠΎ ΠΏΠΎ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡ | |||
|
|
36 | * ΠΌΠΎΠ³ΡΡ ΡΠΎΡ ΡΠ°Π½ΡΡΡ Π»Π΅Π½ΠΈΠ²ΡΡ ΡΠ΅ΠΌΠ°Π½ΡΠΈΠΊΡ | |||
|
|
37 | ||||
|
|
38 | ΠΡΠΎ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΠΏΠΎΠ»Π΅Π·Π½ΠΎ ΡΠ°ΠΌ, Π³Π΄Π΅ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ Π΄ΠΎΠ»ΠΆΠ½Π° Π±ΡΡΡ replayable, Π½ΠΎ ΠΏΡΠΈ ΡΡΠΎΠΌ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½Π° ΡΠ°ΡΠΈΡΡ Π·Π° ΡΠΎΠ±ΠΎΠΉ Π΄ΠΎΡΠΎΠ³ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ. | |||
|
|
39 | ||||
|
|
40 | --- | |||
|
|
41 | ||||
|
|
42 | ## ΠΡΠΎΠ±Π»Π΅ΠΌΠ°, ΠΊΠΎΡΠΎΡΡΡ ΡΡΠΎ ΡΠ΅ΡΠ°Π΅Ρ | |||
|
|
43 | ||||
|
|
44 | Π Π°ΡΡΠΌΠΎΡΡΠΈΠΌ ΡΠΈΠΏΠΈΡΠ½ΡΡ ΡΠΈΡΡΠ°ΡΠΈΡ Π² Gradle-ΠΏΠΎΠ΄ΠΎΠ±Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ. | |||
|
|
45 | ||||
|
|
46 | ΠΡΡΡ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ ΠΎΠ±ΡΠ΅ΠΊΡΠΎΠ², Π½Π° ΠΊΠΎΡΠΎΡΡΡ Π°Π΄Π°ΠΏΡΠ΅Ρ Ρ ΠΎΡΠ΅Ρ ΠΏΠΎΠ΄ΠΏΠΈΡΠ°ΡΡΡΡ: | |||
|
|
47 | ||||
|
|
48 | ```java | |||
|
|
49 | projections.getProjections().all(projection -> { | |||
|
|
50 | ... | |||
|
|
51 | }); | |||
|
|
52 | ``` | |||
|
|
53 | ||||
|
|
54 | ΠΡΠ»ΠΈ `projection` β ΡΡΠΎ ΡΡΠΆΡΠ»ΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ, Π²Π½ΡΡΡΠΈ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΡΠΆΠ΅ Π»Π΅ΠΆΠ°Ρ: | |||
|
|
55 | ||||
|
|
56 | * Π²ΡΡΠΈΡΠ»Π΅Π½Π½ΡΠ΅ bindings | |||
|
|
57 | * ΠΏΡΠΎΠ²Π°ΠΉΠ΄Π΅ΡΡ Π½Π° source sets | |||
|
|
58 | * derived state | |||
|
|
59 | * ΠΏΠΎΠ»Ρ-ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π½ΡΠ΅ ΡΡΡΠ½ΠΎΡΡΠΈ | |||
|
|
60 | ||||
|
|
61 | ΡΠΎ `all(...)` Π½Π°ΡΠΈΠ½Π°Π΅Ρ ΡΠ°Π½ΠΎ ΡΠ°ΡΠΊΡΡΠ²Π°ΡΡ ΡΠΎ, ΡΡΠΎ Ρ ΠΎΡΠ΅Π»ΠΎΡΡ ΠΎΡΡΠ°Π²ΠΈΡΡ Π»Π΅Π½ΠΈΠ²ΡΠΌ. | |||
|
|
62 | ||||
|
|
63 | ΠΠΎΡΠ²Π»ΡΡΡΡΡ ΡΠΈΠΌΠΏΡΠΎΠΌΡ: | |||
|
|
64 | ||||
|
|
65 | * Π»ΠΎΠΌΠ°Π΅ΡΡΡ lazy semantics | |||
|
|
66 | * ΡΠ°ΡΡΡΡ ΡΠ²ΡΠ·Π½ΠΎΡΡΡ | |||
|
|
67 | * ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡΡ Π²Π°ΠΆΠ΅Π½ ΠΏΠΎΡΡΠ΄ΠΎΠΊ ΠΏΡΠΈΠΌΠ΅Π½Π΅Π½ΠΈΡ ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ² | |||
|
|
68 | * Ρ ΠΎΡΠ΅ΡΡΡ Π·Π°Π²ΠΎΠ΄ΠΈΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΠ΅ ΡΠΎΠ±ΡΡΠΈΡ: `onCreated`, `onResolved`, `onMaterialized` | |||
|
|
69 | ||||
|
|
70 | ΠΡΡ ΠΈΡΠ΅ΠΊΡΡΡΠ° Π½Π°ΡΠΈΠ½Π°Π΅Ρ ΡΠΊΡΠΈΠΏΠ΅ΡΡ Π½Π΅ ΠΏΠΎΡΠΎΠΌΡ, ΡΡΠΎ `all(...)` ΠΏΠ»ΠΎΡ , Π° ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ **ΡΠ»ΠΈΡΠΊΠΎΠΌ ΠΌΠ½ΠΎΠ³ΠΎ ΡΠΌΡΡΠ»Π° Π·Π°ΡΡΠ½ΡΡΠΎ Π² Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ**. | |||
|
|
71 | ||||
|
|
72 | --- | |||
|
|
73 | ||||
|
|
74 | ## Π Π΅ΡΠ΅Π½ΠΈΠ΅: Π½Π°Π±Π»ΡΠ΄Π°ΡΡ identity, Π° Π½Π΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ | |||
|
|
75 | ||||
|
|
76 | ΠΡΠ»ΠΈ ΡΠ΄Π΅Π»Π°ΡΡ Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΌΠΈ ΡΠΎΠ»ΡΠΊΠΎ identity objects, ΠΊΠ°ΡΡΠΈΠ½Π° ΠΌΠ΅Π½ΡΠ΅ΡΡΡ. | |||
|
|
77 | ||||
|
|
78 | ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ: | |||
|
|
79 | ||||
|
|
80 | ```java | |||
|
|
81 | public interface SourceSetProjection extends Named { | |||
|
|
82 | } | |||
|
|
83 | ``` | |||
|
|
84 | ||||
|
|
85 | Π’Π°ΠΊΠΎΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΡΠΎΠ»ΡΠΊΠΎ identity: | |||
|
|
86 | ||||
|
|
87 | * `name` | |||
|
|
88 | * Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎ, Π΅ΡΡ ΠΏΠ°ΡΡ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΡ Π²ΡΠ±ΠΎΡΠ° | |||
|
|
89 | * Π½ΠΎ Π½Π΅ ΡΡΠΆΡΠ»ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ | |||
|
|
90 | ||||
|
|
91 | Π’ΠΎΠ³Π΄Π° ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠ°: | |||
|
|
92 | ||||
|
|
93 | ```java | |||
|
|
94 | projections.getProjections().all(projection -> { | |||
|
|
95 | ... | |||
|
|
96 | }); | |||
|
|
97 | ``` | |||
|
|
98 | ||||
|
|
99 | Π±ΠΎΠ»ΡΡΠ΅ Π½Π΅ ΡΡΡΠ°ΡΠ½Π°. | |||
|
|
100 | ΠΠ½Π° eagerly materializes ΡΠΎΠ»ΡΠΊΠΎ Π΄Π΅ΡΡΠ²ΡΠ΅ ΠΊΠ»ΡΡΠΈ, Π° Π½Π΅ Π²Π΅ΡΡ Π°Π³ΡΠ΅Π³Π°ΡΠ½ΡΠΉ Π³ΡΠ°Ρ. | |||
|
|
101 | ||||
|
|
102 | Π‘ΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π·Π°ΠΏΡΠ°ΡΠΈΠ²Π°Π΅ΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎ: | |||
|
|
103 | ||||
|
|
104 | ```java | |||
|
|
105 | Set<VariantLayerBinding> bindings = projections.getBindings(projection); | |||
|
|
106 | NamedDomainObjectProvider<GenericSourceSet> sourceSet = | |||
|
|
107 | materializer.getSourceSet(projection.getName()); | |||
|
|
108 | ``` | |||
|
|
109 | ||||
|
|
110 | Π Π²ΠΎΡ ΡΡΠΈ Π²ΡΠ·ΠΎΠ²Ρ ΡΠΆΠ΅ ΠΌΠΎΠ³ΡΡ Π±ΡΡΡ: | |||
|
|
111 | ||||
|
|
112 | * lazy | |||
|
|
113 | * Π²ΡΡΠΈΡΠ»ΡΠ΅ΠΌΡΠΌΠΈ | |||
|
|
114 | * ΠΊΡΡΠΈΡΡΠ΅ΠΌΡΠΌΠΈ | |||
|
|
115 | * ΠΏΡΠΈΠ²ΡΠ·Π°Π½Π½ΡΠΌΠΈ ΠΊ runtime lifecycle | |||
|
|
116 | ||||
|
|
117 | --- | |||
|
|
118 | ||||
|
|
119 | ## ΠΡΠΈΠΌΠ΅Ρ | |||
|
|
120 | ||||
|
|
121 | ### ΠΠ΅ΡΠ΄Π°ΡΠ½ΡΠΉ Π²Π°ΡΠΈΠ°Π½Ρ | |||
|
|
122 | ||||
|
|
123 | ΠΡΠ΅Π΄ΡΡΠ°Π²ΠΈΠΌ ΡΠ°ΠΊΠΎΠΉ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ: | |||
|
|
124 | ||||
|
|
125 | ```java | |||
|
|
126 | public interface SourceSetProjection extends Named { | |||
|
|
127 | Set<VariantLayerBinding> getBindings(); | |||
|
|
128 | NamedDomainObjectProvider<GenericSourceSet> getSourceSet(); | |||
|
|
129 | } | |||
|
|
130 | ``` | |||
|
|
131 | ||||
|
|
132 | ΠΡΠΎΠ±Π»Π΅ΠΌΡ ΡΡΡ ΡΡΠ°Π·Ρ Π²ΠΈΠ΄Π½Ρ: | |||
|
|
133 | ||||
|
|
134 | * `SourceSetProjection` ΡΠΆΠ΅ Π½Π΅ identity object, Π° ΠΏΠΎΡΡΠΈ Π°Π³ΡΠ΅Π³Π°Ρ | |||
|
|
135 | * Π²Π½ΡΡΡΠΈ ΡΠΌΠ΅ΡΠ°Π½Ρ: | |||
|
|
136 | ||||
|
|
137 | * symbolic identity | |||
|
|
138 | * relation data | |||
|
|
139 | * runtime reference Π² ΡΡΠΆΠΎΠΉ Π΄ΠΎΠΌΠ΅Π½ | |||
|
|
140 | * ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠ° ΡΠ΅ΡΠ΅Π· `all(...)` Π½Π°ΡΠΈΠ½Π°Π΅Ρ ΡΠ°ΡΠΈΡΡ Π·Π° ΡΠΎΠ±ΠΎΠΉ Π±ΠΎΠ»ΡΡΠ΅, ΡΠ΅ΠΌ Ρ ΠΎΡΠ΅Π»ΠΎΡΡ Π±Ρ | |||
|
|
141 | ||||
|
|
142 | ΠΠ° ΡΠ»ΠΎΠ²Π°Ρ ΠΎΠ±ΡΠ΅ΠΊΡ Π½Π°Π·ΡΠ²Π°Π΅ΡΡΡ βprojectionβ, Π° ΠΏΠΎ ΡΠ°ΠΊΡΡ Π²Π½ΡΡΡΠΈ Ρ Π½Π΅Π³ΠΎ ΡΠΆΠ΅ ΠΏΠΎΠ»ΡΠΈΡΡΠ΅ΠΌΡ. | |||
|
|
143 | ||||
|
|
144 | --- | |||
|
|
145 | ||||
|
|
146 | ### ΠΠΎΠ»Π΅Π΅ ΡΠ΄Π°ΡΠ½ΡΠΉ Π²Π°ΡΠΈΠ°Π½Ρ | |||
|
|
147 | ||||
|
|
148 | Π Π°Π·Π΄Π΅Π»ΡΠ΅ΠΌ ΠΎΡΠ²Π΅ΡΡΡΠ²Π΅Π½Π½ΠΎΡΡΡ: | |||
|
|
149 | ||||
|
|
150 | ```java | |||
|
|
151 | public interface SourceSetProjection extends Named { | |||
|
|
152 | } | |||
|
|
153 | ``` | |||
|
|
154 | ||||
|
|
155 | ```java | |||
|
|
156 | public interface SourceSetProjections { | |||
|
|
157 | NamedDomainObjectCollection<SourceSetProjection> getProjections(); | |||
|
|
158 | Set<VariantLayerBinding> getBindings(String sourceSetName); | |||
|
|
159 | } | |||
|
|
160 | ``` | |||
|
|
161 | ||||
|
|
162 | ```java | |||
|
|
163 | public interface SourceSetMaterializer { | |||
|
|
164 | NamedDomainObjectProvider<GenericSourceSet> getSourceSet(String sourceSetName); | |||
|
|
165 | } | |||
|
|
166 | ``` | |||
|
|
167 | ||||
|
|
168 | Π’Π΅ΠΏΠ΅ΡΡ ΡΡΠ΅Π½Π°ΡΠΈΠΉ Π°Π΄Π°ΠΏΡΠ΅ΡΠ° Π²ΡΠ³Π»ΡΠ΄ΠΈΡ ΡΠ°ΠΊ: | |||
|
|
169 | ||||
|
|
170 | ```java | |||
|
|
171 | projections.getProjections().all(projection -> { | |||
|
|
172 | Set<VariantLayerBinding> bindings = | |||
|
|
173 | projections.getBindings(projection.getName()); | |||
|
|
174 | ||||
|
|
175 | NamedDomainObjectProvider<GenericSourceSet> sourceSet = | |||
|
|
176 | materializer.getSourceSet(projection.getName()); | |||
|
|
177 | ||||
|
|
178 | // apply adapter-specific policy | |||
|
|
179 | }); | |||
|
|
180 | ``` | |||
|
|
181 | ||||
|
|
182 | Π§ΡΠΎ ΠΈΠ·ΠΌΠ΅Π½ΠΈΠ»ΠΎΡΡ: | |||
|
|
183 | ||||
|
|
184 | * replayable ΠΏΠΎΠ΄ΠΏΠΈΡΠΊΠ° ΡΠΎΡ ΡΠ°Π½ΠΈΠ»Π°ΡΡ | |||
|
|
185 | * eager materialization Π΄ΠΎΠΏΡΡΡΠΈΠΌΠ°, ΠΏΠΎΡΠΎΠΌΡ ΡΡΠΎ `SourceSetProjection` Π΄Π΅ΡΡΠ²ΡΠΉ | |||
|
|
186 | * Π΄ΠΎΡΠΎΠ³ΠΎΠ΅ ΠΈ Π²ΡΡΠΈΡΠ»ΡΠ΅ΠΌΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΡΠ»ΠΎ Π² ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ API | |||
|
|
187 | * materialization ΠΎΡΡΠ°ΡΡΡΡ ΠΏΠΎΠ΄ ΠΊΠΎΠ½ΡΡΠΎΠ»Π΅ΠΌ ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π²Π»Π°Π΄Π΅Π»ΡΡΠ° | |||
|
|
188 | ||||
|
|
189 | --- | |||
|
|
190 | ||||
|
|
191 | ## ΠΠΎΡΠ΅ΠΌΡ ΡΡΠΎ Π»ΡΡΡΠ΅ ΡΠΎΠ±ΡΡΠΈΠΉ | |||
|
|
192 | ||||
|
|
193 | ΠΠΎΠ³Π΄Π° Π² ΠΌΠΎΠ΄Π΅Π»ΠΈ ΡΠΌΠ΅ΡΠ°Π½Ρ identity ΠΈ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅, ΠΎΡΠ΅Π½Ρ Π±ΡΡΡΡΠΎ Ρ ΠΎΡΠ΅ΡΡΡ ΠΈΠ·ΠΎΠ±ΡΠ΅ΡΠ°ΡΡ ΡΠΎΠ±ΡΡΠΈΡ: | |||
|
|
194 | ||||
|
|
195 | * `projectionCreated` | |||
|
|
196 | * `projectionResolved` | |||
|
|
197 | * `sourceSetAvailable` | |||
|
|
198 | * `sourceSetMaterialized` | |||
|
|
199 | ||||
|
|
200 | ΠΠΎΡΠΎΠΌΡ ΡΡΠΎ Π² ΠΊΠ°ΠΊΠΎΠΉ-ΡΠΎ ΠΌΠΎΠΌΠ΅Π½Ρ ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡΡ Π²Π°ΠΆΠ½ΠΎ, **ΠΊΠΎΠ³Π΄Π° ΠΈΠΌΠ΅Π½Π½ΠΎ** ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΆΠ΅ βΠ΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π³ΠΎΡΠΎΠ²β. | |||
|
|
201 | ||||
|
|
202 | ΠΡΠ»ΠΈ ΠΆΠ΅ Π½Π°Π±Π»ΡΠ΄Π°Π΅ΠΌΡΠΉ ΠΎΠ±ΡΠ΅ΠΊΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΡ ΡΠΎΠ»ΡΠΊΠΎ identity, Π° Π²ΡΡ ΡΡΠΆΡΠ»ΠΎΠ΅ ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌΠΈ Π²ΡΠ·ΠΎΠ²Π°ΠΌΠΈ, ΡΠΎΠ±ΡΡΠΈΠΉΠ½Π°Ρ ΠΌΠΎΠ΄Π΅Π»Ρ ΡΠ°ΡΡΠΎ Π²ΠΎΠΎΠ±ΡΠ΅ Π½Π΅ Π½ΡΠΆΠ½Π°. | |||
|
|
203 | ||||
|
|
204 | ΠΠΎΠ»ΡΡΠ°Π΅ΡΡΡ Π±ΠΎΠ»Π΅Π΅ ΡΠΏΠΎΠΊΠΎΠΉΠ½Π°Ρ ΡΡ Π΅ΠΌΠ°: | |||
|
|
205 | ||||
|
|
206 | * Π΅ΡΡΡ **identity registry** | |||
|
|
207 | * Π΅ΡΡΡ **lookup API** Π΄Π»Ρ ΡΠ²ΡΠ·Π΅ΠΉ | |||
|
|
208 | * Π΅ΡΡΡ **lazy materialization API** Π΄Π»Ρ ΡΡΠΆΡΠ»ΡΡ ΡΡΡΠ½ΠΎΡΡΠ΅ΠΉ | |||
|
|
209 | ||||
|
|
210 | Π’ΠΎ Π΅ΡΡΡ Π²ΠΌΠ΅ΡΡΠΎ ΡΠΎΠ±ΡΡΠΈΠΉ: | |||
|
|
211 | ||||
|
|
212 | > βΠΊΠΎΠ³Π΄Π° ΠΎΠ±ΡΠ΅ΠΊΡ ΡΡΠ°Π½Π΅Ρ Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π³ΠΎΡΠΎΠ², Ρ ΡΡΠΎ-ΡΠΎ ΡΠ΄Π΅Π»Π°Ρβ | |||
|
|
213 | ||||
|
|
214 | ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΡΡ ΠΎΠ±ΡΡΠ½ΡΠΉ ΠΈ ΠΏΠΎΠ½ΡΡΠ½ΡΠΉ flow: | |||
|
|
215 | ||||
|
|
216 | > βΡ Π²ΠΈΠΆΡ identity, Π° Π½ΡΠΆΠ½ΠΎΠ΅ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΠΏΡΠΎΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎ, ΠΊΠΎΠ³Π΄Π° ΠΎΠ½ΠΎ ΠΌΠ½Π΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ ΠΏΠΎΠ½Π°Π΄ΠΎΠ±ΠΈΡΡΡβ | |||
|
|
217 | ||||
|
|
218 | ΠΡΠΎ ΠΏΡΠΎΡΠ΅ ΠΈ Π΄Π»Ρ reasoning, ΠΈ Π΄Π»Ρ ΡΠ΅ΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ, ΠΈ Π΄Π»Ρ ΡΠ²ΠΎΠ»ΡΡΠΈΠΈ API. | |||
|
|
219 | ||||
|
|
220 | --- | |||
|
|
221 | ||||
|
|
222 | ## Π§ΡΠΎ ΠΈΠΌΠ΅Π½Π½ΠΎ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΆΠΈΡΡ Π² identity object | |||
|
|
223 | ||||
|
|
224 | Identity object Π½Π΅ ΠΎΠ±ΡΠ·Π°Π½ Π±ΡΡΡ βΠ°Π±ΡΠΎΠ»ΡΡΠ½ΠΎ ΠΏΡΡΡΡΠΌβ. | |||
|
|
225 | ΠΠ½ ΠΌΠΎΠΆΠ΅Ρ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ **ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΠ΅ Π²ΡΠ±ΠΎΡΠ°**, Π΅ΡΠ»ΠΈ ΠΎΠ½ΠΈ: | |||
|
|
226 | ||||
|
|
227 | * Π΄Π΅ΡΠ΅Π²Ρ | |||
|
|
228 | * ΡΡΠ°Π±ΠΈΠ»ΡΠ½Ρ | |||
|
|
229 | * Π½Π΅ ΡΡΠ΅Π±ΡΡΡ ΡΡΠΆΡΠ»ΠΎΠΉ ΠΈΠ½ΠΈΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΠΈ | |||
|
|
230 | * Π½Π΅ ΠΏΡΠ΅Π²ΡΠ°ΡΠ°ΡΡ ΠΎΠ±ΡΠ΅ΠΊΡ Π² Π°Π³ΡΠ΅Π³Π°Ρ | |||
|
|
231 | ||||
|
|
232 | ΠΠ°ΠΏΡΠΈΠΌΠ΅Ρ: | |||
|
|
233 | ||||
|
|
234 | * `id` | |||
|
|
235 | * `name` | |||
|
|
236 | * `kind` | |||
|
|
237 | * `domain key` | |||
|
|
238 | * maybe `type` | |||
|
|
239 | ||||
|
|
240 | ΠΠΎ ΠΎΠ½ Π½Π΅ Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΠΎΠ΄Π΅ΡΠΆΠ°ΡΡ: | |||
|
|
241 | ||||
|
|
242 | * Π²ΡΡΠΈΡΠ»ΡΠ΅ΠΌΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π°Π³ΡΠ΅Π³Π°ΡΠ° | |||
|
|
243 | * ΡΡΡΠ»ΠΊΠΈ Π²ΡΠ΅ΠΌΠ΅Π½ΠΈ Π²ΡΠΏΠΎΠ»Π½Π΅Π½ΠΈΡ Π½Π° ΡΡΠΆΠΈΠ΅ Π΄ΠΎΠΌΠ΅Π½Ρ | |||
|
|
244 | * lazy providers Π½Π° heavy objects | |||
|
|
245 | * derived state, ΠΊΠΎΡΠΎΡΡΠΉ ΠΌΠΎΠΆΠ΅Ρ ΠΏΡΠΎΠ²ΠΎΡΠΈΡΠΎΠ²Π°ΡΡ ΡΠ°Π½Π½ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ | |||
|
|
246 | ||||
|
|
247 | Π₯ΠΎΡΠΎΡΠ°Ρ ΠΏΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠ°Ρ ΡΠΎΡΠΌΡΠ»Π°: | |||
|
|
248 | ||||
|
|
249 | **identity object contains selection metadata; aggregate content is obtained separately.** | |||
|
|
250 | ||||
|
|
251 | --- | |||
|
|
252 | ||||
|
|
253 | ## ΠΠΎΠ³Π΄Π° ΡΡΠΎΡ ΠΏΡΠΈΠ½ΡΠΈΠΏ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ ΠΏΠΎΠ»Π΅Π·Π΅Π½ | |||
|
|
254 | ||||
|
|
255 | ΠΠ½ ΠΎΡΠΎΠ±Π΅Π½Π½ΠΎ Ρ ΠΎΡΠΎΡ, Π΅ΡΠ»ΠΈ: | |||
|
|
256 | ||||
|
|
257 | * Π΅ΡΡΡ replayable Π½Π°Π±Π»ΡΠ΄Π΅Π½ΠΈΠ΅ ΡΠ΅ΡΠ΅Π· `all(...)` | |||
|
|
258 | * identity-ΠΎΠ±ΡΠ΅ΠΊΡΡ Π΄Π΅ΡΡΠ²ΡΠ΅ ΠΈ ΠΏΠΎΡΡΠΈ immutable | |||
|
|
259 | * ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ Π°Π³ΡΠ΅Π³Π°ΡΠ° ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π΄ΠΎΡΠΎΠ³ΠΈΠΌ | |||
|
|
260 | * Π½ΡΠΆΠ΅Π½ clean split ΠΌΠ΅ΠΆΠ΄Ρ symbolic model ΠΈ runtime model | |||
|
|
261 | * Ρ ΠΎΡΠ΅ΡΡΡ ΠΈΠ·Π±Π΅ΠΆΠ°ΡΡ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΠΎΠΉ ΡΠΎΠ±ΡΡΠΈΠΉΠ½ΠΎΠΉ ΡΠΈΠ½Ρ | |||
|
|
262 | ||||
|
|
263 | ΠΠ»Ρ Gradle-ΠΏΠΎΠ΄ΠΎΠ±Π½ΡΡ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΡΡΠΎ Π²ΠΎΠΎΠ±ΡΠ΅ ΠΎΡΠ΅Π½Ρ Π΅ΡΡΠ΅ΡΡΠ²Π΅Π½Π½ΡΠΉ ΠΏΡΠΈΡΠΌ. | |||
|
|
264 | ||||
|
|
265 | --- | |||
|
|
266 | ||||
|
|
267 | ## ΠΠΎΠ³Π΄Π° ΠΎΠ½ Π½Π΅ Π½ΡΠΆΠ΅Π½ | |||
|
|
268 | ||||
|
|
269 | ΠΡΠ»ΠΈ ΠΎΠ±ΡΠ΅ΠΊΡ: | |||
|
|
270 | ||||
|
|
271 | * ΠΌΠ°Π»Π΅Π½ΡΠΊΠΈΠΉ | |||
|
|
272 | * Π΄Π΅ΡΡΠ²ΡΠΉ | |||
|
|
273 | * ΡΠΆΠ΅ ΡΠ°ΠΌ ΠΏΠΎ ΡΠ΅Π±Π΅ ΠΈ Π΅ΡΡΡ Π²ΡΡ Π΅Π³ΠΎ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅ | |||
|
|
274 | ||||
|
|
275 | ΡΠΎ ΡΠ°Π·Π΄Π΅Π»Π΅Π½ΠΈΠ΅ Π½Π° identity ΠΈ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ lookup API ΠΌΠΎΠΆΠ΅Ρ Π±ΡΡΡ Π»ΠΈΡΠ½ΠΈΠΌ. | |||
|
|
276 | ||||
|
|
277 | Π’ΠΎ Π΅ΡΡΡ ΡΡΠΎΡ ΠΏΡΠΈΠ½ΡΠΈΠΏ ΠΏΠΎΠ»Π΅Π·Π΅Π½ Π½Π΅ ΠΊΠ°ΠΊ Π΄ΠΎΠ³ΠΌΠ°, Π° ΠΊΠ°ΠΊ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½Ρ. | |||
|
|
278 | ΠΠ³ΠΎ ΡΡΠΎΠΈΡ ΠΏΡΠΈΠΌΠ΅Π½ΡΡΡ ΡΠ°ΠΌ, Π³Π΄Π΅ ΡΠ΅Π°Π»ΡΠ½ΠΎ Π΅ΡΡΡ ΡΠΈΡΠΊ ΡΠΌΠ΅ΡΠ΅Π½ΠΈΡ: | |||
|
|
279 | ||||
|
|
280 | * ΠΊΠ»ΡΡΠ° | |||
|
|
281 | * ΡΠΎΡΡΠΎΡΠ½ΠΈΡ | |||
|
|
282 | * Π²ΡΡΠΈΡΠ»Π΅Π½ΠΈΡ | |||
|
|
283 | * runtime reference | |||
|
|
284 | ||||
|
|
285 | --- | |||
|
|
286 | ||||
|
|
287 | ## ΠΡΠ°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈΠΉ ΠΈΡΠΎΠ³ | |||
|
|
288 | ||||
|
|
289 | ΠΡΠ»ΠΈ ΡΡΠΎΡΠΌΡΠ»ΠΈΡΠΎΠ²Π°ΡΡ ΠΊΠΎΡΠΎΡΠΊΠΎ, ΡΠΎ ΠΏΡΠΈΠ½ΡΠΈΠΏ ΡΠ°ΠΊΠΎΠΉ: | |||
|
|
290 | ||||
|
|
291 | 1. **Identity objects** ΡΠΎΠ΄Π΅ΡΠΆΠ°Ρ ΡΠΎΠ»ΡΠΊΠΎ identity ΠΈ Π΄Π΅ΡΡΠ²ΡΠ΅ ΠΌΠ΅ΡΠ°Π΄Π°Π½Π½ΡΠ΅ Π²ΡΠ±ΠΎΡΠ°. | |||
|
|
292 | 2. **ΠΠ³ΡΠ΅Π³Π°ΡΠ½ΠΎΠ΅ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ΅** Π½Π΅ Ρ ΡΠ°Π½ΠΈΡΡΡ Π²Π½ΡΡΡΠΈ Π½ΠΈΡ , Π° ΠΏΠΎΠ»ΡΡΠ°Π΅ΡΡΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠΌΠΈ API-Π²ΡΠ·ΠΎΠ²Π°ΠΌΠΈ. | |||
|
|
293 | 3. ΠΡΠΈ API ΡΠΆΠ΅ ΠΌΠΎΠ³ΡΡ Π²ΡΠΏΠΎΠ»Π½ΡΡΡ: | |||
|
|
294 | ||||
|
|
295 | * lazy resolution | |||
|
|
296 | * ΠΊΡΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ | |||
|
|
297 | * heavy computation | |||
|
|
298 | * materialization | |||
|
|
299 | 4. ΠΠ»Π°Π³ΠΎΠ΄Π°ΡΡ ΡΡΠΎΠΌΡ ΠΌΠΎΠΆΠ½ΠΎ Π±Π΅Π·ΠΎΠΏΠ°ΡΠ½ΠΎ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°ΡΡ replayable ΠΌΠ΅Ρ Π°Π½ΠΈΠ·ΠΌΡ Π²ΡΠΎΠ΄Π΅ `all(...)`, Π½Π΅ ΡΠ°Π·ΡΡΡΠ°Ρ Π»Π΅Π½ΠΈΠ²ΡΡ ΡΠ΅ΠΌΠ°Π½ΡΠΈΠΊΡ ΡΠ°ΠΌ, Π³Π΄Π΅ ΠΎΠ½Π° Π΄Π΅ΠΉΡΡΠ²ΠΈΡΠ΅Π»ΡΠ½ΠΎ Π²Π°ΠΆΠ½Π°. | |||
|
|
300 | ||||
|
|
301 | ΠΠΌΠ΅Π½Π½ΠΎ ΡΠ°ΠΊ ΡΠ΄Π°ΡΡΡΡ ΡΠΎΠ²ΠΌΠ΅ΡΡΠΈΡΡ: | |||
|
|
302 | ||||
|
|
303 | * ΠΏΡΠΎΡΡΠΎΠ΅ Π½Π°Π±Π»ΡΠ΄Π΅Π½ΠΈΠ΅ | |||
|
|
304 | * ΡΠΈΡΡΡΡ ΠΌΠΎΠ΄Π΅Π»Ρ | |||
|
|
305 | * ΠΎΡΡΡΡΡΡΠ²ΠΈΠ΅ ΡΠΎΠ±ΡΡΠ²Π΅Π½Π½ΡΡ ΡΠΎΠ±ΡΡΠΈΠΉ | |||
|
|
306 | * Π»Π΅Π½ΠΈΠ²ΡΡ ΠΌΠ°ΡΠ΅ΡΠΈΠ°Π»ΠΈΠ·Π°ΡΠΈΡ ΡΡΠΆΡΠ»ΠΎΠ³ΠΎ ΡΠΎΡΡΠΎΡΠ½ΠΈΡ | |||
| @@ -0,0 +1,185 | |||||
|
|
1 | package org.implab.gradle.variants; | |||
|
|
2 | ||||
|
|
3 | import java.util.ArrayList; | |||
|
|
4 | import java.util.HashMap; | |||
|
|
5 | import java.util.HashSet; | |||
|
|
6 | import java.util.List; | |||
|
|
7 | import java.util.Map; | |||
|
|
8 | import java.util.Set; | |||
|
|
9 | import java.util.function.Consumer; | |||
|
|
10 | import java.util.stream.Stream; | |||
|
|
11 | ||||
|
|
12 | import org.eclipse.jdt.annotation.NonNullByDefault; | |||
|
|
13 | import org.gradle.api.Action; | |||
|
|
14 | import org.gradle.api.NamedDomainObjectCollection; | |||
|
|
15 | import org.gradle.api.NamedDomainObjectContainer; | |||
|
|
16 | import org.gradle.api.NamedDomainObjectProvider; | |||
|
|
17 | import org.gradle.api.Plugin; | |||
|
|
18 | import org.gradle.api.Project; | |||
|
|
19 | import org.implab.gradle.common.core.lang.Deferred; | |||
|
|
20 | import org.implab.gradle.common.core.lang.Strings; | |||
|
|
21 | import org.implab.gradle.common.sources.GenericSourceSet; | |||
|
|
22 | import org.implab.gradle.common.sources.SourcesPlugin; | |||
|
|
23 | import org.implab.gradle.variants.model.Layer; | |||
|
|
24 | import org.implab.gradle.variants.model.Variant; | |||
|
|
25 | import org.implab.gradle.variants.model.VariantsExtension; | |||
|
|
26 | import org.implab.gradle.variants.sources.LayerProjectionRule; | |||
|
|
27 | import org.implab.gradle.variants.sources.SourceSetMaterializer; | |||
|
|
28 | import org.implab.gradle.variants.sources.SourceSetProjection; | |||
|
|
29 | import org.implab.gradle.variants.sources.SourceSetProjections; | |||
|
|
30 | import org.implab.gradle.variants.sources.VariantLayerBinding; | |||
|
|
31 | import org.implab.gradle.variants.sources.VariantSourcesContext; | |||
|
|
32 | import org.implab.gradle.variants.sources.VariantSourcesExtension; | |||
|
|
33 | ||||
|
|
34 | @NonNullByDefault | |||
|
|
35 | public abstract class VariantSourcesPlugin implements Plugin<Project> { | |||
|
|
36 | @Override | |||
|
|
37 | public void apply(Project target) { | |||
|
|
38 | // Apply the main VariantsPlugin to ensure the core variant model is available. | |||
|
|
39 | target.getPlugins().apply(VariantsPlugin.class); | |||
|
|
40 | target.getPlugins().apply(SourcesPlugin.class); | |||
|
|
41 | // Access the VariantsExtension to configure variant sources. | |||
|
|
42 | var variantsExtension = target.getExtensions().getByType(VariantsExtension.class); | |||
|
|
43 | var objectFactory = target.getObjects(); | |||
|
|
44 | ||||
|
|
45 | var sources = SourcesPlugin.getSourcesExtension(target); | |||
|
|
46 | ||||
|
|
47 | var deferred = new Deferred<VariantSourcesContext>(); | |||
|
|
48 | var layerProjectionRules = objectFactory.domainObjectContainer(LayerProjectionRule.class); | |||
|
|
49 | ||||
|
|
50 | var variantSourcesExtension = new VariantSourcesExtension() { | |||
|
|
51 | @Override | |||
|
|
52 | public NamedDomainObjectContainer<LayerProjectionRule> getLayerRules() { | |||
|
|
53 | return layerProjectionRules; | |||
|
|
54 | } | |||
|
|
55 | ||||
|
|
56 | @Override | |||
|
|
57 | public void whenFinalized(Action<? super VariantSourcesContext> action) { | |||
|
|
58 | deferred.whenResolved(action::execute); | |||
|
|
59 | } | |||
|
|
60 | }; | |||
|
|
61 | target.getExtensions().add(VariantSourcesExtension.class, "variantSources", variantSourcesExtension); | |||
|
|
62 | ||||
|
|
63 | // create convention to automatically create layer projection rules for each | |||
|
|
64 | // variant layer | |||
|
|
65 | variantsExtension.getLayers().all(layer -> { | |||
|
|
66 | // Automatically create a layer projection rule for each variant layer. | |||
|
|
67 | variantSourcesExtension.layer(layer.getName(), rule -> { | |||
|
|
68 | // Configure the source set name pattern based on the layer name. | |||
|
|
69 | rule.getSourceSetNamePattern() | |||
|
|
70 | .convention("{variant}{layerCapitalized}") | |||
|
|
71 | .finalizeValueOnRead(); | |||
|
|
72 | }); | |||
|
|
73 | }); | |||
|
|
74 | ||||
|
|
75 | var projections = objectFactory.domainObjectContainer(SourceSetProjection.class); | |||
|
|
76 | ||||
|
|
77 | Map<String, Set<VariantLayerBinding>> projectionBindings = new HashMap<>(); | |||
|
|
78 | ||||
|
|
79 | var sourceSetProjections = new SourceSetProjections() { | |||
|
|
80 | @Override | |||
|
|
81 | public NamedDomainObjectCollection<SourceSetProjection> getProjections() { | |||
|
|
82 | return projections; | |||
|
|
83 | } | |||
|
|
84 | ||||
|
|
85 | @Override | |||
|
|
86 | public Set<VariantLayerBinding> getBindings(String sourceSetName) { | |||
|
|
87 | return projectionBindings.getOrDefault(sourceSetName, Set.of()); | |||
|
|
88 | } | |||
|
|
89 | ||||
|
|
90 | @Override | |||
|
|
91 | public Set<VariantLayerBinding> getBindings(SourceSetProjection projection) { | |||
|
|
92 | return getBindings(projection.getName()); | |||
|
|
93 | } | |||
|
|
94 | }; | |||
|
|
95 | ||||
|
|
96 | Set<String> materializedSourceSets = new HashSet<>(); | |||
|
|
97 | ||||
|
|
98 | var materializer = new SourceSetMaterializer() { | |||
|
|
99 | @Override | |||
|
|
100 | public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(String sourceSetName) { | |||
|
|
101 | return materializedSourceSets.add(sourceSetName) | |||
|
|
102 | ? sources.register(sourceSetName) | |||
|
|
103 | : sources.named(sourceSetName); | |||
|
|
104 | } | |||
|
|
105 | }; | |||
|
|
106 | ||||
|
|
107 | var bindings = new VariantBindings(); | |||
|
|
108 | ||||
|
|
109 | target.afterEvaluate(t -> { | |||
|
|
110 | // Once the project is evaluated, resolve the deferred context and finalize the | |||
|
|
111 | // sources configuration. | |||
|
|
112 | variantsExtension.getLayers().all(bindings::addLayer); | |||
|
|
113 | variantsExtension.getVariants().all(bindings::addVariant); | |||
|
|
114 | ||||
|
|
115 | variantsExtension.getLayers().all(layer -> { | |||
|
|
116 | // For each layer, apply the projection rules to generate source set projections. | |||
|
|
117 | ||||
|
|
118 | var rule = layerProjectionRules.maybeCreate(layer.getName()); | |||
|
|
119 | var pattern = rule.getSourceSetNamePattern().getOrElse("{variant}{layerCapitalized}"); | |||
|
|
120 | // Generate source set names based on the pattern and variant/layer information. | |||
|
|
121 | // This is a simplified example; real implementation would need to consider | |||
|
|
122 | // all variants and layers. | |||
|
|
123 | var sourceSetName = pattern.replace("{layer}", layer.getName()) | |||
|
|
124 | .replace("{variant}", "main") // Placeholder for actual variant name | |||
|
|
125 | .replace("{layerCapitalized}", Strings.capitalize(layer.getName())); | |||
|
|
126 | ||||
|
|
127 | var projection = objectFactory.newInstance(SourceSetProjection.class, sourceSetName); | |||
|
|
128 | projections.add(projection); | |||
|
|
129 | // Bind the projection to the corresponding variant layer. | |||
|
|
130 | projectionBindings.computeIfAbsent(sourceSetName, k -> new HashSet<>()) | |||
|
|
131 | .add(new VariantLayerBinding(layer.getName(), projection)); | |||
|
|
132 | }); | |||
|
|
133 | ||||
|
|
134 | var context = new VariantSourcesContext() { | |||
|
|
135 | ||||
|
|
136 | @Override | |||
|
|
137 | public SourceSetProjections getProjections() { | |||
|
|
138 | return sourceSetProjections; | |||
|
|
139 | } | |||
|
|
140 | ||||
|
|
141 | @Override | |||
|
|
142 | public SourceSetMaterializer getMaterializer() { | |||
|
|
143 | return materializer; | |||
|
|
144 | } | |||
|
|
145 | ||||
|
|
146 | // Implementation of the context that provides access to variant and layer | |||
|
|
147 | // information. | |||
|
|
148 | }; | |||
|
|
149 | deferred.resolve(context); | |||
|
|
150 | }); | |||
|
|
151 | } | |||
|
|
152 | ||||
|
|
153 | class VariantBindings { | |||
|
|
154 | private final Set<Layer> layers = new HashSet<>(); | |||
|
|
155 | private final Set<Variant> variants = new HashSet<>(); | |||
|
|
156 | ||||
|
|
157 | private final List<Consumer<? super VariantLayerBinding>> listeners = new ArrayList<>(); | |||
|
|
158 | ||||
|
|
159 | void addLayer(Layer layer) { | |||
|
|
160 | layers.add(layer); | |||
|
|
161 | variants.stream() | |||
|
|
162 | .map(variant -> VariantLayerBinding.of(variant, layer)) | |||
|
|
163 | .forEach(this::notifyBindingAdded); | |||
|
|
164 | } | |||
|
|
165 | ||||
|
|
166 | void addVariant(Variant variant) { | |||
|
|
167 | variants.add(variant); | |||
|
|
168 | layers.stream() | |||
|
|
169 | .map(layer -> VariantLayerBinding.of(variant, layer)) | |||
|
|
170 | .forEach(this::notifyBindingAdded); | |||
|
|
171 | } | |||
|
|
172 | ||||
|
|
173 | void whenBindingAdded(Consumer<? super VariantLayerBinding> listener) { | |||
|
|
174 | layers.stream() | |||
|
|
175 | .flatMap(layer -> variants.stream().map(variant -> VariantLayerBinding.of(variant, layer))) | |||
|
|
176 | .forEach(listener); | |||
|
|
177 | listeners.add(listener); | |||
|
|
178 | } | |||
|
|
179 | ||||
|
|
180 | private void notifyBindingAdded(VariantLayerBinding binding) { | |||
|
|
181 | listeners.forEach(listener -> listener.accept(binding)); | |||
|
|
182 | } | |||
|
|
183 | } | |||
|
|
184 | ||||
|
|
185 | } | |||
| @@ -0,0 +1,70 | |||||
|
|
1 | package org.implab.gradle.variants.model; | |||
|
|
2 | ||||
|
|
3 | import java.util.HashSet; | |||
|
|
4 | import java.util.Set; | |||
|
|
5 | import java.util.function.Consumer; | |||
|
|
6 | import java.util.stream.Stream; | |||
|
|
7 | ||||
|
|
8 | import org.gradle.api.Action; | |||
|
|
9 | import org.gradle.api.Named; | |||
|
|
10 | import org.gradle.api.provider.SetProperty; | |||
|
|
11 | import org.implab.gradle.common.core.lang.Closures; | |||
|
|
12 | import org.implab.gradle.common.core.lang.Strings; | |||
|
|
13 | ||||
|
|
14 | import groovy.lang.Closure; | |||
|
|
15 | ||||
|
|
16 | public interface VariantDefinition extends Named { | |||
|
|
17 | /** | |||
|
|
18 | * Role bindings declared inside this variant. | |||
|
|
19 | * | |||
|
|
20 | * The binding pair of role and layer names. | |||
|
|
21 | */ | |||
|
|
22 | SetProperty<RoleLayerBinding> getRoleBindings(); | |||
|
|
23 | ||||
|
|
24 | /** | |||
|
|
25 | * Creates or returns an existing role binding and configures it. | |||
|
|
26 | */ | |||
|
|
27 | default void role(String name, Action<? super RoleSpec> action) { | |||
|
|
28 | var spec = new RoleSpec(name); | |||
|
|
29 | action.execute(spec); | |||
|
|
30 | spec.accept(getRoleBindings()::add); | |||
|
|
31 | } | |||
|
|
32 | ||||
|
|
33 | default void role(String name, Closure<?> closure) { | |||
|
|
34 | role(name, Closures.action(closure)); | |||
|
|
35 | } | |||
|
|
36 | ||||
|
|
37 | default void finalizeVariant() { | |||
|
|
38 | getRoleBindings().finalizeValue(); | |||
|
|
39 | } | |||
|
|
40 | ||||
|
|
41 | public static class RoleSpec { | |||
|
|
42 | private final String name; | |||
|
|
43 | private final Set<String> layerNames; | |||
|
|
44 | ||||
|
|
45 | public RoleSpec(String name) { | |||
|
|
46 | this.name = name; | |||
|
|
47 | this.layerNames = new HashSet<>(); | |||
|
|
48 | } | |||
|
|
49 | ||||
|
|
50 | public String getName() { | |||
|
|
51 | return name; | |||
|
|
52 | } | |||
|
|
53 | ||||
|
|
54 | public Set<String> getLayerNames() { | |||
|
|
55 | return layerNames; | |||
|
|
56 | } | |||
|
|
57 | ||||
|
|
58 | public void layers(String name, String... extraNames) { | |||
|
|
59 | Stream.concat(Stream.of(name), Stream.of(extraNames)) | |||
|
|
60 | .map(Strings::requireNonBlank) | |||
|
|
61 | .forEach(this.layerNames::add); | |||
|
|
62 | } | |||
|
|
63 | ||||
|
|
64 | void accept(Consumer<? super RoleLayerBinding> consumer) { | |||
|
|
65 | layerNames.stream() | |||
|
|
66 | .map(layerName -> new RoleLayerBinding(name, layerName)) | |||
|
|
67 | .forEach(consumer); | |||
|
|
68 | } | |||
|
|
69 | } | |||
|
|
70 | } | |||
| @@ -0,0 +1,26 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import org.gradle.api.Action; | |||
|
|
4 | import org.gradle.api.Named; | |||
|
|
5 | import org.gradle.api.provider.Property; | |||
|
|
6 | ||||
|
|
7 | /** | |||
|
|
8 | * Projection rule for a layer. | |||
|
|
9 | */ | |||
|
|
10 | public interface LayerProjectionRule extends Named { | |||
|
|
11 | ||||
|
|
12 | /** | |||
|
|
13 | * Pattern used to calculate the source set name. | |||
|
|
14 | * Examples: | |||
|
|
15 | * "{layer}" | |||
|
|
16 | * "{variant}{layerCapitalized}" | |||
|
|
17 | */ | |||
|
|
18 | Property<String> getSourceSetNamePattern(); | |||
|
|
19 | ||||
|
|
20 | /** | |||
|
|
21 | * Optional hook for future extension. | |||
|
|
22 | */ | |||
|
|
23 | default void configure(Action<? super LayerProjectionRule> action) { | |||
|
|
24 | action.execute(this); | |||
|
|
25 | } | |||
|
|
26 | } No newline at end of file | |||
| @@ -0,0 +1,15 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import org.gradle.api.NamedDomainObjectProvider; | |||
|
|
4 | import org.implab.gradle.common.sources.GenericSourceSet; | |||
|
|
5 | ||||
|
|
6 | /** | |||
|
|
7 | * Materializes symbolic source set names into actual GenericSourceSet instances. | |||
|
|
8 | */ | |||
|
|
9 | public interface SourceSetMaterializer { | |||
|
|
10 | NamedDomainObjectProvider<GenericSourceSet> getSourceSet(String sourceSetName); | |||
|
|
11 | ||||
|
|
12 | default NamedDomainObjectProvider<GenericSourceSet> getSourceSet(SourceSetProjection projection) { | |||
|
|
13 | return getSourceSet(projection.getName()); | |||
|
|
14 | } | |||
|
|
15 | } No newline at end of file | |||
| @@ -0,0 +1,10 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import org.gradle.api.Named; | |||
|
|
4 | ||||
|
|
5 | /** | |||
|
|
6 | * Represents a projected source set. This is an identity object and doesn't contain any state. | |||
|
|
7 | * The name of the projection is used as the source set name by the {@link SourceSetMaterializer}. | |||
|
|
8 | */ | |||
|
|
9 | public interface SourceSetProjection extends Named { | |||
|
|
10 | } | |||
| @@ -0,0 +1,28 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import java.util.Set; | |||
|
|
4 | ||||
|
|
5 | import org.gradle.api.NamedDomainObjectCollection; | |||
|
|
6 | ||||
|
|
7 | /** | |||
|
|
8 | * Registry of symbolic source set names produced by sources projection. | |||
|
|
9 | * | |||
|
|
10 | * Identity in this registry is the GenericSourceSet name. | |||
|
|
11 | */ | |||
|
|
12 | public interface SourceSetProjections { | |||
|
|
13 | ||||
|
|
14 | /** | |||
|
|
15 | * Returns all source set projections. This is a separate | |||
|
|
16 | */ | |||
|
|
17 | NamedDomainObjectCollection<SourceSetProjection> getProjections(); | |||
|
|
18 | ||||
|
|
19 | /** | |||
|
|
20 | * Returns all logical bindings projected into the given source set name. | |||
|
|
21 | */ | |||
|
|
22 | Set<VariantLayerBinding> getBindings(String sourceSetName); | |||
|
|
23 | ||||
|
|
24 | /** | |||
|
|
25 | * Returns all logical bindings projected into the given source set name. | |||
|
|
26 | */ | |||
|
|
27 | Set<VariantLayerBinding> getBindings(SourceSetProjection projection); | |||
|
|
28 | } No newline at end of file | |||
| @@ -0,0 +1,27 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import org.implab.gradle.variants.model.Layer; | |||
|
|
4 | import org.implab.gradle.variants.model.Variant; | |||
|
|
5 | ||||
|
|
6 | /** | |||
|
|
7 | * Logical usage of a layer inside a variant. | |||
|
|
8 | * Identity: (variantName, layerName) | |||
|
|
9 | */ | |||
|
|
10 | public interface VariantLayerBinding { | |||
|
|
11 | Variant getVariant(); | |||
|
|
12 | Layer getLayer(); | |||
|
|
13 | ||||
|
|
14 | public static VariantLayerBinding of(Variant variant, Layer layer) { | |||
|
|
15 | return new VariantLayerBinding() { | |||
|
|
16 | @Override | |||
|
|
17 | public Variant getVariant() { | |||
|
|
18 | return variant; | |||
|
|
19 | } | |||
|
|
20 | ||||
|
|
21 | @Override | |||
|
|
22 | public Layer getLayer() { | |||
|
|
23 | return layer; | |||
|
|
24 | } | |||
|
|
25 | }; | |||
|
|
26 | } | |||
|
|
27 | } No newline at end of file | |||
| @@ -0,0 +1,7 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | public interface VariantSourcesContext { | |||
|
|
4 | SourceSetProjections getProjections(); | |||
|
|
5 | ||||
|
|
6 | SourceSetMaterializer getMaterializer(); | |||
|
|
7 | } | |||
| @@ -0,0 +1,37 | |||||
|
|
1 | package org.implab.gradle.variants.sources; | |||
|
|
2 | ||||
|
|
3 | import org.gradle.api.Action; | |||
|
|
4 | import org.gradle.api.NamedDomainObjectContainer; | |||
|
|
5 | import org.implab.gradle.common.core.lang.Closures; | |||
|
|
6 | ||||
|
|
7 | import groovy.lang.Closure; | |||
|
|
8 | ||||
|
|
9 | public interface VariantSourcesExtension { | |||
|
|
10 | ||||
|
|
11 | /** | |||
|
|
12 | * Projection rules keyed by layer name. | |||
|
|
13 | */ | |||
|
|
14 | NamedDomainObjectContainer<LayerProjectionRule> getLayerRules(); | |||
|
|
15 | ||||
|
|
16 | /** | |||
|
|
17 | * Creates or returns an existing layer rule and configures it. | |||
|
|
18 | */ | |||
|
|
19 | default LayerProjectionRule layerRule(String name) { | |||
|
|
20 | return getLayerRules().maybeCreate(name); | |||
|
|
21 | } | |||
|
|
22 | ||||
|
|
23 | /** | |||
|
|
24 | * Creates or returns an existing layer rule and configures it. | |||
|
|
25 | */ | |||
|
|
26 | default LayerProjectionRule layer(String name, Action<? super LayerProjectionRule> action) { | |||
|
|
27 | LayerProjectionRule rule = layerRule(name); | |||
|
|
28 | action.execute(rule); | |||
|
|
29 | return rule; | |||
|
|
30 | } | |||
|
|
31 | ||||
|
|
32 | void whenFinalized(Action<? super VariantSourcesContext> action); | |||
|
|
33 | ||||
|
|
34 | default void whenFinalized(Closure<?> closure) { | |||
|
|
35 | whenFinalized(Closures.action(closure)); | |||
|
|
36 | } | |||
|
|
37 | } No newline at end of file | |||
General Comments 0
You need to be logged in to leave comments.
Login now
