##// END OF EJS Templates
Refine variant artifacts publication lifecycle- Remove assembly task access from outgoing slot publication spec- Keep whenOutgoingSlot focused on publication attributes only- Decouple materialization policy handler from artifact assemblies- Drop eager afterEvaluate outgoing configuration realization- Add reference coverage for lazy Gradle outgoing variants- Exercise primary and secondary artifact resolution without forced realization- Keep slot body customization in ArtifactAssemblySpec
cin -
r52:3939ecb6e9a4 default
parent child
Show More
@@ -0,0 +1,14
1 package org.implab.gradle.internal;
2
3 import org.gradle.api.Named;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.gradle.api.model.ObjectFactory;
6
7 public final class IdentityContainerFactory {
8 private IdentityContainerFactory() {
9 }
10
11 public static <T extends Named> NamedDomainObjectContainer<T> create(ObjectFactory objectFactory, Class<T> type) {
12 return objectFactory.domainObjectContainer(type, name -> objectFactory.named(type, name));
13 }
14 }
@@ -0,0 +1,1
1 implementation-class=org.implab.gradle.variants.SourcesPlugin
@@ -0,0 +1,86
1 package org.implab.gradle.variants;
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertNotNull;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
7 import java.io.IOException;
8 import java.util.Properties;
9
10 import org.gradle.testkit.runner.BuildResult;
11 import org.junit.jupiter.api.Test;
12
13 class PluginMarkerFunctionalTest extends AbstractFunctionalTest {
14
15 @Test
16 void pluginMarkersPointToCurrentImplementations() throws Exception {
17 assertMarker("org.implab.gradle-variants.properties", VariantsPlugin.class);
18 assertMarker("org.implab.gradle-sources.properties", SourcesPlugin.class);
19 assertMarker("org.implab.gradle-variants-sources.properties", VariantSourcesPlugin.class);
20 assertMarker("org.implab.gradle-variants-artifacts.properties", VariantArtifactsPlugin.class);
21 }
22
23 @Test
24 void pluginIdsApplyCurrentExtensions() throws Exception {
25 writeSettings("variants-plugin-ids");
26 writeBuildFile("""
27 apply plugin: 'org.implab.gradle-variants'
28 apply plugin: 'org.implab.gradle-sources'
29 apply plugin: 'org.implab.gradle-variants-sources'
30 apply plugin: 'org.implab.gradle-variants-artifacts'
31
32 tasks.register('probe') {
33 doLast {
34 println("variantsExtension=" + (project.extensions.findByName('variants') != null))
35 println("sourcesExtension=" + (project.extensions.findByName('sources') != null))
36 println("variantSourcesExtension=" + (project.extensions.findByName('variantSources') != null))
37 println("variantArtifactsExtension=" + (project.extensions.findByName('variantArtifacts') != null))
38 }
39 }
40 """);
41
42 BuildResult result = runner("probe").build();
43
44 assertTrue(result.getOutput().contains("variantsExtension=true"));
45 assertTrue(result.getOutput().contains("sourcesExtension=true"));
46 assertTrue(result.getOutput().contains("variantSourcesExtension=true"));
47 assertTrue(result.getOutput().contains("variantArtifactsExtension=true"));
48 }
49
50 @Test
51 void sourcesPluginSupportsStandaloneSourceSetWorkflow() throws Exception {
52 writeSettings("sources-plugin-standalone");
53 writeFile("inputs/main.js", "console.log('main')\n");
54 writeBuildFile("""
55 apply plugin: 'org.implab.gradle-sources'
56
57 sources.create('main') {
58 declareOutputs('js')
59 registerOutput('js', layout.projectDirectory.file('inputs/main.js'))
60 }
61
62 tasks.register('probe') {
63 doLast {
64 def main = sources.named('main').get()
65 println("sourceSet=" + main.name)
66 println("jsFiles=" + main.output('js').files.collect { it.name }.sort().join(','))
67 }
68 }
69 """);
70
71 BuildResult result = runner("probe").build();
72
73 assertTrue(result.getOutput().contains("sourceSet=main"));
74 assertTrue(result.getOutput().contains("jsFiles=main.js"));
75 }
76
77 private static void assertMarker(String markerName, Class<?> implementationClass) throws IOException {
78 var resourceName = "META-INF/gradle-plugins/" + markerName;
79 var resource = PluginMarkerFunctionalTest.class.getClassLoader().getResourceAsStream(resourceName);
80 assertNotNull(resource, "Missing plugin marker " + resourceName);
81
82 var properties = new Properties();
83 properties.load(resource);
84 assertEquals(implementationClass.getName(), properties.getProperty("implementation-class"));
85 }
86 }
This diff has been collapsed as it changes many lines, (563 lines changed) Show them Hide them
@@ -0,0 +1,563
1 package org.implab.gradle.variants;
2
3 import static org.junit.jupiter.api.Assertions.assertTrue;
4
5 import org.gradle.testkit.runner.BuildResult;
6 import org.gradle.testkit.runner.TaskOutcome;
7 import org.junit.jupiter.api.Test;
8
9 class VariantArtifactsPluginFunctionalTest extends AbstractFunctionalTest {
10
11 @Test
12 void gradleReferenceLazyOutgoingConfigurationAllowsSecondaryArtifactSelection() throws Exception {
13 writeFile("settings.gradle", """
14 rootProject.name = 'gradle-reference-outgoing-resolution'
15 include 'producer', 'consumer'
16 """);
17 writeFile("producer/inputs/typesPackage", "types\n");
18 writeFile("producer/inputs/js", "js\n");
19 writeBuildFile("""
20 import org.gradle.api.attributes.Attribute
21
22 def variantAttr = Attribute.of('test.variant', String)
23 def slotAttr = Attribute.of('test.slot', String)
24
25 project(':producer') {
26 def browserElements = configurations.consumable('browserElements')
27
28 println('reference: registered browserElements provider')
29
30 browserElements.configure { configuration ->
31 println('reference: configuring browserElements')
32
33 configuration.attributes.attribute(variantAttr, 'browser')
34 configuration.outgoing.attributes.attribute(slotAttr, 'typesPackage')
35 configuration.outgoing.artifact(layout.projectDirectory.file('inputs/typesPackage'))
36
37 configuration.outgoing.variants.create('js') { secondary ->
38 println('reference: creating js outgoing variant')
39
40 secondary.attributes.attribute(slotAttr, 'js')
41 secondary.artifact(layout.projectDirectory.file('inputs/js'))
42 }
43 }
44 }
45
46 project(':consumer') {
47 configurations {
48 compileView {
49 canBeResolved = true
50 canBeConsumed = false
51 canBeDeclared = true
52 attributes {
53 attribute(variantAttr, 'browser')
54 attribute(slotAttr, 'typesPackage')
55 }
56 }
57 }
58
59 dependencies {
60 compileView project(':producer')
61 }
62
63 tasks.register('probe') {
64 doLast {
65 println('reference: resolving primary files')
66
67 def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',')
68
69 println('reference: resolving secondary files')
70
71 def jsFiles = configurations.compileView.incoming.artifactView {
72 attributes {
73 attribute(slotAttr, 'js')
74 }
75 }.files.files.collect { it.name }.sort().join(',')
76
77 println('compileFiles=' + compileFiles)
78 println('jsFiles=' + jsFiles)
79 }
80 }
81 }
82 """);
83
84 BuildResult result = runner(":consumer:probe").build();
85 var output = result.getOutput();
86 var registered = output.indexOf("reference: registered browserElements provider");
87 var resolvingPrimary = output.indexOf("reference: resolving primary files");
88 var configuring = output.indexOf("reference: configuring browserElements");
89 var creatingSecondary = output.indexOf("reference: creating js outgoing variant");
90 var resolvingSecondary = output.indexOf("reference: resolving secondary files");
91
92 assertTrue(registered >= 0);
93 assertTrue(resolvingPrimary >= 0);
94 assertTrue(configuring >= 0);
95 assertTrue(creatingSecondary >= 0);
96 assertTrue(resolvingSecondary >= 0);
97 assertTrue(registered < resolvingPrimary);
98 assertTrue(resolvingPrimary < configuring);
99 assertTrue(configuring < creatingSecondary);
100 assertTrue(creatingSecondary < resolvingSecondary);
101 assertTrue(output.contains("compileFiles=typesPackage"));
102 assertTrue(output.contains("jsFiles=js"));
103 }
104
105 @Test
106 void materializesPrimaryAndSecondarySlotsAndInvokesOutgoingHooks() throws Exception {
107 writeSettings("variant-artifacts-slots");
108 writeFile("inputs/base.js", "console.log('base')\n");
109 writeFile("inputs/amd.js", "console.log('amd')\n");
110 writeFile("inputs/mainJs.txt", "mainJs marker\n");
111 writeFile("inputs/amdJs.txt", "amdJs marker\n");
112 writeBuildFile("""
113 import org.gradle.api.attributes.Attribute
114
115 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
116
117 def variantAttr = Attribute.of('test.variant', String)
118 def slotAttr = Attribute.of('test.slot', String)
119
120 variants.layers.create('mainBase')
121 variants.layers.create('mainAmd')
122 variants.roles.create('main')
123 variants.roles.create('test')
124 variants.variant('browser') {
125 role('main') {
126 layers('mainBase', 'mainAmd')
127 }
128 }
129
130 variantSources {
131 layer('mainBase') {
132 declareOutputs('js')
133 registerOutput('js', layout.projectDirectory.file('inputs/base.js'))
134 }
135 layer('mainAmd') {
136 declareOutputs('js')
137 registerOutput('js', layout.projectDirectory.file('inputs/amd.js'))
138 }
139 }
140
141 variantArtifacts {
142 variant('browser') {
143 primarySlot('mainJs') {
144 fromRole('main') {
145 output('js')
146 }
147 from(layout.projectDirectory.file('inputs/mainJs.txt'))
148 }
149 slot('amdJs') {
150 fromLayer('mainAmd') {
151 output('js')
152 }
153 from(layout.projectDirectory.file('inputs/amdJs.txt'))
154 }
155 }
156
157 whenOutgoingConfiguration { publication ->
158 publication.configuration {
159 attributes.attribute(variantAttr, publication.variant.name)
160 }
161 }
162
163 whenOutgoingSlot { publication ->
164 def slotName = publication.artifactSlot.slot.name
165 publication.artifactAttributes {
166 attribute(slotAttr, slotName)
167 }
168 }
169 }
170
171 tasks.register('probe') {
172 dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_mainJs'
173 dependsOn 'assembleVariantArtifactSlot_v7_browser_s5_amdJs'
174
175 doLast {
176 def mainDir = layout.buildDirectory.dir('variant-assemblies/browser/mainJs').get().asFile
177 def amdDir = layout.buildDirectory.dir('variant-assemblies/browser/amdJs').get().asFile
178
179 assert new File(mainDir, 'base.js').exists()
180 assert new File(mainDir, 'amd.js').exists()
181 assert new File(mainDir, 'mainJs.txt').exists()
182
183 assert !new File(amdDir, 'base.js').exists()
184 assert new File(amdDir, 'amd.js').exists()
185 assert new File(amdDir, 'amdJs.txt').exists()
186
187 def elements = configurations.getByName('browserElements')
188 def amdVariant = elements.outgoing.variants.getByName('amdJs')
189
190 println('variantAttr=' + elements.attributes.getAttribute(variantAttr))
191 println('primarySlotAttr=' + elements.outgoing.attributes.getAttribute(slotAttr))
192 println('amdSlotAttr=' + amdVariant.attributes.getAttribute(slotAttr))
193 println('configurations=' + configurations.matching { it.name == 'browserElements' }.collect { it.name }.join(','))
194 println('secondaryVariants=' + elements.outgoing.variants.collect { it.name }.sort().join(','))
195 }
196 }
197 """);
198
199 BuildResult result = runner("probe").build();
200
201 assertTrue(result.getOutput().contains("variantAttr=browser"));
202 assertTrue(result.getOutput().contains("primarySlotAttr=mainJs"));
203 assertTrue(result.getOutput().contains("amdSlotAttr=amdJs"));
204 assertTrue(result.getOutput().contains("configurations=browserElements"));
205 assertTrue(result.getOutput().contains("secondaryVariants=amdJs"));
206 assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS);
207 }
208
209 @Test
210 void allowsSingleSlotVariantWithoutExplicitPrimarySlot() throws Exception {
211 writeSettings("variant-artifacts-single-slot");
212 writeBuildFile("""
213 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
214
215 variants.layers.create('main')
216 variants.roles.create('main')
217 variants.variant('browser') {
218 role('main') {
219 layers('main')
220 }
221 }
222
223 variantSources.layer('main') {
224 declareOutputs('types')
225 }
226
227 variantArtifacts {
228 variant('browser') {
229 slot('typesPackage') {
230 fromVariant {
231 output('types')
232 }
233 }
234 }
235 }
236
237 tasks.register('probe') {
238 doLast {
239 variantArtifacts.whenAvailable { ctx ->
240 def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser')
241 println('primary=' + ctx.findOutgoing(browser).get().primarySlot.get().name)
242 }
243 }
244 }
245 """);
246
247 BuildResult result = runner("probe").build();
248
249 assertTrue(result.getOutput().contains("primary=typesPackage"));
250 }
251
252 @Test
253 void materializesDirectSlotInputsWithoutVariantSourceBindings() throws Exception {
254 writeSettings("variant-artifacts-direct-input");
255 writeFile("inputs/bundle.js", "console.log('bundle')\n");
256 writeBuildFile("""
257 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
258
259 variants.layers.create('main')
260 variants.roles.create('main')
261 variants.variant('browser') {
262 role('main') {
263 layers('main')
264 }
265 }
266
267 variantArtifacts {
268 variant('browser') {
269 primarySlot('bundle') {
270 from(layout.projectDirectory.file('inputs/bundle.js'))
271 }
272 }
273 }
274
275 tasks.register('probe') {
276 dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle'
277
278 doLast {
279 def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile
280 assert new File(bundleDir, 'bundle.js').exists()
281 }
282 }
283 """);
284
285 BuildResult result = runner("probe").build();
286
287 assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS);
288 }
289
290 @Test
291 void combinesDirectAndTopologyAwareSlotInputs() throws Exception {
292 writeSettings("variant-artifacts-combined-inputs");
293 writeFile("inputs/base.js", "console.log('base')\n");
294 writeFile("inputs/marker.txt", "marker\n");
295 writeBuildFile("""
296 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
297
298 variants.layers.create('main')
299 variants.roles.create('main')
300 variants.variant('browser') {
301 role('main') {
302 layers('main')
303 }
304 }
305
306 variantSources.layer('main') {
307 declareOutputs('js')
308 registerOutput('js', layout.projectDirectory.file('inputs/base.js'))
309 }
310
311 variantArtifacts {
312 variant('browser') {
313 primarySlot('bundle') {
314 fromVariant {
315 output('js')
316 }
317 from(layout.projectDirectory.file('inputs/marker.txt'))
318 }
319 }
320 }
321
322 tasks.register('probe') {
323 dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle'
324
325 doLast {
326 def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile
327 assert new File(bundleDir, 'base.js').exists()
328 assert new File(bundleDir, 'marker.txt').exists()
329 }
330 }
331 """);
332
333 BuildResult result = runner("probe").build();
334
335 assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS);
336 }
337
338 @Test
339 void failsOnUnknownVariantReference() throws Exception {
340 writeSettings("variant-artifacts-missing-variant");
341 writeBuildFile("""
342 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
343
344 variants.layers.create('main')
345
346 variantArtifacts {
347 variant('browser') {
348 slot('mainJs') {
349 fromVariant {
350 output('js')
351 }
352 }
353 }
354 }
355 """);
356
357 assertBuildFails("isn't declared", "help");
358 }
359
360 @Test
361 void failsOnUnknownRoleReference() throws Exception {
362 writeSettings("variant-artifacts-missing-role");
363 writeBuildFile("""
364 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
365
366 variants.layers.create('main')
367 variants.roles.create('main')
368 variants.variant('browser') {
369 role('main') {
370 layers('main')
371 }
372 }
373
374 variantArtifacts {
375 variant('browser') {
376 slot('mainJs') {
377 fromRole('test') {
378 output('js')
379 }
380 }
381 }
382 }
383 """);
384
385 assertBuildFails("Role projection for variant 'browser' and role 'test' not found", "help");
386 }
387
388 @Test
389 void failsWhenPrimarySlotIsMissingForMultipleSlots() throws Exception {
390 writeSettings("variant-artifacts-missing-primary");
391 writeBuildFile("""
392 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
393
394 variants.layers.create('main')
395 variants.roles.create('main')
396 variants.variant('browser') {
397 role('main') {
398 layers('main')
399 }
400 }
401
402 variantSources.layer('main') {
403 declareOutputs('types', 'js')
404 }
405
406 variantArtifacts {
407 variant('browser') {
408 slot('typesPackage') {
409 fromVariant {
410 output('types')
411 }
412 }
413 slot('js') {
414 fromVariant {
415 output('js')
416 }
417 }
418 }
419 }
420
421 tasks.register('probe') {
422 doLast {
423 variantArtifacts.whenAvailable { ctx ->
424 def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser')
425 ctx.findOutgoing(browser).get().primarySlot.get()
426 }
427 }
428 }
429 """);
430
431 assertBuildFails("Multiple slots declared for browser, please specify primary slot explicitly", "probe");
432 }
433
434 @Test
435 void failsOnLayerReferenceOutsideVariantTopology() throws Exception {
436 writeSettings("variant-artifacts-layer-outside-topology");
437 writeBuildFile("""
438 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
439
440 variants.layers.create('mainBase')
441 variants.layers.create('extra')
442 variants.roles.create('main')
443 variants.variant('browser') {
444 role('main') {
445 layers('mainBase')
446 }
447 }
448
449 variantArtifacts {
450 variant('browser') {
451 slot('extraJs') {
452 fromLayer('extra') {
453 output('js')
454 }
455 }
456 }
457 }
458 """);
459
460 assertBuildFails("Compile unit for variant 'browser' and layer 'extra' not found", "help");
461 }
462
463 @Test
464 void preservesPrimaryResolutionAndAllowsSecondaryArtifactSelection() throws Exception {
465 writeFile("settings.gradle", """
466 rootProject.name = 'variant-artifacts-resolution'
467 include 'producer', 'consumer'
468 """);
469 writeFile("producer/inputs/types.d.ts", "export type Foo = string\n");
470 writeFile("producer/inputs/index.js", "export const foo = 'bar'\n");
471 writeBuildFile("""
472 import org.gradle.api.attributes.Attribute
473
474 def variantAttr = Attribute.of('test.variant', String)
475 def slotAttr = Attribute.of('test.slot', String)
476
477 subprojects {
478 apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin
479 }
480
481 project(':producer') {
482 variants.layers.create('main')
483 variants.roles.create('main')
484 variants.variant('browser') {
485 role('main') {
486 layers('main')
487 }
488 }
489
490 variantSources.layer('main') {
491 declareOutputs('types', 'js')
492 registerOutput('types', layout.projectDirectory.file('inputs/types.d.ts'))
493 registerOutput('js', layout.projectDirectory.file('inputs/index.js'))
494 }
495
496 variantArtifacts {
497 variant('browser') {
498 primarySlot('typesPackage') {
499 fromVariant {
500 output('types')
501 }
502 }
503 slot('js') {
504 fromVariant {
505 output('js')
506 }
507 }
508 }
509
510 whenOutgoingConfiguration { publication ->
511 publication.configuration {
512 attributes.attribute(variantAttr, publication.variant.name)
513 }
514 }
515
516 whenOutgoingSlot { publication ->
517 publication.artifactAttributes {
518 attribute(slotAttr, publication.artifactSlot.slot.name)
519 }
520 }
521 }
522
523 }
524
525 project(':consumer') {
526 configurations {
527 compileView {
528 canBeResolved = true
529 canBeConsumed = false
530 canBeDeclared = true
531 attributes {
532 attribute(variantAttr, 'browser')
533 attribute(slotAttr, 'typesPackage')
534 }
535 }
536 }
537
538 dependencies {
539 compileView project(':producer')
540 }
541
542 tasks.register('probe') {
543 doLast {
544 def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',')
545 def jsFiles = configurations.compileView.incoming.artifactView {
546 attributes {
547 attribute(slotAttr, 'js')
548 }
549 }.files.files.collect { it.name }.sort().join(',')
550
551 println('compileFiles=' + compileFiles)
552 println('jsFiles=' + jsFiles)
553 }
554 }
555 }
556 """);
557
558 BuildResult result = runner(":consumer:probe").build();
559
560 assertTrue(result.getOutput().contains("compileFiles=typesPackage"));
561 assertTrue(result.getOutput().contains("jsFiles=js"));
562 }
563 }
@@ -1,40 +1,41
1 package org.implab.gradle.common.sources;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.GradleException;
3 import org.gradle.api.GradleException;
4 import org.gradle.api.NamedDomainObjectContainer;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.gradle.api.Plugin;
5 import org.gradle.api.Plugin;
6 import org.gradle.api.Project;
6 import org.gradle.api.Project;
7 import org.gradle.api.logging.Logger;
7 import org.gradle.api.logging.Logger;
8 import org.gradle.api.logging.Logging;
8 import org.gradle.api.logging.Logging;
9 import org.implab.gradle.variants.sources.GenericSourceSet;
9
10
10 /**
11 /**
11 * This plugin creates a {@code sources} extension which is
12 * This plugin creates a {@code sources} extension which is
12 * a container for {@link GenericSourceSet}.
13 * a container for {@link GenericSourceSet}.
13 *
14 *
14 */
15 */
15 public abstract class SourcesPlugin implements Plugin<Project> {
16 public abstract class SourcesPlugin implements Plugin<Project> {
16 private static final Logger logger = Logging.getLogger(SourcesPlugin.class);
17 private static final Logger logger = Logging.getLogger(SourcesPlugin.class);
17
18
18 private static final String SOURCES_EXTENSION_NAME = "sources";
19 private static final String SOURCES_EXTENSION_NAME = "sources";
19
20
20 @Override
21 @Override
21 public void apply(Project target) {
22 public void apply(Project target) {
22 logger.debug("Registering '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
23 logger.debug("Registering '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
23 var sources = target.getObjects().domainObjectContainer(GenericSourceSet.class);
24 var sources = target.getObjects().domainObjectContainer(GenericSourceSet.class);
24 target.getExtensions().add(SOURCES_EXTENSION_NAME, sources);
25 target.getExtensions().add(SOURCES_EXTENSION_NAME, sources);
25 }
26 }
26
27
27 public static NamedDomainObjectContainer<GenericSourceSet> getSourcesExtension(Project target) {
28 public static NamedDomainObjectContainer<GenericSourceSet> getSourcesExtension(Project target) {
28 var extensions = target.getExtensions();
29 var extensions = target.getExtensions();
29
30
30 @SuppressWarnings("unchecked")
31 @SuppressWarnings("unchecked")
31 var extension = (NamedDomainObjectContainer<GenericSourceSet>) extensions.findByName(SOURCES_EXTENSION_NAME);
32 var extension = (NamedDomainObjectContainer<GenericSourceSet>) extensions.findByName(SOURCES_EXTENSION_NAME);
32
33
33 if (extension == null) {
34 if (extension == null) {
34 logger.error("Sources extension '{}' isn't found on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
35 logger.error("Sources extension '{}' isn't found on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
35 throw new GradleException("Sources extension isn't found");
36 throw new GradleException("Sources extension isn't found");
36 }
37 }
37 logger.debug("Resolved '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
38 logger.debug("Resolved '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
38 return extension;
39 return extension;
39 }
40 }
40 }
41 }
@@ -1,112 +1,112
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.Plugin;
4 import org.gradle.api.Plugin;
5 import org.gradle.api.Project;
5 import org.gradle.api.Project;
6 import org.implab.gradle.common.core.lang.Deferred;
6 import org.implab.gradle.common.core.lang.Deferred;
7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
18 import org.implab.gradle.variants.core.Variant;
18 import org.implab.gradle.variants.core.Variant;
19 import org.implab.gradle.variants.core.VariantsExtension;
19 import org.implab.gradle.variants.core.VariantsExtension;
20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
22
22
23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
24
24
25 @Override
25 @Override
26 public void apply(Project target) {
26 public void apply(Project target) {
27 var extensions = target.getExtensions();
27 var extensions = target.getExtensions();
28 var objects = target.getObjects();
28 var objects = target.getObjects();
29 var providers = target.getProviders();
29 var providers = target.getProviders();
30 var plugins = target.getPlugins();
30 var plugins = target.getPlugins();
31 var configurations = target.getConfigurations();
31 var configurations = target.getConfigurations();
32 var tasks = target.getTasks();
32 var tasks = target.getTasks();
33 var layout = target.getLayout();
33 var layout = target.getLayout();
34
34
35 // Apply the main VariantsPlugin to ensure the core variant model is available.
35 // Apply the main VariantsPlugin to ensure the core variant model is available.
36 plugins.apply(VariantsPlugin.class);
36 plugins.apply(VariantsPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
38 // Access the VariantsExtension to configure variant sources.
38 // Access the VariantsExtension to configure variant sources.
39 var variantsExtension = extensions.getByType(VariantsExtension.class);
39 var variantsExtension = extensions.getByType(VariantsExtension.class);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
41
41
42 var deferred = new Deferred<OutgoingVariantsContext>();
42 var deferred = new Deferred<OutgoingVariantsContext>();
43
43
44 variantsExtension.whenFinalized(variants -> {
44 variantsExtension.whenFinalized(variants -> {
45
45
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 var assemblies = new ArtifactAssemblyRegistry();
47 var assemblies = new ArtifactAssemblyRegistry();
48
48
49 // wire artifact assemblies to configuration variants
49 // wire artifact assemblies to configuration variants
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 var primarySlotConvention = new SingleSlotConvention(providers);
51 var primarySlotConvention = new SingleSlotConvention(providers);
52 var materializationHandler = new MaterializationPolicyHandler(assemblies);
52 var materializationHandler = new MaterializationPolicyHandler();
53
53
54 // bind slot assemblies to outgoing variants
54 // bind slot assemblies to outgoing variants
55 outgoing.configureEach(assembliesBridge::execute);
55 outgoing.configureEach(assembliesBridge::execute);
56 // apply primary slot convention when outgoing variant has a single slot
56 // apply primary slot convention when outgoing variant has a single slot
57 outgoing.configureEach(primarySlotConvention::execute);
57 outgoing.configureEach(primarySlotConvention::execute);
58 // apply materialization policy hooks to outgoing variants
58 // apply materialization policy hooks to outgoing variants
59 outgoing.configureEach(materializationHandler::execute);
59 outgoing.configureEach(materializationHandler::execute);
60
60
61 sourcesExtension.whenFinalized(sources -> {
61 sourcesExtension.whenFinalized(sources -> {
62 var assemblyHandler = new ArtifactAssemblyHandler(
62 var assemblyHandler = new ArtifactAssemblyHandler(
63 objects,
63 objects,
64 tasks,
64 tasks,
65 assemblies,
65 assemblies,
66 sources.getCompileUnits(),
66 sources.getCompileUnits(),
67 sources.getRoleProjections(),
67 sources.getRoleProjections(),
68 sources.getSourceSets());
68 sources.getSourceSets());
69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
70
70
71 deferred.resolve(new DefaultOutgoingVariantsContext(
71 deferred.resolve(new DefaultOutgoingVariantsContext(
72 assemblies,
72 assemblies,
73 outgoing,
73 outgoing,
74 assemblyHandler,
74 assemblyHandler,
75 materializationHandler));
75 materializationHandler));
76 });
76 });
77
77
78 });
78 });
79
79
80 var variantArtifacts = new VariantArtifactsExtension() {
80 var variantArtifacts = new VariantArtifactsExtension() {
81
81
82 @Override
82 @Override
83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
84 deferred.whenResolved(context -> {
84 deferred.whenResolved(context -> {
85 var variant = objects.named(Variant.class, variantName);
85 var variant = objects.named(Variant.class, variantName);
86 context.configureVariant(variant, action);
86 context.configureVariant(variant, action);
87 });
87 });
88 }
88 }
89
89
90 @Override
90 @Override
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 deferred.whenResolved(handler -> action.execute(handler));
92 deferred.whenResolved(handler -> action.execute(handler));
93 }
93 }
94
94
95 @Override
95 @Override
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
98
98
99 }
99 }
100
100
101 @Override
101 @Override
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
104 }
104 }
105
105
106 };
106 };
107
107
108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
109
109
110 }
110 }
111
111
112 }
112 }
@@ -1,156 +1,156
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import java.util.Objects;
3 import java.util.Objects;
4 import java.util.function.Predicate;
4 import java.util.function.Predicate;
5
5
6 import org.eclipse.jdt.annotation.NonNullByDefault;
6 import org.eclipse.jdt.annotation.NonNullByDefault;
7 import org.gradle.api.Action;
7 import org.gradle.api.Action;
8 import org.gradle.api.InvalidUserDataException;
8 import org.gradle.api.InvalidUserDataException;
9 import org.gradle.api.Named;
9 import org.gradle.api.Named;
10 import org.gradle.api.Plugin;
10 import org.gradle.api.Plugin;
11 import org.gradle.api.Project;
11 import org.gradle.api.Project;
12 import org.implab.gradle.common.core.lang.Deferred;
12 import org.implab.gradle.common.core.lang.Deferred;
13 import org.implab.gradle.common.core.lang.Strings;
13 import org.implab.gradle.common.core.lang.Strings;
14 import org.implab.gradle.common.sources.GenericSourceSet;
15 import org.implab.gradle.variants.core.Layer;
14 import org.implab.gradle.variants.core.Layer;
16 import org.implab.gradle.variants.core.Variant;
15 import org.implab.gradle.variants.core.Variant;
17 import org.implab.gradle.variants.core.VariantsExtension;
16 import org.implab.gradle.variants.core.VariantsExtension;
18 import org.implab.gradle.variants.core.VariantsView;
17 import org.implab.gradle.variants.core.VariantsView;
19 import org.implab.gradle.variants.sources.CompileUnit;
18 import org.implab.gradle.variants.sources.CompileUnit;
20 import org.implab.gradle.variants.sources.CompileUnitsView;
19 import org.implab.gradle.variants.sources.CompileUnitsView;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
21 import org.implab.gradle.variants.sources.RoleProjectionsView;
21 import org.implab.gradle.variants.sources.RoleProjectionsView;
22 import org.implab.gradle.variants.sources.VariantSourcesContext;
22 import org.implab.gradle.variants.sources.VariantSourcesContext;
23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
30
30
31 @NonNullByDefault
31 @NonNullByDefault
32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
34
34
35 @Override
35 @Override
36 public void apply(Project target) {
36 public void apply(Project target) {
37 var extensions = target.getExtensions();
37 var extensions = target.getExtensions();
38
38
39 // Apply the main VariantsPlugin to ensure the core variant model is available.
39 // Apply the main VariantsPlugin to ensure the core variant model is available.
40 target.getPlugins().apply(VariantsPlugin.class);
40 target.getPlugins().apply(VariantsPlugin.class);
41 // Access the VariantsExtension to configure variant sources.
41 // Access the VariantsExtension to configure variant sources.
42 var variantsExtension = extensions.getByType(VariantsExtension.class);
42 var variantsExtension = extensions.getByType(VariantsExtension.class);
43 var objectFactory = target.getObjects();
43 var objectFactory = target.getObjects();
44
44
45 var deferred = new Deferred<VariantSourcesContext>();
45 var deferred = new Deferred<VariantSourcesContext>();
46
46
47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
49
49
50 variantsExtension.whenFinalized(variants -> {
50 variantsExtension.whenFinalized(variants -> {
51 // create variant views
51 // create variant views
52 var compileUnits = CompileUnitsView.of(variants);
52 var compileUnits = CompileUnitsView.of(variants);
53 var roleProjections = RoleProjectionsView.of(variants);
53 var roleProjections = RoleProjectionsView.of(variants);
54
54
55 // create registries
55 // create registries
56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
57 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
57 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
58
58
59 // build compile unit namer
59 // build compile unit namer
60 var compileUnitNamer = CompileUnitNamer.builder()
60 var compileUnitNamer = CompileUnitNamer.builder()
61 .addUnits(compileUnits.getUnits())
61 .addUnits(compileUnits.getUnits())
62 .nameCollisionPolicy(namingPolicy.policy())
62 .nameCollisionPolicy(namingPolicy.policy())
63 .build();
63 .build();
64
64
65 // create the context
65 // create the context
66 var context = new DefaultVariantSourcesContext(
66 var context = new DefaultVariantSourcesContext(
67 variants,
67 variants,
68 compileUnits,
68 compileUnits,
69 roleProjections,
69 roleProjections,
70 compileUnitNamer,
70 compileUnitNamer,
71 sourceSetRegistry,
71 sourceSetRegistry,
72 sourceSetConfiguration
72 sourceSetConfiguration
73 );
73 );
74 deferred.resolve(context);
74 deferred.resolve(context);
75 });
75 });
76
76
77 var variantSourcesExtension = new VariantSourcesExtension() {
77 var variantSourcesExtension = new VariantSourcesExtension() {
78 @Override
78 @Override
79 public void whenFinalized(Action<? super VariantSourcesContext> action) {
79 public void whenFinalized(Action<? super VariantSourcesContext> action) {
80 deferred.whenResolved(action::execute);
80 deferred.whenResolved(action::execute);
81 }
81 }
82
82
83 @Override
83 @Override
84 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
84 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
85 action.execute(lateConfigurationPolicy);
85 action.execute(lateConfigurationPolicy);
86 }
86 }
87
87
88 @Override
88 @Override
89 public void namingPolicy(Action<? super NamingPolicySpec> action) {
89 public void namingPolicy(Action<? super NamingPolicySpec> action) {
90 action.execute(namingPolicy);
90 action.execute(namingPolicy);
91 }
91 }
92
92
93 @Override
93 @Override
94 public void variant(String variantName, Action<? super GenericSourceSet> action) {
94 public void variant(String variantName, Action<? super GenericSourceSet> action) {
95 Strings.argumentNotNullOrBlank(variantName, "variantName");
95 Strings.argumentNotNullOrBlank(variantName, "variantName");
96 Objects.requireNonNull(action, "action can't be null");
96 Objects.requireNonNull(action, "action can't be null");
97
97
98 lateConfigurationPolicy.finalizePolicy();
98 lateConfigurationPolicy.finalizePolicy();
99
99
100 whenFinalized(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
100 whenFinalized(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
101 }
101 }
102
102
103 @Override
103 @Override
104 public void layer(String layerName, Action<? super GenericSourceSet> action) {
104 public void layer(String layerName, Action<? super GenericSourceSet> action) {
105 // protect external DSL
105 // protect external DSL
106 Strings.argumentNotNullOrBlank(layerName, "layerName");
106 Strings.argumentNotNullOrBlank(layerName, "layerName");
107 Objects.requireNonNull(action, "action can't be null");
107 Objects.requireNonNull(action, "action can't be null");
108
108
109 lateConfigurationPolicy.finalizePolicy();
109 lateConfigurationPolicy.finalizePolicy();
110
110
111 whenFinalized(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
111 whenFinalized(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
112 }
112 }
113
113
114 @Override
114 @Override
115 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
115 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
116 Strings.argumentNotNullOrBlank(layerName, "layerName");
116 Strings.argumentNotNullOrBlank(layerName, "layerName");
117 Strings.argumentNotNullOrBlank(variantName, "variantName");
117 Strings.argumentNotNullOrBlank(variantName, "variantName");
118 Objects.requireNonNull(action, "action can't be null");
118 Objects.requireNonNull(action, "action can't be null");
119
119
120 lateConfigurationPolicy.finalizePolicy();
120 lateConfigurationPolicy.finalizePolicy();
121
121
122 whenFinalized(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
122 whenFinalized(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
123 }
123 }
124 };
124 };
125
125
126 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
126 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
127
127
128 }
128 }
129
129
130 private static Layer resolveLayer(VariantsView variants, String name) {
130 private static Layer resolveLayer(VariantsView variants, String name) {
131 return variants.getLayers().stream()
131 return variants.getLayers().stream()
132 .filter(named(name))
132 .filter(named(name))
133 .findAny()
133 .findAny()
134 .orElseThrow(() -> new IllegalArgumentException("Layer '" + name + "' isn't declared"));
134 .orElseThrow(() -> new IllegalArgumentException("Layer '" + name + "' isn't declared"));
135 }
135 }
136
136
137 private static Variant resolveVariant(VariantsView variants, String name) {
137 private static Variant resolveVariant(VariantsView variants, String name) {
138 return variants.getVariants().stream()
138 return variants.getVariants().stream()
139 .filter(named(name))
139 .filter(named(name))
140 .findAny()
140 .findAny()
141 .orElseThrow(() -> new IllegalArgumentException("Variant '" + name + "' is't declared"));
141 .orElseThrow(() -> new IllegalArgumentException("Variant '" + name + "' is't declared"));
142 }
142 }
143
143
144 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
144 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
145 return ctx.getCompileUnits().findUnit(
145 return ctx.getCompileUnits().findUnit(
146 resolveVariant(ctx.getVariants(), variantName),
146 resolveVariant(ctx.getVariants(), variantName),
147 resolveLayer(ctx.getVariants(), layerName))
147 resolveLayer(ctx.getVariants(), layerName))
148 .orElseThrow(() -> new InvalidUserDataException(
148 .orElseThrow(() -> new InvalidUserDataException(
149 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
149 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
150 }
150 }
151
151
152 private static Predicate<Named> named(String name) {
152 private static Predicate<Named> named(String name) {
153 return named -> named.getName().equals(name);
153 return named -> named.getName().equals(name);
154 }
154 }
155
155
156 }
156 }
@@ -1,101 +1,102
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.NamedDomainObjectContainer;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.gradle.api.Plugin;
5 import org.gradle.api.Plugin;
6 import org.gradle.api.Project;
6 import org.gradle.api.Project;
7 import org.gradle.api.model.ObjectFactory;
7 import org.gradle.api.model.ObjectFactory;
8 import org.implab.gradle.common.core.lang.Deferred;
8 import org.implab.gradle.common.core.lang.Deferred;
9 import org.implab.gradle.internal.IdentityContainerFactory;
9 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Role;
11 import org.implab.gradle.variants.core.Role;
11 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.VariantDefinition;
13 import org.implab.gradle.variants.core.VariantDefinition;
13 import org.implab.gradle.variants.core.VariantsExtension;
14 import org.implab.gradle.variants.core.VariantsExtension;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.core.VariantsView;
15
16
16 /**
17 /**
17 * <ul>
18 * <ul>
18 * <li> {@link Variant} defines compilation semantics
19 * <li> {@link Variant} defines compilation semantics
19 * <li> {@link Layer} defines compilation partition
20 * <li> {@link Layer} defines compilation partition
20 * <li> {@link Role} defines result grouping / publication intent
21 * <li> {@link Role} defines result grouping / publication intent
21 * </ul>
22 * </ul>
22 *
23 *
23 */
24 */
24 public abstract class VariantsPlugin implements Plugin<Project> {
25 public abstract class VariantsPlugin implements Plugin<Project> {
25 @Override
26 @Override
26 public void apply(Project target) {
27 public void apply(Project target) {
27
28
28 var objectFactory = target.getObjects();
29 var objectFactory = target.getObjects();
29
30
30 var variantsExtension = new DefaultVariantsExtension(objectFactory);
31 var variantsExtension = new DefaultVariantsExtension(objectFactory);
31 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
32 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
32 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
33 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
33
34
34 }
35 }
35
36
36 static class DefaultVariantsExtension implements VariantsExtension {
37 static class DefaultVariantsExtension implements VariantsExtension {
37
38
38 private final NamedDomainObjectContainer<Layer> layers;
39 private final NamedDomainObjectContainer<Layer> layers;
39 private final NamedDomainObjectContainer<Role> roles;
40 private final NamedDomainObjectContainer<Role> roles;
40 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
41 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
41 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
42 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
42 private final ObjectFactory objectFactory;
43 private final ObjectFactory objectFactory;
43 private boolean finalized = false;
44 private boolean finalized = false;
44
45
45 public DefaultVariantsExtension(ObjectFactory objectFactory) {
46 public DefaultVariantsExtension(ObjectFactory objectFactory) {
46 this.objectFactory = objectFactory;
47 this.objectFactory = objectFactory;
47 this.layers = objectFactory.domainObjectContainer(Layer.class);
48 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
48 this.roles = objectFactory.domainObjectContainer(Role.class);
49 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
49 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
50 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
50 }
51 }
51
52
52 @Override
53 @Override
53 public NamedDomainObjectContainer<Layer> getLayers() {
54 public NamedDomainObjectContainer<Layer> getLayers() {
54 return layers;
55 return layers;
55 }
56 }
56
57
57 @Override
58 @Override
58 public NamedDomainObjectContainer<Role> getRoles() {
59 public NamedDomainObjectContainer<Role> getRoles() {
59 return roles;
60 return roles;
60 }
61 }
61
62
62 @Override
63 @Override
63 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
64 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
64 return variantDefinitions;
65 return variantDefinitions;
65 }
66 }
66
67
67 @Override
68 @Override
68 public void whenFinalized(Action<? super VariantsView> action) {
69 public void whenFinalized(Action<? super VariantsView> action) {
69 finalizedResult.whenResolved(action::execute);
70 finalizedResult.whenResolved(action::execute);
70 }
71 }
71
72
72 void finalizeExtension() {
73 void finalizeExtension() {
73 if (finalized)
74 if (finalized)
74 return;
75 return;
75
76
76 finalized = true;
77 finalized = true;
77
78
78 // freeze defined variants
79 // freeze defined variants
79 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
80 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
80
81
81 // build a snapshot
82 // build a snapshot
82 var viewBuilder = VariantsView.builder();
83 var viewBuilder = VariantsView.builder();
83
84
84 // calculate and add variants
85 // calculate and add variants
85 variantDefinitions.stream()
86 variantDefinitions.stream()
86 .map(def -> objectFactory.named(Variant.class, def.getName()))
87 .map(def -> objectFactory.named(Variant.class, def.getName()))
87 .forEach(viewBuilder::addVariant);
88 .forEach(viewBuilder::addVariant);
88 // add layers
89 // add layers
89 layers.forEach(viewBuilder::addLayer);
90 layers.forEach(viewBuilder::addLayer);
90 // add roles
91 // add roles
91 roles.forEach(viewBuilder::addRole);
92 roles.forEach(viewBuilder::addRole);
92 // add definitions
93 // add definitions
93 variantDefinitions.forEach(viewBuilder::addDefinition);
94 variantDefinitions.forEach(viewBuilder::addDefinition);
94 // assemble the view
95 // assemble the view
95 var view = viewBuilder.build();
96 var view = viewBuilder.build();
96 // set the result and call hooks
97 // set the result and call hooks
97 finalizedResult.resolve(view);
98 finalizedResult.resolve(view);
98 }
99 }
99 }
100 }
100
101
101 }
102 }
@@ -1,61 +1,40
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.Task;
5 import org.gradle.api.attributes.AttributeContainer;
4 import org.gradle.api.attributes.AttributeContainer;
6 import groovy.lang.Closure;
5 import groovy.lang.Closure;
7 import org.implab.gradle.common.core.lang.Closures;
6 import org.implab.gradle.common.core.lang.Closures;
8
7
9 /**
8 /**
10 * Materialized outgoing publication state of a single slot.
9 * Materialized outgoing publication state of a single slot.
11 *
10 *
12 * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific
11 * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific
13 * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which
12 * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which
14 * is limited to the root outgoing configuration of the variant.
13 * is limited to the root outgoing configuration of the variant.
15 */
14 */
16 public interface OutgoingConfigurationSlotSpec {
15 public interface OutgoingConfigurationSlotSpec {
17 /**
16 /**
18 * Returns the published slot identity.
17 * Returns the published slot identity.
19 *
18 *
20 * @return slot identity
19 * @return slot identity
21 */
20 */
22 ArtifactSlot getArtifactSlot();
21 ArtifactSlot getArtifactSlot();
23
22
24 /**
23 /**
25 * Returns the assembly backing the published slot.
26 *
27 * @return slot assembly
28 */
29 ArtifactAssembly getAssembly();
30
31 /**
32 * Returns whether this slot is the primary outgoing artifact set of the variant.
24 * Returns whether this slot is the primary outgoing artifact set of the variant.
33 *
25 *
34 * @return {@code true} for the primary slot
26 * @return {@code true} for the primary slot
35 */
27 */
36 boolean isPrimary();
28 boolean isPrimary();
37
29
38 /**
30 /**
39 * Configures the task producing the slot artifact.
40 *
41 * @param action task configuration action
42 */
43 default void assemblyTask(Action<? super Task> action) {
44 getAssembly().getAssemblyTask().configure(action);
45 }
46
47 default void assemblyTask(Closure<?> closure) {
48 assemblyTask(Closures.action(closure));
49 }
50
51 /**
52 * Configures attributes of this slot publication.
31 * Configures attributes of this slot publication.
53 *
32 *
54 * @param action artifact attribute configuration action
33 * @param action artifact attribute configuration action
55 */
34 */
56 void artifactAttributes(Action<? super AttributeContainer> action);
35 void artifactAttributes(Action<? super AttributeContainer> action);
57
36
58 default void artifactAttributes(Closure<?> closure) {
37 default void artifactAttributes(Closure<?> closure) {
59 artifactAttributes(Closures.action(closure));
38 artifactAttributes(Closures.action(closure));
60 }
39 }
61 }
40 }
@@ -1,118 +1,107
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 import org.gradle.api.Action;
4 import org.gradle.api.Action;
5 import org.gradle.api.artifacts.Configuration;
5 import org.gradle.api.artifacts.Configuration;
6 import org.gradle.api.attributes.AttributeContainer;
6 import org.gradle.api.attributes.AttributeContainer;
7 import org.implab.gradle.internal.ReplayableQueue;
7 import org.implab.gradle.internal.ReplayableQueue;
8 import org.implab.gradle.variants.artifacts.ArtifactAssemblies;
9 import org.implab.gradle.variants.artifacts.ArtifactAssembly;
10 import org.implab.gradle.variants.artifacts.ArtifactSlot;
8 import org.implab.gradle.variants.artifacts.ArtifactSlot;
11 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
9 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
12 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
10 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
13 import org.implab.gradle.variants.artifacts.OutgoingVariant;
11 import org.implab.gradle.variants.artifacts.OutgoingVariant;
14 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.Variant;
15
13
16 /**
14 /**
17 * Handles outgoing artifact materialization policy.
15 * Handles outgoing artifact materialization policy.
18 *
16 *
19 * <p>Materialization is the phase where the plugin interprets variant artifact
17 * <p>Materialization is the phase where the plugin interprets variant artifact
20 * declarations as Gradle outgoing publication state: a consumable configuration,
18 * declarations as Gradle outgoing publication state: a consumable configuration,
21 * its primary artifact set, secondary artifact variants, attributes, and backing
19 * its primary artifact set, secondary artifact variants, and attributes.
22 * assembly tasks.
23 *
20 *
24 * <p>The handler provides extension points for customizing the materialized
21 * <p>The handler provides extension points for customizing the materialized
25 * Gradle-facing objects. These hooks intentionally expose Gradle API objects.
22 * Gradle-facing objects. These hooks intentionally expose Gradle API objects.
26 * This allows advanced customization, but also means that callers can bypass
23 * This allows advanced customization, but also means that callers can bypass
27 * the plugin model. Such customizations are considered caller responsibility.
24 * the plugin model. Such customizations are considered caller responsibility.
28 *
25 *
29 * <p>The internal binding mechanics are not part of the public contract. The
26 * <p>The internal binding mechanics are not part of the public contract. The
30 * contract is the materialized outgoing state exposed through the specification
27 * contract is the materialized outgoing state exposed through the specification
31 * objects.
28 * objects.
32 */
29 */
33 @NonNullByDefault
30 @NonNullByDefault
34 public class MaterializationPolicyHandler implements Action<OutgoingVariant> {
31 public class MaterializationPolicyHandler implements Action<OutgoingVariant> {
35
32
36 private final ArtifactAssemblies resolver;
37
38 private final ReplayableQueue<OutgoingConfigurationSpec> variantMaterialization = new ReplayableQueue<>();
33 private final ReplayableQueue<OutgoingConfigurationSpec> variantMaterialization = new ReplayableQueue<>();
39 private final ReplayableQueue<OutgoingConfigurationSlotSpec> slotMaterialization = new ReplayableQueue<>();
34 private final ReplayableQueue<OutgoingConfigurationSlotSpec> slotMaterialization = new ReplayableQueue<>();
40
35
41 public MaterializationPolicyHandler(ArtifactAssemblies resolver) {
36 public MaterializationPolicyHandler() {
42 this.resolver = resolver;
43 }
37 }
44
38
45 @Override
39 @Override
46 public void execute(OutgoingVariant outgoingVariant) {
40 public void execute(OutgoingVariant outgoingVariant) {
47 var slots = outgoingVariant.getSlots();
41 var slots = outgoingVariant.getSlots();
48 var primarySlotProvider = outgoingVariant.getPrimarySlot();
42 var primarySlotProvider = outgoingVariant.getPrimarySlot();
49 var variant = outgoingVariant.getVariant();
43 var variant = outgoingVariant.getVariant();
50
44
51 // связываем конфигурацию
45 // связываем конфигурацию
52 outgoingVariant.configureOutgoing(configuration -> {
46 outgoingVariant.configureOutgoing(configuration -> {
53 var primarySlot = primarySlotProvider.get();
47 var primarySlot = primarySlotProvider.get();
54 var outgoing = configuration.getOutgoing();
48 var outgoing = configuration.getOutgoing();
55
49
56 variantMaterialized(variant, configuration);
50 variantMaterialized(variant, configuration);
57
51
58 slotMaterialized(new ArtifactSlot(variant, primarySlot), true, outgoing.getAttributes());
52 slotMaterialized(new ArtifactSlot(variant, primarySlot), true, outgoing.getAttributes());
59
53
60 outgoing.getVariants().configureEach(variantConfiguration -> {
54 slots.forEach(slot -> {
61 var slotName = variantConfiguration.getName();
55 if (slot.equals(primarySlot))
62 var slot = slots.findByName(slotName);
56 return;
63 if (slot != null) {
57
64 slotMaterialized(new ArtifactSlot(variant, slot), false, variantConfiguration.getAttributes());
58 var variantConfiguration = outgoing.getVariants().getByName(slot.getName());
65 }
59 slotMaterialized(new ArtifactSlot(variant, slot), false, variantConfiguration.getAttributes());
66 });
60 });
67 });
61 });
68 };
62 };
69
63
70 public void whenVariantMaterialized(Action<? super OutgoingConfigurationSpec> action) {
64 public void whenVariantMaterialized(Action<? super OutgoingConfigurationSpec> action) {
71 variantMaterialization.forEach(action::execute);
65 variantMaterialization.forEach(action::execute);
72 }
66 }
73
67
74 public void whenSlotMaterialized(Action<? super OutgoingConfigurationSlotSpec> action) {
68 public void whenSlotMaterialized(Action<? super OutgoingConfigurationSlotSpec> action) {
75 slotMaterialization.forEach(action::execute);
69 slotMaterialization.forEach(action::execute);
76 }
70 }
77
71
78 private void variantMaterialized(Variant variant, Configuration configuration) {
72 private void variantMaterialized(Variant variant, Configuration configuration) {
79 variantMaterialization.add(new OutgoingConfigurationSpec() {
73 variantMaterialization.add(new OutgoingConfigurationSpec() {
80
74
81 @Override
75 @Override
82 public Variant getVariant() {
76 public Variant getVariant() {
83 return variant;
77 return variant;
84 }
78 }
85
79
86 @Override
80 @Override
87 public Configuration getConfiguration() {
81 public Configuration getConfiguration() {
88 return configuration;
82 return configuration;
89 }
83 }
90
84
91 });
85 });
92 }
86 }
93
87
94 private void slotMaterialized(ArtifactSlot slot, boolean primary, AttributeContainer attributes) {
88 private void slotMaterialized(ArtifactSlot slot, boolean primary, AttributeContainer attributes) {
95 slotMaterialization.add(new OutgoingConfigurationSlotSpec() {
89 slotMaterialization.add(new OutgoingConfigurationSlotSpec() {
96 @Override
90 @Override
97 public boolean isPrimary() {
91 public boolean isPrimary() {
98 return primary;
92 return primary;
99 }
93 }
100
94
101 @Override
95 @Override
102 public ArtifactSlot getArtifactSlot() {
96 public ArtifactSlot getArtifactSlot() {
103 return slot;
97 return slot;
104 }
98 }
105
99
106 @Override
100 @Override
107 public ArtifactAssembly getAssembly() {
108 return resolver.require(slot);
109 }
110
111 @Override
112 public void artifactAttributes(Action<? super AttributeContainer> action) {
101 public void artifactAttributes(Action<? super AttributeContainer> action) {
113 action.execute(attributes);
102 action.execute(attributes);
114 }
103 }
115 });
104 });
116 }
105 }
117
106
118 }
107 }
@@ -1,121 +1,122
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.LinkedHashMap;
3 import java.util.LinkedHashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Optional;
5 import java.util.Optional;
6 import java.util.Set;
6 import java.util.Set;
7 import java.util.function.Consumer;
7 import java.util.function.Consumer;
8
8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.gradle.api.InvalidUserDataException;
10 import org.gradle.api.InvalidUserDataException;
11 import org.gradle.api.NamedDomainObjectContainer;
11 import org.gradle.api.NamedDomainObjectContainer;
12 import org.gradle.api.NamedDomainObjectProvider;
12 import org.gradle.api.NamedDomainObjectProvider;
13 import org.gradle.api.artifacts.Configuration;
13 import org.gradle.api.artifacts.Configuration;
14 import org.gradle.api.artifacts.ConfigurationContainer;
14 import org.gradle.api.artifacts.ConfigurationContainer;
15 import org.gradle.api.model.ObjectFactory;
15 import org.gradle.api.model.ObjectFactory;
16 import org.gradle.api.provider.Property;
16 import org.gradle.api.provider.Property;
17 import org.implab.gradle.internal.IdentityContainerFactory;
17 import org.implab.gradle.internal.ReplayableQueue;
18 import org.implab.gradle.internal.ReplayableQueue;
18 import org.implab.gradle.variants.artifacts.OutgoingVariant;
19 import org.implab.gradle.variants.artifacts.OutgoingVariant;
19 import org.implab.gradle.variants.artifacts.Slot;
20 import org.implab.gradle.variants.artifacts.Slot;
20 import org.implab.gradle.variants.core.Variant;
21 import org.implab.gradle.variants.core.Variant;
21
22
22 /**
23 /**
23 * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами
24 * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами
24 * сборки. Связь устанавливается 1:1.
25 * сборки. Связь устанавливается 1:1.
25 */
26 */
26 @NonNullByDefault
27 @NonNullByDefault
27 public class OutgoingRegistry {
28 public class OutgoingRegistry {
28 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
29 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
29 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
30 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
30 private final ConfigurationContainer configurations;
31 private final ConfigurationContainer configurations;
31 private final ObjectFactory objects;
32 private final ObjectFactory objects;
32 private final Set<Variant> declaredVariants;
33 private final Set<Variant> declaredVariants;
33
34
34 public OutgoingRegistry(
35 public OutgoingRegistry(
35 ConfigurationContainer configurations,
36 ConfigurationContainer configurations,
36 ObjectFactory objects,
37 ObjectFactory objects,
37 Set<Variant> declaredVariants) {
38 Set<Variant> declaredVariants) {
38 this.configurations = configurations;
39 this.configurations = configurations;
39 this.objects = objects;
40 this.objects = objects;
40 this.declaredVariants = declaredVariants;
41 this.declaredVariants = declaredVariants;
41 }
42 }
42
43
43 public Optional<OutgoingVariant> find(Variant variant) {
44 public Optional<OutgoingVariant> find(Variant variant) {
44 return Optional.ofNullable(outgoingByVariant.get(variant));
45 return Optional.ofNullable(outgoingByVariant.get(variant));
45 }
46 }
46
47
47 public OutgoingVariant maybeCreate(Variant variant) {
48 public OutgoingVariant maybeCreate(Variant variant) {
48 return find(variant).orElseGet(() -> create(variant));
49 return find(variant).orElseGet(() -> create(variant));
49 }
50 }
50
51
51 public OutgoingVariant create(Variant variant) {
52 public OutgoingVariant create(Variant variant) {
52 if (!declaredVariants.contains(variant))
53 if (!declaredVariants.contains(variant))
53 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
54 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
54 if (outgoingByVariant.containsKey(variant))
55 if (outgoingByVariant.containsKey(variant))
55 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
56 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
56
57
57 var configuration = configurations.consumable(outgoingConfigurationName(variant));
58 var configuration = configurations.consumable(outgoingConfigurationName(variant));
58 var outgoing = new Outgoing(variant, configuration);
59 var outgoing = new Outgoing(variant, configuration);
59
60
60 outgoingByVariant.put(variant, outgoing);
61 outgoingByVariant.put(variant, outgoing);
61
62
62 outgoingVariants.add(outgoing);
63 outgoingVariants.add(outgoing);
63
64
64 return outgoing;
65 return outgoing;
65 }
66 }
66
67
67 /**
68 /**
68 * Replayable hook which is applied when an outgoing variant is defined
69 * Replayable hook which is applied when an outgoing variant is defined
69 *
70 *
70 * @param action
71 * @param action
71 */
72 */
72 public void configureEach(Consumer<? super OutgoingVariant> action) {
73 public void configureEach(Consumer<? super OutgoingVariant> action) {
73 outgoingVariants.forEach(action);
74 outgoingVariants.forEach(action);
74 }
75 }
75
76
76 private String outgoingConfigurationName(Variant variant) {
77 private String outgoingConfigurationName(Variant variant) {
77 return variant.getName() + "Elements";
78 return variant.getName() + "Elements";
78 }
79 }
79
80
80 private class Outgoing implements OutgoingVariant {
81 private class Outgoing implements OutgoingVariant {
81
82
82 private final Variant variant;
83 private final Variant variant;
83
84
84 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
85 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
85
86
86 private final NamedDomainObjectContainer<Slot> slots;
87 private final NamedDomainObjectContainer<Slot> slots;
87
88
88 private final Property<Slot> primarySlot;
89 private final Property<Slot> primarySlot;
89
90
90 public Outgoing(
91 public Outgoing(
91 Variant variant,
92 Variant variant,
92 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
93 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
93 this.variant = variant;
94 this.variant = variant;
94 this.configurationProvider = configurationProvider;
95 this.configurationProvider = configurationProvider;
95 this.slots = objects.domainObjectContainer(Slot.class);
96 this.slots = IdentityContainerFactory.create(objects, Slot.class);
96 this.primarySlot = objects.property(Slot.class);
97 this.primarySlot = objects.property(Slot.class);
97 primarySlot.finalizeValueOnRead();
98 primarySlot.finalizeValueOnRead();
98 }
99 }
99
100
100 @Override
101 @Override
101 public Property<Slot> getPrimarySlot() {
102 public Property<Slot> getPrimarySlot() {
102 return primarySlot;
103 return primarySlot;
103 }
104 }
104
105
105 @Override
106 @Override
106 public Variant getVariant() {
107 public Variant getVariant() {
107 return variant;
108 return variant;
108 }
109 }
109
110
110 @Override
111 @Override
111 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
112 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
112 return configurationProvider;
113 return configurationProvider;
113 }
114 }
114
115
115 @Override
116 @Override
116 public NamedDomainObjectContainer<Slot> getSlots() {
117 public NamedDomainObjectContainer<Slot> getSlots() {
117 return slots;
118 return slots;
118 }
119 }
119 }
120 }
120
121
121 }
122 }
@@ -1,78 +1,78
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import java.util.HashMap;
3 import java.util.HashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Objects;
5 import java.util.Objects;
6 import java.util.Optional;
6 import java.util.Optional;
7 import java.util.Set;
7 import java.util.Set;
8 import java.util.stream.Collectors;
8 import java.util.stream.Collectors;
9
9
10 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.eclipse.jdt.annotation.NonNullByDefault;
11 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
12 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Role;
13 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
16
16
17 @NonNullByDefault
17 @NonNullByDefault
18 public final class CompileUnitsView {
18 public final class CompileUnitsView {
19
19
20 private final VariantsView variants;
20 private final VariantsView variants;
21 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
21 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
22
22
23 private CompileUnitsView(VariantsView variants) {
23 private CompileUnitsView(VariantsView variants) {
24 this.variants = variants;
24 this.variants = variants;
25 }
25 }
26
26
27 public Set<CompileUnit> getUnits() {
27 public Set<CompileUnit> getUnits() {
28 return variants.getEntries().stream()
28 return variants.getEntries().stream()
29 .map(CompileUnit::of)
29 .map(CompileUnit::of)
30 .collect(Collectors.toUnmodifiableSet());
30 .collect(Collectors.toUnmodifiableSet());
31 }
31 }
32
32
33 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
33 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
34 Objects.requireNonNull(variant, "Variant can't be null");
34 Objects.requireNonNull(variant, "Variant can't be null");
35
35
36 return unitsByVariant.computeIfAbsent(variant, key -> variants
36 return unitsByVariant.computeIfAbsent(variant, key -> variants
37 .getEntriesForVariant(variant).stream()
37 .getEntriesForVariant(variant).stream()
38 .map(CompileUnit::of)
38 .map(CompileUnit::of)
39 .collect(Collectors.toUnmodifiableSet()));
39 .collect(Collectors.toUnmodifiableSet()));
40 }
40 }
41
41
42 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
42 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
43 Objects.requireNonNull(variant, "Variant can't be null");
43 Objects.requireNonNull(variant, "Variant can't be null");
44 Objects.requireNonNull(layer, "Layer can't be null");
44 Objects.requireNonNull(layer, "Layer can't be null");
45
45
46 return getUnitsForVariant(variant).stream()
46 return getUnitsForVariant(variant).stream()
47 .filter(u -> u.layer().equals(layer))
47 .filter(u -> u.layer().equals(layer))
48 .findAny();
48 .findAny();
49 }
49 }
50
50
51 public boolean contains(Variant variant, Layer layer) {
51 public boolean contains(Variant variant, Layer layer) {
52 return findUnit(variant, layer).isPresent();
52 return findUnit(variant, layer).isPresent();
53 }
53 }
54
54
55 /**
55 /**
56 * In which logical roles this compile unit participates.
56 * In which logical roles this compile unit participates.
57 */
57 */
58
58
59 public Set<Role> getRoles(CompileUnit unit) {
59 public Set<Role> getRoles(CompileUnit unit) {
60 Objects.requireNonNull(unit, "Compile unit can't be null");
60 Objects.requireNonNull(unit, "Compile unit can't be null");
61 return variants.getEntriesForVariant(unit.variant()).stream()
61 return variants.getEntriesForVariant(unit.variant()).stream()
62 .filter(entry -> entry.layer().equals(unit.layer()))
62 .filter(entry -> entry.layer().equals(unit.layer()))
63 .map(VariantRoleLayer::role)
63 .map(VariantRoleLayer::role)
64 .collect(Collectors.toUnmodifiableSet());
64 .collect(Collectors.toUnmodifiableSet());
65 }
65 }
66
66
67 public CompileUnit requireUnit(Variant variant, Layer layer) {
67 public CompileUnit requireUnit(Variant variant, Layer layer) {
68 return findUnit(variant, layer)
68 return findUnit(variant, layer)
69 .orElseThrow(() -> new IllegalArgumentException(
69 .orElseThrow(() -> new IllegalArgumentException(
70 "Compile unit for variant '" + variant.getName()
70 "Compile unit for variant '" + variant.getName()
71 + "' and layer '" + layer.getName() + "' not found"));
71 + "' and layer '" + layer.getName() + "' not found"));
72 }
72 }
73
73
74 public static CompileUnitsView of(VariantsView variantsView) {
74 public static CompileUnitsView of(VariantsView variantsView) {
75 Objects.requireNonNull(variantsView, "variantsView can't be null");
75 Objects.requireNonNull(variantsView, "variantsView can't be null");
76 return new CompileUnitsView(variantsView);
76 return new CompileUnitsView(variantsView);
77 }
77 }
78 } No newline at end of file
78 }
@@ -1,206 +1,206
1 package org.implab.gradle.common.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import java.io.File;
3 import java.io.File;
4 import java.nio.file.Paths;
4 import java.nio.file.Paths;
5 import java.util.HashSet;
5 import java.util.HashSet;
6 import java.util.LinkedHashMap;
6 import java.util.LinkedHashMap;
7 import java.util.List;
7 import java.util.List;
8 import java.util.Map;
8 import java.util.Map;
9 import java.util.Objects;
9 import java.util.Objects;
10 import java.util.Set;
10 import java.util.Set;
11 import java.util.concurrent.Callable;
11 import java.util.concurrent.Callable;
12 import java.util.function.Function;
12 import java.util.function.Function;
13 import java.util.stream.Collectors;
13 import java.util.stream.Collectors;
14 import java.util.stream.Stream;
14 import java.util.stream.Stream;
15
15
16 import javax.inject.Inject;
16 import javax.inject.Inject;
17
17
18 import org.gradle.api.InvalidUserDataException;
18 import org.gradle.api.InvalidUserDataException;
19 import org.gradle.api.Named;
19 import org.gradle.api.Named;
20 import org.gradle.api.NamedDomainObjectContainer;
20 import org.gradle.api.NamedDomainObjectContainer;
21 import org.gradle.api.Task;
21 import org.gradle.api.Task;
22 import org.gradle.api.file.ConfigurableFileCollection;
22 import org.gradle.api.file.ConfigurableFileCollection;
23 import org.gradle.api.file.DirectoryProperty;
23 import org.gradle.api.file.DirectoryProperty;
24 import org.gradle.api.file.FileCollection;
24 import org.gradle.api.file.FileCollection;
25 import org.gradle.api.file.ProjectLayout;
25 import org.gradle.api.file.ProjectLayout;
26 import org.gradle.api.file.SourceDirectorySet;
26 import org.gradle.api.file.SourceDirectorySet;
27 import org.gradle.api.model.ObjectFactory;
27 import org.gradle.api.model.ObjectFactory;
28 import org.gradle.api.tasks.TaskProvider;
28 import org.gradle.api.tasks.TaskProvider;
29 import org.gradle.util.Configurable;
29 import org.gradle.util.Configurable;
30 import org.implab.gradle.common.core.lang.Closures;
30 import org.implab.gradle.common.core.lang.Closures;
31
31
32 import groovy.lang.Closure;
32 import groovy.lang.Closure;
33
33
34 /**
34 /**
35 * A configurable source set abstraction with named outputs.
35 * A configurable source set abstraction with named outputs.
36 *
36 *
37 * <p>
37 * <p>
38 * Each instance aggregates multiple {@link SourceDirectorySet source sets}
38 * Each instance aggregates multiple {@link SourceDirectorySet source sets}
39 * under a shared name and exposes typed outputs that must be declared up front.
39 * under a shared name and exposes typed outputs that must be declared up front.
40 * Default locations are {@code src/<name>} for sources and
40 * Default locations are {@code src/<name>} for sources and
41 * {@code build/<name>} for outputs, both of which can be customized via the
41 * {@code build/<name>} for outputs, both of which can be customized via the
42 * exposed {@link DirectoryProperty} setters.
42 * exposed {@link DirectoryProperty} setters.
43 * </p>
43 * </p>
44 *
44 *
45 * <p>
45 * <p>
46 * Outputs are grouped by names to make task wiring explicit. An output must be
46 * Outputs are grouped by names to make task wiring explicit. An output must be
47 * declared with {@link #declareOutputs(String, String...)} before files can be
47 * declared with {@link #declareOutputs(String, String...)} before files can be
48 * registered against it. Attempting to register or retrieve an undeclared
48 * registered against it. Attempting to register or retrieve an undeclared
49 * output results in
49 * output results in
50 * {@link InvalidUserDataException}.
50 * {@link InvalidUserDataException}.
51 * </p>
51 * </p>
52 */
52 */
53 public abstract class GenericSourceSet
53 public abstract class GenericSourceSet
54 implements Named, Configurable<GenericSourceSet> {
54 implements Named, Configurable<GenericSourceSet> {
55 private final String name;
55 private final String name;
56
56
57 private final NamedDomainObjectContainer<SourceDirectorySet> sourceDirectorySets;
57 private final NamedDomainObjectContainer<SourceDirectorySet> sourceDirectorySets;
58
58
59 private final Map<String, ConfigurableFileCollection> outputs;
59 private final Map<String, ConfigurableFileCollection> outputs;
60
60
61 private final FileCollection allOutputs;
61 private final FileCollection allOutputs;
62
62
63 private final FileCollection allSourceDirectories;
63 private final FileCollection allSourceDirectories;
64
64
65 private final ObjectFactory objects;
65 private final ObjectFactory objects;
66
66
67 private final Set<String> declaredOutputs = new HashSet<>();
67 private final Set<String> declaredOutputs = new HashSet<>();
68
68
69 @Inject
69 @Inject
70 public GenericSourceSet(String name, ObjectFactory objects, ProjectLayout layout) {
70 public GenericSourceSet(String name, ObjectFactory objects, ProjectLayout layout) {
71 this.name = name;
71 this.name = name;
72 this.objects = objects;
72 this.objects = objects;
73
73
74 sourceDirectorySets = objects.domainObjectContainer(
74 sourceDirectorySets = objects.domainObjectContainer(
75 SourceDirectorySet.class,
75 SourceDirectorySet.class,
76 this::createSourceDirectorySet);
76 this::createSourceDirectorySet);
77
77
78 outputs = new LinkedHashMap<>();
78 outputs = new LinkedHashMap<>();
79
79
80 allSourceDirectories = objects.fileCollection().from(sourceDirectoriesProvider());
80 allSourceDirectories = objects.fileCollection().from(sourceDirectoriesProvider());
81
81
82 allOutputs = objects.fileCollection().from(outputsProvider());
82 allOutputs = objects.fileCollection().from(outputsProvider());
83
83
84 getSourceSetDir().convention(layout
84 getSourceSetDir().convention(layout
85 .getProjectDirectory()
85 .getProjectDirectory()
86 .dir(Paths.get("src", name).toString()));
86 .dir(Paths.get("src", name).toString()));
87
87
88 getOutputsDir().convention(layout
88 getOutputsDir().convention(layout
89 .getBuildDirectory()
89 .getBuildDirectory()
90 .dir(name));
90 .dir(name));
91 }
91 }
92
92
93 @Override
93 @Override
94 public String getName() {
94 public String getName() {
95 return name;
95 return name;
96 }
96 }
97
97
98 /**
98 /**
99 * Base directory for this source set. Defaults to {@code src/<name>} under
99 * Base directory for this source set. Defaults to {@code src/<name>} under
100 * the project directory.
100 * the project directory.
101 */
101 */
102 public abstract DirectoryProperty getSourceSetDir();
102 public abstract DirectoryProperty getSourceSetDir();
103
103
104 /**
104 /**
105 * Base directory for outputs of this source set. Defaults to
105 * Base directory for outputs of this source set. Defaults to
106 * {@code build/<name>}.
106 * {@code build/<name>}.
107 */
107 */
108 public abstract DirectoryProperty getOutputsDir();
108 public abstract DirectoryProperty getOutputsDir();
109
109
110 /**
110 /**
111 * The container of {@link SourceDirectorySet} instances that belong to this
111 * The container of {@link SourceDirectorySet} instances that belong to this
112 * logical source set.
112 * logical source set.
113 */
113 */
114 public NamedDomainObjectContainer<SourceDirectorySet> getSets() {
114 public NamedDomainObjectContainer<SourceDirectorySet> getSets() {
115 return sourceDirectorySets;
115 return sourceDirectorySets;
116 }
116 }
117
117
118 /**
118 /**
119 * All registered outputs grouped across output names.
119 * All registered outputs grouped across output names.
120 */
120 */
121 public FileCollection getAllOutputs() {
121 public FileCollection getAllOutputs() {
122 return allOutputs;
122 return allOutputs;
123 }
123 }
124
124
125 /**
125 /**
126 * All source directories from every contained {@link SourceDirectorySet}.
126 * All source directories from every contained {@link SourceDirectorySet}.
127 */
127 */
128 public FileCollection getAllSourceDirectories() {
128 public FileCollection getAllSourceDirectories() {
129 return allSourceDirectories;
129 return allSourceDirectories;
130 }
130 }
131
131
132 /**
132 /**
133 * Returns the file collection for the specified output name, creating it
133 * Returns the file collection for the specified output name, creating it
134 * if necessary.
134 * if necessary.
135 *
135 *
136 * @throws InvalidUserDataException if the output was not declared
136 * @throws InvalidUserDataException if the output was not declared
137 */
137 */
138 public FileCollection output(String name) {
138 public FileCollection output(String name) {
139 return configurableOutput(name);
139 return configurableOutput(name);
140 }
140 }
141
141
142 private ConfigurableFileCollection configurableOutput(String name) {
142 private ConfigurableFileCollection configurableOutput(String name) {
143 requireDeclaredOutput(name);
143 requireDeclaredOutput(name);
144 return outputs.computeIfAbsent(name, key -> objects.fileCollection());
144 return outputs.computeIfAbsent(name, key -> objects.fileCollection());
145 }
145 }
146
146
147 /**
147 /**
148 * Declares allowed output names. Outputs must be declared before registering
148 * Declares allowed output names. Outputs must be declared before registering
149 * files under them.
149 * files under them.
150 */
150 */
151 public void declareOutputs(String name, String... extra) {
151 public void declareOutputs(String name, String... extra) {
152 Stream.concat(Stream.of(name), Stream.of(extra))
152 Stream.concat(Stream.of(name), Stream.of(extra))
153 .map(Objects::requireNonNull)
153 .map(Objects::requireNonNull)
154 .forEach(declaredOutputs::add);
154 .forEach(declaredOutputs::add);
155 }
155 }
156
156
157 /**
157 /**
158 * Registers files produced elsewhere under the given output.
158 * Registers files produced elsewhere under the given output.
159 */
159 */
160 public void registerOutput(String name, Object... files) {
160 public void registerOutput(String name, Object... files) {
161 configurableOutput(name).from(files);
161 configurableOutput(name).from(files);
162 }
162 }
163
163
164 /**
164 /**
165 * Registers output files produced by a task, using a mapper to extract the
165 * Registers output files produced by a task, using a mapper to extract the
166 * output from the task. The task will be added as a build dependency of this
166 * output from the task. The task will be added as a build dependency of this
167 * output.
167 * output.
168 */
168 */
169 public <T extends Task> void registerOutput(String name, TaskProvider<T> task,
169 public <T extends Task> void registerOutput(String name, TaskProvider<T> task,
170 Function<? super T, ?> mapper) {
170 Function<? super T, ?> mapper) {
171 configurableOutput(name).from(task.map(mapper::apply))
171 configurableOutput(name).from(task.map(mapper::apply))
172 .builtBy(task);
172 .builtBy(task);
173 }
173 }
174
174
175 /**
175 /**
176 * Applies a Groovy closure to this source set, enabling DSL-style
176 * Applies a Groovy closure to this source set, enabling DSL-style
177 * configuration.
177 * configuration.
178 */
178 */
179 @Override
179 @Override
180 public GenericSourceSet configure(Closure configure) {
180 public GenericSourceSet configure(Closure configure) {
181 Closures.apply(configure, this);
181 Closures.apply(configure, this);
182 return this;
182 return this;
183 }
183 }
184
184
185 private SourceDirectorySet createSourceDirectorySet(String name) {
185 private SourceDirectorySet createSourceDirectorySet(String name) {
186 return objects.sourceDirectorySet(name, name);
186 return objects.sourceDirectorySet(name, name);
187 }
187 }
188
188
189 private void requireDeclaredOutput(String outputName) {
189 private void requireDeclaredOutput(String outputName) {
190 if (!declaredOutputs.contains(outputName)) {
190 if (!declaredOutputs.contains(outputName)) {
191 throw new InvalidUserDataException(
191 throw new InvalidUserDataException(
192 "Output '" + outputName + "' is not declared for source set '" + name + "'");
192 "Output '" + outputName + "' is not declared for source set '" + name + "'");
193 }
193 }
194 }
194 }
195
195
196 private Callable<List<? extends FileCollection>> outputsProvider() {
196 private Callable<List<? extends FileCollection>> outputsProvider() {
197 return () -> outputs.values().stream().toList();
197 return () -> outputs.values().stream().toList();
198 }
198 }
199
199
200 private Callable<Set<File>> sourceDirectoriesProvider() {
200 private Callable<Set<File>> sourceDirectoriesProvider() {
201 return () -> sourceDirectorySets.stream()
201 return () -> sourceDirectorySets.stream()
202 .flatMap(x -> x.getSrcDirs().stream())
202 .flatMap(x -> x.getSrcDirs().stream())
203 .collect(Collectors.toSet());
203 .collect(Collectors.toSet());
204 }
204 }
205
205
206 }
206 }
@@ -1,83 +1,83
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import java.util.HashMap;
3 import java.util.HashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Objects;
5 import java.util.Objects;
6 import java.util.Optional;
6 import java.util.Optional;
7 import java.util.Set;
7 import java.util.Set;
8 import java.util.stream.Collectors;
8 import java.util.stream.Collectors;
9
9
10 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
11 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantsView;
13 import org.implab.gradle.variants.core.VariantsView;
14
14
15 public final class RoleProjectionsView {
15 public final class RoleProjectionsView {
16 private final VariantsView variants;
16 private final VariantsView variants;
17
17
18 private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>();
18 private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>();
19
19
20 private RoleProjectionsView(VariantsView variants) {
20 private RoleProjectionsView(VariantsView variants) {
21 this.variants = variants;
21 this.variants = variants;
22 }
22 }
23
23
24 public Set<RoleProjection> getProjections() {
24 public Set<RoleProjection> getProjections() {
25 return variants.getEntries().stream()
25 return variants.getEntries().stream()
26 .map(RoleProjection::of)
26 .map(RoleProjection::of)
27 .collect(Collectors.toUnmodifiableSet());
27 .collect(Collectors.toUnmodifiableSet());
28 }
28 }
29
29
30 public Set<RoleProjection> getProjectionsForVariant(Variant variant) {
30 public Set<RoleProjection> getProjectionsForVariant(Variant variant) {
31 Objects.requireNonNull(variant, "Variant can't be null");
31 Objects.requireNonNull(variant, "Variant can't be null");
32 return projectionsByVariant.computeIfAbsent(variant, key -> variants
32 return projectionsByVariant.computeIfAbsent(variant, key -> variants
33 .getEntriesForVariant(variant).stream()
33 .getEntriesForVariant(variant).stream()
34 .map(RoleProjection::of)
34 .map(RoleProjection::of)
35 .collect(Collectors.toUnmodifiableSet()));
35 .collect(Collectors.toUnmodifiableSet()));
36 }
36 }
37
37
38 public Set<RoleProjection> getProjectionsForRole(Role role) {
38 public Set<RoleProjection> getProjectionsForRole(Role role) {
39 Objects.requireNonNull(role, "Role can't be null");
39 Objects.requireNonNull(role, "Role can't be null");
40 return variants.getEntriesForRole(role).stream()
40 return variants.getEntriesForRole(role).stream()
41 .map(RoleProjection::of)
41 .map(RoleProjection::of)
42 .collect(Collectors.toUnmodifiableSet());
42 .collect(Collectors.toUnmodifiableSet());
43 }
43 }
44
44
45 public Optional<RoleProjection> findProjection(Variant variant, Role role) {
45 public Optional<RoleProjection> findProjection(Variant variant, Role role) {
46 Objects.requireNonNull(variant, "Variant can't be null");
46 Objects.requireNonNull(variant, "Variant can't be null");
47 Objects.requireNonNull(role, "Role can't be null");
47 Objects.requireNonNull(role, "Role can't be null");
48 return variants.getEntriesForVariant(variant).stream()
48 return variants.getEntriesForVariant(variant).stream()
49 .filter(entry -> entry.role().equals(role))
49 .filter(entry -> entry.role().equals(role))
50 .map(RoleProjection::of)
50 .map(RoleProjection::of)
51 .findAny();
51 .findAny();
52 }
52 }
53
53
54 public boolean contains(Variant variant, Role role) {
54 public boolean contains(Variant variant, Role role) {
55 return findProjection(variant, role).isPresent();
55 return findProjection(variant, role).isPresent();
56 }
56 }
57
57
58 public Set<CompileUnit> getUnits(RoleProjection projection) {
58 public Set<CompileUnit> getUnits(RoleProjection projection) {
59 Objects.requireNonNull(projection, "Role projection can't be null");
59 Objects.requireNonNull(projection, "Role projection can't be null");
60 return variants.getEntriesForVariant(projection.variant()).stream()
60 return variants.getEntriesForVariant(projection.variant()).stream()
61 .filter(entry -> entry.role().equals(projection.role()))
61 .filter(entry -> entry.role().equals(projection.role()))
62 .map(CompileUnit::of)
62 .map(CompileUnit::of)
63 .collect(Collectors.toUnmodifiableSet());
63 .collect(Collectors.toUnmodifiableSet());
64
64
65 }
65 }
66
66
67 public RoleProjection requireProjection(Variant variant, Role role) {
67 public RoleProjection requireProjection(Variant variant, Role role) {
68 return findProjection(variant, role)
68 return findProjection(variant, role)
69 .orElseThrow(() -> new IllegalArgumentException(
69 .orElseThrow(() -> new IllegalArgumentException(
70 "Role projection for variant '" + variant.getName()
70 "Role projection for variant '" + variant.getName()
71 + "' and role '" + role.getName() + "' not found"));
71 + "' and role '" + role.getName() + "' not found"));
72 }
72 }
73
73
74 public Set<Layer> getLayers(RoleProjection projection) {
74 public Set<Layer> getLayers(RoleProjection projection) {
75 return getUnits(projection).stream()
75 return getUnits(projection).stream()
76 .map(CompileUnit::layer)
76 .map(CompileUnit::layer)
77 .collect(java.util.stream.Collectors.toUnmodifiableSet());
77 .collect(java.util.stream.Collectors.toUnmodifiableSet());
78 }
78 }
79
79
80 public static RoleProjectionsView of(VariantsView variantsView) {
80 public static RoleProjectionsView of(VariantsView variantsView) {
81 return new RoleProjectionsView(variantsView);
81 return new RoleProjectionsView(variantsView);
82 }
82 }
83 } No newline at end of file
83 }
@@ -1,21 +1,20
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.gradle.api.NamedDomainObjectProvider;
3 import org.gradle.api.NamedDomainObjectProvider;
4 import org.implab.gradle.common.sources.GenericSourceSet;
5
4
6 /**
5 /**
7 * Materializes symbolic source set names into actual {@link GenericSourceSet}
6 * Materializes symbolic source set names into actual {@link GenericSourceSet}
8 * instances.
7 * instances.
9 *
8 *
10 * <p>Symbolic names are assigned from the finalized compile-unit model using
9 * <p>Symbolic names are assigned from the finalized compile-unit model using
11 * the selected
10 * the selected
12 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
11 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
13 */
12 */
14 public interface SourceSetMaterializer {
13 public interface SourceSetMaterializer {
15 /**
14 /**
16 * Returns a lazy provider for the source set corresponding to the compile unit.
15 * Returns a lazy provider for the source set corresponding to the compile unit.
17 *
16 *
18 * <p>The provider is stable and cached per compile unit.
17 * <p>The provider is stable and cached per compile unit.
19 */
18 */
20 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
19 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
21 }
20 }
@@ -1,73 +1,72
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.implab.gradle.common.sources.GenericSourceSet;
5 import org.implab.gradle.variants.core.Layer;
4 import org.implab.gradle.variants.core.Layer;
6 import org.implab.gradle.variants.core.Variant;
5 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.VariantsView;
6 import org.implab.gradle.variants.core.VariantsView;
8
7
9 /**
8 /**
10 * Registry of symbolic source set names produced by sources projection.
9 * Registry of symbolic source set names produced by sources projection.
11 *
10 *
12 * <p>Identity in this registry is the {@link GenericSourceSet} name assigned
11 * <p>Identity in this registry is the {@link GenericSourceSet} name assigned
13 * by the finalized
12 * by the finalized
14 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
13 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
15 */
14 */
16 public interface VariantSourcesContext {
15 public interface VariantSourcesContext {
17
16
18 /**
17 /**
19 * Finalized core model.
18 * Finalized core model.
20 */
19 */
21 VariantsView getVariants();
20 VariantsView getVariants();
22
21
23 /**
22 /**
24 * Derived compile-side view.
23 * Derived compile-side view.
25 */
24 */
26 CompileUnitsView getCompileUnits();
25 CompileUnitsView getCompileUnits();
27
26
28 /**
27 /**
29 * Derived role-side view.
28 * Derived role-side view.
30 */
29 */
31 RoleProjectionsView getRoleProjections();
30 RoleProjectionsView getRoleProjections();
32
31
33 /**
32 /**
34 * Lazy source set provider service.
33 * Lazy source set provider service.
35 */
34 */
36 SourceSetMaterializer getSourceSets();
35 SourceSetMaterializer getSourceSets();
37
36
38 /**
37 /**
39 * Configures all GenericSourceSets produced from the given layer.
38 * Configures all GenericSourceSets produced from the given layer.
40 *
39 *
41 * The action is applied:
40 * The action is applied:
42 * - to already materialized source sets of this layer
41 * - to already materialized source sets of this layer
43 * - to all future source sets of this layer
42 * - to all future source sets of this layer
44 *
43 *
45 * <p>For future source sets, selector precedence and registration order are
44 * <p>For future source sets, selector precedence and registration order are
46 * preserved by the materializer.
45 * preserved by the materializer.
47 *
46 *
48 * <p>For already materialized source sets, behavior is governed by
47 * <p>For already materialized source sets, behavior is governed by
49 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
48 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
50 * In warn/allow modes the action is applied as a late imperative step and does
49 * In warn/allow modes the action is applied as a late imperative step and does
51 * not retroactively restore selector precedence.
50 * not retroactively restore selector precedence.
52 */
51 */
53 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
52 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
54
53
55 /**
54 /**
56 * Configures all GenericSourceSets produced from the given variant.
55 * Configures all GenericSourceSets produced from the given variant.
57 *
56 *
58 * <p>Late application semantics for already materialized source sets are
57 * <p>Late application semantics for already materialized source sets are
59 * governed by
58 * governed by
60 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
59 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
61 */
60 */
62 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
61 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
63
62
64 /**
63 /**
65 * Configures the GenericSourceSet produced from the given compile unit.
64 * Configures the GenericSourceSet produced from the given compile unit.
66 *
65 *
67 * <p>Late application semantics for already materialized source sets are
66 * <p>Late application semantics for already materialized source sets are
68 * governed by
67 * governed by
69 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
68 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
70 */
69 */
71 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
70 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
72
71
73 }
72 }
@@ -1,168 +1,167
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 import org.gradle.api.Action;
4 import org.gradle.api.Action;
5 import org.implab.gradle.common.core.lang.Closures;
5 import org.implab.gradle.common.core.lang.Closures;
6 import org.implab.gradle.common.sources.GenericSourceSet;
7 import groovy.lang.Closure;
6 import groovy.lang.Closure;
8
7
9 @NonNullByDefault
8 @NonNullByDefault
10 public interface VariantSourcesExtension {
9 public interface VariantSourcesExtension {
11
10
12 /**
11 /**
13 * Selects how selector rules behave when they target an already materialized
12 * Selects how selector rules behave when they target an already materialized
14 * {@link GenericSourceSet}.
13 * {@link GenericSourceSet}.
15 *
14 *
16 * <p>This policy is single-valued:
15 * <p>This policy is single-valued:
17 * <ul>
16 * <ul>
18 * <li>it must be selected before the first selector rule is registered via
17 * <li>it must be selected before the first selector rule is registered via
19 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
20 * {@link #unit(String, String, Action)};</li>
19 * {@link #unit(String, String, Action)};</li>
21 * <li>once selected, it cannot be changed later;</li>
20 * <li>once selected, it cannot be changed later;</li>
22 * <li>the policy controls both diagnostics and late-application semantics.</li>
21 * <li>the policy controls both diagnostics and late-application semantics.</li>
23 * </ul>
22 * </ul>
24 *
23 *
25 * <p>If not selected explicitly, the default is
24 * <p>If not selected explicitly, the default is
26 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
25 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
27 */
26 */
28 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
27 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
29
28
30 default void lateConfigurationPolicy(Closure<?> closure) {
29 default void lateConfigurationPolicy(Closure<?> closure) {
31 lateConfigurationPolicy(Closures.action(closure));
30 lateConfigurationPolicy(Closures.action(closure));
32 }
31 }
33
32
34 /**
33 /**
35 * Selects how compile-unit name collisions are handled when the finalized
34 * Selects how compile-unit name collisions are handled when the finalized
36 * source context is created.
35 * source context is created.
37 *
36 *
38 * <p>This policy is single-valued:
37 * <p>This policy is single-valued:
39 * <ul>
38 * <ul>
40 * <li>it must be selected before the finalized
39 * <li>it must be selected before the finalized
41 * {@link VariantSourcesContext} becomes observable through
40 * {@link VariantSourcesContext} becomes observable through
42 * {@link #whenFinalized(Action)};</li>
41 * {@link #whenFinalized(Action)};</li>
43 * <li>once the context is being created, the policy is fixed and cannot be
42 * <li>once the context is being created, the policy is fixed and cannot be
44 * changed later;</li>
43 * changed later;</li>
45 * <li>the policy governs validation of compile-unit names produced by the
44 * <li>the policy governs validation of compile-unit names produced by the
46 * source-set materializer.</li>
45 * source-set materializer.</li>
47 * </ul>
46 * </ul>
48 *
47 *
49 * <p>If not selected explicitly, the default is
48 * <p>If not selected explicitly, the default is
50 * {@link NamingPolicySpec#failOnNameCollision()}.
49 * {@link NamingPolicySpec#failOnNameCollision()}.
51 */
50 */
52 void namingPolicy(Action<? super NamingPolicySpec> action);
51 void namingPolicy(Action<? super NamingPolicySpec> action);
53
52
54 default void namingPolicy(Closure<?> closure) {
53 default void namingPolicy(Closure<?> closure) {
55 namingPolicy(Closures.action(closure));
54 namingPolicy(Closures.action(closure));
56 }
55 }
57
56
58 /**
57 /**
59 * Registers a selector rule for all compile units of the given layer.
58 * Registers a selector rule for all compile units of the given layer.
60 *
59 *
61 * <p>Registering the first selector rule fixes the selected
60 * <p>Registering the first selector rule fixes the selected
62 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
61 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
63 * lifecycle.
62 * lifecycle.
64 */
63 */
65 void layer(String layerName, Action<? super GenericSourceSet> action);
64 void layer(String layerName, Action<? super GenericSourceSet> action);
66
65
67 default void layer(String layerName, Closure<?> closure) {
66 default void layer(String layerName, Closure<?> closure) {
68 layer(layerName, Closures.action(closure));
67 layer(layerName, Closures.action(closure));
69 }
68 }
70
69
71 /**
70 /**
72 * Registers a selector rule for all compile units of the given variant.
71 * Registers a selector rule for all compile units of the given variant.
73 *
72 *
74 * <p>Registering the first selector rule fixes the selected
73 * <p>Registering the first selector rule fixes the selected
75 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
74 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
76 * lifecycle.
75 * lifecycle.
77 */
76 */
78 void variant(String variantName, Action<? super GenericSourceSet> action);
77 void variant(String variantName, Action<? super GenericSourceSet> action);
79
78
80 default void variant(String variantName, Closure<?> closure) {
79 default void variant(String variantName, Closure<?> closure) {
81 variant(variantName, Closures.action(closure));
80 variant(variantName, Closures.action(closure));
82 }
81 }
83
82
84 /**
83 /**
85 * Registers a selector rule for one exact compile unit.
84 * Registers a selector rule for one exact compile unit.
86 *
85 *
87 * <p>Registering the first selector rule fixes the selected
86 * <p>Registering the first selector rule fixes the selected
88 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
87 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
89 * lifecycle.
88 * lifecycle.
90 */
89 */
91 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
90 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
92
91
93 default void unit(String variantName, String layerName, Closure<?> closure) {
92 default void unit(String variantName, String layerName, Closure<?> closure) {
94 unit(variantName, layerName, Closures.action(closure));
93 unit(variantName, layerName, Closures.action(closure));
95 }
94 }
96
95
97 /**
96 /**
98 * Invoked when finalized variants-derived source context becomes available.
97 * Invoked when finalized variants-derived source context becomes available.
99 *
98 *
100 * Replayable:
99 * Replayable:
101 * <ul>
100 * <ul>
102 * <li>if called before variants finalization, action is queued
101 * <li>if called before variants finalization, action is queued
103 * <li>if called after variants finalization, action is invoked immediately
102 * <li>if called after variants finalization, action is invoked immediately
104 * </ul>
103 * </ul>
105 *
104 *
106 * <p>By the time this callback becomes observable, compile-unit naming
105 * <p>By the time this callback becomes observable, compile-unit naming
107 * policy has already been fixed and symbolic source-set names for finalized
106 * policy has already been fixed and symbolic source-set names for finalized
108 * compile units are determined.
107 * compile units are determined.
109 */
108 */
110 void whenFinalized(Action<? super VariantSourcesContext> action);
109 void whenFinalized(Action<? super VariantSourcesContext> action);
111
110
112 default void whenFinalized(Closure<?> closure) {
111 default void whenFinalized(Closure<?> closure) {
113 whenFinalized(Closures.action(closure));
112 whenFinalized(Closures.action(closure));
114 }
113 }
115
114
116
115
117 /**
116 /**
118 * Imperative selector for the late-configuration mode.
117 * Imperative selector for the late-configuration mode.
119 *
118 *
120 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
119 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
121 */
120 */
122 interface LateConfigurationPolicySpec {
121 interface LateConfigurationPolicySpec {
123 /**
122 /**
124 * Rejects selector registration if it targets any already materialized
123 * Rejects selector registration if it targets any already materialized
125 * source set.
124 * source set.
126 */
125 */
127 void failOnLateConfiguration();
126 void failOnLateConfiguration();
128
127
129 /**
128 /**
130 * Allows late selector registration, but emits a warning when it targets an
129 * Allows late selector registration, but emits a warning when it targets an
131 * already materialized source set.
130 * already materialized source set.
132 *
131 *
133 * <p>For such targets, selector precedence is not re-established
132 * <p>For such targets, selector precedence is not re-established
134 * retroactively. The action is applied as a late imperative step, after the
133 * retroactively. The action is applied as a late imperative step, after the
135 * state already produced at the materialization moment.
134 * state already produced at the materialization moment.
136 */
135 */
137 void warnOnLateConfiguration();
136 void warnOnLateConfiguration();
138
137
139 /**
138 /**
140 * Allows late selector registration without a warning when it targets an
139 * Allows late selector registration without a warning when it targets an
141 * already materialized source set.
140 * already materialized source set.
142 *
141 *
143 * <p>For such targets, selector precedence is not re-established
142 * <p>For such targets, selector precedence is not re-established
144 * retroactively. The action is applied as a late imperative step, after the
143 * retroactively. The action is applied as a late imperative step, after the
145 * state already produced at the materialization moment.
144 * state already produced at the materialization moment.
146 */
145 */
147 void allowLateConfiguration();
146 void allowLateConfiguration();
148 }
147 }
149
148
150 interface NamingPolicySpec {
149 interface NamingPolicySpec {
151 /**
150 /**
152 * Rejects finalized compile-unit models that project the same source-set
151 * Rejects finalized compile-unit models that project the same source-set
153 * name for different compile units.
152 * name for different compile units.
154 */
153 */
155 void failOnNameCollision();
154 void failOnNameCollision();
156
155
157 /**
156 /**
158 * Resolves name collisions deterministically for the finalized
157 * Resolves name collisions deterministically for the finalized
159 * compile-unit model.
158 * compile-unit model.
160 *
159 *
161 * <p>Conflicting compile units are ordered canonically by
160 * <p>Conflicting compile units are ordered canonically by
162 * {@code (variant.name, layer.name)}. The first unit keeps the base
161 * {@code (variant.name, layer.name)}. The first unit keeps the base
163 * projected name, and each next unit receives a numeric suffix
162 * projected name, and each next unit receives a numeric suffix
164 * ({@code 2}, {@code 3}, ...).
163 * ({@code 2}, {@code 3}, ...).
165 */
164 */
166 void resolveNameCollision();
165 void resolveNameCollision();
167 }
166 }
168 }
167 }
@@ -1,92 +1,92
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import java.util.HashMap;
3 import java.util.HashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import org.gradle.api.Action;
5 import org.gradle.api.Action;
6 import org.gradle.api.NamedDomainObjectProvider;
6 import org.gradle.api.NamedDomainObjectProvider;
7 import org.implab.gradle.common.sources.GenericSourceSet;
8 import org.implab.gradle.variants.core.Layer;
7 import org.implab.gradle.variants.core.Layer;
9 import org.implab.gradle.variants.core.Variant;
8 import org.implab.gradle.variants.core.Variant;
10 import org.implab.gradle.variants.core.VariantsView;
9 import org.implab.gradle.variants.core.VariantsView;
11 import org.implab.gradle.variants.sources.CompileUnit;
10 import org.implab.gradle.variants.sources.CompileUnit;
12 import org.implab.gradle.variants.sources.CompileUnitsView;
11 import org.implab.gradle.variants.sources.CompileUnitsView;
12 import org.implab.gradle.variants.sources.GenericSourceSet;
13 import org.implab.gradle.variants.sources.RoleProjectionsView;
13 import org.implab.gradle.variants.sources.RoleProjectionsView;
14 import org.implab.gradle.variants.sources.SourceSetMaterializer;
14 import org.implab.gradle.variants.sources.SourceSetMaterializer;
15 import org.implab.gradle.variants.sources.VariantSourcesContext;
15 import org.implab.gradle.variants.sources.VariantSourcesContext;
16
16
17 public class DefaultVariantSourcesContext implements VariantSourcesContext {
17 public class DefaultVariantSourcesContext implements VariantSourcesContext {
18 private final VariantsView variantsView;
18 private final VariantsView variantsView;
19 private final CompileUnitsView compileUnitsView;
19 private final CompileUnitsView compileUnitsView;
20 private final RoleProjectionsView roleProjectionsView;
20 private final RoleProjectionsView roleProjectionsView;
21 private final SourceSetMaterializer sourceSetMaterializer;
21 private final SourceSetMaterializer sourceSetMaterializer;
22 private final SourceSetRegistry sourceSetRegistry;
22 private final SourceSetRegistry sourceSetRegistry;
23 private final CompileUnitNamer compileUnitNamer;
23 private final CompileUnitNamer compileUnitNamer;
24 private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry;
24 private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry;
25
25
26 public DefaultVariantSourcesContext(
26 public DefaultVariantSourcesContext(
27 VariantsView variantsView,
27 VariantsView variantsView,
28 CompileUnitsView compileUnitsView,
28 CompileUnitsView compileUnitsView,
29 RoleProjectionsView roleProjectionsView,
29 RoleProjectionsView roleProjectionsView,
30 CompileUnitNamer compileUnitNamer,
30 CompileUnitNamer compileUnitNamer,
31 SourceSetRegistry sourceSetRegistry,
31 SourceSetRegistry sourceSetRegistry,
32 SourceSetConfigurationRegistry sourceSetConfigurationRegistry) {
32 SourceSetConfigurationRegistry sourceSetConfigurationRegistry) {
33 this.variantsView = variantsView;
33 this.variantsView = variantsView;
34 this.compileUnitNamer = compileUnitNamer;
34 this.compileUnitNamer = compileUnitNamer;
35 this.compileUnitsView = compileUnitsView;
35 this.compileUnitsView = compileUnitsView;
36 this.roleProjectionsView = roleProjectionsView;
36 this.roleProjectionsView = roleProjectionsView;
37 this.sourceSetRegistry = sourceSetRegistry;
37 this.sourceSetRegistry = sourceSetRegistry;
38 this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry;
38 this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry;
39
39
40 sourceSetMaterializer = new LocalSourceSetMaterializer();
40 sourceSetMaterializer = new LocalSourceSetMaterializer();
41 }
41 }
42
42
43 @Override
43 @Override
44 public VariantsView getVariants() {
44 public VariantsView getVariants() {
45 return variantsView;
45 return variantsView;
46 }
46 }
47
47
48 @Override
48 @Override
49 public CompileUnitsView getCompileUnits() {
49 public CompileUnitsView getCompileUnits() {
50 return compileUnitsView;
50 return compileUnitsView;
51 }
51 }
52
52
53 @Override
53 @Override
54 public RoleProjectionsView getRoleProjections() {
54 public RoleProjectionsView getRoleProjections() {
55 return roleProjectionsView;
55 return roleProjectionsView;
56 }
56 }
57
57
58 @Override
58 @Override
59 public SourceSetMaterializer getSourceSets() {
59 public SourceSetMaterializer getSourceSets() {
60 return sourceSetMaterializer;
60 return sourceSetMaterializer;
61 }
61 }
62
62
63 @Override
63 @Override
64 public void configureLayer(Layer layer, Action<? super GenericSourceSet> action) {
64 public void configureLayer(Layer layer, Action<? super GenericSourceSet> action) {
65 sourceSetConfigurationRegistry.addLayerAction(layer, action);
65 sourceSetConfigurationRegistry.addLayerAction(layer, action);
66 }
66 }
67
67
68 @Override
68 @Override
69 public void configureVariant(Variant variant, Action<? super GenericSourceSet> action) {
69 public void configureVariant(Variant variant, Action<? super GenericSourceSet> action) {
70 sourceSetConfigurationRegistry.addVariantAction(variant, action);
70 sourceSetConfigurationRegistry.addVariantAction(variant, action);
71 }
71 }
72
72
73 @Override
73 @Override
74 public void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action) {
74 public void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action) {
75 sourceSetConfigurationRegistry.addCompileUnitAction(unit, action);
75 sourceSetConfigurationRegistry.addCompileUnitAction(unit, action);
76 }
76 }
77
77
78 class LocalSourceSetMaterializer implements SourceSetMaterializer {
78 class LocalSourceSetMaterializer implements SourceSetMaterializer {
79 private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>();
79 private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>();
80
80
81 @Override
81 @Override
82 public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) {
82 public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) {
83 return registeredSources.computeIfAbsent(unit, k -> {
83 return registeredSources.computeIfAbsent(unit, k -> {
84 var sourcesName = compileUnitNamer.resolveName(unit);
84 var sourcesName = compileUnitNamer.resolveName(unit);
85 sourceSetRegistry.whenMaterialized(sourcesName,
85 sourceSetRegistry.whenMaterialized(sourcesName,
86 sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet));
86 sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet));
87 return sourceSetRegistry.sourceSets().register(sourcesName);
87 return sourceSetRegistry.sourceSets().register(sourcesName);
88 });
88 });
89 }
89 }
90
90
91 }
91 }
92 }
92 }
@@ -1,114 +1,114
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import java.text.MessageFormat;
3 import java.text.MessageFormat;
4 import java.util.LinkedHashMap;
4 import java.util.LinkedHashMap;
5 import java.util.LinkedList;
5 import java.util.LinkedList;
6 import java.util.List;
6 import java.util.List;
7 import java.util.Map;
7 import java.util.Map;
8 import java.util.function.Consumer;
8 import java.util.function.Consumer;
9 import java.util.function.Supplier;
9 import java.util.function.Supplier;
10 import java.util.stream.Collectors;
10 import java.util.stream.Collectors;
11
11
12 import org.eclipse.jdt.annotation.NonNullByDefault;
12 import org.eclipse.jdt.annotation.NonNullByDefault;
13 import org.gradle.api.Action;
13 import org.gradle.api.Action;
14 import org.gradle.api.Named;
14 import org.gradle.api.Named;
15 import org.gradle.api.logging.Logger;
15 import org.gradle.api.logging.Logger;
16 import org.gradle.api.logging.Logging;
16 import org.gradle.api.logging.Logging;
17 import org.implab.gradle.common.sources.GenericSourceSet;
18 import org.implab.gradle.variants.core.Layer;
17 import org.implab.gradle.variants.core.Layer;
19 import org.implab.gradle.variants.core.Variant;
18 import org.implab.gradle.variants.core.Variant;
20 import org.implab.gradle.variants.sources.CompileUnit;
19 import org.implab.gradle.variants.sources.CompileUnit;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
21
21
22 @NonNullByDefault
22 @NonNullByDefault
23 public class SourceSetConfigurationRegistry {
23 public class SourceSetConfigurationRegistry {
24 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
24 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
25
25
26 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
26 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
27 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
27 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
28 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
28 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
29
29
30 private final Supplier<LateConfigurationMode> lateConfigurationMode;
30 private final Supplier<LateConfigurationMode> lateConfigurationMode;
31
31
32 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
32 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
33 this.lateConfigurationMode = lateConfigurationMode;
33 this.lateConfigurationMode = lateConfigurationMode;
34 }
34 }
35
35
36 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
36 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
37 addToActions(
37 addToActions(
38 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
38 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
39 action,
39 action,
40 MessageFormat.format(
40 MessageFormat.format(
41 "Source sets for [layer={0}] layer already materialized",
41 "Source sets for [layer={0}] layer already materialized",
42 layer.getName()));
42 layer.getName()));
43 }
43 }
44
44
45 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
45 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
46 addToActions(
46 addToActions(
47 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
47 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
48 action,
48 action,
49 MessageFormat.format(
49 MessageFormat.format(
50 "Source sets for [variant={0}] variant already materialized",
50 "Source sets for [variant={0}] variant already materialized",
51 variant.getName()));
51 variant.getName()));
52
52
53 }
53 }
54
54
55 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
55 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
56 addToActions(
56 addToActions(
57 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
57 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
58 action,
58 action,
59 MessageFormat.format(
59 MessageFormat.format(
60 "Source set for [variant={0}, layer={1}] already materialed",
60 "Source set for [variant={0}, layer={1}] already materialed",
61 unit.variant().getName(),
61 unit.variant().getName(),
62 unit.layer().getName()));
62 unit.layer().getName()));
63 }
63 }
64
64
65 private void addToActions(
65 private void addToActions(
66 ReplayableQueue<GenericSourceSet> actions,
66 ReplayableQueue<GenericSourceSet> actions,
67 Action<? super GenericSourceSet> action,
67 Action<? super GenericSourceSet> action,
68 String assertMessage) {
68 String assertMessage) {
69 assertLazyConfiguration(actions.values(), assertMessage);
69 assertLazyConfiguration(actions.values(), assertMessage);
70 actions.forEach(action::execute);
70 actions.forEach(action::execute);
71 }
71 }
72
72
73 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
73 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
74 if (sets.size() == 0)
74 if (sets.size() == 0)
75 return;
75 return;
76
76
77 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
77 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
78
78
79 switch (lateConfigurationMode.get()) {
79 switch (lateConfigurationMode.get()) {
80 case FAIL:
80 case FAIL:
81 throw new IllegalStateException(message + " [" + names + "]");
81 throw new IllegalStateException(message + " [" + names + "]");
82 case WARN:
82 case WARN:
83 logger.warn(message + "\n\t" + names);
83 logger.warn(message + "\n\t" + names);
84 break;
84 break;
85 default:
85 default:
86 break;
86 break;
87 }
87 }
88 }
88 }
89
89
90 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
90 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
91 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
91 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
93 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
93 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
94 }
94 }
95
95
96 class ReplayableQueue<T> {
96 class ReplayableQueue<T> {
97 private final List<Consumer<? super T>> consumers = new LinkedList<>();
97 private final List<Consumer<? super T>> consumers = new LinkedList<>();
98 private final List<T> values = new LinkedList<>();
98 private final List<T> values = new LinkedList<>();
99
99
100 public void add(T value) {
100 public void add(T value) {
101 consumers.forEach(consumer -> consumer.accept(value));
101 consumers.forEach(consumer -> consumer.accept(value));
102 values.add(value);
102 values.add(value);
103 }
103 }
104
104
105 List<T> values() {
105 List<T> values() {
106 return List.copyOf(values);
106 return List.copyOf(values);
107 }
107 }
108
108
109 public void forEach(Consumer<? super T> consumer) {
109 public void forEach(Consumer<? super T> consumer) {
110 values.forEach(consumer);
110 values.forEach(consumer);
111 consumers.add(consumer);
111 consumers.add(consumer);
112 }
112 }
113 }
113 }
114 }
114 }
@@ -1,46 +1,46
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import java.util.HashMap;
3 import java.util.HashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.function.Consumer;
5 import java.util.function.Consumer;
6
6
7 import org.gradle.api.NamedDomainObjectContainer;
7 import org.gradle.api.NamedDomainObjectContainer;
8 import org.gradle.api.model.ObjectFactory;
8 import org.gradle.api.model.ObjectFactory;
9 import org.implab.gradle.common.core.lang.Deferred;
9 import org.implab.gradle.common.core.lang.Deferred;
10 import org.implab.gradle.common.sources.GenericSourceSet;
10 import org.implab.gradle.variants.sources.GenericSourceSet;
11
11
12 public class SourceSetRegistry {
12 public class SourceSetRegistry {
13 private final Map<String, Deferred<GenericSourceSet>> materialized = new HashMap<>();
13 private final Map<String, Deferred<GenericSourceSet>> materialized = new HashMap<>();
14 private final NamedDomainObjectContainer<GenericSourceSet> sourceSets;
14 private final NamedDomainObjectContainer<GenericSourceSet> sourceSets;
15 private final ObjectFactory objectFactory;
15 private final ObjectFactory objectFactory;
16
16
17 public SourceSetRegistry(ObjectFactory objectFactory) {
17 public SourceSetRegistry(ObjectFactory objectFactory) {
18 this.objectFactory = objectFactory;
18 this.objectFactory = objectFactory;
19 this.sourceSets = objectFactory.domainObjectContainer(GenericSourceSet.class, this::createSourceSet);
19 this.sourceSets = objectFactory.domainObjectContainer(GenericSourceSet.class, this::createSourceSet);
20 }
20 }
21
21
22 void forEachMaterialized(Consumer<? super GenericSourceSet> consumer) {
22 void forEachMaterialized(Consumer<? super GenericSourceSet> consumer) {
23 materialized.values().stream()
23 materialized.values().stream()
24 .filter(Deferred::resolved)
24 .filter(Deferred::resolved)
25 .map(Deferred::value)
25 .map(Deferred::value)
26 .forEach(consumer);
26 .forEach(consumer);
27 }
27 }
28
28
29 public NamedDomainObjectContainer<GenericSourceSet> sourceSets() {
29 public NamedDomainObjectContainer<GenericSourceSet> sourceSets() {
30 return sourceSets;
30 return sourceSets;
31 }
31 }
32
32
33 public void whenMaterialized(String name, Consumer<? super GenericSourceSet> consumer) {
33 public void whenMaterialized(String name, Consumer<? super GenericSourceSet> consumer) {
34 materialized(name).whenResolved(consumer);
34 materialized(name).whenResolved(consumer);
35 }
35 }
36
36
37 private GenericSourceSet createSourceSet(String name) {
37 private GenericSourceSet createSourceSet(String name) {
38 var sourceSet = objectFactory.newInstance(GenericSourceSet.class, name);
38 var sourceSet = objectFactory.newInstance(GenericSourceSet.class, name);
39 materialized(name).resolve(sourceSet);
39 materialized(name).resolve(sourceSet);
40 return sourceSet;
40 return sourceSet;
41 }
41 }
42
42
43 private Deferred<GenericSourceSet> materialized(String name) {
43 private Deferred<GenericSourceSet> materialized(String name) {
44 return materialized.computeIfAbsent(name, k -> new Deferred<>());
44 return materialized.computeIfAbsent(name, k -> new Deferred<>());
45 }
45 }
46 }
46 }
@@ -1,1 +1,1
1 implementation-class=org.implab.gradle.common.sources.VariantArtifactsPlugin
1 implementation-class=org.implab.gradle.variants.VariantArtifactsPlugin
@@ -1,1 +1,1
1 implementation-class=org.implab.gradle.common.sources.VariantsSourcesPlugin
1 implementation-class=org.implab.gradle.variants.VariantSourcesPlugin
@@ -1,1 +1,1
1 implementation-class=org.implab.gradle.common.sources.VariantsPlugin
1 implementation-class=org.implab.gradle.variants.VariantsPlugin
@@ -1,83 +1,98
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import static org.junit.jupiter.api.Assertions.assertTrue;
3 import static org.junit.jupiter.api.Assertions.assertTrue;
4
4
5 import java.io.File;
5 import java.io.File;
6 import java.io.IOException;
6 import java.io.IOException;
7 import java.nio.file.Files;
7 import java.nio.file.Files;
8 import java.nio.file.Path;
8 import java.nio.file.Path;
9 import java.util.LinkedHashSet;
9 import java.util.LinkedHashSet;
10 import java.util.List;
10 import java.util.List;
11 import java.util.stream.Collectors;
11 import java.util.stream.Collectors;
12
12
13 import org.gradle.testkit.runner.GradleRunner;
13 import org.gradle.testkit.runner.GradleRunner;
14 import org.gradle.testkit.runner.UnexpectedBuildFailure;
14 import org.gradle.testkit.runner.UnexpectedBuildFailure;
15 import org.implab.gradle.common.sources.GenericSourceSet;
15 import org.implab.gradle.common.core.lang.Deferred;
16 import org.implab.gradle.variants.sources.GenericSourceSet;
16 import org.junit.jupiter.api.io.TempDir;
17 import org.junit.jupiter.api.io.TempDir;
17
18
18 abstract class AbstractFunctionalTest {
19 abstract class AbstractFunctionalTest {
19 private static final String SETTINGS_FILE = "settings.gradle";
20 private static final String SETTINGS_FILE = "settings.gradle";
20 private static final String BUILD_FILE = "build.gradle";
21 private static final String BUILD_FILE = "build.gradle";
21
22
22 @TempDir
23 @TempDir
23 Path testProjectDir;
24 Path testProjectDir;
24
25
25 protected void writeSettings(String rootProjectName) throws IOException {
26 protected void writeSettings(String rootProjectName) throws IOException {
26 writeFile(SETTINGS_FILE, "rootProject.name = '" + rootProjectName + "'\n");
27 writeFile(SETTINGS_FILE, "rootProject.name = '" + rootProjectName + "'\n");
27 }
28 }
28
29
29 protected void writeBuildFile(String body) throws IOException {
30 protected void writeBuildFile(String body) throws IOException {
30 writeFile(BUILD_FILE, buildscriptClasspathBlock() + "\n" + body);
31 writeFile(BUILD_FILE, buildscriptClasspathBlock() + "\n" + body);
31 }
32 }
32
33
33 protected GradleRunner runner(String... arguments) {
34 protected GradleRunner runner(String... arguments) {
34 return GradleRunner.create()
35 return GradleRunner.create()
35 .withProjectDir(testProjectDir.toFile())
36 .withProjectDir(testProjectDir.toFile())
36 .withArguments(arguments)
37 .withArguments(arguments)
37 .forwardOutput();
38 .forwardOutput();
38 }
39 }
39
40
40 protected void assertBuildFails(String expectedError, String... arguments) {
41 protected void assertBuildFails(String expectedError, String... arguments) {
41 var ex = org.junit.jupiter.api.Assertions.assertThrows(
42 var ex = org.junit.jupiter.api.Assertions.assertThrows(
42 UnexpectedBuildFailure.class,
43 UnexpectedBuildFailure.class,
43 () -> runner(arguments).build());
44 () -> runner(arguments).build());
44
45
45 var output = ex.getBuildResult().getOutput();
46 var output = ex.getBuildResult().getOutput();
46 assertTrue(output.contains(expectedError), () -> "Expected [" + expectedError + "] in output:\n" + output);
47 assertTrue(output.contains(expectedError), () -> "Expected [" + expectedError + "] in output:\n" + output);
47 }
48 }
48
49
49 private static String buildscriptClasspathBlock() {
50 private static String buildscriptClasspathBlock() {
50 var classpath = pluginClasspath().stream()
51 var classpath = pluginClasspath().stream()
51 .map(file -> "'" + file.getAbsolutePath().replace("\\", "\\\\") + "'")
52 .map(file -> "'" + file.getAbsolutePath().replace("\\", "\\\\") + "'")
52 .collect(Collectors.joining(", "));
53 .collect(Collectors.joining(", "));
53
54
54 return """
55 return """
55 buildscript {
56 buildscript {
56 dependencies {
57 dependencies {
57 classpath files(%s)
58 classpath files(%s)
58 }
59 }
59 }
60 }
60 """.formatted(classpath);
61 """.formatted(classpath);
61 }
62 }
62
63
63 private static List<File> pluginClasspath() {
64 private static List<File> pluginClasspath() {
64 try {
65 try {
65 var files = new LinkedHashSet<File>();
66 var files = new LinkedHashSet<File>();
66 files.add(codeSource(VariantSourcesPlugin.class));
67 files.add(codeSource(VariantSourcesPlugin.class));
68 files.add(resourceRoot("META-INF/gradle-plugins/org.implab.gradle-variants.properties"));
67 files.add(codeSource(GenericSourceSet.class));
69 files.add(codeSource(GenericSourceSet.class));
70 files.add(codeSource(Deferred.class));
68 return List.copyOf(files);
71 return List.copyOf(files);
69 } catch (Exception e) {
72 } catch (Exception e) {
70 throw new RuntimeException("Unable to build plugin classpath for test", e);
73 throw new RuntimeException("Unable to build plugin classpath for test", e);
71 }
74 }
72 }
75 }
73
76
74 private static File codeSource(Class<?> type) throws Exception {
77 private static File codeSource(Class<?> type) throws Exception {
75 return Path.of(type.getProtectionDomain().getCodeSource().getLocation().toURI()).toFile();
78 return Path.of(type.getProtectionDomain().getCodeSource().getLocation().toURI()).toFile();
76 }
79 }
77
80
78 private void writeFile(String relativePath, String content) throws IOException {
81 private static File resourceRoot(String resourceName) throws Exception {
82 var url = VariantSourcesPlugin.class.getClassLoader().getResource(resourceName);
83 if (url == null)
84 throw new IllegalStateException("Classpath resource isn't found: " + resourceName);
85
86 var path = Path.of(url.toURI());
87 var segments = resourceName.split("/").length;
88 for (var i = 0; i < segments; i++)
89 path = path.getParent();
90 return path.toFile();
91 }
92
93 protected void writeFile(String relativePath, String content) throws IOException {
79 Path path = testProjectDir.resolve(relativePath);
94 Path path = testProjectDir.resolve(relativePath);
80 Files.createDirectories(path.getParent());
95 Files.createDirectories(path.getParent());
81 Files.writeString(path, content);
96 Files.writeString(path, content);
82 }
97 }
83 }
98 }
@@ -1,431 +1,431
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import static org.junit.jupiter.api.Assertions.assertTrue;
3 import static org.junit.jupiter.api.Assertions.assertTrue;
4
4
5 import org.gradle.testkit.runner.BuildResult;
5 import org.gradle.testkit.runner.BuildResult;
6 import org.junit.jupiter.api.Test;
6 import org.junit.jupiter.api.Test;
7
7
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
9
9
10 @Test
10 @Test
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
12 writeSettings("variant-sources-derived-views");
12 writeSettings("variant-sources-derived-views");
13 writeBuildFile("""
13 writeBuildFile("""
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
15
15
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
17 variantsExt.layers.create('main')
17 variantsExt.layers.create('main')
18 variantsExt.layers.create('test')
18 variantsExt.layers.create('test')
19 variantsExt.roles.create('production')
19 variantsExt.roles.create('production')
20 variantsExt.roles.create('test')
20 variantsExt.roles.create('test')
21
21
22 variantsExt.variant('browser') {
22 variantsExt.variant('browser') {
23 role('production') { layers('main') }
23 role('production') { layers('main') }
24 role('test') { layers('main', 'test') }
24 role('test') { layers('main', 'test') }
25 }
25 }
26
26
27 def lines = []
27 def lines = []
28
28
29 variantSources.whenFinalized { ctx ->
29 variantSources.whenFinalized { ctx ->
30 lines << "units=" + ctx.compileUnits.units
30 lines << "units=" + ctx.compileUnits.units
31 .collect { "${it.variant().name}:${it.layer().name}" }
31 .collect { "${it.variant().name}:${it.layer().name}" }
32 .sort()
32 .sort()
33 .join(',')
33 .join(',')
34
34
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
38 def projection = ctx.roleProjections.getProjection(browser, production)
38 def projection = ctx.roleProjections.requireProjection(browser, production)
39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
40
40
41 def left = ctx.sourceSets.getSourceSet(unit)
41 def left = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
43
43
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 .collect { it.layer().name }
45 .collect { it.layer().name }
46 .sort()
46 .sort()
47 .join(',')
47 .join(',')
48 lines << "mainSourceSet=" + left.name
48 lines << "mainSourceSet=" + left.name
49 lines << "sameProvider=" + left.is(right)
49 lines << "sameProvider=" + left.is(right)
50 }
50 }
51
51
52 afterEvaluate {
52 afterEvaluate {
53 variantSources.whenFinalized { ctx ->
53 variantSources.whenFinalized { ctx ->
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
55 }
55 }
56 }
56 }
57
57
58 tasks.register('probe') {
58 tasks.register('probe') {
59 doLast {
59 doLast {
60 lines.each { println(it) }
60 lines.each { println(it) }
61 }
61 }
62 }
62 }
63 """);
63 """);
64
64
65 BuildResult result = runner("probe").build();
65 BuildResult result = runner("probe").build();
66
66
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
72 }
72 }
73
73
74 @Test
74 @Test
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
76 writeSettings("variant-sources-precedence");
76 writeSettings("variant-sources-precedence");
77 writeBuildFile("""
77 writeBuildFile("""
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
79
79
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
81 variantsExt.layers.create('main')
81 variantsExt.layers.create('main')
82 variantsExt.layers.create('test')
82 variantsExt.layers.create('test')
83 variantsExt.roles.create('production')
83 variantsExt.roles.create('production')
84 variantsExt.roles.create('test')
84 variantsExt.roles.create('test')
85
85
86 variantsExt.variant('browser') {
86 variantsExt.variant('browser') {
87 role('production') { layers('main') }
87 role('production') { layers('main') }
88 role('test') { layers('main', 'test') }
88 role('test') { layers('main', 'test') }
89 }
89 }
90
90
91 variantsExt.variant('node') {
91 variantsExt.variant('node') {
92 role('production') { layers('main') }
92 role('production') { layers('main') }
93 }
93 }
94
94
95 def events = []
95 def events = []
96
96
97 variantSources {
97 variantSources {
98 variant('browser') {
98 variant('browser') {
99 events << "variant:" + name
99 events << "variant:" + name
100 }
100 }
101 layer('main') {
101 layer('main') {
102 events << "layer:" + name
102 events << "layer:" + name
103 }
103 }
104 unit('browser', 'main') {
104 unit('browser', 'main') {
105 events << "unit:" + name
105 events << "unit:" + name
106 }
106 }
107 }
107 }
108
108
109 afterEvaluate {
109 afterEvaluate {
110 variantSources.whenFinalized { ctx ->
110 variantSources.whenFinalized { ctx ->
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
115
115
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
120
120
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
124 }
124 }
125 }
125 }
126 """);
126 """);
127
127
128 BuildResult result = runner("help").build();
128 BuildResult result = runner("help").build();
129
129
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
133 }
133 }
134
134
135 @Test
135 @Test
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
137 writeSettings("variant-sources-late-fail");
137 writeSettings("variant-sources-late-fail");
138 writeBuildFile("""
138 writeBuildFile("""
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
140
140
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
142 variantsExt.layers.create('main')
142 variantsExt.layers.create('main')
143 variantsExt.roles.create('production')
143 variantsExt.roles.create('production')
144 variantsExt.variant('browser') {
144 variantsExt.variant('browser') {
145 role('production') { layers('main') }
145 role('production') { layers('main') }
146 }
146 }
147
147
148 afterEvaluate {
148 afterEvaluate {
149 variantSources.whenFinalized { ctx ->
149 variantSources.whenFinalized { ctx ->
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
153
153
154 ctx.sourceSets.getSourceSet(unit).get()
154 ctx.sourceSets.getSourceSet(unit).get()
155 variantSources.layer('main') {
155 variantSources.layer('main') {
156 declareOutputs('late')
156 declareOutputs('late')
157 }
157 }
158 }
158 }
159 }
159 }
160 """);
160 """);
161
161
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
163 }
163 }
164
164
165 @Test
165 @Test
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
167 writeSettings("variant-sources-late-allow");
167 writeSettings("variant-sources-late-allow");
168 writeBuildFile("""
168 writeBuildFile("""
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
170
170
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
172 variantsExt.layers.create('main')
172 variantsExt.layers.create('main')
173 variantsExt.roles.create('production')
173 variantsExt.roles.create('production')
174 variantsExt.variant('browser') {
174 variantsExt.variant('browser') {
175 role('production') { layers('main') }
175 role('production') { layers('main') }
176 }
176 }
177
177
178 variantSources {
178 variantSources {
179 lateConfigurationPolicy {
179 lateConfigurationPolicy {
180 allowLateConfiguration()
180 allowLateConfiguration()
181 }
181 }
182 }
182 }
183
183
184 afterEvaluate {
184 afterEvaluate {
185 variantSources.whenFinalized { ctx ->
185 variantSources.whenFinalized { ctx ->
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
189
189
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
191 variantSources.layer('main') {
191 variantSources.layer('main') {
192 declareOutputs('late')
192 declareOutputs('late')
193 }
193 }
194 sourceSet.output('late')
194 sourceSet.output('late')
195 println('lateAllowed=ok')
195 println('lateAllowed=ok')
196 }
196 }
197 }
197 }
198 """);
198 """);
199
199
200 BuildResult result = runner("help").build();
200 BuildResult result = runner("help").build();
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
202 }
202 }
203
203
204 @Test
204 @Test
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
206 writeSettings("variant-sources-late-warn");
206 writeSettings("variant-sources-late-warn");
207 writeBuildFile("""
207 writeBuildFile("""
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
209
209
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
211 variantsExt.layers.create('main')
211 variantsExt.layers.create('main')
212 variantsExt.roles.create('production')
212 variantsExt.roles.create('production')
213 variantsExt.variant('browser') {
213 variantsExt.variant('browser') {
214 role('production') { layers('main') }
214 role('production') { layers('main') }
215 }
215 }
216
216
217 variantSources {
217 variantSources {
218 lateConfigurationPolicy {
218 lateConfigurationPolicy {
219 warnOnLateConfiguration()
219 warnOnLateConfiguration()
220 }
220 }
221 }
221 }
222
222
223 afterEvaluate {
223 afterEvaluate {
224 variantSources.whenFinalized { ctx ->
224 variantSources.whenFinalized { ctx ->
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
228
228
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
230 variantSources.layer('main') {
230 variantSources.layer('main') {
231 declareOutputs('late')
231 declareOutputs('late')
232 }
232 }
233 sourceSet.output('late')
233 sourceSet.output('late')
234 println('lateWarn=ok')
234 println('lateWarn=ok')
235 }
235 }
236 }
236 }
237 """);
237 """);
238
238
239 BuildResult result = runner("help").build();
239 BuildResult result = runner("help").build();
240
240
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
243 }
243 }
244
244
245 @Test
245 @Test
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
247 writeSettings("variant-sources-late-policy-fixed");
247 writeSettings("variant-sources-late-policy-fixed");
248 writeBuildFile("""
248 writeBuildFile("""
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
250
250
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
252 variantsExt.layers.create('main')
252 variantsExt.layers.create('main')
253 variantsExt.roles.create('production')
253 variantsExt.roles.create('production')
254 variantsExt.variant('browser') {
254 variantsExt.variant('browser') {
255 role('production') { layers('main') }
255 role('production') { layers('main') }
256 }
256 }
257
257
258 variantSources {
258 variantSources {
259 variant('browser') {
259 variant('browser') {
260 declareOutputs('js')
260 declareOutputs('js')
261 }
261 }
262 lateConfigurationPolicy {
262 lateConfigurationPolicy {
263 allowLateConfiguration()
263 allowLateConfiguration()
264 }
264 }
265 }
265 }
266 """);
266 """);
267
267
268 assertBuildFails("Lazy configuration policy already applied", "help");
268 assertBuildFails("Lazy configuration policy already applied", "help");
269 }
269 }
270
270
271 @Test
271 @Test
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
273 writeSettings("variant-sources-name-collision-fail");
273 writeSettings("variant-sources-name-collision-fail");
274 writeBuildFile("""
274 writeBuildFile("""
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
276
276
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
278 variantsExt.layers.create('variantBar')
278 variantsExt.layers.create('variantBar')
279 variantsExt.layers.create('bar')
279 variantsExt.layers.create('bar')
280 variantsExt.roles.create('production')
280 variantsExt.roles.create('production')
281
281
282 variantsExt.variant('foo') {
282 variantsExt.variant('foo') {
283 role('production') { layers('variantBar') }
283 role('production') { layers('variantBar') }
284 }
284 }
285 variantsExt.variant('fooVariant') {
285 variantsExt.variant('fooVariant') {
286 role('production') { layers('bar') }
286 role('production') { layers('bar') }
287 }
287 }
288 """);
288 """);
289
289
290 assertBuildFails("The same source set names are produced by different compile units", "help");
290 assertBuildFails("The same source set names are produced by different compile units", "help");
291 }
291 }
292
292
293 @Test
293 @Test
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
295 writeSettings("variant-sources-name-collision-resolve");
295 writeSettings("variant-sources-name-collision-resolve");
296 writeBuildFile("""
296 writeBuildFile("""
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
298
298
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
300 variantsExt.layers.create('variantBar')
300 variantsExt.layers.create('variantBar')
301 variantsExt.layers.create('bar')
301 variantsExt.layers.create('bar')
302 variantsExt.roles.create('production')
302 variantsExt.roles.create('production')
303
303
304 variantsExt.variant('foo') {
304 variantsExt.variant('foo') {
305 role('production') { layers('variantBar') }
305 role('production') { layers('variantBar') }
306 }
306 }
307 variantsExt.variant('fooVariant') {
307 variantsExt.variant('fooVariant') {
308 role('production') { layers('bar') }
308 role('production') { layers('bar') }
309 }
309 }
310
310
311 variantSources {
311 variantSources {
312 namingPolicy {
312 namingPolicy {
313 resolveNameCollision()
313 resolveNameCollision()
314 }
314 }
315 }
315 }
316
316
317 afterEvaluate {
317 afterEvaluate {
318 variantSources.whenFinalized { ctx ->
318 variantSources.whenFinalized { ctx ->
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
323
323
324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
326
326
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
329 }
329 }
330 }
330 }
331 """);
331 """);
332
332
333 BuildResult result = runner("help").build();
333 BuildResult result = runner("help").build();
334
334
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
337 }
337 }
338
338
339 @Test
339 @Test
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
341 writeSettings("variant-sources-missing-variant");
341 writeSettings("variant-sources-missing-variant");
342 writeBuildFile("""
342 writeBuildFile("""
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
344
344
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
346 variantsExt.layers.create('main')
346 variantsExt.layers.create('main')
347 variantsExt.roles.create('production')
347 variantsExt.roles.create('production')
348 variantsExt.variant('browser') {
348 variantsExt.variant('browser') {
349 role('production') { layers('main') }
349 role('production') { layers('main') }
350 }
350 }
351
351
352 variantSources {
352 variantSources {
353 variant('missing') {
353 variant('missing') {
354 declareOutputs('js')
354 declareOutputs('js')
355 }
355 }
356 }
356 }
357 """);
357 """);
358
358
359 assertBuildFails("Variant 'missing' is't declared", "help");
359 assertBuildFails("Variant 'missing' is't declared", "help");
360 }
360 }
361
361
362 @Test
362 @Test
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
364 writeSettings("variant-sources-missing-layer");
364 writeSettings("variant-sources-missing-layer");
365 writeBuildFile("""
365 writeBuildFile("""
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367
367
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 variantsExt.layers.create('main')
369 variantsExt.layers.create('main')
370 variantsExt.roles.create('production')
370 variantsExt.roles.create('production')
371 variantsExt.variant('browser') {
371 variantsExt.variant('browser') {
372 role('production') { layers('main') }
372 role('production') { layers('main') }
373 }
373 }
374
374
375 variantSources {
375 variantSources {
376 layer('missing') {
376 layer('missing') {
377 declareOutputs('js')
377 declareOutputs('js')
378 }
378 }
379 }
379 }
380 """);
380 """);
381
381
382 assertBuildFails("Layer 'missing' isn't declared", "help");
382 assertBuildFails("Layer 'missing' isn't declared", "help");
383 }
383 }
384
384
385 @Test
385 @Test
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
387 writeSettings("variant-sources-missing-unit");
387 writeSettings("variant-sources-missing-unit");
388 writeBuildFile("""
388 writeBuildFile("""
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390
390
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 variantsExt.layers.create('main')
392 variantsExt.layers.create('main')
393 variantsExt.layers.create('test')
393 variantsExt.layers.create('test')
394 variantsExt.roles.create('production')
394 variantsExt.roles.create('production')
395 variantsExt.variant('browser') {
395 variantsExt.variant('browser') {
396 role('production') { layers('main') }
396 role('production') { layers('main') }
397 }
397 }
398
398
399 variantSources {
399 variantSources {
400 unit('browser', 'test') {
400 unit('browser', 'test') {
401 declareOutputs('js')
401 declareOutputs('js')
402 }
402 }
403 }
403 }
404 """);
404 """);
405
405
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
407 }
407 }
408
408
409 @Test
409 @Test
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
411 writeSettings("variant-sources-name-policy-fixed");
411 writeSettings("variant-sources-name-policy-fixed");
412 writeBuildFile("""
412 writeBuildFile("""
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414
414
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 variantsExt.layers.create('main')
416 variantsExt.layers.create('main')
417 variantsExt.roles.create('production')
417 variantsExt.roles.create('production')
418 variantsExt.variant('browser') {
418 variantsExt.variant('browser') {
419 role('production') { layers('main') }
419 role('production') { layers('main') }
420 }
420 }
421
421
422 variantSources.whenFinalized {
422 variantSources.whenFinalized {
423 variantSources.namingPolicy {
423 variantSources.namingPolicy {
424 resolveNameCollision()
424 resolveNameCollision()
425 }
425 }
426 }
426 }
427 """);
427 """);
428
428
429 assertBuildFails("Naming policy already applied", "help");
429 assertBuildFails("Naming policy already applied", "help");
430 }
430 }
431 }
431 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
This diff has been collapsed as it changes many lines, (608 lines changed) Show them Hide them
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now