##// END OF EJS Templates
variants: refine public API boundary
cin -
r56:c41a563716ec default
parent child
Show More
@@ -0,0 +1,14
1 package org.implab.gradle.variants.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,7
1 /**
2 * Internal implementation utilities of the variants module.
3 *
4 * <p>Types in this package are not part of the public API. They may change,
5 * move, or be removed without compatibility guarantees.
6 */
7 package org.implab.gradle.variants.internal;
@@ -0,0 +1,9
1 /**
2 * Internal implementation of the variant sources plugin.
3 *
4 * <p>Types in this package are not part of the public API. They may change,
5 * move, or be removed without compatibility guarantees. Build logic should use
6 * the public contracts from {@link org.implab.gradle.variants.sources}
7 * instead.
8 */
9 package org.implab.gradle.variants.sources.internal;
@@ -1,53 +1,57
1 1 plugins {
2 2 id "java-library"
3 3 id "ivy-publish"
4 4 }
5 5
6 6 java {
7 7 withJavadocJar()
8 8 withSourcesJar()
9 9 toolchain {
10 10 languageVersion = JavaLanguageVersion.of(21)
11 11 }
12 12 }
13 13
14 14 dependencies {
15 15 compileOnly libs.jdt.annotations
16 16
17 17 api gradleApi(),
18 18 libs.bundles.jackson
19 19
20 20 implementation project(":common")
21 21
22 22 testImplementation gradleTestKit()
23 23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
24 24 testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
25 25 testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.4"
26 26 }
27 27
28 28 task printVersion{
29 29 doLast {
30 30 println "project: $project.group:$project.name:$project.version"
31 31 println "jar: ${->jar.archiveFileName.get()}"
32 32 }
33 33 }
34 34
35 35 test {
36 36 useJUnitPlatform()
37 37 }
38 38
39 javadoc {
40 exclude "**/internal/**"
41 }
42
39 43 publishing {
40 44 repositories {
41 45 ivy {
42 46 url "${System.properties["user.home"]}/ivy-repo"
43 47 }
44 48 }
45 49 publications {
46 50 ivy(IvyPublication) {
47 51 from components.java
48 52 descriptor.description {
49 53 text = providers.provider({ description })
50 54 }
51 55 }
52 56 }
53 57 }
@@ -1,112 +1,112
1 1 package org.implab.gradle.variants;
2 2
3 3 import org.gradle.api.Action;
4 4 import org.gradle.api.Plugin;
5 5 import org.gradle.api.Project;
6 6 import org.implab.gradle.common.core.lang.Deferred;
7 7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
8 8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
9 9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
10 10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
11 11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
12 12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
13 13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
14 14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
15 15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
16 16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
17 17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
18 18 import org.implab.gradle.variants.core.Variant;
19 19 import org.implab.gradle.variants.core.VariantsExtension;
20 20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
21 21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
22 22
23 23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
24 24
25 25 @Override
26 26 public void apply(Project target) {
27 27 var extensions = target.getExtensions();
28 28 var objects = target.getObjects();
29 29 var providers = target.getProviders();
30 30 var plugins = target.getPlugins();
31 31 var configurations = target.getConfigurations();
32 32 var tasks = target.getTasks();
33 33 var layout = target.getLayout();
34 34
35 35 // Apply the main VariantsPlugin to ensure the core variant model is available.
36 36 plugins.apply(VariantsPlugin.class);
37 37 plugins.apply(VariantSourcesPlugin.class);
38 38 // Access the VariantsExtension to configure variant sources.
39 39 var variantsExtension = extensions.getByType(VariantsExtension.class);
40 40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
41 41
42 42 var deferred = new Deferred<OutgoingVariantsContext>();
43 43
44 44 variantsExtension.whenFinalized(variants -> {
45 45
46 46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 47 var assemblies = new ArtifactAssemblyRegistry();
48 48
49 49 // wire artifact assemblies to configuration variants
50 50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 51 var primarySlotConvention = new SingleSlotConvention(providers);
52 52 var materializationHandler = new MaterializationPolicyHandler(objects);
53 53
54 54 // bind slot assemblies to outgoing variants
55 55 outgoing.configureEach(assembliesBridge::execute);
56 56 // apply primary slot convention when outgoing variant has a single slot
57 57 outgoing.configureEach(primarySlotConvention::execute);
58 58 // apply materialization policy hooks to outgoing variants
59 59 outgoing.configureEach(materializationHandler::execute);
60 60
61 sourcesExtension.whenFinalized(sources -> {
61 sourcesExtension.whenAvailable(sources -> {
62 62 var assemblyHandler = new ArtifactAssemblyHandler(
63 63 objects,
64 64 tasks,
65 65 assemblies,
66 66 sources.getCompileUnits(),
67 67 sources.getRoleProjections(),
68 68 sources.getSourceSets());
69 69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
70 70
71 71 deferred.resolve(new DefaultOutgoingVariantsContext(
72 72 assemblies,
73 73 outgoing,
74 74 assemblyHandler,
75 75 materializationHandler));
76 76 });
77 77
78 78 });
79 79
80 80 var variantArtifacts = new VariantArtifactsExtension() {
81 81
82 82 @Override
83 83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
84 84 deferred.whenResolved(context -> {
85 85 var variant = objects.named(Variant.class, variantName);
86 86 context.configureVariant(variant, action);
87 87 });
88 88 }
89 89
90 90 @Override
91 91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 92 deferred.whenResolved(handler -> action.execute(handler));
93 93 }
94 94
95 95 @Override
96 96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
98 98
99 99 }
100 100
101 101 @Override
102 102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
104 104 }
105 105
106 106 };
107 107
108 108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
109 109
110 110 }
111 111
112 112 }
@@ -1,157 +1,157
1 1 package org.implab.gradle.variants;
2 2
3 3 import java.util.Objects;
4 4 import java.util.function.Predicate;
5 5
6 6 import org.eclipse.jdt.annotation.NonNullByDefault;
7 7 import org.gradle.api.Action;
8 8 import org.gradle.api.InvalidUserDataException;
9 9 import org.gradle.api.Named;
10 10 import org.gradle.api.Plugin;
11 11 import org.gradle.api.Project;
12 12 import org.implab.gradle.common.core.lang.Deferred;
13 13 import org.implab.gradle.common.core.lang.Strings;
14 14 import org.implab.gradle.variants.core.Layer;
15 15 import org.implab.gradle.variants.core.Variant;
16 16 import org.implab.gradle.variants.core.VariantsExtension;
17 17 import org.implab.gradle.variants.core.VariantsView;
18 18 import org.implab.gradle.variants.sources.CompileUnit;
19 19 import org.implab.gradle.variants.sources.CompileUnitsView;
20 20 import org.implab.gradle.variants.sources.GenericSourceSet;
21 21 import org.implab.gradle.variants.sources.RoleProjectionsView;
22 22 import org.implab.gradle.variants.sources.VariantSourcesContext;
23 23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
24 24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
25 25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
26 26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
27 27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
28 28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
29 29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
30 30
31 31 @NonNullByDefault
32 32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
33 33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
34 34
35 35 @Override
36 36 public void apply(Project target) {
37 37 var extensions = target.getExtensions();
38 38
39 39 // Apply the main VariantsPlugin to ensure the core variant model is available.
40 40 target.getPlugins().apply(VariantsPlugin.class);
41 41 // Access the VariantsExtension to configure variant sources.
42 42 var variantsExtension = extensions.getByType(VariantsExtension.class);
43 43 var objectFactory = target.getObjects();
44 44
45 45 var deferred = new Deferred<VariantSourcesContext>();
46 46
47 47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
48 48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
49 49
50 50 variantsExtension.whenFinalized(variants -> {
51 51 // create variant views
52 52 var compileUnits = CompileUnitsView.of(variants);
53 53 var roleProjections = RoleProjectionsView.of(variants);
54 54
55 55 // create registries
56 56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
57 57 lateConfigurationPolicy.finalizePolicy();
58 58 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
59 59
60 60 // build compile unit namer
61 61 var compileUnitNamer = CompileUnitNamer.builder()
62 62 .addUnits(compileUnits.getUnits())
63 63 .nameCollisionPolicy(namingPolicy.policy())
64 64 .build();
65 65
66 66 // create the context
67 67 var context = new DefaultVariantSourcesContext(
68 68 variants,
69 69 compileUnits,
70 70 roleProjections,
71 71 compileUnitNamer,
72 72 sourceSetRegistry,
73 73 sourceSetConfiguration
74 74 );
75 75 deferred.resolve(context);
76 76 });
77 77
78 78 var variantSourcesExtension = new VariantSourcesExtension() {
79 79 @Override
80 public void whenFinalized(Action<? super VariantSourcesContext> action) {
80 public void whenAvailable(Action<? super VariantSourcesContext> action) {
81 81 deferred.whenResolved(action::execute);
82 82 }
83 83
84 84 @Override
85 85 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
86 86 action.execute(lateConfigurationPolicy);
87 87 }
88 88
89 89 @Override
90 90 public void namingPolicy(Action<? super NamingPolicySpec> action) {
91 91 action.execute(namingPolicy);
92 92 }
93 93
94 94 @Override
95 95 public void variant(String variantName, Action<? super GenericSourceSet> action) {
96 96 Strings.argumentNotNullOrBlank(variantName, "variantName");
97 97 Objects.requireNonNull(action, "action can't be null");
98 98
99 99 lateConfigurationPolicy.finalizePolicy();
100 100
101 whenFinalized(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
101 whenAvailable(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
102 102 }
103 103
104 104 @Override
105 105 public void layer(String layerName, Action<? super GenericSourceSet> action) {
106 106 // protect external DSL
107 107 Strings.argumentNotNullOrBlank(layerName, "layerName");
108 108 Objects.requireNonNull(action, "action can't be null");
109 109
110 110 lateConfigurationPolicy.finalizePolicy();
111 111
112 whenFinalized(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
112 whenAvailable(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
113 113 }
114 114
115 115 @Override
116 116 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
117 117 Strings.argumentNotNullOrBlank(layerName, "layerName");
118 118 Strings.argumentNotNullOrBlank(variantName, "variantName");
119 119 Objects.requireNonNull(action, "action can't be null");
120 120
121 121 lateConfigurationPolicy.finalizePolicy();
122 122
123 whenFinalized(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
123 whenAvailable(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
124 124 }
125 125 };
126 126
127 127 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
128 128
129 129 }
130 130
131 131 private static Layer resolveLayer(VariantsView variants, String name) {
132 132 return variants.getLayers().stream()
133 133 .filter(named(name))
134 134 .findAny()
135 135 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared"));
136 136 }
137 137
138 138 private static Variant resolveVariant(VariantsView variants, String name) {
139 139 return variants.getVariants().stream()
140 140 .filter(named(name))
141 141 .findAny()
142 142 .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared"));
143 143 }
144 144
145 145 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
146 146 return ctx.getCompileUnits().findUnit(
147 147 resolveVariant(ctx.getVariants(), variantName),
148 148 resolveLayer(ctx.getVariants(), layerName))
149 149 .orElseThrow(() -> new InvalidUserDataException(
150 150 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
151 151 }
152 152
153 153 private static Predicate<Named> named(String name) {
154 154 return named -> named.getName().equals(name);
155 155 }
156 156
157 157 }
@@ -1,102 +1,102
1 1 package org.implab.gradle.variants;
2 2
3 3 import org.gradle.api.Action;
4 4 import org.gradle.api.NamedDomainObjectContainer;
5 5 import org.gradle.api.Plugin;
6 6 import org.gradle.api.Project;
7 7 import org.gradle.api.model.ObjectFactory;
8 8 import org.implab.gradle.common.core.lang.Deferred;
9 import org.implab.gradle.internal.IdentityContainerFactory;
10 9 import org.implab.gradle.variants.core.Layer;
11 10 import org.implab.gradle.variants.core.Role;
12 11 import org.implab.gradle.variants.core.Variant;
13 12 import org.implab.gradle.variants.core.VariantDefinition;
14 13 import org.implab.gradle.variants.core.VariantsExtension;
15 14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.internal.IdentityContainerFactory;
16 16
17 17 /**
18 18 * <ul>
19 19 * <li> {@link Variant} defines compilation semantics
20 20 * <li> {@link Layer} defines compilation partition
21 21 * <li> {@link Role} defines result grouping / publication intent
22 22 * </ul>
23 23 *
24 24 */
25 25 public abstract class VariantsPlugin implements Plugin<Project> {
26 26 @Override
27 27 public void apply(Project target) {
28 28
29 29 var objectFactory = target.getObjects();
30 30
31 31 var variantsExtension = new DefaultVariantsExtension(objectFactory);
32 32 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
33 33 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
34 34
35 35 }
36 36
37 37 static class DefaultVariantsExtension implements VariantsExtension {
38 38
39 39 private final NamedDomainObjectContainer<Layer> layers;
40 40 private final NamedDomainObjectContainer<Role> roles;
41 41 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
42 42 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
43 43 private final ObjectFactory objectFactory;
44 44 private boolean finalized = false;
45 45
46 46 public DefaultVariantsExtension(ObjectFactory objectFactory) {
47 47 this.objectFactory = objectFactory;
48 48 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
49 49 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
50 50 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
51 51 }
52 52
53 53 @Override
54 54 public NamedDomainObjectContainer<Layer> getLayers() {
55 55 return layers;
56 56 }
57 57
58 58 @Override
59 59 public NamedDomainObjectContainer<Role> getRoles() {
60 60 return roles;
61 61 }
62 62
63 63 @Override
64 64 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
65 65 return variantDefinitions;
66 66 }
67 67
68 68 @Override
69 69 public void whenFinalized(Action<? super VariantsView> action) {
70 70 finalizedResult.whenResolved(action::execute);
71 71 }
72 72
73 73 void finalizeExtension() {
74 74 if (finalized)
75 75 return;
76 76
77 77 finalized = true;
78 78
79 79 // freeze defined variants
80 80 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
81 81
82 82 // build a snapshot
83 83 var viewBuilder = VariantsView.builder();
84 84
85 85 // calculate and add variants
86 86 variantDefinitions.stream()
87 87 .map(def -> objectFactory.named(Variant.class, def.getName()))
88 88 .forEach(viewBuilder::addVariant);
89 89 // add layers
90 90 layers.forEach(viewBuilder::addLayer);
91 91 // add roles
92 92 roles.forEach(viewBuilder::addRole);
93 93 // add definitions
94 94 variantDefinitions.forEach(viewBuilder::addDefinition);
95 95 // assemble the view
96 96 var view = viewBuilder.build();
97 97 // set the result and call hooks
98 98 finalizedResult.resolve(view);
99 99 }
100 100 }
101 101
102 102 }
@@ -1,124 +1,124
1 1 package org.implab.gradle.variants.artifacts.internal;
2 2
3 3 import java.util.LinkedHashMap;
4 4 import java.util.Map;
5 5 import java.util.Optional;
6 6 import java.util.Set;
7 7 import java.util.function.Consumer;
8 8
9 9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 10 import org.gradle.api.InvalidUserDataException;
11 11 import org.gradle.api.NamedDomainObjectContainer;
12 12 import org.gradle.api.NamedDomainObjectProvider;
13 13 import org.gradle.api.artifacts.Configuration;
14 14 import org.gradle.api.artifacts.ConfigurationContainer;
15 15 import org.gradle.api.model.ObjectFactory;
16 16 import org.gradle.api.provider.Property;
17 import org.implab.gradle.internal.IdentityContainerFactory;
18 17 import org.implab.gradle.common.core.lang.ReplayableQueue;
19 18 import org.implab.gradle.variants.artifacts.OutgoingVariant;
20 19 import org.implab.gradle.variants.artifacts.Slot;
21 20 import org.implab.gradle.variants.core.Variant;
21 import org.implab.gradle.variants.internal.IdentityContainerFactory;
22 22
23 23 /**
24 24 * Registry of variant-level outgoing models.
25 25 *
26 26 * <p>Each declared outgoing model owns one lazy Gradle consumable configuration
27 27 * and one live slot identity container.
28 28 */
29 29 @NonNullByDefault
30 30 public class OutgoingRegistry {
31 31 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
32 32 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
33 33 private final ConfigurationContainer configurations;
34 34 private final ObjectFactory objects;
35 35 private final Set<Variant> declaredVariants;
36 36
37 37 public OutgoingRegistry(
38 38 ConfigurationContainer configurations,
39 39 ObjectFactory objects,
40 40 Set<Variant> declaredVariants) {
41 41 this.configurations = configurations;
42 42 this.objects = objects;
43 43 this.declaredVariants = declaredVariants;
44 44 }
45 45
46 46 public Optional<OutgoingVariant> find(Variant variant) {
47 47 return Optional.ofNullable(outgoingByVariant.get(variant));
48 48 }
49 49
50 50 public OutgoingVariant maybeCreate(Variant variant) {
51 51 return find(variant).orElseGet(() -> create(variant));
52 52 }
53 53
54 54 public OutgoingVariant create(Variant variant) {
55 55 if (!declaredVariants.contains(variant))
56 56 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
57 57 if (outgoingByVariant.containsKey(variant))
58 58 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
59 59
60 60 var configuration = configurations.consumable(outgoingConfigurationName(variant));
61 61 var outgoing = new Outgoing(variant, configuration);
62 62
63 63 outgoingByVariant.put(variant, outgoing);
64 64
65 65 outgoingVariants.add(outgoing);
66 66
67 67 return outgoing;
68 68 }
69 69
70 70 /**
71 71 * Registers a replayable hook for outgoing variant declarations.
72 72 *
73 73 * @param action outgoing variant action
74 74 */
75 75 public void configureEach(Consumer<? super OutgoingVariant> action) {
76 76 outgoingVariants.forEach(action);
77 77 }
78 78
79 79 private String outgoingConfigurationName(Variant variant) {
80 80 return variant.getName() + "Elements";
81 81 }
82 82
83 83 private class Outgoing implements OutgoingVariant {
84 84
85 85 private final Variant variant;
86 86
87 87 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
88 88
89 89 private final NamedDomainObjectContainer<Slot> slots;
90 90
91 91 private final Property<Slot> primarySlot;
92 92
93 93 public Outgoing(
94 94 Variant variant,
95 95 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
96 96 this.variant = variant;
97 97 this.configurationProvider = configurationProvider;
98 98 this.slots = IdentityContainerFactory.create(objects, Slot.class);
99 99 this.primarySlot = objects.property(Slot.class);
100 100 primarySlot.finalizeValueOnRead();
101 101 }
102 102
103 103 @Override
104 104 public Property<Slot> getPrimarySlot() {
105 105 return primarySlot;
106 106 }
107 107
108 108 @Override
109 109 public Variant getVariant() {
110 110 return variant;
111 111 }
112 112
113 113 @Override
114 114 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
115 115 return configurationProvider;
116 116 }
117 117
118 118 @Override
119 119 public NamedDomainObjectContainer<Slot> getSlots() {
120 120 return slots;
121 121 }
122 122 }
123 123
124 124 }
@@ -1,170 +1,170
1 1 package org.implab.gradle.variants.sources;
2 2
3 3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 4 import org.gradle.api.Action;
5 5 import org.implab.gradle.common.core.lang.Closures;
6 6 import groovy.lang.Closure;
7 7
8 8 @NonNullByDefault
9 9 public interface VariantSourcesExtension {
10 10
11 11 /**
12 12 * Selects how selector rules behave when they target an already materialized
13 13 * {@link GenericSourceSet}.
14 14 *
15 15 * <p>This policy is single-valued:
16 16 * <ul>
17 17 * <li>it must be selected before the first selector rule is registered via
18 18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
19 19 * {@link #unit(String, String, Action)};</li>
20 * <li>it must be selected before the finalized context becomes observable via
21 * {@link #whenFinalized(Action)};</li>
22 * <li>once selected or once the finalized context is being created, it cannot
20 * <li>it must be selected before the source context becomes observable via
21 * {@link #whenAvailable(Action)};</li>
22 * <li>once selected or once source context creation begins, it cannot
23 23 * be changed later;</li>
24 24 * <li>the policy controls both diagnostics and late-application semantics.</li>
25 25 * </ul>
26 26 *
27 27 * <p>If not selected explicitly, the default is
28 28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
29 29 */
30 30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
31 31
32 32 default void lateConfigurationPolicy(Closure<?> closure) {
33 33 lateConfigurationPolicy(Closures.action(closure));
34 34 }
35 35
36 36 /**
37 * Selects how compile-unit name collisions are handled when the finalized
38 * source context is created.
37 * Selects how compile-unit name collisions are handled when the source
38 * context is created from the finalized variant model.
39 39 *
40 40 * <p>This policy is single-valued:
41 41 * <ul>
42 42 * <li>it must be selected before the finalized
43 43 * {@link VariantSourcesContext} becomes observable through
44 * {@link #whenFinalized(Action)};</li>
44 * {@link #whenAvailable(Action)};</li>
45 45 * <li>once the context is being created, the policy is fixed and cannot be
46 46 * changed later;</li>
47 47 * <li>the policy governs validation of compile-unit names produced by the
48 48 * source-set materializer.</li>
49 49 * </ul>
50 50 *
51 51 * <p>If not selected explicitly, the default is
52 52 * {@link NamingPolicySpec#failOnNameCollision()}.
53 53 */
54 54 void namingPolicy(Action<? super NamingPolicySpec> action);
55 55
56 56 default void namingPolicy(Closure<?> closure) {
57 57 namingPolicy(Closures.action(closure));
58 58 }
59 59
60 60 /**
61 61 * Registers a selector rule for all compile units of the given layer.
62 62 *
63 63 * <p>Registering the first selector rule fixes the selected
64 64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
65 65 * lifecycle.
66 66 */
67 67 void layer(String layerName, Action<? super GenericSourceSet> action);
68 68
69 69 default void layer(String layerName, Closure<?> closure) {
70 70 layer(layerName, Closures.action(closure));
71 71 }
72 72
73 73 /**
74 74 * Registers a selector rule for all compile units of the given variant.
75 75 *
76 76 * <p>Registering the first selector rule fixes the selected
77 77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
78 78 * lifecycle.
79 79 */
80 80 void variant(String variantName, Action<? super GenericSourceSet> action);
81 81
82 82 default void variant(String variantName, Closure<?> closure) {
83 83 variant(variantName, Closures.action(closure));
84 84 }
85 85
86 86 /**
87 87 * Registers a selector rule for one exact compile unit.
88 88 *
89 89 * <p>Registering the first selector rule fixes the selected
90 90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
91 91 * lifecycle.
92 92 */
93 93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
94 94
95 95 default void unit(String variantName, String layerName, Closure<?> closure) {
96 96 unit(variantName, layerName, Closures.action(closure));
97 97 }
98 98
99 99 /**
100 * Invoked when finalized variants-derived source context becomes available.
100 * Invoked when the variants-derived source context becomes available.
101 101 *
102 102 * Replayable:
103 103 * <ul>
104 104 * <li>if called before variants finalization, action is queued
105 105 * <li>if called after variants finalization, action is invoked immediately
106 106 * </ul>
107 107 *
108 108 * <p>By the time this callback becomes observable, compile-unit naming
109 109 * policy has already been fixed and symbolic source-set names for finalized
110 110 * compile units are determined.
111 111 */
112 void whenFinalized(Action<? super VariantSourcesContext> action);
112 void whenAvailable(Action<? super VariantSourcesContext> action);
113 113
114 default void whenFinalized(Closure<?> closure) {
115 whenFinalized(Closures.action(closure));
114 default void whenAvailable(Closure<?> closure) {
115 whenAvailable(Closures.action(closure));
116 116 }
117 117
118 118
119 119 /**
120 120 * Imperative selector for the late-configuration mode.
121 121 *
122 122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
123 123 */
124 124 interface LateConfigurationPolicySpec {
125 125 /**
126 126 * Rejects selector registration if it targets any already materialized
127 127 * source set.
128 128 */
129 129 void failOnLateConfiguration();
130 130
131 131 /**
132 132 * Allows late selector registration, but emits a warning when it targets an
133 133 * already materialized source set.
134 134 *
135 135 * <p>For such targets, selector precedence is not re-established
136 136 * retroactively. The action is applied as a late imperative step, after the
137 137 * state already produced at the materialization moment.
138 138 */
139 139 void warnOnLateConfiguration();
140 140
141 141 /**
142 142 * Allows late selector registration without a warning when it targets an
143 143 * already materialized source set.
144 144 *
145 145 * <p>For such targets, selector precedence is not re-established
146 146 * retroactively. The action is applied as a late imperative step, after the
147 147 * state already produced at the materialization moment.
148 148 */
149 149 void allowLateConfiguration();
150 150 }
151 151
152 152 interface NamingPolicySpec {
153 153 /**
154 154 * Rejects finalized compile-unit models that project the same source-set
155 155 * name for different compile units.
156 156 */
157 157 void failOnNameCollision();
158 158
159 159 /**
160 160 * Resolves name collisions deterministically for the finalized
161 161 * compile-unit model.
162 162 *
163 163 * <p>Conflicting compile units are ordered canonically by
164 164 * {@code (variant.name, layer.name)}. The first unit keeps the base
165 165 * projected name, and each next unit receives a numeric suffix
166 166 * ({@code 2}, {@code 3}, ...).
167 167 */
168 168 void resolveNameCollision();
169 169 }
170 170 }
@@ -1,454 +1,454
1 1 package org.implab.gradle.variants;
2 2
3 3 import static org.junit.jupiter.api.Assertions.assertTrue;
4 4
5 5 import org.gradle.testkit.runner.BuildResult;
6 6 import org.junit.jupiter.api.Test;
7 7
8 8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
9 9
10 10 @Test
11 11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
12 12 writeSettings("variant-sources-derived-views");
13 13 writeBuildFile("""
14 14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
15 15
16 16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
17 17 variantsExt.layers.create('main')
18 18 variantsExt.layers.create('test')
19 19 variantsExt.roles.create('production')
20 20 variantsExt.roles.create('test')
21 21
22 22 variantsExt.variant('browser') {
23 23 role('production') { layers('main') }
24 24 role('test') { layers('main', 'test') }
25 25 }
26 26
27 27 def lines = []
28 28
29 variantSources.whenFinalized { ctx ->
29 variantSources.whenAvailable { ctx ->
30 30 lines << "units=" + ctx.compileUnits.units
31 31 .collect { "${it.variant().name}:${it.layer().name}" }
32 32 .sort()
33 33 .join(',')
34 34
35 35 def browser = ctx.variants.variants.find { it.name == 'browser' }
36 36 def production = ctx.variants.roles.find { it.name == 'production' }
37 37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
38 38 def projection = ctx.roleProjections.requireProjection(browser, production)
39 39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
40 40
41 41 def left = ctx.sourceSets.getSourceSet(unit)
42 42 def right = ctx.sourceSets.getSourceSet(unit)
43 43
44 44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 45 .collect { it.layer().name }
46 46 .sort()
47 47 .join(',')
48 48 lines << "mainSourceSet=" + left.name
49 49 lines << "sameProvider=" + left.is(right)
50 50 }
51 51
52 52 afterEvaluate {
53 variantSources.whenFinalized { ctx ->
53 variantSources.whenAvailable { ctx ->
54 54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
55 55 }
56 56 }
57 57
58 58 tasks.register('probe') {
59 59 doLast {
60 60 lines.each { println(it) }
61 61 }
62 62 }
63 63 """);
64 64
65 65 BuildResult result = runner("probe").build();
66 66
67 67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
68 68 assertTrue(result.getOutput().contains("projectionUnits=main"));
69 69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
70 70 assertTrue(result.getOutput().contains("sameProvider=true"));
71 71 assertTrue(result.getOutput().contains("late:variants=browser"));
72 72 }
73 73
74 74 @Test
75 75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
76 76 writeSettings("variant-sources-precedence");
77 77 writeBuildFile("""
78 78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
79 79
80 80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
81 81 variantsExt.layers.create('main')
82 82 variantsExt.layers.create('test')
83 83 variantsExt.roles.create('production')
84 84 variantsExt.roles.create('test')
85 85
86 86 variantsExt.variant('browser') {
87 87 role('production') { layers('main') }
88 88 role('test') { layers('main', 'test') }
89 89 }
90 90
91 91 variantsExt.variant('node') {
92 92 role('production') { layers('main') }
93 93 }
94 94
95 95 def events = []
96 96
97 97 variantSources {
98 98 variant('browser') {
99 99 events << "variant:" + name
100 100 }
101 101 layer('main') {
102 102 events << "layer:" + name
103 103 }
104 104 unit('browser', 'main') {
105 105 events << "unit:" + name
106 106 }
107 107 }
108 108
109 109 afterEvaluate {
110 variantSources.whenFinalized { ctx ->
110 variantSources.whenAvailable { ctx ->
111 111 def browser = ctx.variants.variants.find { it.name == 'browser' }
112 112 def node = ctx.variants.variants.find { it.name == 'node' }
113 113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
114 114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
115 115
116 116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
117 117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
118 118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
119 119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
120 120
121 121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
122 122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
123 123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
124 124 }
125 125 }
126 126 """);
127 127
128 128 BuildResult result = runner("help").build();
129 129
130 130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
131 131 assertTrue(result.getOutput().contains("browserTest=variant"));
132 132 assertTrue(result.getOutput().contains("nodeMain=layer"));
133 133 }
134 134
135 135 @Test
136 136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
137 137 writeSettings("variant-sources-late-fail");
138 138 writeBuildFile("""
139 139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
140 140
141 141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
142 142 variantsExt.layers.create('main')
143 143 variantsExt.roles.create('production')
144 144 variantsExt.variant('browser') {
145 145 role('production') { layers('main') }
146 146 }
147 147
148 148 afterEvaluate {
149 variantSources.whenFinalized { ctx ->
149 variantSources.whenAvailable { ctx ->
150 150 def browser = ctx.variants.variants.find { it.name == 'browser' }
151 151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
152 152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
153 153
154 154 ctx.sourceSets.getSourceSet(unit).get()
155 155 variantSources.layer('main') {
156 156 declareOutputs('late')
157 157 }
158 158 }
159 159 }
160 160 """);
161 161
162 162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
163 163 }
164 164
165 165 @Test
166 166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
167 167 writeSettings("variant-sources-late-allow");
168 168 writeBuildFile("""
169 169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
170 170
171 171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
172 172 variantsExt.layers.create('main')
173 173 variantsExt.roles.create('production')
174 174 variantsExt.variant('browser') {
175 175 role('production') { layers('main') }
176 176 }
177 177
178 178 variantSources {
179 179 lateConfigurationPolicy {
180 180 allowLateConfiguration()
181 181 }
182 182 }
183 183
184 184 afterEvaluate {
185 variantSources.whenFinalized { ctx ->
185 variantSources.whenAvailable { ctx ->
186 186 def browser = ctx.variants.variants.find { it.name == 'browser' }
187 187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
188 188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
189 189
190 190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
191 191 variantSources.layer('main') {
192 192 declareOutputs('late')
193 193 }
194 194 sourceSet.output('late')
195 195 println('lateAllowed=ok')
196 196 }
197 197 }
198 198 """);
199 199
200 200 BuildResult result = runner("help").build();
201 201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
202 202 }
203 203
204 204 @Test
205 205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
206 206 writeSettings("variant-sources-late-warn");
207 207 writeBuildFile("""
208 208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
209 209
210 210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
211 211 variantsExt.layers.create('main')
212 212 variantsExt.roles.create('production')
213 213 variantsExt.variant('browser') {
214 214 role('production') { layers('main') }
215 215 }
216 216
217 217 variantSources {
218 218 lateConfigurationPolicy {
219 219 warnOnLateConfiguration()
220 220 }
221 221 }
222 222
223 223 afterEvaluate {
224 variantSources.whenFinalized { ctx ->
224 variantSources.whenAvailable { ctx ->
225 225 def browser = ctx.variants.variants.find { it.name == 'browser' }
226 226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
227 227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
228 228
229 229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
230 230 variantSources.layer('main') {
231 231 declareOutputs('late')
232 232 }
233 233 sourceSet.output('late')
234 234 println('lateWarn=ok')
235 235 }
236 236 }
237 237 """);
238 238
239 239 BuildResult result = runner("help").build();
240 240
241 241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
242 242 assertTrue(result.getOutput().contains("lateWarn=ok"));
243 243 }
244 244
245 245 @Test
246 246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
247 247 writeSettings("variant-sources-late-policy-fixed");
248 248 writeBuildFile("""
249 249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
250 250
251 251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
252 252 variantsExt.layers.create('main')
253 253 variantsExt.roles.create('production')
254 254 variantsExt.variant('browser') {
255 255 role('production') { layers('main') }
256 256 }
257 257
258 258 variantSources {
259 259 variant('browser') {
260 260 declareOutputs('js')
261 261 }
262 262 lateConfigurationPolicy {
263 263 allowLateConfiguration()
264 264 }
265 265 }
266 266 """);
267 267
268 268 assertBuildFails("Lazy configuration policy already applied", "help");
269 269 }
270 270
271 271 @Test
272 272 void failsOnProjectedNameCollisionByDefault() throws Exception {
273 273 writeSettings("variant-sources-name-collision-fail");
274 274 writeBuildFile("""
275 275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
276 276
277 277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
278 278 variantsExt.layers.create('variantBar')
279 279 variantsExt.layers.create('bar')
280 280 variantsExt.roles.create('production')
281 281
282 282 variantsExt.variant('foo') {
283 283 role('production') { layers('variantBar') }
284 284 }
285 285 variantsExt.variant('fooVariant') {
286 286 role('production') { layers('bar') }
287 287 }
288 288 """);
289 289
290 290 assertBuildFails("The same source set names are produced by different compile units", "help");
291 291 }
292 292
293 293 @Test
294 294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
295 295 writeSettings("variant-sources-name-collision-resolve");
296 296 writeBuildFile("""
297 297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
298 298
299 299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
300 300 variantsExt.layers.create('variantBar')
301 301 variantsExt.layers.create('bar')
302 302 variantsExt.roles.create('production')
303 303
304 304 variantsExt.variant('foo') {
305 305 role('production') { layers('variantBar') }
306 306 }
307 307 variantsExt.variant('fooVariant') {
308 308 role('production') { layers('bar') }
309 309 }
310 310
311 311 variantSources {
312 312 namingPolicy {
313 313 resolveNameCollision()
314 314 }
315 315 }
316 316
317 317 afterEvaluate {
318 variantSources.whenFinalized { ctx ->
318 variantSources.whenAvailable { ctx ->
319 319 def foo = ctx.variants.variants.find { it.name == 'foo' }
320 320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
321 321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
322 322 def bar = ctx.variants.layers.find { it.name == 'bar' }
323 323
324 324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
325 325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
326 326
327 327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
328 328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
329 329 }
330 330 }
331 331 """);
332 332
333 333 BuildResult result = runner("help").build();
334 334
335 335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
336 336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
337 337 }
338 338
339 339 @Test
340 340 void failsOnUnknownVariantSelectorTarget() throws Exception {
341 341 writeSettings("variant-sources-missing-variant");
342 342 writeBuildFile("""
343 343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
344 344
345 345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
346 346 variantsExt.layers.create('main')
347 347 variantsExt.roles.create('production')
348 348 variantsExt.variant('browser') {
349 349 role('production') { layers('main') }
350 350 }
351 351
352 352 variantSources {
353 353 variant('missing') {
354 354 declareOutputs('js')
355 355 }
356 356 }
357 357 """);
358 358
359 359 assertBuildFails("Variant 'missing' isn't declared", "help");
360 360 }
361 361
362 362 @Test
363 363 void failsOnUnknownLayerSelectorTarget() throws Exception {
364 364 writeSettings("variant-sources-missing-layer");
365 365 writeBuildFile("""
366 366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367 367
368 368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 369 variantsExt.layers.create('main')
370 370 variantsExt.roles.create('production')
371 371 variantsExt.variant('browser') {
372 372 role('production') { layers('main') }
373 373 }
374 374
375 375 variantSources {
376 376 layer('missing') {
377 377 declareOutputs('js')
378 378 }
379 379 }
380 380 """);
381 381
382 382 assertBuildFails("Layer 'missing' isn't declared", "help");
383 383 }
384 384
385 385 @Test
386 386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
387 387 writeSettings("variant-sources-missing-unit");
388 388 writeBuildFile("""
389 389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390 390
391 391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 392 variantsExt.layers.create('main')
393 393 variantsExt.layers.create('test')
394 394 variantsExt.roles.create('production')
395 395 variantsExt.variant('browser') {
396 396 role('production') { layers('main') }
397 397 }
398 398
399 399 variantSources {
400 400 unit('browser', 'test') {
401 401 declareOutputs('js')
402 402 }
403 403 }
404 404 """);
405 405
406 406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
407 407 }
408 408
409 409 @Test
410 410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
411 411 writeSettings("variant-sources-name-policy-fixed");
412 412 writeBuildFile("""
413 413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414 414
415 415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 416 variantsExt.layers.create('main')
417 417 variantsExt.roles.create('production')
418 418 variantsExt.variant('browser') {
419 419 role('production') { layers('main') }
420 420 }
421 421
422 variantSources.whenFinalized {
422 variantSources.whenAvailable {
423 423 variantSources.namingPolicy {
424 424 resolveNameCollision()
425 425 }
426 426 }
427 427 """);
428 428
429 429 assertBuildFails("Naming policy already applied", "help");
430 430 }
431 431
432 432 @Test
433 433 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
434 434 writeSettings("variant-sources-late-policy-context-fixed");
435 435 writeBuildFile("""
436 436 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
437 437
438 438 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
439 439 variantsExt.layers.create('main')
440 440 variantsExt.roles.create('production')
441 441 variantsExt.variant('browser') {
442 442 role('production') { layers('main') }
443 443 }
444 444
445 variantSources.whenFinalized {
445 variantSources.whenAvailable {
446 446 variantSources.lateConfigurationPolicy {
447 447 allowLateConfiguration()
448 448 }
449 449 }
450 450 """);
451 451
452 452 assertBuildFails("Lazy configuration policy already applied", "help");
453 453 }
454 454 }
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now