##// END OF EJS Templates
Set the project version to 0.1.0, add publication descriptions/license metadata, and keep module-level docs as compatibility pointers to the root documentation.
cin -
r60:e376d0cab00e default
parent child
Show More
@@ -0,0 +1,24
1 BSD 2-Clause License
2
3 Copyright (c) 2026 gradle-common contributors.
4
5 Redistribution and use in source and binary forms, with or without modification,
6 are permitted provided that the following conditions are met:
7
8 1. Redistributions of source code must retain the above copyright notice, this
9 list of conditions and the following disclaimer.
10
11 2. Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
19 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
22 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,99
1 # Local Ivy Publishing
2
3 This project currently publishes only to a local Ivy repository. Maven Central,
4 signing, and Gradle Plugin Portal publication are intentionally out of scope for
5 the current preparation step.
6
7 Published Ivy descriptors include the BSD-2-Clause license metadata.
8
9 ## Repository
10
11 The configured Ivy repository is:
12
13 ```text
14 ${user.home}/ivy-repo
15 ```
16
17 This is defined in:
18
19 - `common/build.gradle`
20 - `variants/build.gradle`
21
22 ## Verify Before Publishing
23
24 Run a full clean verification:
25
26 ```bash
27 ./gradlew clean check javadoc jar sourcesJar javadocJar --rerun-tasks
28 ```
29
30 Optional configuration-cache smoke check:
31
32 ```bash
33 ./gradlew check --configuration-cache
34 ```
35
36 ## Publish
37
38 Publish all modules:
39
40 ```bash
41 ./gradlew publish
42 ```
43
44 Publish modules explicitly:
45
46 ```bash
47 ./gradlew :common:publishIvyPublicationToIvyRepository \
48 :variants:publishIvyPublicationToIvyRepository
49 ```
50
51 Safe smoke publish into a temporary repository:
52
53 ```bash
54 ./gradlew -Duser.home=/tmp/gradle-common-ivy-smoke \
55 :common:publishIvyPublicationToIvyRepository \
56 :variants:publishIvyPublicationToIvyRepository \
57 --rerun-tasks
58 ```
59
60 ## Consume Locally
61
62 Use `buildscript` classpath for now:
63
64 ```groovy
65 buildscript {
66 repositories {
67 ivy {
68 url "${System.properties['user.home']}/ivy-repo"
69 }
70 mavenCentral()
71 }
72 dependencies {
73 classpath 'org.implab.gradle:variants:0.1.0'
74 }
75 }
76
77 apply plugin: 'org.implab.gradle-variants'
78 apply plugin: 'org.implab.gradle-variants-sources'
79 ```
80
81 The `plugins {}` DSL needs generated plugin marker artifacts and is not part of
82 the current local Ivy contract.
83
84 ## Published Artifacts
85
86 Each module publishes:
87
88 - `<module>-<version>.jar`
89 - `<module>-<version>-sources.jar`
90 - `<module>-<version>-javadoc.jar`
91 - `ivy.xml`
92 - Gradle module metadata
93
94 ## Current Coordinates
95
96 ```text
97 org.implab.gradle:common:0.1.0
98 org.implab.gradle:variants:0.1.0
99 ```
@@ -0,0 +1,296
1 # gradle-common
2
3 Java 21 multi-project build with shared Gradle utilities and experimental
4 variant-oriented Gradle plugins.
5
6 The repository currently publishes to a local Ivy repository only. Maven Central
7 and Gradle Plugin Portal publication are intentionally not configured yet.
8
9 ## Modules
10
11 - `common` - shared Gradle utilities, JSON helpers, shell execution helpers, and
12 small core value/util classes.
13 - `variants` - Gradle plugins for variant topology, source-set materialization,
14 and outgoing artifact slots.
15
16 ## Requirements
17
18 - JDK 21.
19 - Gradle Wrapper from this repository, currently Gradle 8.10.2.
20
21 The produced bytecode targets Java 21.
22
23 ## License
24
25 This project is licensed under the BSD 2-Clause "Simplified" License
26 (`BSD-2-Clause`). See [LICENSE](LICENSE).
27
28 ## Local Build
29
30 ```bash
31 ./gradlew clean check javadoc jar sourcesJar javadocJar --rerun-tasks
32 ```
33
34 Configuration cache smoke check:
35
36 ```bash
37 ./gradlew check --configuration-cache
38 ```
39
40 ## Local Ivy Publication
41
42 The current publication target is:
43
44 ```text
45 ${user.home}/ivy-repo
46 ```
47
48 Publish both modules locally:
49
50 ```bash
51 ./gradlew :common:publishIvyPublicationToIvyRepository \
52 :variants:publishIvyPublicationToIvyRepository
53 ```
54
55 or:
56
57 ```bash
58 ./gradlew publish
59 ```
60
61 The publication includes:
62
63 - main jar
64 - sources jar
65 - javadoc jar
66 - Ivy descriptor
67 - Gradle module metadata
68
69 ## Local Consumption
70
71 Current plugin ids are packaged as classic Gradle plugin marker resources inside
72 the `variants` jar:
73
74 - `org.implab.gradle-variants`
75 - `org.implab.gradle-sources`
76 - `org.implab.gradle-variants-sources`
77 - `org.implab.gradle-variants-artifacts`
78
79 Until Gradle Plugin Portal marker artifacts are configured, consume the plugin
80 through `buildscript` classpath:
81
82 ```groovy
83 buildscript {
84 repositories {
85 ivy {
86 url "${System.properties['user.home']}/ivy-repo"
87 }
88 mavenCentral()
89 }
90 dependencies {
91 classpath 'org.implab.gradle:variants:0.1.0'
92 }
93 }
94
95 apply plugin: 'org.implab.gradle-variants'
96 apply plugin: 'org.implab.gradle-variants-sources'
97 ```
98
99 The `plugins { id(...) version(...) }` DSL is not part of the current local Ivy
100 contract.
101
102 ## Variants DSL
103
104 `variants` defines the finalized build topology. It does not create compile
105 tasks, source directories, or outgoing publications by itself.
106
107 ```groovy
108 apply plugin: 'org.implab.gradle-variants'
109
110 variants.layers.create('main')
111 variants.layers.create('test')
112 variants.roles.create('main')
113 variants.roles.create('test')
114
115 variants.variant('browser') {
116 role('main') {
117 layers('main')
118 }
119 role('test') {
120 layers('main', 'test')
121 }
122 }
123
124 variants.whenFinalized { view ->
125 println view.entries.collect {
126 "${it.variant().name}:${it.role().name}:${it.layer().name}"
127 }.sort()
128 }
129 ```
130
131 The finalized model exposes cheap identity objects: `Variant`, `Role`, `Layer`,
132 and the normalized relation `(variant, role, layer)`.
133
134 ## Sources DSL
135
136 `sources` creates standalone `GenericSourceSet` objects. This is useful for
137 fallback workflows that do not need variant topology.
138
139 ```groovy
140 apply plugin: 'org.implab.gradle-sources'
141
142 sources.create('main') {
143 declareOutputs('js')
144 registerOutput('js', layout.projectDirectory.file('inputs/main.js'))
145
146 sets.create('ts') {
147 srcDir 'src/main/ts'
148 }
149 }
150 ```
151
152 `SourcesPlugin` applies layout conventions:
153
154 - `sourceSetDir = src/<sourceSet>`
155 - `outputsDir = build/out/<sourceSet>`
156
157 The base `GenericSourceSet` model itself is convention-free.
158
159 ## Variant Sources DSL
160
161 `variantSources` derives compile units from finalized `variants`.
162
163 A compile unit is:
164
165 ```text
166 (variant, layer)
167 ```
168
169 Selectors configure materialized compile-unit source sets:
170
171 ```groovy
172 apply plugin: 'org.implab.gradle-variants-sources'
173
174 variants.layers.create('main')
175 variants.roles.create('main')
176 variants.variant('browser') {
177 role('main') {
178 layers('main')
179 }
180 }
181
182 variantSources {
183 layer('main') {
184 sourceSet {
185 declareOutputs('js')
186 registerOutput('js', layout.projectDirectory.file('inputs/browser.js'))
187 }
188 }
189
190 configureEach {
191 println "sourceSet=${sourceSet.name}, variant=${variant.name}, layer=${layer.name}"
192 }
193 }
194 ```
195
196 Selector order for future materialization is:
197
198 ```text
199 configureEach -> variant -> layer -> unit
200 ```
201
202 Late selector registration is controlled by:
203
204 ```groovy
205 variantSources {
206 lateConfigurationPolicy {
207 failOnLateConfiguration()
208 }
209 }
210 ```
211
212 Compile-unit source set names are generated by default as:
213
214 ```text
215 <variant> + capitalize(<layer>)
216 ```
217
218 Name collisions fail by default and may be resolved deterministically:
219
220 ```groovy
221 variantSources {
222 namingPolicy {
223 resolveNameCollision()
224 }
225 }
226 ```
227
228 ## Variant Artifacts DSL
229
230 `variantArtifacts` is an experimental outgoing artifact layer over `variants`
231 and `variantSources`.
232
233 The current model maps:
234
235 - `Variant` to a variant-level consumable outgoing configuration.
236 - `Slot` to a Gradle outgoing artifact variant inside that configuration.
237 - `primarySlot` to the primary artifact set of the outgoing configuration.
238
239 ```groovy
240 apply plugin: 'org.implab.gradle-variants-artifacts'
241
242 variants.layers.create('main')
243 variants.roles.create('main')
244 variants.variant('browser') {
245 role('main') {
246 layers('main')
247 }
248 }
249
250 variantSources.layer('main') {
251 sourceSet {
252 declareOutputs('types', 'js')
253 registerOutput('types', layout.projectDirectory.file('inputs/index.d.ts'))
254 registerOutput('js', layout.projectDirectory.file('inputs/index.js'))
255 }
256 }
257
258 variantArtifacts {
259 variant('browser') {
260 primarySlot('typesPackage') {
261 fromVariant {
262 output('types')
263 }
264 }
265 slot('js') {
266 fromVariant {
267 output('js')
268 }
269 }
270 }
271
272 whenOutgoingConfiguration { publication ->
273 publication.configuration {
274 description = "Outgoing contract for ${publication.variant.name}"
275 }
276 }
277
278 whenOutgoingSlot { publication ->
279 println "slot=${publication.artifactSlot.slot.name}, primary=${publication.primary}"
280 }
281 }
282 ```
283
284 The artifact API is still considered pre-1.0 and may change.
285
286 ## Publication Status
287
288 Current status:
289
290 - local Ivy publication only
291 - no Maven Central publication metadata
292 - no signing
293 - no Gradle Plugin Portal marker artifacts
294 - BSD-2-Clause license committed
295
296 Before external publication, see [RELEASE_CHECKLIST.md](RELEASE_CHECKLIST.md).
@@ -0,0 +1,63
1 # Release Checklist
2
3 This checklist tracks what should be true before publishing outside the local
4 Ivy repository.
5
6 ## Current Scope
7
8 For now the project is prepared for local Ivy publication only.
9
10 ## Required Before External Publication
11
12 - Add Maven publication (`maven-publish`) if publishing to Maven repositories.
13 - Add signing for Maven Central or any repository that requires signed
14 artifacts.
15 - Add complete POM metadata: project name, description, URL, license,
16 developers, and SCM coordinates.
17 - Keep Java 21 as the public baseline and document it for consumers.
18 - Decide whether `variants` artifact APIs are published as experimental or
19 split into a later module.
20 - Add Gradle plugin marker artifact generation if `plugins { id(...) version(...) }`
21 must work.
22 - Add a published-consumption smoke test that resolves artifacts from a
23 temporary local repository.
24
25 ## Local Ivy Release Steps
26
27 1. Ensure the Mercurial working tree is clean.
28 2. Run:
29
30 ```bash
31 ./gradlew clean check javadoc jar sourcesJar javadocJar --rerun-tasks
32 ```
33
34 3. Optionally run:
35
36 ```bash
37 ./gradlew check --configuration-cache
38 ```
39
40 4. Publish locally:
41
42 ```bash
43 ./gradlew publish
44 ```
45
46 5. Optionally smoke-publish into `/tmp` first:
47
48 ```bash
49 ./gradlew -Duser.home=/tmp/gradle-common-ivy-smoke \
50 :common:publishIvyPublicationToIvyRepository \
51 :variants:publishIvyPublicationToIvyRepository \
52 --rerun-tasks
53 ```
54
55 6. Verify `${user.home}/ivy-repo/org.implab.gradle` contains `common` and
56 `variants` for the expected version.
57
58 ## API Status
59
60 - `common` is intended to be a shared utility library.
61 - `variants` core and source APIs are pre-1.0 and should be treated as
62 evolving.
63 - `variantArtifacts` is experimental and may change more aggressively.
@@ -1,7 +1,8
1 1 syntax: glob
2 2 .gradle/
3 3 .codex/
4 build/
4 5 common/build/
5 6 common/bin/
6 7 variants/build/
7 8 variants/bin/
@@ -1,51 +1,57
1 1 plugins {
2 2 id "java-library"
3 3 id "ivy-publish"
4 4 }
5 5
6 description = "Shared Gradle build utilities used by Implab plugins"
7
6 8 java {
7 9 withJavadocJar()
8 10 withSourcesJar()
9 11 toolchain {
10 12 languageVersion = JavaLanguageVersion.of(21)
11 13 }
12 14 }
13 15
14 16 dependencies {
15 17 compileOnly libs.jdt.annotations
16 18
17 19 api gradleApi(),
18 20 libs.bundles.jackson
19 21
20 22 testImplementation gradleTestKit()
21 23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
22 24 testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
23 25 testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.4"
24 26 }
25 27
26 28 task printVersion{
27 29 doLast {
28 30 println "project: $project.group:$project.name:$project.version"
29 31 println "jar: ${->jar.archiveFileName.get()}"
30 32 }
31 33 }
32 34
33 35 test {
34 36 useJUnitPlatform()
35 37 }
36 38
37 39 publishing {
38 40 repositories {
39 41 ivy {
40 42 url "${System.properties["user.home"]}/ivy-repo"
41 43 }
42 44 }
43 45 publications {
44 46 ivy(IvyPublication) {
45 47 from components.java
46 48 descriptor.description {
47 49 text = providers.provider({ description })
48 50 }
51 descriptor.license {
52 name = "BSD-2-Clause"
53 url = "https://spdx.org/licenses/BSD-2-Clause.html"
54 }
49 55 }
50 56 }
51 57 }
@@ -1,134 +1,18
1 # Gradle Common Sources Model
2
3 ## NAME
4
5 `gradle-common/common` β€” Π½Π°Π±ΠΎΡ€ ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ² для модСлирования Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ² сборки,
6 рСгистрации source sets ΠΈ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ этой ΠΌΠΎΠ΄Π΅Π»ΠΈ с toolchain-Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π°ΠΌΠΈ.
7
8 ## SYNOPSIS
9
10 ```groovy
11 plugins {
12 id 'org.implab.gradle-variants-sources'
13 }
14
15 variants {
16 layer('mainBase')
17 layer('mainAmd')
18
19 variant('browser') {
20 role('main') { layers('mainBase', 'mainAmd') }
21 }
22 }
23
24 variantSources {
25 bind('mainBase') {
26 configureSourceSet {
27 declareOutputs('compiled')
28 }
29 }
30
31 bind('mainAmd').sourceSetNamePattern = '{variant}{layerCap}'
1 # Gradle Common
32 2
33 whenRegistered { sourceSetName() }
34
35 whenBound { ctx ->
36 ctx.configureSourceSet {
37 declareOutputs('typings')
38 }
39 }
40 }
41 ```
42
43 ## DESCRIPTION
44
45 ΠœΠΎΠ΄ΡƒΠ»ΡŒ состоит ΠΈΠ· Ρ‚Ρ€Π΅Ρ… логичСских частСй:
3 `common` is the shared utility module used by the Gradle plugins in this
4 repository.
46 5
47 - `variants` β€” дСкларативная домСнная модСль сборки;
48 - `sources` β€” модСль физичСски рСгистрируСмых source sets;
49 - `variantSources` β€” Π°Π΄Π°ΠΏΡ‚Π΅Ρ€, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ связываСт ΠΏΠ΅Ρ€Π²Ρ‹Π΅ Π΄Π²Π΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ.
50
51 НиТС раскрытиС ΠΊΠ°ΠΆΠ΄ΠΎΠΉ части.
52
53 ### variants
54
55 `variants` Π·Π°Π΄Π°Π΅Ρ‚ структуру пространства сборки: ΠΊΠ°ΠΊΠΈΠ΅ Π΅ΡΡ‚ΡŒ слои, ΠΊΠ°ΠΊΠΈΠ΅ Ρ€ΠΎΠ»ΠΈ
56 ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ эти слои Π² ΠΊΠ°ΠΆΠ΄ΠΎΠΌ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π΅, ΠΊΠ°ΠΊΠΈΠ΅ Π΅ΡΡ‚ΡŒ Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ ΠΈ artifact slots.
57 МодСль Π½Π΅ создаСт Π·Π°Π΄Π°Ρ‡ΠΈ ΠΈ Π½Π΅ привязана ΠΊ TS/JS.
58
59 ΠŸΡ€Π°ΠΊΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠΉ смысл:
60
61 - Ρ„ΠΎΡ€ΠΌΠ°Π»ΠΈΠ·ΠΎΠ²Π°Ρ‚ΡŒ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Ρƒ сборки;
62 - Π΄Π°Ρ‚ΡŒ Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π°ΠΌ Π΅Π΄ΠΈΠ½Ρ‹ΠΉ источник ΠΏΡ€Π°Π²Π΄Ρ‹.
63
64 ### sources
6 It contains:
65 7
66 `sources` описываСт нСзависимыС source sets (`GenericSourceSet`) с ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½Ρ‹ΠΌΠΈ
67 outputs. Π­Ρ‚ΠΎ ΡƒΠΆΠ΅ "физичСский" ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ, ΠΊ ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΌΡƒ ΡƒΠ΄ΠΎΠ±Π½ΠΎ ΠΏΡ€ΠΈΠ²ΡΠ·Ρ‹Π²Π°Ρ‚ΡŒ Π·Π°Π΄Π°Ρ‡ΠΈ,
68 Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Ρ‹ ΠΈ task inputs/outputs.
69
70 ΠŸΡ€Π°ΠΊΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠΉ смысл:
71
72 - ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ Π΅Π΄ΠΈΠ½Ρ‹ΠΉ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ ΠΏΠΎ Π²Ρ…ΠΎΠ΄Π°ΠΌ/Π²Ρ‹Ρ…ΠΎΠ΄Π°ΠΌ;
73 - Ρ€Π΅Π³ΠΈΡΡ‚Ρ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹ Π·Π°Π΄Π°Ρ‡ ΠΊΠ°ΠΊ outputs source set;
74 - ΠΌΠΈΠ½ΠΈΠΌΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Ρ€ΡƒΡ‡Π½Ρ‹Π΅ `dependsOn` Π·Π° счСт ΠΌΠΎΠ΄Π΅Π»ΠΈ outputs.
75
76 ### variantSources
77
78 `variantSources` рСгистрируСт source sets Π½Π° основС `variants`, примСняСт
79 ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΡŽ layer-bindings ΠΈ ΠΎΡ‚Π΄Π°Π΅Ρ‚ события (`whenRegistered`, `whenBound`) для
80 Π°Π΄Π°ΠΏΡ‚Π΅Ρ€ΠΎΠ² Π΄Ρ€ΡƒΠ³ΠΈΡ… ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠ².
81
82 ΠŸΡ€Π°ΠΊΡ‚ΠΈΡ‡Π΅ΡΠΊΠΈΠΉ смысл:
83
84 - ΠΏΠ΅Ρ€Π΅Π²ΠΎΠ΄ΠΈΡ‚ΡŒ Π»ΠΎΠ³ΠΈΡ‡Π΅ΡΠΊΡƒΡŽ модСль `variants` Π² executable-модСль `sources`;
85 - Π½Π°Π²Π΅ΡˆΠΈΠ²Π°Ρ‚ΡŒ ΠΏΠΎΠ»ΠΈΡ‚ΠΈΠΊΠΈ toolchain Π½Π° зарСгистрированныС source sets;
86 - ΡΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠ»Π°Π³ΠΈΠ½Ρ‹ Ρ‡Π΅Ρ€Π΅Π· replayable callback-ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚.
87
88 ## DOMAIN MODEL
89
90 - `BuildLayer` β€” canonical identity-model объявлСнного слоя.
91 - `BuildVariant` β€” Π°Π³Ρ€Π΅Π³Π°Ρ‚ Ρ€ΠΎΠ»Π΅ΠΉ, Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚ΠΎΠ², Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Π½Ρ‹Ρ… слотов.
92 - `BuildRole` β€” Ρ€ΠΎΠ»ΡŒ Π²Π½ΡƒΡ‚Ρ€ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°, содСрТит ссылки Π½Π° declared layer names.
93 - `GenericSourceSet` β€” зарСгистрированный Π½Π°Π±ΠΎΡ€ исходников ΠΈ outputs.
94 - `LayerBindingSpec` β€” ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ DSL-contract adapter policy/callbacks для слоя.
95 - `SourceSetRegistration` β€” payload события рСгистрации source set.
96 - `SourceSetUsageBinding` β€” payload события usage-binding.
97
98 ## EVENT CONTRACT
8 - core Gradle helper utilities
9 - small language/value helpers
10 - shell execution helpers
11 - JSON DSL and JSON-writing task support
99 12
100 - `whenRegistered`:
101 - событиС Π½ΠΎΠ²ΠΎΠ³ΠΎ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½ΠΎΠ³ΠΎ source set name;
102 - replayable.
103 - `whenBound`:
104 - событиС ΠΊΠ°ΠΆΠ΄ΠΎΠΉ usage-связки `variant/role/layer`;
105 - replayable.
106
107 Closure callbacks Ρ€Π°Π±ΠΎΡ‚Π°ΡŽΡ‚ Π² delegate-first Ρ€Π΅ΠΆΠΈΠΌΠ΅ (`@DelegatesTo`). Для
108 Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… closure рСкомСндуСтся явный ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ (`ctx -> ...`).
109
110 ## KEY CLASSES
13 It no longer contains the variant/source/artifact plugin model. Those plugins
14 live in the `variants` module.
111 15
112 - `SourcesPlugin` β€” рСгистрируСт extension `sources`.
113 - `GenericSourceSet` β€” модСль источников/outputs для ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΈΠΌΠ΅Π½ΠΈ.
114 - `VariantsPlugin` β€” рСгистрируСт extension `variants` ΠΈ lifecycle finalize.
115 - `BuildVariantsExtension` β€” ΠΊΠΎΡ€Π½Π΅Π²ΠΎΠΉ API ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ².
116 - `BuildVariant` β€” API Ρ€ΠΎΠ»Π΅ΠΉ, attributes ΠΈ artifact slots Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°.
117 - `VariantsSourcesPlugin` β€” примСняСт `variants` + `sources` ΠΈ запускаСт Π°Π΄Π°ΠΏΡ‚Π΅Ρ€.
118 - `VariantSourcesExtension` β€” API bind/events registration.
119 - `LayerBindingSpec` β€” слой-ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Ρ‹ΠΉ DSL для policy/configuration source set.
120 - `SourceSetRegistration` β€” payload `whenRegistered(...)`.
121 - `SourceSetUsageBinding` β€” payload `whenBound(...)`.
16 See the root [README.md](../README.md) and [PUBLISHING.md](../PUBLISHING.md) for
17 current build and local Ivy publication instructions.
122 18
123 ## NOTES
124
125 - Marker ids:
126 - `org.implab.gradle-variants`
127 - `org.implab.gradle-variants-sources`
128 - `SourcesPlugin` ΠΏΠΎΠΊΠ° class-only (Π±Π΅Π· marker id).
129
130 ## SEE ALSO
131
132 - `sources-plugin.md`
133 - `variants-plugin.md`
134 - `variant-sources-plugin.md`
@@ -1,83 +1,7
1 # Sources Plugin
2
3 ## NAME
4
5 `SourcesPlugin` ΠΈ extension `sources`.
6
7 ## SYNOPSIS
8
9 ```groovy
10 // ΠžΠ±Ρ‹Ρ‡Π½ΠΎ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π°Π΅Ρ‚ΡΡ Ρ‚Ρ€Π°Π½Π·ΠΈΡ‚ΠΈΠ²Π½ΠΎ Ρ‡Π΅Ρ€Π΅Π· org.implab.gradle-variants-sources
11
12 sources {
13 register('main') {
14 declareOutputs('compiled', 'typings')
15
16 sets {
17 ts { srcDir 'src/main/ts' }
18 js { srcDir 'src/main/js' }
19 }
20 }
21 }
22 ```
23
24 ## DESCRIPTION
25
26 `SourcesPlugin` рСгистрируСт extension `sources` Ρ‚ΠΈΠΏΠ°
27 `NamedDomainObjectContainer<GenericSourceSet>`.
28
29 `GenericSourceSet` β€” это Π°Π²Ρ‚ΠΎΠ½ΠΎΠΌΠ½Ρ‹ΠΉ source bundle с Ρ‡Π΅Ρ‚ΠΊΠΈΠΌ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ΠΎΠΌ outputs.
30
31 ### sourceSetDir
32
33 Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³ Π½Π°Π±ΠΎΡ€Π°. ΠšΠΎΠ½Π²Π΅Π½Ρ†ΠΈΡ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ: `src/<name>`.
34
35 ### outputsDir
36
37 Π‘Π°Π·ΠΎΠ²Ρ‹ΠΉ ΠΊΠ°Ρ‚Π°Π»ΠΎΠ³ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² Π½Π°Π±ΠΎΡ€Π°. ΠšΠΎΠ½Π²Π΅Π½Ρ†ΠΈΡ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ: `build/<name>`.
38
39 ### sets
1 # Moved: Sources Plugin
40 2
41 ΠšΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ `SourceDirectorySet` Π²Π½ΡƒΡ‚Ρ€ΠΈ `GenericSourceSet`. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для
42 логичСского раздСлСния ΠΏΠΎΠ΄ΠΏΠ°ΠΏΠΎΠΊ (Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ `ts`, `js`, `typings`).
43
44 ### outputs contract
45
46 Outputs ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½Ρ‹Π΅ ΠΈ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΠΎΠ±ΡŠΡΠ²Π»Π΅Π½Ρ‹ Π·Π°Ρ€Π°Π½Π΅Π΅:
47
48 - `declareOutputs(...)` β€” дСкларация доступных output keys;
49 - `output(name)` β€” доступ ΠΊ `ConfigurableFileCollection` для output key;
50 - `registerOutput(...)` β€” рСгистрация output ΠΈΠ· Ρ„Π°ΠΉΠ»ΠΎΠ² ΠΈΠ»ΠΈ task provider.
51
52 Π’Π°ΠΊΠΎΠΉ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ ΡƒΠΏΡ€ΠΎΡ‰Π°Π΅Ρ‚ wiring Π·Π°Π΄Π°Ρ‡ Ρ‡Π΅Ρ€Π΅Π· inputs/outputs Π±Π΅Π· Ρ€ΡƒΡ‡Π½ΠΎΠ³ΠΎ
53 `dependsOn`.
54
55 ## API
56
57 ### SourcesPlugin
58
59 - `apply(Project)` β€” добавляСт extension `sources` Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚.
60 - `getSourcesExtension(Project)` β€” Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ `GenericSourceSet`.
3 The `SourcesPlugin` implementation is now part of the `variants` module, not
4 the `common` module.
61 5
62 ### GenericSourceSet
6 Current documentation is maintained in the root [README.md](../README.md).
63 7
64 - `getSourceSetDir()` β€” root directory источников Π½Π°Π±ΠΎΡ€Π°.
65 - `getOutputsDir()` β€” root directory Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚ΠΎΠ² Π½Π°Π±ΠΎΡ€Π°.
66 - `getSets()` β€” ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ ΠΏΠΎΠ΄Π½Π°Π±ΠΎΡ€ΠΎΠ² `SourceDirectorySet`.
67 - `declareOutputs(...)` β€” ΠΎΠ±ΡŠΡΠ²Π»ΡΠ΅Ρ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½Π½Ρ‹Π΅ output names.
68 - `output(name)` β€” Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ `FileCollection` для ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ output.
69 - `registerOutput(name, files...)` β€” добавляСт Ρ„Π°ΠΉΠ»Ρ‹ Π² output.
70 - `registerOutput(name, task, mapper)` β€” связываСт output с task provider.
71 - `getAllOutputs()` β€” Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ `FileCollection` всСх outputs.
72 - `getAllSourceDirectories()` β€” Π°Π³Ρ€Π΅Π³ΠΈΡ€ΠΎΠ²Π°Π½Π½Ρ‹ΠΉ `FileCollection` всСх source dirs.
73
74 ## KEY CLASSES
75
76 - `SourcesPlugin` β€” рСгистрация extension `sources`.
77 - `GenericSourceSet` β€” модСль источников ΠΈ outputs.
78
79 ## NOTES
80
81 - ΠžΠ±Ρ€Π°Ρ‰Π΅Π½ΠΈΠ΅ ΠΊ `output(name)` Π±Π΅Π· ΠΏΡ€Π΅Π΄Π²Π°Ρ€ΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎΠ³ΠΎ `declareOutputs(name)`
82 ΠΏΡ€ΠΈΠ²ΠΎΠ΄ΠΈΡ‚ ΠΊ ошибкС Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ.
83 - Плагин `sources` сСйчас Π±Π΅Π· marker id.
@@ -1,354 +1,8
1 # Variant Artifacts Plugin
2
3 ## NAME
4
5 `VariantsArtifactsPlugin` ΠΈ extension `variantArtifacts`.
6
7 ## SYNOPSIS
8
9 ```groovy
10 import org.gradle.api.attributes.Attribute
11
12 plugins {
13 id 'org.implab.gradle-variants-artifacts'
14 }
15
16 def variantAttr = Attribute.of('test.variant', String)
17 def slotAttr = Attribute.of('test.slot', String)
18
19 variants {
20 layer('main')
21
22 variant('browser') {
23 role('main') { layers('main') }
24 }
25 }
26
27 variantSources {
28 bind('main') {
29 configureSourceSet {
30 declareOutputs('types', 'js', 'resources')
31 }
32 }
33 }
34
35 variantArtifacts {
36 variant('browser') {
37 primarySlot('typesPackage') {
38 fromVariant {
39 output('types')
40 }
41 }
42
43 slot('js') {
44 fromVariant {
45 output('js')
46 }
47 }
48
49 slot('resources') {
50 fromVariant {
51 output('resources')
52 }
53 }
54 }
55
56 whenOutgoingVariant { publication ->
57 publication.configureConfiguration {
58 attributes.attribute(variantAttr, publication.variantName())
59 }
60
61 publication.primarySlot().configureArtifactAttributes {
62 attribute(slotAttr, publication.primarySlot().slotName())
63 }
64
65 publication.requireSlot('js').configureArtifactAttributes {
66 attribute(slotAttr, 'js')
67 }
68
69 publication.requireSlot('resources').configureArtifactAttributes {
70 attribute(slotAttr, 'resources')
71 }
72 }
73 }
74 ```
75
76 ## DESCRIPTION
77
78 `VariantsArtifactsPlugin` примСняСт `VariantsSourcesPlugin`, Π·Π°Ρ‚Π΅ΠΌ строит
79 outgoing publication model ΠΏΠΎΠ²Π΅Ρ€Ρ… `variantSources`.
80
81 ### publication model
82
83 Для ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ `variantArtifacts.variant('<name>')` публикуСтся ΠΎΠ΄ΠΈΠ½ outgoing
84 build variant:
85
86 - primary configuration `<variant>Elements`;
87 - primary artifact slot на самой configuration;
88 - secondary variants Π²Π½ΡƒΡ‚Ρ€ΠΈ `configuration.outgoing.variants` для ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… slots.
89
90 ΠŸΡ€ΠΈΠΌΠ΅Ρ€:
91
92 - `browserElements`
93 - primary slot: `typesPackage`
94 - secondary variants: `js`, `resources`
95
96 Π­Ρ‚ΠΎ раздСляСт:
97
98 - graph selection build variant-Π°;
99 - artifact selection Π²Π½ΡƒΡ‚Ρ€ΠΈ ΡƒΠΆΠ΅ Π²Ρ‹Π±Ρ€Π°Π½Π½ΠΎΠ³ΠΎ variant-Π°.
100
101 ### slot contributions ΠΈ DSL
102
103 `slot('<name>')` описываСт artifact representation Π½Π΅ ΠΊΠ°ΠΊ ΠΎΠ΄ΠΈΠ½ Ρ„Π°ΠΉΠ» ΠΈΠ»ΠΈ ΠΎΠ΄Π½Ρƒ
104 Π·Π°Π΄Π°Ρ‡Ρƒ, Π° ΠΊΠ°ΠΊ Π½Π°Π±ΠΎΡ€ contributions, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΎΡ‚ΠΎΠΌ materialize-ятся Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ
105 `ArtifactAssembly`.
106
107 Π’Π΅ΠΊΡƒΡ‰ΠΈΠΉ DSL ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Π΄Π²Π° Π²ΠΈΠ΄Π° contributions:
108
109 - topology-aware:
110 - `fromVariant { output(...) }`
111 - `fromRole('<role>') { output(...) }`
112 - `fromLayer('<layer>') { output(...) }`
113 - direct:
114 - `from(someFileOrProviderOrTaskOutput)`
115
116 Бмысл DSL ΠΏΠΎ слоям:
117
118 - `fromVariant/fromRole/fromLayer` Π²Ρ‹Π±ΠΈΡ€Π°ΡŽΡ‚ ΠΎΠ±Π»Π°ΡΡ‚ΡŒ topology model, Π² ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠΉ
119 contribution Π°ΠΊΡ‚ΠΈΠ²Π΅Π½;
120 - `output(...)` Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ named output ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ `GenericSourceSet`;
121 - `from(Object)` добавляСт direct contribution, Π½Π΅ зависящий ΠΎΡ‚
122 `variantSources` bindings;
123 - ΠΈΡ‚ΠΎΠ³ΠΎΠ²Ρ‹ΠΉ contribution ΠΏΡ€ΠΈ materialization:
124 - провСряСт, ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ Π»ΠΈ Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΠΉ `SourceSetUsageBinding`;
125 - Π²Ρ‹Π΄Π°Π΅Ρ‚ object для `files.from(...)`;
126 - ΠΏΡ€ΠΈ нСобходимости Π²Ρ‹Π΄Π°Π΅Ρ‚ `BindingKey`, Ссли Ρ‚Π°ΠΊΠΎΠΉ contribution Π΄ΠΎΠ»ΠΆΠ΅Π½
127 ΡΡ…Π»ΠΎΠΏΡ‹Π²Π°Ρ‚ΡŒΡΡ ΠΏΠΎ logical identity.
128
129 Бвязь slot-Π° с ΠΎΡΡ‚Π°Π»ΡŒΠ½ΠΎΠΉ модСлью:
130
131 - `variants` Π·Π°Π΄Π°Π΅Ρ‚ topology variant/role/layer;
132 - `variantSources` ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ topology Π² concrete `SourceSetUsageBinding`;
133 - `variantArtifacts.slot(...)` описываСт, ΠΊΠ°ΠΊΠΈΠ΅ bindings Π½Π°Π΄ΠΎ Π²ΠΊΠ»ΡŽΡ‡ΠΈΡ‚ΡŒ Π² slot;
134 - `VariantArtifactsResolver` ΠΏΡ€Π΅Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ contributions slot-Π° Π² `FileCollection`;
135 - `VariantArtifactsPlugin` рСгистрируСт для slot-Π° ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ `ArtifactAssembly`;
136 - `OutgoingVariantPublication` ΠΈ `OutgoingArtifactSlotPublication` ΠΏΡƒΠ±Π»ΠΈΠΊΡƒΡŽΡ‚
137 ΡƒΠΆΠ΅ собранныС slot artifacts Π½Π°Ρ€ΡƒΠΆΡƒ.
138
139 ΠšΠ°ΠΆΠ΄Ρ‹ΠΉ slot materialize-ится Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ `ArtifactAssembly`:
140
141 - task: `process<Variant><Slot>`;
142 - output dir: `build/variant-artifacts/<variant>/<slot>`.
143
144 ### primary slot
145
146 Primary slot Π·Π°Π΄Π°Π΅Ρ‚ artifact, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ публикуСтся ΠΊΠ°ΠΊ основной artifact
147 configuration `<variant>Elements`.
148
149 Π€ΠΎΡ€ΠΌΡ‹ DSL:
150
151 ```groovy
152 variant('browser') {
153 primarySlot('typesPackage')
154
155 slot('typesPackage') {
156 fromVariant { output('types') }
157 }
158 }
159 ```
160
161 ΠΈΠ»ΠΈ sugar:
162
163 ```groovy
164 variant('browser') {
165 primarySlot('typesPackage') {
166 fromVariant { output('types') }
167 }
168 }
169 ```
170
171 ΠŸΡ€Π°Π²ΠΈΠ»Π°:
172
173 - Ссли slot ΠΎΠ΄ΠΈΠ½, ΠΎΠ½ считаСтся primary нСявно;
174 - Ссли slots нСсколько, `primarySlot(...)` обязатСлСн;
175 - `primarySlot` Π΄ΠΎΠ»ΠΆΠ΅Π½ ΡΡΡ‹Π»Π°Ρ‚ΡŒΡΡ Π½Π° ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ slot.
1 # Moved: Variant Artifacts Plugin
176 2
177 ## LIFECYCLE
178
179 - `VariantsArtifactsPlugin` ΠΆΠ΄Π΅Ρ‚ `variants.whenFinalized(...)`;
180 - послС этого Π²Π°Π»ΠΈΠ΄ΠΈΡ€ΡƒΠ΅Ρ‚ `variantArtifacts`;
181 - рСгистрируСт `ArtifactAssembly` ΠΏΠΎ ΠΊΠ°ΠΆΠ΄ΠΎΠΌΡƒ slot;
182 - materialize-ΠΈΡ‚ outgoing publications;
183 - Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ `whenOutgoingVariant(...)`;
184 - callbacks replayable.
185
186 ПослС finalize ΠΌΡƒΡ‚Π°Ρ†ΠΈΠΈ `variantArtifacts` Π·Π°ΠΏΡ€Π΅Ρ‰Π΅Π½Ρ‹.
187
188 ## EVENTS
189
190 ### whenOutgoingVariant
191
192 Replayable callback Π½Π° Π³ΠΎΡ‚ΠΎΠ²ΡƒΡŽ outgoing publication variant-Π°.
193
194 ΠŸΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ для:
195
196 - настройки ΠΎΠ±Ρ‰ΠΈΡ… attributes build variant-Π° ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·;
197 - настройки per-slot artifact attributes;
198 - Π΄ΠΎΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ `ArtifactAssembly`.
199
200 ## PAYLOAD TYPES
201
202 ### OutgoingVariantPublication
203
204 Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠΈΡ‚:
205
206 - `variantName()`;
207 - `topologyVariant()`;
208 - `variantArtifact()`;
209 - `configuration()` β€” primary `<variant>Elements`;
210 - `primarySlot()`;
211 - `slots()` β€” всС slot publications;
212 - `secondarySlots()`;
213 - `findSlot(name)`, `requireSlot(name)`.
214
215 Sugar:
216
217 - `configureConfiguration(Action|Closure)`.
218
219 ### OutgoingArtifactSlotPublication
220
221 Π‘ΠΎΠ΄Π΅Ρ€ΠΆΠΈΡ‚:
222
223 - `slotName()`;
224 - `primary()`;
225 - `slot()` β€” модСль `VariantArtifactSlot`;
226 - `assembly()`.
227
228 Sugar:
229
230 - `configureAssembly(Action|Closure)`;
231 - `configureArtifactAttributes(Action|Closure)`.
232
233 `configureArtifactAttributes(...)` ΠΏΠΈΡˆΠ΅Ρ‚ attributes:
234
235 - в `Configuration.attributes` для primary slot;
236 - в `ConfigurationVariant.attributes` для secondary slot.
237
238 ## CONSUMER SIDE
239
240 ### primary resolution
241
242 ΠžΠ±Ρ‹Ρ‡Π½ΠΎΠ΅ inter-project resolution Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ primary artifact `<variant>Elements`.
243
244 ΠŸΡ€ΠΈΠΌΠ΅Ρ€:
245
246 ```groovy
247 configurations {
248 compileView {
249 canBeResolved = true
250 canBeConsumed = false
251 canBeDeclared = true
252 attributes {
253 attribute(variantAttr, 'browser')
254 attribute(slotAttr, 'typesPackage')
255 }
256 }
257 }
258
259 dependencies {
260 compileView project(':producer')
261 }
262 ```
263
264 ### artifact selection for secondary slots
3 The `VariantArtifactsPlugin` implementation is now part of the `variants`
4 module, not the `common` module.
265 5
266 Secondary artifacts Π²Ρ‹Π±ΠΈΡ€Π°ΡŽΡ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· `artifactView`.
267
268 ```groovy
269 def jsFiles = configurations.compileView.incoming.artifactView {
270 attributes {
271 attribute(slotAttr, 'js')
272 }
273 }.files
274 ```
275
276 Π—Π΄Π΅ΡΡŒ graph variant ΡƒΠΆΠ΅ Π²Ρ‹Π±Ρ€Π°Π½, Π° `artifactView` Π²Ρ‹Π±ΠΈΡ€Π°Π΅Ρ‚ Π½ΡƒΠΆΠ½Ρ‹ΠΉ secondary
277 artifact representation.
278
279 ## VALIDATION
280
281 ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚ΡΡ:
282
283 - variant сущСствуСт Π² topology model;
284 - slot contributions Π½Π΅ ΡΡΡ‹Π»Π°ΡŽΡ‚ΡΡ Π½Π° нСизвСстныС role/layer;
285 - ΠΏΡ€ΠΈ Π½Π΅ΡΠΊΠΎΠ»ΡŒΠΊΠΈΡ… slots ΡƒΠΊΠ°Π·Π°Π½ `primarySlot`;
286 - `primarySlot` ссылаСтся Π½Π° ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠΉ slot.
287
288 ## API
289
290 ### VariantArtifactsExtension
291
292 - `variant(String)` β€” ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ/ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ variant artifact model;
293 - `variant(String, Action|Closure)` β€” ΡΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ variant artifact;
294 - `getVariants()` β€” ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€ variant artifacts;
295 - `findVariant(name)`, `requireVariant(name)`;
296 - `whenOutgoingVariant(...)`.
297
298 ### VariantArtifact
299
300 - `slot(String)` β€” ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ/ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ slot;
301 - `slot(String, Action|Closure)` β€” ΡΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ slot;
302 - `primarySlot(String)` β€” Π½Π°Π·Π½Π°Ρ‡ΠΈΡ‚ΡŒ primary slot;
303 - `primarySlot(String, Action|Closure)` β€” sugar: configure slot + mark as primary;
304 - `getSlots()`;
305 - `findSlot(name)`, `requireSlot(name)`;
306 - `findPrimarySlotName()`, `requirePrimarySlotName()`;
307 - `findPrimarySlot()`, `requirePrimarySlot()`.
308
309 ### VariantArtifactSlot
6 Current documentation is maintained in the root [README.md](../README.md) and
7 [variant_artifacts.md](../variant_artifacts.md).
310 8
311 - `from(Object)`;
312 - `fromVariant(...)`;
313 - `fromRole(String, ...)`;
314 - `fromLayer(String, ...)`.
315
316 ВнутрСнняя модСль:
317
318 - slot Ρ…Ρ€Π°Π½ΠΈΡ‚ contributions, Π° Π½Π΅ строковыС rules;
319 - `fromVariant/fromRole/fromLayer` ΡΠΎΠ·Π΄Π°ΡŽΡ‚ topology-aware contributions;
320 - `from(Object)` создаСт direct contribution, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ materialize-ится Π΄Π°ΠΆΠ΅
321 Ссли Ρƒ variant-Π° Π½Π΅Ρ‚ Π½ΠΈ ΠΎΠ΄Π½ΠΎΠ³ΠΎ `SourceSetUsageBinding`;
322 - slot ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΠΎ Ρ…Ρ€Π°Π½ΠΈΡ‚ topology references для validation:
323 `referencedRoleNames()` ΠΈ `referencedLayerNames()`.
324
325 ### OutputSelectionSpec
326
327 - `output(name)`;
328 - `output(name, extra...)`.
329
330 `OutputSelectionSpec` это Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠΉ DSL-buffer для ΠΎΠ΄Π½ΠΎΠ³ΠΎ Π±Π»ΠΎΠΊΠ°
331 `fromVariant/fromRole/fromLayer`. Он локально Π½Π°ΠΊΠ°ΠΏΠ»ΠΈΠ²Π°Π΅Ρ‚ contributions ΠΈ
332 ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅Ρ‚ ΠΈΡ… Π² slot Ρ‚ΠΎΠ»ΡŒΠΊΠΎ послС ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎΠ³ΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½ΠΈΡ configure-Π±Π»ΠΎΠΊΠ°.
333
334 ## KEY CLASSES
335
336 - `VariantsArtifactsPlugin` β€” plugin adapter ΠΈ materialization outgoing variants.
337 - `VariantArtifactsExtension` β€” root DSL ΠΈ lifecycle.
338 - `VariantArtifact` β€” outgoing build variant model.
339 - `VariantArtifactSlot` β€” artifact representation slot.
340 - `VariantArtifactsResolver` β€” adapter ΠΌΠ΅ΠΆΠ΄Ρƒ `variantSources` bindings ΠΈ
341 contribution model slot-Π°.
342 - `OutgoingVariantPublication` β€” payload variant-level publication callback.
343 - `OutgoingArtifactSlotPublication` β€” payload per-slot publication callback.
344 - `ArtifactAssembly` β€” assembled files for a slot.
345
346 ## NOTES
347
348 - `common` Π½Π΅ навязываСт Π΄ΠΎΠΌΠ΅Π½Π½ΡƒΡŽ Π»ΠΎΠ³ΠΈΠΊΡƒ Π²Ρ‹Π±ΠΎΡ€Π° primary slot.
349 - `common` Π½Π΅ фиксируСт значСния `usage`, `libraryelements` ΠΈ ΠΏΡ€ΠΎΡ‡ΠΈΡ…
350 slot-specific attributes.
351 - `common` Π½Π΅ ΡΠΌΠ΅ΡˆΠΈΠ²Π°Π΅Ρ‚ эту модСль с ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΌΠΈ publish осями Π²Ρ€ΠΎΠ΄Π΅ package
352 metadata.
353 - Closure callbacks ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ delegate-first; для Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… closure ΡƒΠ΄ΠΎΠ±Π½Π΅Π΅
354 явный ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ (`publication -> ...`, `slotPublication -> ...`).
@@ -1,159 +1,9
1 # Variant Sources Plugin
2
3 ## NAME
4
5 `VariantsSourcesPlugin` ΠΈ extension `variantSources`.
6
7 ## SYNOPSIS
8
9 ```groovy
10 plugins {
11 id 'org.implab.gradle-variants-sources'
12 }
13
14 variants {
15 layer('main')
16
17 variant('browser') {
18 role('main') { layers('main') }
19 }
20
21 variant('node') {
22 role('main') { layers('main') }
23 }
24 }
25
26 variantSources {
27 bind('main').sourceSetNamePattern = '{layer}'
28
29 bind('main') {
30 configureSourceSet {
31 declareOutputs('compiled')
32 }
33 }
34
35 whenRegistered { sourceSetName() }
36 whenBound('browser') { roleName() }
37 }
38 ```
1 # Moved: Variant Sources Plugin
39 2
40 ## DESCRIPTION
41
42 `VariantsSourcesPlugin` примСняСт `VariantsPlugin` ΠΈ `SourcesPlugin`, Π·Π°Ρ‚Π΅ΠΌ
43 рСгистрируСт source sets ΠΈΠ· ΠΌΠΎΠ΄Π΅Π»ΠΈ `variants`.
44
45 Π’ΠΎΡ‡ΠΊΠ° запуска registration:
46
47 - `variants.whenFinalized(model -> registerSourceSets(...))`
48
49 ### registration
50
51 Для ΠΊΠ°ΠΆΠ΄ΠΎΠΉ usage-связки `variant/role/layer` вычисляСтся имя source set,
52 рСгистрируСтся `GenericSourceSet` (Ссли ΠΎΠ½ Π΅Ρ‰Π΅ Π½Π΅ сущСствуСт), Π·Π°Ρ‚Π΅ΠΌ
53 Π²Ρ‹Π·Ρ‹Π²Π°ΡŽΡ‚ΡΡ callbacks.
54
55 ### binding
56
57 `bind('<layer>')` Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅Ρ‚ `LayerBindingSpec` ΠΈ Π·Π°Π΄Π°Π΅Ρ‚ policy для этого
58 слоя:
59
60 - ΠΊΠ°ΠΊ ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Ρ‚ΡŒ source set;
61 - ΠΊΠ°ΠΊ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ source set;
62 - ΠΊΠ°ΠΊΠΈΠ΅ callbacks Π²Ρ‹Π·Π²Π°Ρ‚ΡŒ Π½Π° registration/binding.
63
64 ### sourceSetNamePattern
65
66 `sourceSetNamePattern` опрСдСляСт naming policy зарСгистрированного source set.
67
68 Default:
69
70 - `{variant}{layerCap}`
71
72 Tokens:
73
74 - `{variant}`, `{variantCap}`
75 - `{role}`, `{roleCap}`
76 - `{layer}`, `{layerCap}`
77
78 Имя санитизируСтся (`[^A-Za-z0-9_.-] -> _`).
3 The `VariantSourcesPlugin` implementation is now part of the `variants` module,
4 not the `common` module.
79 5
80 ΠžΠ³Ρ€Π°Π½ΠΈΡ‡Π΅Π½ΠΈΠ΅:
81
82 - ΠΎΠ΄ΠΈΠ½ `sourceSetName` Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΠΏΠΎΡ€ΠΎΠΆΠ΄Π΅Π½ Ρ€Π°Π·Π½Ρ‹ΠΌΠΈ слоями.
83
84 ## EVENTS
85
86 ### whenRegistered
87
88 - callback Π½Π° Π½ΠΎΠ²Ρ‹ΠΉ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ source set;
89 - replayable;
90 - ΠΏΡ€ΠΈ shared source set срабатываСт ΠΎΠ΄ΠΈΠ½ Ρ€Π°Π·.
91
92 ### whenBound
93
94 - callback Π½Π° ΠΊΠ°ΠΆΠ΄ΡƒΡŽ usage-связку `variant/role/layer`;
95 - replayable;
96 - ΠΏΠΎΠ΄Ρ…ΠΎΠ΄ΠΈΡ‚ для per-usage Π»ΠΎΠ³ΠΈΠΊΠΈ.
97
98 ### variant filter
99
100 Π€ΠΈΠ»ΡŒΡ‚Ρ€ ΠΏΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρƒ ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ usage-binding:
101
102 - `whenBound(String variantName, ...)`
103
104 ## PAYLOAD TYPES
105
106 `SourceSetRegistration` содСрТит:
107
108 - `layerName`, `sourceSetName`;
109 - `sourceSet` (`NamedDomainObjectProvider<GenericSourceSet>`).
110
111 Sugar:
112
113 - `configureSourceSet(Action|Closure)`.
114
115 `SourceSetUsageBinding` содСрТит:
116
117 - `variantName`, `roleName`, `layerName`, `sourceSetName`;
118 - `sourceSet` (`NamedDomainObjectProvider<GenericSourceSet>`).
6 Current documentation is maintained in the root [README.md](../README.md),
7 [variant_sources.md](../variant_sources.md), and
8 [variant_sources_precedence.md](../variant_sources_precedence.md).
119 9
120 Sugar:
121
122 - `configureSourceSet(Action|Closure)`.
123
124 ## API
125
126 ### VariantSourcesExtension
127
128 - `bind(BuildLayer)` β€” ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ/ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ binding для canonical layer identity.
129 - `bind(String)` β€” ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ‚ΡŒ/ΡΠΎΠ·Π΄Π°Ρ‚ΡŒ binding ΠΏΠΎ ΠΈΠΌΠ΅Π½ΠΈ слоя.
130 - `bind(String, Action|Closure)` β€” ΡΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ binding.
131 - `bind(BuildLayer, Action|Closure)` β€” ΡΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ binding ΠΏΠΎ `BuildLayer`.
132 - `getBindings()` β€” read-only snapshot Ρ‚Π΅ΠΊΡƒΡ‰ΠΈΡ… bindings.
133 - `whenRegistered(...)` β€” Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ callbacks рСгистрации source set.
134 - `whenBound(...)` β€” Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ callbacks usage-binding.
135 - `whenBound(String variantName, ...)` β€” usage-binding callbacks с variant-filter.
136
137 ### LayerBindingSpec
138
139 - `sourceSetNamePattern` β€” naming policy для source set слоя.
140 - `configureSourceSet(...)` β€” слойная конфигурация `GenericSourceSet`.
141 - `whenRegistered(...)` β€” callbacks рСгистрации Π² Ρ€Π°ΠΌΠΊΠ°Ρ… слоя.
142 - `whenBound(...)` β€” callbacks usage-binding Π² Ρ€Π°ΠΌΠΊΠ°Ρ… слоя.
143
144 ## KEY CLASSES
145
146 - `VariantsSourcesPlugin` β€” Ρ‚ΠΎΡ‡ΠΊΠ° Π²Ρ…ΠΎΠ΄Π° plugin adapter.
147 - `VariantSourcesExtension` β€” Π³Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹ΠΉ DSL bind/events.
148 - `LayerBindingSpec` β€” ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ DSL-contract layer-local policy/callbacks.
149 - `SourceSetRegistration` β€” payload рСгистрации source set.
150 - `SourceSetUsageBinding` β€” payload usage-binding.
151
152 ## NOTES
153
154 - `sourceSetNamePattern` фиксируСтся ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΌ Ρ‡Ρ‚Π΅Π½ΠΈΠΈ Π² registration
155 (`finalizeValueOnRead`).
156 - runtime state bindings скрыт Π²Π½ΡƒΡ‚Ρ€ΠΈ adapter implementation (`LayerBinding`).
157 - name-based bindings рСзолвятся ΠΊ canonical `BuildLayer` Ρ‡Π΅Ρ€Π΅Π· registry `variants`.
158 - Closure callbacks ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ delegate-first.
159 - Для Π²Π»ΠΎΠΆΠ΅Π½Π½Ρ‹Ρ… closure Π»ΡƒΡ‡ΡˆΠ΅ явный ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ (`ctx -> ...`).
@@ -1,112 +1,8
1 # Variants Plugin
2
3 ## NAME
4
5 `VariantsPlugin` ΠΈ extension `variants`.
6
7 ## SYNOPSIS
8
9 ```groovy
10 plugins {
11 id 'org.implab.gradle-variants'
12 }
13
14 variants {
15 layer('mainBase')
16 layer('mainAmd')
17
18 variant('browser') {
19 attributes {
20 string('jsRuntime', 'browser')
21 string('jsModule', 'amd')
22 }
23
24 role('main') {
25 layers('mainBase', 'mainAmd')
26 }
27
28 artifactSlot('mainCompiled')
29 }
30 }
31 ```
32
33 ## DESCRIPTION
34
35 `VariantsPlugin` Π·Π°Π΄Π°Π΅Ρ‚ Π΄ΠΎΠΌΠ΅Π½Π½ΡƒΡŽ модСль сборки ΠΈ Π΅Π΅ Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΡŽ. Плагин Π½Π΅
36 рСгистрируСт compile/copy/bundle Π·Π°Π΄Π°Ρ‡ΠΈ Π½Π°ΠΏΡ€ΡΠΌΡƒΡŽ.
37
38 ### layers
39
40 Π“Π»ΠΎΠ±Π°Π»ΡŒΠ½Ρ‹Π΅ логичСскиС слои. Π‘Π»ΡƒΠΆΠ°Ρ‚ Π΅Π΄ΠΈΠ½Ρ‹ΠΌ словарСм ΠΈΠΌΠ΅Π½, Π½Π° ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Π·Π°Ρ‚Π΅ΠΌ
41 ΡΡΡ‹Π»Π°ΡŽΡ‚ΡΡ Ρ€ΠΎΠ»ΠΈ.
42
43 ### variants
44
45 Π˜ΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½Ρ‹Π΅ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Ρ‹ исполнСния/пакСтирования (`browser`, `node`, ΠΈ Ρ‚.Π΄.).
46 Π’Π°Ρ€ΠΈΠ°Π½Ρ‚ Π°Π³Ρ€Π΅Π³ΠΈΡ€ΡƒΠ΅Ρ‚ Ρ€ΠΎΠ»ΠΈ, Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ ΠΈ artifact slots.
47
48 ### roles
49
50 Роль описываСт Π½Π°Π±ΠΎΡ€ слоСв Π² ΠΏΡ€Π΅Π΄Π΅Π»Π°Ρ… Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° (`main`, `test`, `tools`).
51 Одна Ρ€ΠΎΠ»ΡŒ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡΡ‹Π»Π°Ρ‚ΡŒΡΡ Π½Π° нСсколько слоСв.
52
53 ### attributes
1 # Moved: Variants Plugin
54 2
55 Typed-Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ (`Attribute<T> -> Provider<T>`) для ΠΏΠ΅Ρ€Π΅Π΄Π°Ρ‡ΠΈ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€ΠΎΠ² Π²
56 Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Ρ‹ ΠΈ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΡŽ Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚ΠΎΠ².
57
58 ### artifact slots
59
60 Π˜ΠΌΠ΅Π½ΠΎΠ²Π°Π½Π½Ρ‹Π΅ слоты ΠΎΠΆΠΈΠ΄Π°Π΅ΠΌΡ‹Ρ… Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚ΠΎΠ² Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°. Π˜ΡΠΏΠΎΠ»ΡŒΠ·ΡƒΡŽΡ‚ΡΡ ΠΊΠ°ΠΊ ΠΊΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚
61 ΠΌΠ΅ΠΆΠ΄Ρƒ модСлью Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° ΠΈ ΠΏΠ»Π°Π³ΠΈΠ½Π°ΠΌΠΈ, ΡΠΎΠ·Π΄Π°ΡŽΡ‰ΠΈΠΌΠΈ/ΠΏΡƒΠ±Π»ΠΈΠΊΡƒΡŽΡ‰ΠΈΠΌΠΈ Ρ€Π΅Π·ΡƒΠ»ΡŒΡ‚Π°Ρ‚Ρ‹.
62
63 ## VALIDATION
64
65 Π’ `finalizeModel()` выполняСтся ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠ°:
66
67 - Ρ€ΠΎΠ»ΡŒ Π½Π΅ ΠΌΠΎΠΆΠ΅Ρ‚ ΡΡΡ‹Π»Π°Ρ‚ΡŒΡΡ Π½Π° нСизвСстный layer;
68 - пустыС ΠΈΠΌΠ΅Π½Π° layer Π·Π°ΠΏΡ€Π΅Ρ‰Π΅Π½Ρ‹;
69 - ΠΈΠΌΠ΅Π½Π° Ρ€ΠΎΠ»Π΅ΠΉ Π² Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹;
70 - ΠΈΠΌΠ΅Π½Π° artifact slots Π² Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π΅ Π΄ΠΎΠ»ΠΆΠ½Ρ‹ Π±Ρ‹Ρ‚ΡŒ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹.
71
72 ## LIFECYCLE
73
74 - `VariantsPlugin` Π²Ρ‹Π·Ρ‹Π²Π°Π΅Ρ‚ `variants.finalizeModel()` Π½Π° `afterEvaluate`.
75 - послС `finalizeModel()` ΠΌΡƒΡ‚Π°Ρ†ΠΈΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π·Π°ΠΏΡ€Π΅Ρ‰Π΅Π½Ρ‹.
76 - `whenFinalized(...)` replayable.
77
78 ## API
79
80 ### BuildVariantsExtension
3 The `VariantsPlugin` implementation is now part of the `variants` module, not
4 the `common` module.
81 5
82 - `layer(...)` β€” объявлСниС ΠΈΠ»ΠΈ конфигурация `BuildLayer`.
83 - `variant(...)` β€” объявлСниС ΠΈΠ»ΠΈ конфигурация `BuildVariant`.
84 - `layers { layer(...) }`, `variants { ... }` β€” grouped DSL Π±Π΅Π· ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ container API Π½Π°Ρ€ΡƒΠΆΡƒ.
85 - `all(...)` β€” callback для всСх Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ².
86 - `findLayer(name)`, `requireLayer(name)`, `getAllLayers()` β€” доступ ΠΊ registry слоСв.
87 - `getAll()`, `find(name)`, `require(name)` β€” доступ ΠΊ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°ΠΌ.
88 - `validate()` β€” явный запуск Π²Π°Π»ΠΈΠ΄Π°Ρ†ΠΈΠΈ.
89 - `finalizeModel()` β€” валидация + финализация ΠΌΠΎΠ΄Π΅Π»ΠΈ.
90 - `whenFinalized(...)` β€” callback ΠΏΠΎ Π·Π°Π²Π΅Ρ€ΡˆΠ΅Π½Π½ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΠΈ (replayable).
91
92 ### BuildVariant
93
94 - `attributes { ... }` β€” Π°Ρ‚Ρ€ΠΈΠ±ΡƒΡ‚Ρ‹ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° (+ sugar `string/bool/integer`).
95 - `role(...)`, `roles { ... }` β€” Ρ€ΠΎΠ»ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°.
96 - `artifactSlot(...)`, `artifactSlots { ... }` β€” Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Π½Ρ‹Π΅ слоты.
6 Current documentation is maintained in the root [README.md](../README.md) and
7 the design notes in [variant_sources.md](../variant_sources.md).
97 8
98 ## KEY CLASSES
99
100 - `VariantsPlugin` β€” Ρ‚ΠΎΡ‡ΠΊΠ° Π²Ρ…ΠΎΠ΄Π° ΠΏΠ»Π°Π³ΠΈΠ½Π°.
101 - `BuildVariantsExtension` β€” root extension ΠΈ lifecycle.
102 - `BuildVariant` β€” агрСгатная модСль Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°.
103 - `BuildLayer` β€” canonical identity-model слоя.
104 - `BuildRole` β€” модСль Ρ€ΠΎΠ»ΠΈ.
105 - `BuildArtifactSlot` β€” модСль Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Π½ΠΎΠ³ΠΎ слота.
106 - `VariantAttributes` β€” typed wrapper для variant attributes.
107
108 ## NOTES
109
110 - МодСль `variants` intentionally agnostic к toolchain.
111 - Layer registry хранится ΠΊΠ°ΠΊ явная identity-map, Π° Π½Π΅ ΠΊΠ°ΠΊ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½Ρ‹ΠΉ `NamedDomainObjectContainer`.
112 - Π˜Π½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΡ с Π·Π°Π΄Π°Ρ‡Π°ΠΌΠΈ выполняСтся Ρ‡Π΅Ρ€Π΅Π· `variantSources` ΠΈ Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Ρ‹.
@@ -1,2 +1,2
1 1 group=org.implab.gradle
2 version=1.0 No newline at end of file
2 version=0.1.0
@@ -1,694 +1,698
1 1 # Variants and Variant Artifacts
2 2
3 > Design note. The current user-facing DSL and local publication workflow are
4 > documented in [README.md](README.md). Some snippets below are conceptual and
5 > describe model direction rather than exact public API.
6
3 7 ## Overview
4 8
5 9 This document describes the artifact model built on top of `variants` and
6 10 `variantSources`.
7 11
8 12 The goal is to define:
9 13
10 14 - a stable artifact-facing model for outgoing contracts;
11 15 - a DSL for declaring slots and their inputs;
12 16 - a resolver bridge between `variantArtifacts` and `variantSources`;
13 17 - extension points for deduplication and similar policies;
14 18 - a model that remains live during configuration and does not require an
15 19 artificial freeze of slot content.
16 20
17 21 The design follows the same split already used elsewhere:
18 22
19 23 - `variants` defines the closed domain topology;
20 24 - `variantSources` defines source materialization semantics over that topology;
21 25 - `variantArtifacts` defines outgoing artifact contracts over that topology.
22 26
23 27 ---
24 28
25 29 ## Core idea
26 30
27 31 `variantArtifacts` is not the owner of the variant model and not the owner of
28 32 source-set materialization.
29 33
30 34 Its purpose is narrower:
31 35
32 36 - decide which variants participate in outgoing publication;
33 37 - define artifact slots for those outgoing variants;
34 38 - declare how each slot gathers its inputs.
35 39
36 40 This makes `variantArtifacts` an outgoing-contract layer, not a compilation or
37 41 source-materialization layer.
38 42
39 43 ---
40 44
41 45 ## Model boundaries
42 46
43 47 ### What belongs to `variants`
44 48
45 49 - identities: `Variant`, `Role`, `Layer`;
46 50 - normalized topology relation `(variant, role, layer)`;
47 51 - finalization of the core domain model;
48 52 - `VariantsView` and derived topology views.
49 53
50 54 ### What belongs to `variantSources`
51 55
52 56 - compile units and role projections derived from finalized `variants`;
53 57 - source-set naming policy;
54 58 - source-set materialization;
55 59 - lazy access to `GenericSourceSet` providers and their named outputs.
56 60
57 61 ### What belongs to `variantArtifacts`
58 62
59 63 - selection of outgoing variants;
60 64 - slot identities inside an outgoing variant;
61 65 - slot assembly declarations;
62 66 - root outgoing configurations for outgoing variants;
63 67 - slot-level assembly bodies;
64 68 - publication-facing hooks.
65 69
66 70 ### What does not belong to `variantArtifacts`
67 71
68 72 - ownership of variant existence;
69 73 - ownership of source-set naming;
70 74 - direct mutation of source materialization internals;
71 75 - compiler- or toolchain-specific logic;
72 76 - eager flattening of source inputs into files during DSL declaration.
73 77
74 78 ---
75 79
76 80 ## Outgoing subset
77 81
78 82 Not every declared `Variant` must become outgoing.
79 83
80 84 `variantArtifacts` defines an outgoing subset of variants.
81 85
82 86 For each outgoing variant there is one root outgoing aggregate:
83 87
84 88 - one root consumable `Configuration`;
85 89 - one or more artifact slots;
86 90 - one primary slot;
87 91 - optional secondary slots.
88 92
89 93 This is why `OutgoingConfiguration` is a real model object and not merely a
90 94 publication event payload.
91 95
92 96 It represents a live build-facing aggregate:
93 97
94 98 - it has its own attributes;
95 99 - it is visible to other build logic as soon as registered;
96 100 - it contains lazy handles rather than eagerly materialized state;
97 101 - it is distinct from slot assembly state.
98 102
99 103 ---
100 104
101 105 ## Identity-first split
102 106
103 107 The artifact model should preserve the same identity-first principle used for
104 108 the rest of the project.
105 109
106 110 ### Identity objects
107 111
108 112 - `Variant`
109 113 - `Slot`
110 114 - `ArtifactSlot = (variant, slot)`
111 115
112 116 These objects are:
113 117
114 118 - cheap;
115 119 - replayable;
116 120 - suitable for discovery and selection.
117 121
118 122 ### Stateful objects
119 123
120 124 - `OutgoingConfiguration`
121 125 - `ArtifactAssembly`
122 126
123 127 These objects are:
124 128
125 129 - build-facing;
126 130 - allowed to contain Gradle lazy handles;
127 131 - obtained through dedicated APIs rather than embedded into identity objects.
128 132
129 133 ### Rule of thumb
130 134
131 135 - slot identity is cheap and replayable;
132 136 - inside one `OutgoingConfiguration`, `Slot` is the natural local identity;
133 137 - `ArtifactSlot` is useful when slot identity must be referenced outside the
134 138 parent outgoing configuration;
135 139 - slot body is stateful and resolved separately;
136 140 - outgoing configuration is a live aggregate, not a mere snapshot.
137 141
138 142 ---
139 143
140 144 ## Artifact model
141 145
142 146 Conceptually:
143 147
144 148 ```java
145 149 interface VariantArtifactsContext {
146 150 VariantsView getVariants();
147 151 void all(Action<? super OutgoingConfiguration> action);
148 152 Optional<OutgoingConfiguration> findArtifacts(Variant variant);
149 153 OutgoingConfiguration requireArtifacts(Variant variant);
150 154 ArtifactAssemblies getAssemblies();
151 155 }
152 156 ```
153 157
154 158 ```java
155 159 interface OutgoingConfiguration {
156 160 Variant getVariant();
157 161 NamedDomainObjectProvider<Configuration> getOutgoingConfiguration();
158 162 NamedDomainObjectContainer<Slot> getSlots();
159 163 Property<Slot> getPrimarySlot();
160 164 }
161 165 ```
162 166
163 167 ```java
164 168 interface ArtifactAssemblies {
165 169 ArtifactAssembly resolveSlot(ArtifactSlot slot);
166 170 }
167 171 ```
168 172
169 173 This intentionally uses a Gradle-style local slot container rather than a
170 174 separate `ArtifactSlotsView`.
171 175
172 176 The reason is simple:
173 177
174 178 - inside one `OutgoingConfiguration`, slot identity is local and naturally
175 179 expressed as `Slot`;
176 180 - a dedicated `ArtifactSlotsView` would suggest a detached readonly projection
177 181 without clearly owning mutation/configuration semantics;
178 182 - `NamedDomainObjectContainer<Slot>` better matches the live configuration model
179 183 and keeps the parent aggregate as the owner of slot structure.
180 184
181 185 `ArtifactSlot` remains useful, but only as a fully qualified identity outside
182 186 the parent aggregate:
183 187
184 188 - resolver APIs;
185 189 - global payloads;
186 190 - cross-variant references.
187 191
188 192 Important distinction:
189 193
190 194 - `OutgoingConfiguration` describes outgoing publication structure;
191 195 - `ArtifactAssembly` describes how one slot artifact is assembled.
192 196
193 197 An `ArtifactAssembly` does not create the root outgoing configuration. It serves
194 198 one already declared slot inside that configuration.
195 199
196 200 ### Primary slot ownership
197 201
198 202 Primary status belongs to the parent outgoing configuration, not to the slot
199 203 itself.
200 204
201 205 This is why the preferred container-level contract is:
202 206
203 207 ```java
204 208 Property<Slot> getPrimarySlot();
205 209 ```
206 210
207 211 and not a slot-local boolean or a derived `ArtifactSlot` reference.
208 212
209 213 Reasons:
210 214
211 215 - the primary role is assigned by the parent aggregate;
212 216 - within one `OutgoingConfiguration`, the variant identity is already known, so
213 217 `Slot` is sufficient and avoids redundant `(variant, slot)` duplication;
214 218 - `Property` matches the rest of the Gradle-facing live model better than a
215 219 custom `findPrimarySlot()` style API;
216 220 - `Property` gives useful write/configure/finalize semantics without inventing a
217 221 separate special lifecycle abstraction.
218 222
219 223 Expected usage:
220 224
221 225 - DSL or adapters may assign the primary slot through `set(...)`;
222 226 - adapters that want to provide a default may use `convention(...)`;
223 227 - the property may remain unset while the model is still incomplete;
224 228 - at materialization time the property may be finalized;
225 229 - if more than one slot exists and `primarySlot` is still unset at the
226 230 materialization point, that is a model error.
227 231
228 232 The model should still enforce that the selected primary slot belongs to the
229 233 same `OutgoingConfiguration`.
230 234
231 235 ### Proposed public shape
232 236
233 237 With these constraints, the preferred public structure is:
234 238
235 239 ```java
236 240 interface VariantArtifactsContext {
237 241 VariantsView getVariants();
238 242 void all(Action<? super OutgoingConfiguration> action);
239 243 Optional<OutgoingConfiguration> findArtifacts(Variant variant);
240 244 OutgoingConfiguration requireArtifacts(Variant variant);
241 245 ArtifactAssemblies getAssemblies();
242 246 }
243 247
244 248 interface OutgoingConfiguration {
245 249 Variant getVariant();
246 250 NamedDomainObjectProvider<Configuration> getOutgoingConfiguration();
247 251 NamedDomainObjectContainer<Slot> getSlots();
248 252 Property<Slot> getPrimarySlot();
249 253 }
250 254
251 255 interface ArtifactAssemblies {
252 256 ArtifactAssembly resolveSlot(ArtifactSlot slot);
253 257 }
254 258
255 259 record ArtifactSlot(Variant variant, Slot slot) {}
256 260 ```
257 261
258 262 This gives:
259 263
260 264 - one global registry of outgoing variants;
261 265 - one local slot container per outgoing variant;
262 266 - one fully qualified slot identity for resolver and cross-aggregate use;
263 267 - one explicit service for slot assembly materialization.
264 268
265 269 ### Minimal internal shape
266 270
267 271 The preferred minimal internal structure is:
268 272
269 273 ```java
270 274 final class VariantArtifactsRegistry implements VariantArtifactsContext {
271 275 OutgoingConfiguration outgoingConfiguration(Variant variant);
272 276 ArtifactAssemblyRules slotRules(ArtifactSlot slot);
273 277 ArtifactAssemblies assemblies();
274 278 }
275 279
276 280 interface ArtifactAssemblyRules {
277 281 void from(Object input);
278 282 void fromVariant(Action<? super OutputSelectionSpec> action);
279 283 void fromRole(String roleName, Action<? super OutputSelectionSpec> action);
280 284 void fromLayer(String layerName, Action<? super OutputSelectionSpec> action);
281 285 }
282 286 ```
283 287
284 288 Responsibility split:
285 289
286 290 - `VariantArtifactsRegistry` owns the whole artifact model;
287 291 - `OutgoingConfiguration` owns only the structural variant-local aggregate;
288 292 - `ArtifactAssemblyRules` owns the content declaration of one slot;
289 293 - `ArtifactAssemblies` materializes `ArtifactAssembly` from
290 294 `ArtifactSlot -> ArtifactAssemblyRules`.
291 295
292 296 This keeps `OutgoingConfiguration` focused on structure and avoids overloading it
293 297 with slot-content APIs such as `rules(slot)`.
294 298
295 299 ### DSL binding
296 300
297 301 The DSL should be connected through the registry, not by making
298 302 `OutgoingConfiguration` responsible for content rules.
299 303
300 304 Conceptually:
301 305
302 306 ```java
303 307 variantArtifacts.variant("browser", spec -> {
304 308 spec.slot("runtime", assembly -> {
305 309 assembly.fromRole("production", out -> out.output("js"));
306 310 });
307 311 });
308 312 ```
309 313
310 314 Operationally this means:
311 315
312 316 1. registry creates or returns `OutgoingConfiguration` for the variant;
313 317 2. slot declaration creates or returns `Slot` inside that outgoing configuration;
314 318 3. registry forms `ArtifactSlot(variant, slot)`;
315 319 4. registry resolves `slotRules(artifactSlot)`;
316 320 5. bound `ArtifactAssemblySpec` writes into those rules.
317 321
318 322 So the DSL writes:
319 323
320 324 - structure into `OutgoingConfiguration`;
321 325 - slot content into registry-owned `ArtifactAssemblyRules`.
322 326
323 327 This is the intended bridge point between the public DSL and the internal
324 328 resolver/materialization model.
325 329
326 330 ---
327 331
328 332 ## Live model and monotonic structure
329 333
330 334 `variantArtifacts` should be treated as a live configuration model during the
331 335 whole configuration phase.
332 336
333 337 This means:
334 338
335 339 - slot inputs remain live;
336 340 - `from(...)`, `fromVariant(...)`, `fromRole(...)`, `fromLayer(...)` may keep
337 341 contributing inputs until task execution;
338 342 - `ArtifactAssembly` may expose live `FileCollection`, `Provider`, and task
339 343 wiring;
340 344 - external task outputs remain outside the control of this model and must be
341 345 accepted as live inputs.
342 346
343 347 The model should therefore avoid a mandatory freeze phase for slot content.
344 348
345 349 Instead, it should follow a monotonic rule:
346 350
347 351 - outgoing variant existence may grow;
348 352 - slot existence may grow;
349 353 - slot content may grow;
350 354 - publication-visible identity should not be retroactively redefined.
351 355
352 356 In practice this means:
353 357
354 358 - slot names are stable once declared;
355 359 - primary slot designation is structural;
356 360 - slot input content remains live.
357 361
358 362 This also means that the model does not need a dedicated freeze phase for slot
359 363 content merely because the root outgoing configuration was registered earlier.
360 364
361 365 Early registration of the root `Configuration` and live evolution of slot input
362 366 content are compatible concerns.
363 367
364 368 ---
365 369
366 370 ## DSL principles
367 371
368 372 The DSL should remain declarative and symbolic.
369 373
370 374 It should describe:
371 375
372 376 - which variant is outgoing;
373 377 - which slots exist;
374 378 - which slot is primary;
375 379 - which selectors contribute inputs to each slot.
376 380
377 381 It should not directly expose:
378 382
379 383 - `GenericSourceSet`;
380 384 - `FileCollection`;
381 385 - concrete resolved files from `variantSources`;
382 386 - internal resolver state.
383 387
384 388 ### DSL shape
385 389
386 390 Conceptually:
387 391
388 392 ```groovy
389 393 variantArtifacts {
390 394 variant("browser") {
391 395 primarySlot("runtime") {
392 396 fromRole("production") {
393 397 output("js")
394 398 output("resources")
395 399 }
396 400 }
397 401
398 402 slot("types") {
399 403 fromVariant {
400 404 output("dts")
401 405 }
402 406 }
403 407
404 408 slot("sources") {
405 409 fromLayer("main") {
406 410 output("sources")
407 411 }
408 412 }
409 413
410 414 slot("bundleMetadata") {
411 415 from(someTask)
412 416 from(layout.buildDirectory.file("generated/meta.json"))
413 417 }
414 418 }
415 419 }
416 420 ```
417 421
418 422 ### Meaning of contribution forms
419 423
420 424 - `from(Object)` adds a direct input independent from `variantSources`;
421 425 - `fromVariant { output(...) }` selects named outputs from all compile units of
422 426 the current variant;
423 427 - `fromRole(role) { output(...) }` selects named outputs from compile units that
424 428 belong to the given role projection;
425 429 - `fromLayer(layer) { output(...) }` selects named outputs from the compile unit
426 430 of the current variant and the given layer, if such unit exists.
427 431
428 432 The DSL stores declarations, not resolved file collections.
429 433
430 434 ---
431 435
432 436 ## Contribution model
433 437
434 438 Internally the DSL should compile to slot contributions.
435 439
436 440 Conceptually:
437 441
438 442 - `DirectContribution`
439 443 - `VariantOutputContribution`
440 444 - `RoleOutputContribution`
441 445 - `LayerOutputContribution`
442 446
443 447 These contributions should remain symbolic for as long as possible.
444 448
445 449 They should not resolve source sets or files at declaration time.
446 450
447 451 Each contribution is expected to provide:
448 452
449 453 - its selection scope;
450 454 - the requested output names;
451 455 - enough symbolic identity for later validation and resolver policies.
452 456
453 457 ---
454 458
455 459 ## Resolver bridge between `variantSources` and `variantArtifacts`
456 460
457 461 This is the central integration point.
458 462
459 463 `variantArtifacts` should not access mutable internals of `variantSources`.
460 464
461 465 Instead, it should resolve slot inputs through the public finalized
462 466 `VariantSourcesContext`.
463 467
464 468 ### Bridge responsibilities
465 469
466 470 The bridge:
467 471
468 472 - takes slot contribution declarations;
469 473 - expands them against finalized variant topology;
470 474 - maps logical selectors to compile units and role projections;
471 475 - obtains source sets lazily through `VariantSourcesContext`;
472 476 - resolves named outputs from those source sets;
473 477 - builds the live input model for an `ArtifactAssembly`.
474 478
475 479 ### Bridge input
476 480
477 481 - current outgoing variant identity;
478 482 - slot contribution declarations;
479 483 - `VariantSourcesContext`.
480 484
481 485 ### Bridge output
482 486
483 487 - a live collection of logical slot inputs;
484 488 - later adapted to `FileCollection` or other assembly-facing input models.
485 489
486 490 ### Resolution semantics
487 491
488 492 For one outgoing variant:
489 493
490 494 - `fromVariant { output(x) }`
491 495 - expands to all `CompileUnit` of that variant;
492 496 - `fromRole(role) { output(x) }`
493 497 - expands to `RoleProjection(variant, role)` and then to its compile units;
494 498 - `fromLayer(layer) { output(x) }`
495 499 - expands to one compile unit `(variant, layer)` when it exists;
496 500 - `from(Object)`
497 501 - bypasses `variantSources` completely.
498 502
499 503 After compile units are known, the bridge asks
500 504 `ctx.getSourceSets().getSourceSet(unit)` for each selected unit and resolves the
501 505 requested named output.
502 506
503 507 This keeps `variantArtifacts` independent from source-set naming internals and
504 508 other materialization details.
505 509
506 510 ---
507 511
508 512 ## Validation
509 513
510 514 Validation should be structural and symbolic.
511 515
512 516 It should validate:
513 517
514 518 - outgoing variant refers to an existing `Variant`;
515 519 - referenced `Role` exists in that variant projection space;
516 520 - referenced `Layer` exists in that variant compile-unit space;
517 521 - primary slot is defined when needed;
518 522 - primary slot refers to a slot declared in the same outgoing configuration.
519 523
520 524 Validation should not require eager materialization of source sets or eager
521 525 resolution of files.
522 526
523 527 ---
524 528
525 529 ## Deduplication and policy extension points
526 530
527 531 Deduplication is important, but it should not be baked into the DSL itself.
528 532
529 533 The correct place for it is the resolver bridge, after symbolic contributions
530 534 have been expanded to logical inputs but before they are finally adapted to
531 535 assembly-facing file collections.
532 536
533 537 ### Why not in the DSL
534 538
535 539 At declaration time it is still unknown whether selectors overlap:
536 540
537 541 - `fromVariant`
538 542 - `fromRole`
539 543 - `fromLayer`
540 544
541 545 may all describe the same logical source output.
542 546
543 547 ### Why not rely only on `FileCollection`
544 548
545 549 `FileCollection` may still provide useful physical deduplication, but it is too
546 550 late and too file-oriented to serve as the only semantic mechanism.
547 551
548 552 The artifact model should first deduplicate logical inputs, then let Gradle
549 553 perform any additional physical deduplication.
550 554
551 555 ### Default expectation
552 556
553 557 The default resolver should support:
554 558
555 559 - deduplication of topology-aware inputs by logical identity;
556 560 - no implicit deduplication of direct `from(Object)` inputs.
557 561
558 562 Logical identity should be based on domain meaning, for example:
559 563
560 564 - `(CompileUnit, outputName)`
561 565
562 566 and not on projected source-set names.
563 567
564 568 This is important because source-set naming policy belongs to `variantSources`
565 569 and must not silently redefine artifact semantics.
566 570
567 571 ### Extension points
568 572
569 573 The model should provide explicit internal extension points for:
570 574
571 575 - deduplication policy;
572 576 - logical input identity;
573 577 - adaptation of resolved logical inputs to assembly-facing objects.
574 578
575 579 Conceptually:
576 580
577 581 ```java
578 582 interface SlotInputDedupPolicy { ... }
579 583 interface LogicalSlotInputIdentity { ... }
580 584 interface SlotInputAdapter { ... }
581 585 ```
582 586
583 587 The default implementation may remain simple, but these seams should exist from
584 588 the start.
585 589
586 590 ---
587 591
588 592 ## Publication hooks
589 593
590 594 Publication hooks remain useful, but they should observe the live structural
591 595 model rather than define it.
592 596
593 597 Examples:
594 598
595 - `whenOutgoingVariant(...)`
599 - `whenOutgoingConfiguration(...)`
596 600 - `whenOutgoingSlot(...)`
597 601
598 602 These hooks are adapter-facing customization points over already declared
599 603 outgoing structure:
600 604
601 605 - root configuration attributes;
602 606 - slot artifact attributes;
603 607 - assembly task tweaks.
604 608
605 609 The recommended way to connect publication-facing `Spec` objects to the
606 610 structural model is by backlink, not by moving slot rules into publication
607 611 types.
608 612
609 613 Conceptually:
610 614
611 615 ```java
612 616 interface OutgoingConfigurationSpec {
613 617 OutgoingConfiguration getOutgoingArtifacts();
614 618 Variant getVariant();
615 619 Configuration getConfiguration();
616 620 }
617 621
618 622 interface OutgoingArtifactSlotSpec {
619 623 ArtifactSlot getArtifactSlot();
620 624 ArtifactAssembly getAssembly();
621 625 boolean isPrimary();
622 626 }
623 627 ```
624 628
625 629 In this arrangement:
626 630
627 631 - `OutgoingConfigurationSpec` remains a publication-facing facade;
628 632 - `OutgoingConfigurationSpec` may expose the structural aggregate when an
629 633 adapter needs it;
630 634 - slot rules still belong to `VariantArtifactsRegistry`;
631 635 - publication specs do not become owners of declaration or resolver state.
632 636
633 637 They should not become the primary structural API for the artifact model.
634 638
635 639 This is why a separate phase-oriented `OutgoingPublicationsContext` is not
636 640 required.
637 641
638 642 The live `OutgoingConfiguration` aggregate is sufficient.
639 643
640 644 ---
641 645
642 646 ## Design principles
643 647
644 648 ### 1. Keep topology ownership in `variants`
645 649
646 650 `variantArtifacts` selects from the topology model. It does not own it.
647 651
648 652 ### 2. Keep source ownership in `variantSources`
649 653
650 654 `variantArtifacts` consumes source materialization through a resolver bridge. It
651 655 does not own source-set semantics.
652 656
653 657 ### 3. Keep the DSL symbolic
654 658
655 659 The DSL declares intent and selection rules, not materialized files.
656 660
657 661 ### 4. Keep slot content live
658 662
659 663 Do not introduce an artificial finalize phase for slot content unless a real
660 664 semantic need appears.
661 665
662 666 ### 5. Fix only structural identity
663 667
664 668 Slot name, primary designation, and outgoing shape are structural. Slot inputs
665 669 remain live.
666 670
667 671 ### 6. Resolve through dedicated bridges
668 672
669 673 Cross-model integration belongs in a resolver service, not in DSL classes.
670 674
671 675 ### 7. Add policy seams early
672 676
673 677 Deduplication and similar concerns should have extension points from the start,
674 678 even if the initial implementation is conservative.
675 679
676 680 ---
677 681
678 682 ## Summary
679 683
680 684 `variantArtifacts` should be modeled as a live outgoing-contract layer over
681 685 `variants`, with source input resolution delegated to a dedicated bridge over
682 686 `variantSources`.
683 687
684 688 The resulting shape is:
685 689
686 690 - `variants` owns topology;
687 691 - `variantSources` owns source materialization;
688 692 - `variantArtifacts` owns outgoing contract structure;
689 693 - a resolver bridge connects symbolic slot declarations to live source-derived
690 694 inputs;
691 695 - deduplication and similar concerns are policies of that bridge, not of the
692 696 DSL itself;
693 697 - slot content stays live during configuration;
694 698 - only publication-visible structure is treated as stable identity.
@@ -1,872 +1,877
1 1 # Variants and Variant Sources
2 2
3 > Design note. The current user-facing DSL and local publication workflow are
4 > documented in [README.md](README.md). Some snippets below are conceptual and
5 > describe model direction rather than exact public API.
6
3 7 ## Overview
4 8
5 9 This document describes a two-layer model for build variants:
6 10
7 11 - `variants` defines the **core domain model**
8 12 - `variantSources` defines **source materialization semantics** for that model
9 13
10 14 The main goal is to keep the core model small, explicit, and stable, while allowing source-related behavior to remain flexible and adapter-friendly.
11 15
12 16 The model is intentionally split into:
13 17
14 18 1. a **closed, finalized domain model**
15 19 2. an **open, runtime-oriented source materialization model**
16 20
17 21 This separation is important because compilation, source aggregation, publication, and adapter-specific behavior do not belong to the same abstraction layer.
18 22
19 23 ---
20 24
21 25 ## Core idea
22 26
23 27 The `variants` model is based on three independent domains:
24 28
25 29 - `Layer`
26 30 - `Role`
27 31 - `Variant`
28 32
29 33 A finalized `VariantsView` contains the normalized relation:
30 34
31 35 - `(variant, role, layer)`
32 36
33 37 This relation is the source of truth.
34 38
35 39 Everything else is derived from it.
36 40
37 41 ---
38 42
39 43 ## `variants`: the core domain model
40 44
41 45 ### Purpose
42 46
43 47 `variants` describes:
44 48
45 49 - what layers exist
46 50 - what roles exist
47 51 - what variants exist
48 52 - which `(variant, role, layer)` combinations are valid
49 53
50 54 It does **not** describe:
51 55
52 56 - source directories
53 57 - source roots
54 58 - source set materialization
55 59 - compilation tasks
56 60 - publication mechanics
57 61 - source set inheritance
58 62 - layer merge behavior for a concrete toolchain
59 63
60 64 Those concerns are intentionally outside the core model.
61 65
62 66 ---
63 67
64 68 ## Core DSL example
65 69
66 70 ```groovy
67 71 variants {
68 72 layers {
69 73 main()
70 74 test()
71 75 generated()
72 76 rjs()
73 77 cjs()
74 78 }
75 79
76 80 roles {
77 81 production()
78 82 test()
79 83 tool()
80 84 }
81 85
82 86 variant("browser") {
83 87 role("production") {
84 88 layers("main", "generated", "rjs")
85 89 }
86 90 role("test") {
87 91 layers("main", "test", "generated", "rjs")
88 92 }
89 93 }
90 94
91 95 variant("nodejs") {
92 96 role("production") {
93 97 layers("main", "generated", "cjs")
94 98 }
95 99 role("test") {
96 100 layers("main", "test", "generated", "cjs")
97 101 }
98 102 role("tool") {
99 103 layers("main", "generated", "cjs")
100 104 }
101 105 }
102 106 }
103 107 ```
104 108
105 109 ### Interpretation
106 110
107 111 This example means:
108 112
109 113 * `browser` production uses `main`, `generated`, `rjs`
110 114 * `browser` test uses `main`, `test`, `generated`, `rjs`
111 115 * `nodejs` production uses `main`, `generated`, `cjs`
112 116 * `nodejs` test uses `main`, `test`, `generated`, `cjs`
113 117 * `nodejs` tool uses `main`, `generated`, `cjs`
114 118
115 119 The model is purely declarative.
116 120
117 121 ---
118 122
119 123 ## Identity and references
120 124
121 125 `Layer`, `Role`, and `Variant` are identity objects.
122 126
123 127 They exist as declared domain values.
124 128
125 129 References between model elements are symbolic:
126 130
127 131 * layers are referenced by layer name
128 132 * roles are referenced by role name
129 133 * variants are referenced by variant name
130 134
131 135 This is intentional.
132 136
133 137 The core model is declarative, not navigation-oriented.
134 138
135 139 It is acceptable for aggregates to hold symbolic references to foreign domain values, as long as identity is clearly defined and validated later.
136 140
137 141 ---
138 142
139 143 ## Finalization
140 144
141 145 The `variants` model is finalized once.
142 146
143 147 Finalization is an internal lifecycle transition. It is typically triggered privately, for example from `afterEvaluate`, but that mechanism is not part of the public API contract.
144 148
145 149 The public contract is:
146 150
147 151 * `variants.whenFinalized(...)`
148 152
149 153 This callback is **replayable**:
150 154
151 155 * if called before finalization, the action is queued
152 156 * if called after finalization, the action is invoked immediately
153 157
154 158 The callback receives a finalized, read-only view of the model.
155 159
156 160 Example:
157 161
158 162 ```java
159 163 variants.whenFinalized(view -> {
160 164 // use finalized VariantsView here
161 165 });
162 166 ```
163 167
164 168 ---
165 169
166 170 ## `VariantsView`
167 171
168 172 `VariantsView` is the finalized representation of the core model.
169 173
170 174 It contains:
171 175
172 176 * all declared `Layer`
173 177 * all declared `Role`
174 178 * all declared `Variant`
175 179 * all normalized entries `(variant, role, layer)`
176 180
177 181 Conceptually:
178 182
179 183 ```java
180 184 interface VariantsView {
181 185 Set<Layer> getLayers();
182 186 Set<Role> getRoles();
183 187 Set<Variant> getVariants();
184 188 Set<VariantRoleLayer> getEntries();
185 189 }
186 190 ```
187 191
188 192 Where:
189 193
190 194 ```java
191 195 record VariantRoleLayer(Variant variant, Role role, Layer layer) {}
192 196 ```
193 197
194 198 This view is:
195 199
196 200 * immutable
197 201 * normalized
198 202 * validated
199 203 * independent from DSL internals
200 204
201 205 ---
202 206
203 207 ## Derived views
204 208
205 209 Two important views can be derived from `VariantsView`:
206 210
207 211 * `CompileUnitsView`
208 212 * `RoleProjectionsView`
209 213
210 214 These views are not part of the raw core model itself, but they are naturally derived from it.
211 215
212 216 ---
213 217
214 218 ## `CompileUnitsView`
215 219
216 220 ### Purpose
217 221
218 222 A compile unit is defined as:
219 223
220 224 * `(variant, layer)`
221 225
222 226 This is based on the following rationale:
223 227
224 228 * `variant` defines compilation semantics
225 229 * `layer` partitions a variant into separate compilation units
226 230 * `role` is not a compilation boundary
227 231
228 232 This is especially useful for toolchains such as TypeScript, where compilation is often more practical or more correct per layer than for the whole variant at once.
229 233
230 234 ### Example
231 235
232 236 From:
233 237
234 238 * `(browser, production, main)`
235 239 * `(browser, production, rjs)`
236 240 * `(browser, test, main)`
237 241 * `(browser, test, test)`
238 242 * `(browser, test, rjs)`
239 243
240 244 we derive compile units:
241 245
242 246 * `(browser, main)`
243 247 * `(browser, rjs)`
244 248 * `(browser, test)`
245 249
246 250 ### Conceptual API
247 251
248 252 ```java
249 253 interface CompileUnitsView {
250 254 Set<CompileUnit> getUnits();
251 255 Set<CompileUnit> getUnitsForVariant(Variant variant);
252 256 boolean contains(Variant variant, Layer layer);
253 257 Set<Role> getRoles(CompileUnit unit);
254 258 }
255 259
256 260 record CompileUnit(Variant variant, Layer layer) {}
257 261 ```
258 262
259 263 ### Meaning
260 264
261 265 `CompileUnitsView` answers:
262 266
263 267 * what can be compiled
264 268 * how a variant is partitioned into compile units
265 269 * which logical roles include a given compile unit
266 270
267 271 ---
268 272
269 273 ## `RoleProjectionsView`
270 274
271 275 ### Purpose
272 276
273 277 A role projection is defined as:
274 278
275 279 * `(variant, role)`
276 280
277 281 This is based on the following rationale:
278 282
279 283 * `role` is not about compilation
280 284 * `role` groups compile units by purpose
281 285 * roles are more closely related to publication, aggregation, assembly, or result grouping
282 286
283 287 ### Example
284 288
285 289 For `browser`:
286 290
287 291 * `production` includes compile units:
288 292
289 293 * `(browser, main)`
290 294 * `(browser, rjs)`
291 295
292 296 * `test` includes compile units:
293 297
294 298 * `(browser, main)`
295 299 * `(browser, test)`
296 300 * `(browser, rjs)`
297 301
298 302 ### Conceptual API
299 303
300 304 ```java
301 305 interface RoleProjectionsView {
302 306 Set<RoleProjection> getProjections();
303 307 Set<RoleProjection> getProjectionsForVariant(Variant variant);
304 308 Set<RoleProjection> getProjectionsForRole(Role role);
305 309 Set<CompileUnit> getUnits(RoleProjection projection);
306 310 }
307 311
308 312 record RoleProjection(Variant variant, Role role) {}
309 313 ```
310 314
311 315 ### Meaning
312 316
313 317 `RoleProjectionsView` answers:
314 318
315 319 * how compile units are grouped by purpose
316 320 * what belongs to `production`, `test`, `tool`, etc.
317 321 * what should be aggregated or published together
318 322
319 323 ---
320 324
321 325 ## Why `CompileUnitsView` and `RoleProjectionsView` are not part of `VariantsView`
322 326
323 327 `VariantsView` is intentionally minimal.
324 328
325 329 It expresses the domain relation:
326 330
327 331 * `(variant, role, layer)`
328 332
329 333 `CompileUnitsView` and `RoleProjectionsView` are **derived interpretations** of that relation.
330 334
331 335 They are natural and useful, but they are still interpretations:
332 336
333 337 * `CompileUnit = (variant, layer)`
334 338 * `RoleProjection = (variant, role)`
335 339
336 340 This is why they are better treated as derived views rather than direct core model primitives.
337 341
338 342 ---
339 343
340 344 ## `variantSources`: source semantics for layers
341 345
342 346 ### Purpose
343 347
344 348 `variantSources` does **not** define variants.
345 349
346 350 It defines how a declared `Layer` contributes sources.
347 351
348 352 In other words:
349 353
350 354 * `variants` defines **what exists**
351 355 * `variantSources` defines **how layers become source inputs**
352 356
353 357 This distinction is important.
354 358
355 359 `variantSources` does not own the variant model. It interprets it.
356 360
357 361 ---
358 362
359 363 ## Main idea
360 364
361 365 A layer source rule describes the source contribution of a layer.
362 366
363 367 This is independent of any concrete variant or role.
364 368
365 369 Conceptually:
366 370
367 371 * `Layer -> source contribution rule`
368 372
369 373 Examples of source contribution semantics:
370 374
371 375 * base directory
372 376 * source directories
373 377 * logical source kinds (`ts`, `js`, `resources`)
374 378 * declared outputs (`js`, `dts`, `resources`)
375 379
376 380 ---
377 381
378 382 ## `variantSources` DSL example
379 383
380 384 ```groovy
381 385 variantSources {
382 386 layerRule("main") {
383 387 from("src/main")
384 388
385 389 set("ts") {
386 390 srcDir("ts")
387 391 }
388 392 set("js") {
389 393 srcDir("js")
390 394 }
391 395 set("resources") {
392 396 srcDir("resources")
393 397 }
394 398
395 399 outputs("js", "dts", "resources")
396 400 }
397 401
398 402 layerRule("test") {
399 403 from("src/test")
400 404
401 405 set("ts") {
402 406 srcDir("ts")
403 407 }
404 408 set("resources") {
405 409 srcDir("resources")
406 410 }
407 411
408 412 outputs("js", "dts", "resources")
409 413 }
410 414
411 415 layerRule("rjs") {
412 416 from("src/rjs")
413 417
414 418 set("ts") {
415 419 srcDir("ts")
416 420 }
417 421
418 422 outputs("js", "dts")
419 423 }
420 424
421 425 layerRule("cjs") {
422 426 from("src/cjs")
423 427
424 428 set("ts") {
425 429 srcDir("ts")
426 430 }
427 431
428 432 outputs("js", "dts")
429 433 }
430 434 }
431 435 ```
432 436
433 437 ### Interpretation
434 438
435 439 This means:
436 440
437 441 * `main` contributes `ts`, `js`, and `resources`
438 442 * `test` contributes `ts` and `resources`
439 443 * `rjs` contributes `ts`
440 444 * `cjs` contributes `ts`
441 445
442 446 These are layer rules only.
443 447
444 448 They do not yet say which variant consumes them.
445 449
446 450 ---
447 451
448 452 ## Why `variantSources` remains open
449 453
450 454 Unlike `variants`, `variantSources` does not need to be closed in the same way.
451 455
452 456 Reasons:
453 457
454 458 * the DSL is internal to source materialization
455 459 * the source of truth for unit existence is already finalized in `VariantsView`
456 460 * `SourceSetMaterializer` returns `NamedDomainObjectProvider<GenericSourceSet>`
457 461 * adapters may need to refine source-related behavior after `variants` is finalized
458 462
459 463 Therefore:
460 464
461 465 * `variants` is finalized
462 466 * `variantSources` may remain open
463 467
464 468 This is not a contradiction.
465 469
466 470 It reflects the difference between:
467 471
468 472 * a closed domain model
469 473 * an open infrastructure/materialization model
470 474
471 475 This openness is still constrained by explicit policy fixation points:
472 476
473 477 * late-configuration policy is fixed when the first selector rule is registered
474 478 * naming policy is fixed when the finalized `VariantSourcesContext` is created
475 479
476 480 ---
477 481
478 482 ## Late configuration policy
479 483
480 484 Openness of `variantSources` does not mean that late configuration is
481 485 semantically neutral.
482 486
483 487 Selector rules may be added after the finalized context becomes available, but
484 488 their behavior against already materialized `GenericSourceSet` objects must be
485 489 controlled explicitly.
486 490
487 491 Conceptually, `variantSources` exposes a policy choice such as:
488 492
489 493 ```groovy
490 494 variantSources {
491 495 lateConfigurationPolicy {
492 496 failOnLateConfiguration()
493 497 }
494 498 }
495 499 ```
496 500
497 501 Available modes are:
498 502
499 503 * `failOnLateConfiguration()`
500 504 * `warnOnLateConfiguration()`
501 505 * `allowLateConfiguration()`
502 506
503 507 Meaning:
504 508
505 509 * `fail` rejects selector rules that target already materialized source sets
506 510 * `warn` allows them but emits a warning
507 511 * `allow` allows them silently
508 512
509 513 This policy is intentionally modeled as an imperative choice, not as a mutable
510 514 property:
511 515
512 516 * it must be chosen before the first selector rule is added
513 * selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
517 * selector rules here mean `configureEach(...)`, `variant(...)`, `layer(...)`,
518 and `unit(...)`
514 519 * once chosen, it cannot be changed later
515 520 * it controls runtime behavior, not just a stored value
516 521 * the enforcement point is the first selector registration itself, not variants
517 522 finalization in isolation
518 523
519 524 For source sets configured before materialization, selector precedence remains:
520 525
521 526 ```text
522 variant < layer < unit
527 configureEach < variant < layer < unit
523 528 ```
524 529
525 530 For already materialized source sets in `warn` and `allow` modes:
526 531
527 532 * the late action is applied as an imperative follow-up step
528 533 * selector precedence is not reconstructed retroactively
529 534 * actual observation order is the order in which late actions are registered
530 535
531 536 ---
532 537
533 538 ## Compile-unit naming policy
534 539
535 540 Source-set naming is treated as a separate policy concern from selector
536 541 registration.
537 542
538 543 Conceptually, `variantSources` exposes:
539 544
540 545 ```groovy
541 546 variantSources {
542 547 namingPolicy {
543 548 failOnNameCollision()
544 549 }
545 550 }
546 551 ```
547 552
548 553 The base projected name of a compile unit is:
549 554
550 555 ```text
551 556 variantName + capitalize(layerName)
552 557 ```
553 558
554 559 Examples:
555 560
556 561 * `(browser, main)` -> `browserMain`
557 562 * `(browser, rjs)` -> `browserRjs`
558 563
559 564 Available modes are:
560 565
561 566 * `failOnNameCollision()` - reject finalized compile-unit models that project
562 567 the same source-set name for different compile units
563 568 * `resolveNameCollision()` - resolve such conflicts deterministically
564 569
565 570 ### `resolveNameCollision()` semantics
566 571
567 572 Conflicting compile units are ordered canonically by:
568 573
569 574 ```text
570 575 (variant.name, layer.name)
571 576 ```
572 577
573 578 Within one conflicting group:
574 579
575 580 * the first compile unit keeps the base name
576 581 * the second gets suffix `2`
577 582 * the third gets suffix `3`
578 583 * and so on
579 584
580 585 For example, if:
581 586
582 587 * `(foo, variantBar)` projects to `fooVariantBar`
583 588 * `(fooVariant, bar)` also projects to `fooVariantBar`
584 589
585 590 then canonical ordering yields:
586 591
587 592 * `(foo, variantBar)` -> `fooVariantBar`
588 593 * `(fooVariant, bar)` -> `fooVariantBar2`
589 594
590 595 ### Fixation point
591 596
592 597 Naming policy is fixed when the finalized `VariantSourcesContext` is created.
593 598
594 599 Operationally this means:
595 600
596 601 * naming policy must be selected before `variantSources.whenFinalized(...)`
597 602 becomes observable
598 603 * compile-unit names are projected and validated before queued
599 604 `whenFinalized(...)` callbacks are replayed
600 605 * changing naming policy from inside a `whenFinalized(...)` callback is too late
601 606
602 607 This differs intentionally from late-configuration policy:
603 608
604 609 * late-configuration policy is fixed by the first selector rule
605 610 * naming policy is fixed by finalized-context creation
606 611
607 612 ---
608 613
609 614 ## `VariantSourcesContext`
610 615
611 616 `variantSources.whenFinalized(...)` remains useful, but not because `variantSources` itself is frozen.
612 617
613 618 Its purpose is to provide access to a finalized context derived from `variants`.
614 619
615 620 This context contains:
616 621
617 622 * `CompileUnitsView`
618 623 * `RoleProjectionsView`
619 624 * `SourceSetMaterializer`
620 625
621 626 By the time the context becomes observable:
622 627
623 628 * compile-unit naming policy is already fixed
624 629 * symbolic source-set names for finalized compile units are already determined
625 630
626 631 Conceptually:
627 632
628 633 ```java
629 634 interface VariantSourcesContext {
630 635 CompileUnitsView getCompileUnits();
631 636 RoleProjectionsView getRoleProjections();
632 637 SourceSetMaterializer getSourceSets();
633 638 }
634 639 ```
635 640
636 641 This callback is also replayable.
637 642
638 643 Example:
639 644
640 645 ```java
641 646 variantSources.whenFinalized(ctx -> {
642 647 var units = ctx.getCompileUnits();
643 648 var roles = ctx.getRoleProjections();
644 649 var sourceSets = ctx.getSourceSets();
645 650 });
646 651 ```
647 652
648 653 ---
649 654
650 655 ## `SourceSetMaterializer`
651 656
652 657 ### Purpose
653 658
654 659 `SourceSetMaterializer` is the official source of truth for materialized source sets.
655 660
656 661 It is responsible for:
657 662
658 663 * lazy creation of `GenericSourceSet`
659 664 * projecting finalized compile units to symbolic source-set names
660 665 * validating or resolving name collisions according to naming policy
661 666 * applying `layerRule`
662 667 * connecting a compile unit to a source set provider
663 668 * exposing source sets to adapters
664 669
665 670 This is the correct place to apply `layerRule`.
666 671
667 672 Adapters should not apply layer rules themselves.
668 673
669 674 ### Conceptual API
670 675
671 676 ```java
672 677 interface SourceSetMaterializer {
673 678 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
674 679 }
675 680 ```
676 681
677 682 ---
678 683
679 684 ## Why `SourceSetMaterializer` should own `layerRule` application
680 685
681 686 If adapters applied `layerRule` directly, responsibility would leak across multiple layers:
682 687
683 688 * one component would know compile units
684 689 * another would know source semantics
685 690 * another would know how to configure `GenericSourceSet`
686 691
687 692 This would make the model harder to reason about.
688 693
689 694 Instead:
690 695
691 696 * `layerRule` is DSL/spec-level
692 697 * `SourceSetMaterializer` is execution/materialization-level
693 698 * adapters are consumption-level
694 699
695 700 This gives a much cleaner separation.
696 701
697 702 ---
698 703
699 704 ## `GenericSourceSet` as materialization target
700 705
701 706 `GenericSourceSet` is the materialized source aggregation object.
702 707
703 708 It is a good fit because it can represent:
704 709
705 710 * multiple logical source sets
706 711 * aggregated source directories
707 712 * declared outputs
708 713 * lazy registration through providers
709 714
710 715 The materializer is therefore the owner of:
711 716
712 717 * creating `GenericSourceSet`
713 718 * populating its source sets
714 719 * declaring outputs
715 720 * returning a provider for later use
716 721
717 722 ---
718 723
719 724 ## How an adapter should use the model
720 725
721 726 Example:
722 727
723 728 ```java
724 729 variantSources.whenFinalized(ctx -> {
725 730 for (CompileUnit unit : ctx.getCompileUnits().getUnits()) {
726 731 var sourceSetProvider = ctx.getSourceSets().getSourceSet(unit);
727 732
728 733 var variant = unit.variant();
729 734 var layer = unit.layer();
730 735
731 736 // create compile task for this compile unit
732 737 // configure compiler options from variant semantics
733 738 // use sourceSetProvider as task input
734 739 }
735 740
736 741 for (RoleProjection projection : ctx.getRoleProjections().getProjections()) {
737 742 var units = ctx.getRoleProjections().getUnits(projection);
738 743
739 744 // aggregate outputs of included compile units
740 745 // use for publication or assembly
741 746 }
742 747 });
743 748 ```
744 749
745 750 ---
746 751
747 752 ## Why compile unit is `(variant, layer)` and not `(variant, role)` or `(variant, role, layer)`
748 753
749 754 ### Not `(variant, role)`
750 755
751 756 Because role is not a compilation boundary.
752 757
753 758 Role is a logical grouping of results.
754 759
755 760 ### Not `(variant, role, layer)`
756 761
757 762 Because role does not define the compile unit itself.
758 763
759 764 A compile unit is a unit of compilation, not a unit of publication grouping.
760 765
761 766 ### Correct interpretation
762 767
763 768 * `(variant, layer)` = compile unit
764 769 * `(variant, role)` = logical result group
765 770 * `(variant, role, layer)` = membership relation between them
766 771
767 772 This is the most coherent separation.
768 773
769 774 ---
770 775
771 776 ## Model boundaries
772 777
773 778 ### What belongs to `variants`
774 779
775 780 * declared domains: `Layer`, `Role`, `Variant`
776 781 * normalized relation `(variant, role, layer)`
777 782 * finalization lifecycle
778 783 * finalized `VariantsView`
779 784
780 785 ### What belongs to derived views
781 786
782 787 * compile units: `(variant, layer)`
783 788 * role projections: `(variant, role)`
784 789
785 790 ### What belongs to `variantSources`
786 791
787 792 * source semantics of layers
788 793 * source materialization rules
789 794 * lazy `GenericSourceSet` provisioning
790 795 * source adapter integration
791 796
792 797 ### What does not belong to `variants`
793 798
794 799 * source directories
795 800 * base paths
796 801 * output declarations
797 802 * source set layout
798 803 * task registration
799 804 * compiler-specific assumptions
800 805
801 806 ---
802 807
803 808 ## Design principles
804 809
805 810 ### 1. Keep the core model small
806 811
807 812 The core model should only contain domain facts.
808 813
809 814 ### 2. Separate domain truth from materialization
810 815
811 816 The existence of compile units comes from `VariantsView`, not from source rules.
812 817
813 818 ### 3. Treat source materialization as infrastructure
814 819
815 820 `variantSources` is an interpretation layer, not the source of truth.
816 821
817 822 ### 4. Prefer replayable finalized hooks
818 823
819 824 Adapters should not depend on raw Gradle lifecycle callbacks such as `afterEvaluate`.
820 825
821 826 ### 5. Make late behavior explicit
822 827
823 828 Late configuration after materialization is a policy decision, not an implicit
824 829 guarantee.
825 830
826 831 ### 6. Keep heavy runtime objects behind providers
827 832
828 833 Materialized `GenericSourceSet` objects should remain behind a lazy API.
829 834
830 835 ### 7. Make name-collision behavior explicit
831 836
832 837 Compile-unit naming must be governed by an explicit policy, not by incidental
833 838 materialization order.
834 839
835 840 ---
836 841
837 842 ## Summary
838 843
839 844 The model is intentionally split into two layers.
840 845
841 846 ### `variants`
842 847
843 848 A closed, finalized domain model:
844 849
845 850 * `Layer`
846 851 * `Role`
847 852 * `Variant`
848 853 * `(variant, role, layer)`
849 854
850 855 ### `variantSources`
851 856
852 857 An open, source-materialization layer:
853 858
854 859 * layer source rules
855 860 * compile-unit source set materialization
856 861 * compile-unit naming policy
857 862 * adapter-facing `GenericSourceSet` providers
858 863
859 864 ### Derived views
860 865
861 866 From the finalized variant model:
862 867
863 868 * `CompileUnitsView`: `(variant, layer)`
864 869 * `RoleProjectionsView`: `(variant, role)`
865 870
866 871 ### Operational interpretation
867 872
868 873 * `variant` defines compilation semantics
869 874 * `layer` partitions compilation
870 875 * `role` groups results by purpose
871 876
872 877 This keeps the core model stable and minimal, while allowing source handling and adapter integration to remain flexible.
@@ -1,337 +1,382
1 1 # `variantSources`: selectors and precedence
2 2
3 3 `variantSources` configures source-set materialization over the compile-unit space.
4 4
5 5 A compile unit is defined as:
6 6
7 7 - `(variant, layer)`
8 8
9 9 This means:
10 10
11 11 - `variant` defines compilation semantics
12 12 - `layer` defines compilation partitioning
13 13
14 14 The `variantSources` DSL does not introduce a separate source model.
15 15 Instead, it provides configuration selectors over the existing compile-unit space.
16 16
17 17 ## Selectors
18 18
19 Three selectors are available:
19 Four selectors are available:
20 20
21 - `configureEach(...)`
21 22 - `variant(...)`
22 23 - `layer(...)`
23 24 - `unit(...)`
24 25
25 26 They all target the same set of compile units, but at different levels of specificity.
26 27
28 ### `configureEach(...)`
29
30 `configureEach(...)` applies configuration to every materialized compile-unit
31 source set.
32
33 Example:
34
35 ```groovy
36 variantSources {
37 configureEach {
38 sourceSet {
39 declareOutputs("js")
40 }
41 }
42 }
43 ```
44
45 Use this selector for global source-set conventions.
46
47 ---
48
27 49 ### `variant(...)`
28 50
29 51 `variant(...)` applies configuration to all compile units that belong to the given variant.
30 52
31 53 Example:
32 54
33 55 ```groovy
34 56 variantSources {
35 57 variant("browser") {
36 declareOutputs("js", "dts")
58 sourceSet {
59 declareOutputs("js", "dts")
60 }
37 61 }
38 62 }
39 63 ```
40 64
41 65 This affects all compile units of `browser`, for example:
42 66
43 67 - `(browser, main)`
44 68 - `(browser, rjs)`
45 69 - `(browser, test)`
46 70
47 71 Use this selector for variant-wide conventions.
48 72
49 73 ---
50 74
51 75 ### `layer(...)`
52 76
53 77 `layer(...)` applies configuration to all compile units that use the given layer.
54 78
55 79 Example:
56 80
57 81 ```groovy
58 82 variantSources {
59 83 layer("main") {
60 set("ts") {
61 srcDir("src/main/ts")
84 sourceSet {
85 sets.create("ts") {
86 srcDir("src/main/ts")
87 }
62 88 }
63 89 }
64 90 }
65 91 ```
66 92
67 93 This affects all compile units with layer `main`, for example:
68 94
69 95 - `(browser, main)`
70 96 - `(nodejs, main)`
71 97 - `(electron, main)`
72 98
73 99 Use this selector for cross-variant layer conventions.
74 100
75 101 ---
76 102
77 103 ### `unit(...)`
78 104
79 105 `unit(...)` applies configuration to one exact compile unit.
80 106
81 107 Example:
82 108
83 109 ```groovy
84 110 variantSources {
85 111 unit("browser", "main") {
86 set("resources") {
87 srcDir("src/browserMain/resources")
112 sourceSet {
113 sets.create("resources") {
114 srcDir("src/browserMain/resources")
115 }
88 116 }
89 117 }
90 118 }
91 119 ```
92 120
93 121 This affects only:
94 122
95 123 - `(browser, main)`
96 124
97 125 Use this selector for the most specific adjustments.
98 126
99 127 ---
100 128
101 129 ## Precedence
102 130
103 131 For each compile unit, source-set configuration is applied in the following order:
104 132
105 133 ```text
106 variant < layer < unit
134 configureEach < variant < layer < unit
107 135 ```
108 136
109 137 This means:
110 138
111 1. `variant(...)` actions are applied first
112 2. `layer(...)` actions are applied next
113 3. `unit(...)` actions are applied last
139 1. `configureEach(...)` actions are applied first
140 2. `variant(...)` actions are applied next
141 3. `layer(...)` actions are applied next
142 4. `unit(...)` actions are applied last
114 143
115 144 Each next level is allowed to refine or override the previous one.
116 145
117 146 ### Within the same level
118 147
119 148 Within the same selector level, actions are applied in registration order.
120 149
121 150 For example, if two plugins both configure `layer("main")`, their actions are applied in the same order in which they were registered.
122 151
123 152 ### Scope of this guarantee
124 153
125 154 This precedence describes the normal materialization order used by the source-set
126 155 materializer.
127 156
128 157 It is stable for source sets that are configured before they are materialized.
129 158
130 159 If a selector rule is added after a target source set has already been
131 160 materialized, the behavior depends on the selected late-configuration policy.
132 161
133 162 - in `fail` mode, such late configuration is rejected
134 163 - in `warn` and `allow` modes, the late action is applied as an imperative
135 164 follow-up step
136 165 - in `warn` and `allow` modes, selector precedence is not reconstructed
137 166 retroactively for already materialized targets
138 167
139 168 ---
140 169
141 170 ## Late Configuration Policy
142 171
143 172 `variantSources` exposes a policy switch for selector rules that target already
144 173 materialized source sets:
145 174
146 175 ```groovy
147 176 variantSources {
148 177 lateConfigurationPolicy {
149 178 failOnLateConfiguration()
150 179 }
151 180 }
152 181 ```
153 182
154 183 Available modes:
155 184
156 185 - `failOnLateConfiguration()` rejects such rules
157 186 - `warnOnLateConfiguration()` allows them and emits a warning
158 187 - `allowLateConfiguration()` allows them silently
159 188
160 189 Policy rules:
161 190
162 191 - the policy must be chosen before the first selector rule is added
163 - selector rules here mean `variant(...)`, `layer(...)`, and `unit(...)`
192 - selector rules here mean `configureEach(...)`, `variant(...)`, `layer(...)`,
193 and `unit(...)`
164 194 - once chosen, the policy cannot be changed later
165 195 - the policy is single-valued; it is not intended to be switched during further
166 196 configuration
167 197 - the enforcement point is the first selector registration itself; finalization
168 198 of `variants` alone does not freeze this policy
169 199
170 200 Operationally:
171 201
172 202 - `fail` preserves the strict precedence contract by rejecting late mutation of
173 203 already materialized targets
174 204 - `warn` and `allow` keep compatibility with imperative late mutation
175 205 - in `warn` and `allow`, already materialized targets observe the late action in
176 actual registration order, not in reconstructed `variant < layer < unit`
177 order
206 actual registration order, not in reconstructed
207 `configureEach < variant < layer < unit` order
178 208
179 209 ---
180 210
181 211 ## Compile-Unit Naming Policy
182 212
183 213 `variantSources` also exposes a policy for projecting compile units to symbolic
184 214 source-set names:
185 215
186 216 ```groovy
187 217 variantSources {
188 218 namingPolicy {
189 219 failOnNameCollision()
190 220 }
191 221 }
192 222 ```
193 223
194 224 Base projected name:
195 225
196 226 - `sourceSetName = variantName + capitalize(layerName)`
197 227
198 228 Example:
199 229
200 230 - `(browser, main)` -> `browserMain`
201 231 - `(browser, rjs)` -> `browserRjs`
202 232
203 233 Available modes:
204 234
205 235 - `failOnNameCollision()` rejects finalized compile-unit models that project the
206 236 same base name for different compile units
207 237 - `resolveNameCollision()` resolves such collisions deterministically
208 238
209 239 ### `resolveNameCollision()` semantics
210 240
211 241 Conflicting compile units are ordered canonically by:
212 242
213 243 ```text
214 244 (variant.name, layer.name)
215 245 ```
216 246
217 247 Name assignment in a conflicting group is:
218 248
219 249 - the first compile unit keeps the base name
220 250 - the second gets suffix `2`
221 251 - the third gets suffix `3`
222 252 - and so on
223 253
224 254 Example:
225 255
226 256 - `(foo, variantBar)` and `(fooVariant, bar)` both project to `fooVariantBar`
227 257 - after canonical ordering:
228 258 - `(foo, variantBar)` -> `fooVariantBar`
229 259 - `(fooVariant, bar)` -> `fooVariantBar2`
230 260
231 261 ### Fixation Point
232 262
233 263 Naming policy is fixed when the finalized `VariantSourcesContext` is created.
234 264
235 265 Operationally this means:
236 266
237 - policy selection must happen before `variantSources.whenFinalized(...)`
267 - policy selection must happen before `variantSources.whenAvailable(...)`
238 268 becomes observable
239 269 - compile-unit names are projected and validated before queued
240 `whenFinalized(...)` callbacks are replayed
241 - changing naming policy from inside a `whenFinalized(...)` callback is too late
270 `whenAvailable(...)` callbacks are replayed
271 - changing naming policy from inside a `whenAvailable(...)` callback is too late
242 272
243 273 ---
244 274
245 275 ## Example
246 276
247 277 ```groovy
248 278 variantSources {
279 configureEach {
280 sourceSet {
281 declareOutputs("js", "dts")
282 }
283 }
284
249 285 variant("browser") {
250 declareOutputs("js", "dts")
286 sourceSet {
287 registerOutput("js", layout.projectDirectory.file("inputs/browser.js"))
288 }
251 289 }
252 290
253 291 layer("main") {
254 set("ts") {
255 srcDir("src/main/ts")
292 sourceSet {
293 sets.create("ts") {
294 srcDir("src/main/ts")
295 }
256 296 }
257 297 }
258 298
259 299 unit("browser", "main") {
260 set("resources") {
261 srcDir("src/browserMain/resources")
300 sourceSet {
301 sets.create("resources") {
302 srcDir("src/browserMain/resources")
303 }
262 304 }
263 305 }
264 306 }
265 307 ```
266 308
267 309 For compile unit `(browser, main)` the effective configuration is built in this order:
268 310
269 311 1. `variant("browser")`
270 312 2. `layer("main")`
271 313 3. `unit("browser", "main")`
272 314
315 The global `configureEach(...)` selector is applied before the listed
316 variant/layer/unit selectors for every compile unit.
317
273 318 For compile unit `(browser, rjs)` the effective configuration is built in this order:
274 319
275 320 1. `variant("browser")`
276 321 2. `layer("rjs")` if present
277 322 3. `unit("browser", "rjs")` if present
278 323
279 324 For compile unit `(nodejs, main)` the effective configuration is built in this order:
280 325
281 326 1. `variant("nodejs")` if present
282 327 2. `layer("main")`
283 328 3. `unit("nodejs", "main")` if present
284 329
285 330 ---
286 331
287 332 ## Model boundary
288 333
289 334 These selectors do not define compile units.
290 335 Compile units are derived from finalized `variants`.
291 336
292 337 `variantSources` only configures how source sets are materialized for those units.
293 338
294 339 This means:
295 340
296 341 - `variants` is the source of truth for compile-unit existence
297 342 - `variantSources` is the source of truth for compile-unit source-set configuration
298 343
299 344 ---
300 345
301 346 ## Operational semantics
302 347
303 348 The `variantSources` API is exposed through a finalized context.
304 349
305 350 Conceptually, configuration is registered against finalized model objects, while DSL sugar may still use names for convenience.
306 351
307 352 Internally, selector-based configuration is accumulated and later applied by the
308 353 source-set materializer when a `GenericSourceSet` is created for a compile unit.
309 354
310 355 This guarantees that:
311 356
312 357 - selector precedence is stable before materialization
313 358 - registration order is preserved
314 359 - configuration of already materialized targets is governed by the selected
315 360 late-configuration policy
316 361 - adapters do not need to depend on raw Gradle lifecycle timing
317 362
318 363 ---
319 364
320 365 ## Summary
321 366
322 367 - compile unit space is `(variant, layer)`
323 368 - `variant(...)`, `layer(...)`, and `unit(...)` are selectors over that space
324 369 - precedence is:
325 370
326 371 ```text
327 variant < layer < unit
372 configureEach < variant < layer < unit
328 373 ```
329 374
330 375 - registration order is preserved within the same selector level
331 376 - already materialized targets are handled by `lateConfigurationPolicy(...)`
332 377 - the late-configuration policy must be selected before the first selector rule
333 378 and cannot be changed later
334 379 - compile-unit naming is governed by `namingPolicy(...)`
335 380 - by default, name collisions fail fast during finalized context creation
336 381 - `variants` defines what exists
337 382 - `variantSources` defines how those compile units are materialized as source sets
@@ -1,57 +1,61
1 1 plugins {
2 2 id "java-library"
3 3 id "ivy-publish"
4 4 }
5 5
6 description = "Variant, source-set, and outgoing artifact model plugins for Gradle builds"
7
6 8 java {
7 9 withJavadocJar()
8 10 withSourcesJar()
9 11 toolchain {
10 12 languageVersion = JavaLanguageVersion.of(21)
11 13 }
12 14 }
13 15
14 16 dependencies {
15 17 compileOnly libs.jdt.annotations
16 18
17 19 api gradleApi(),
18 libs.bundles.jackson
19
20 implementation project(":common")
20 project(":common")
21 21
22 22 testImplementation gradleTestKit()
23 23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
24 24 testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
25 25 testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.4"
26 26 }
27 27
28 28 task printVersion{
29 29 doLast {
30 30 println "project: $project.group:$project.name:$project.version"
31 31 println "jar: ${->jar.archiveFileName.get()}"
32 32 }
33 33 }
34 34
35 35 test {
36 36 useJUnitPlatform()
37 37 }
38 38
39 39 javadoc {
40 40 exclude "**/internal/**"
41 41 }
42 42
43 43 publishing {
44 44 repositories {
45 45 ivy {
46 46 url "${System.properties["user.home"]}/ivy-repo"
47 47 }
48 48 }
49 49 publications {
50 50 ivy(IvyPublication) {
51 51 from components.java
52 52 descriptor.description {
53 53 text = providers.provider({ description })
54 54 }
55 descriptor.license {
56 name = "BSD-2-Clause"
57 url = "https://spdx.org/licenses/BSD-2-Clause.html"
58 }
55 59 }
56 60 }
57 61 }
General Comments 0
You need to be logged in to leave comments. Login now