##// END OF EJS Templates
variants: enforce source configuration policies
cin -
r54:3469331e3c4b default
parent child
Show More
@@ -1,156 +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 lateConfigurationPolicy.finalizePolicy();
57 58 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
58 59
59 60 // build compile unit namer
60 61 var compileUnitNamer = CompileUnitNamer.builder()
61 62 .addUnits(compileUnits.getUnits())
62 63 .nameCollisionPolicy(namingPolicy.policy())
63 64 .build();
64 65
65 66 // create the context
66 67 var context = new DefaultVariantSourcesContext(
67 68 variants,
68 69 compileUnits,
69 70 roleProjections,
70 71 compileUnitNamer,
71 72 sourceSetRegistry,
72 73 sourceSetConfiguration
73 74 );
74 75 deferred.resolve(context);
75 76 });
76 77
77 78 var variantSourcesExtension = new VariantSourcesExtension() {
78 79 @Override
79 80 public void whenFinalized(Action<? super VariantSourcesContext> action) {
80 81 deferred.whenResolved(action::execute);
81 82 }
82 83
83 84 @Override
84 85 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
85 86 action.execute(lateConfigurationPolicy);
86 87 }
87 88
88 89 @Override
89 90 public void namingPolicy(Action<? super NamingPolicySpec> action) {
90 91 action.execute(namingPolicy);
91 92 }
92 93
93 94 @Override
94 95 public void variant(String variantName, Action<? super GenericSourceSet> action) {
95 96 Strings.argumentNotNullOrBlank(variantName, "variantName");
96 97 Objects.requireNonNull(action, "action can't be null");
97 98
98 99 lateConfigurationPolicy.finalizePolicy();
99 100
100 101 whenFinalized(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
101 102 }
102 103
103 104 @Override
104 105 public void layer(String layerName, Action<? super GenericSourceSet> action) {
105 106 // protect external DSL
106 107 Strings.argumentNotNullOrBlank(layerName, "layerName");
107 108 Objects.requireNonNull(action, "action can't be null");
108 109
109 110 lateConfigurationPolicy.finalizePolicy();
110 111
111 112 whenFinalized(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
112 113 }
113 114
114 115 @Override
115 116 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
116 117 Strings.argumentNotNullOrBlank(layerName, "layerName");
117 118 Strings.argumentNotNullOrBlank(variantName, "variantName");
118 119 Objects.requireNonNull(action, "action can't be null");
119 120
120 121 lateConfigurationPolicy.finalizePolicy();
121 122
122 123 whenFinalized(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
123 124 }
124 125 };
125 126
126 127 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
127 128
128 129 }
129 130
130 131 private static Layer resolveLayer(VariantsView variants, String name) {
131 132 return variants.getLayers().stream()
132 133 .filter(named(name))
133 134 .findAny()
134 .orElseThrow(() -> new IllegalArgumentException("Layer '" + name + "' isn't declared"));
135 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared"));
135 136 }
136 137
137 138 private static Variant resolveVariant(VariantsView variants, String name) {
138 139 return variants.getVariants().stream()
139 140 .filter(named(name))
140 141 .findAny()
141 .orElseThrow(() -> new IllegalArgumentException("Variant '" + name + "' is't declared"));
142 .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared"));
142 143 }
143 144
144 145 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
145 146 return ctx.getCompileUnits().findUnit(
146 147 resolveVariant(ctx.getVariants(), variantName),
147 148 resolveLayer(ctx.getVariants(), layerName))
148 149 .orElseThrow(() -> new InvalidUserDataException(
149 150 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
150 151 }
151 152
152 153 private static Predicate<Named> named(String name) {
153 154 return named -> named.getName().equals(name);
154 155 }
155 156
156 157 }
@@ -1,167 +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>once selected, it cannot be changed later;</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
23 * be changed later;</li>
21 24 * <li>the policy controls both diagnostics and late-application semantics.</li>
22 25 * </ul>
23 26 *
24 27 * <p>If not selected explicitly, the default is
25 28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
26 29 */
27 30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
28 31
29 32 default void lateConfigurationPolicy(Closure<?> closure) {
30 33 lateConfigurationPolicy(Closures.action(closure));
31 34 }
32 35
33 36 /**
34 37 * Selects how compile-unit name collisions are handled when the finalized
35 38 * source context is created.
36 39 *
37 40 * <p>This policy is single-valued:
38 41 * <ul>
39 42 * <li>it must be selected before the finalized
40 43 * {@link VariantSourcesContext} becomes observable through
41 44 * {@link #whenFinalized(Action)};</li>
42 45 * <li>once the context is being created, the policy is fixed and cannot be
43 46 * changed later;</li>
44 47 * <li>the policy governs validation of compile-unit names produced by the
45 48 * source-set materializer.</li>
46 49 * </ul>
47 50 *
48 51 * <p>If not selected explicitly, the default is
49 52 * {@link NamingPolicySpec#failOnNameCollision()}.
50 53 */
51 54 void namingPolicy(Action<? super NamingPolicySpec> action);
52 55
53 56 default void namingPolicy(Closure<?> closure) {
54 57 namingPolicy(Closures.action(closure));
55 58 }
56 59
57 60 /**
58 61 * Registers a selector rule for all compile units of the given layer.
59 62 *
60 63 * <p>Registering the first selector rule fixes the selected
61 64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
62 65 * lifecycle.
63 66 */
64 67 void layer(String layerName, Action<? super GenericSourceSet> action);
65 68
66 69 default void layer(String layerName, Closure<?> closure) {
67 70 layer(layerName, Closures.action(closure));
68 71 }
69 72
70 73 /**
71 74 * Registers a selector rule for all compile units of the given variant.
72 75 *
73 76 * <p>Registering the first selector rule fixes the selected
74 77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
75 78 * lifecycle.
76 79 */
77 80 void variant(String variantName, Action<? super GenericSourceSet> action);
78 81
79 82 default void variant(String variantName, Closure<?> closure) {
80 83 variant(variantName, Closures.action(closure));
81 84 }
82 85
83 86 /**
84 87 * Registers a selector rule for one exact compile unit.
85 88 *
86 89 * <p>Registering the first selector rule fixes the selected
87 90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
88 91 * lifecycle.
89 92 */
90 93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
91 94
92 95 default void unit(String variantName, String layerName, Closure<?> closure) {
93 96 unit(variantName, layerName, Closures.action(closure));
94 97 }
95 98
96 99 /**
97 100 * Invoked when finalized variants-derived source context becomes available.
98 101 *
99 102 * Replayable:
100 103 * <ul>
101 104 * <li>if called before variants finalization, action is queued
102 105 * <li>if called after variants finalization, action is invoked immediately
103 106 * </ul>
104 107 *
105 108 * <p>By the time this callback becomes observable, compile-unit naming
106 109 * policy has already been fixed and symbolic source-set names for finalized
107 110 * compile units are determined.
108 111 */
109 112 void whenFinalized(Action<? super VariantSourcesContext> action);
110 113
111 114 default void whenFinalized(Closure<?> closure) {
112 115 whenFinalized(Closures.action(closure));
113 116 }
114 117
115 118
116 119 /**
117 120 * Imperative selector for the late-configuration mode.
118 121 *
119 122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
120 123 */
121 124 interface LateConfigurationPolicySpec {
122 125 /**
123 126 * Rejects selector registration if it targets any already materialized
124 127 * source set.
125 128 */
126 129 void failOnLateConfiguration();
127 130
128 131 /**
129 132 * Allows late selector registration, but emits a warning when it targets an
130 133 * already materialized source set.
131 134 *
132 135 * <p>For such targets, selector precedence is not re-established
133 136 * retroactively. The action is applied as a late imperative step, after the
134 137 * state already produced at the materialization moment.
135 138 */
136 139 void warnOnLateConfiguration();
137 140
138 141 /**
139 142 * Allows late selector registration without a warning when it targets an
140 143 * already materialized source set.
141 144 *
142 145 * <p>For such targets, selector precedence is not re-established
143 146 * retroactively. The action is applied as a late imperative step, after the
144 147 * state already produced at the materialization moment.
145 148 */
146 149 void allowLateConfiguration();
147 150 }
148 151
149 152 interface NamingPolicySpec {
150 153 /**
151 154 * Rejects finalized compile-unit models that project the same source-set
152 155 * name for different compile units.
153 156 */
154 157 void failOnNameCollision();
155 158
156 159 /**
157 160 * Resolves name collisions deterministically for the finalized
158 161 * compile-unit model.
159 162 *
160 163 * <p>Conflicting compile units are ordered canonically by
161 164 * {@code (variant.name, layer.name)}. The first unit keeps the base
162 165 * projected name, and each next unit receives a numeric suffix
163 166 * ({@code 2}, {@code 3}, ...).
164 167 */
165 168 void resolveNameCollision();
166 169 }
167 170 }
@@ -1,42 +1,43
1 1 package org.implab.gradle.variants.sources.internal;
2 2
3 3 import org.implab.gradle.variants.sources.VariantSourcesExtension.LateConfigurationPolicySpec;
4 4
5 5 public class DefaultLateConfigurationPolicySpec implements LateConfigurationPolicySpec {
6 6
7 7 private LateConfigurationMode policyMode = LateConfigurationMode.FAIL;
8 8 private boolean policyApplied = false;
9 9
10 10 public LateConfigurationMode mode() {
11 finalizePolicy();
11 12 return policyMode;
12 13 }
13 14
14 15 public void finalizePolicy() {
15 16 policyApplied = true;
16 17 }
17 18
18 19 @Override
19 20 public void failOnLateConfiguration() {
20 21 assertApplyOnce();
21 22 policyMode = LateConfigurationMode.FAIL;
22 23 }
23 24
24 25 @Override
25 26 public void warnOnLateConfiguration() {
26 27 assertApplyOnce();
27 28 policyMode = LateConfigurationMode.WARN;
28 29 }
29 30
30 31 @Override
31 32 public void allowLateConfiguration() {
32 33 assertApplyOnce();
33 34 policyMode = LateConfigurationMode.APPLY;
34 35 }
35 36
36 37 private void assertApplyOnce() {
37 38 if (policyApplied)
38 39 throw new IllegalStateException("Lazy configuration policy already applied");
39 40 policyApplied = true;
40 41 }
41 42
42 43 }
@@ -1,431 +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 29 variantSources.whenFinalized { 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 53 variantSources.whenFinalized { 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 110 variantSources.whenFinalized { 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 149 variantSources.whenFinalized { 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 185 variantSources.whenFinalized { 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 224 variantSources.whenFinalized { 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 318 variantSources.whenFinalized { 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 assertBuildFails("Variant 'missing' is't declared", "help");
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 422 variantSources.whenFinalized {
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
432 @Test
433 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
434 writeSettings("variant-sources-late-policy-context-fixed");
435 writeBuildFile("""
436 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
437
438 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
439 variantsExt.layers.create('main')
440 variantsExt.roles.create('production')
441 variantsExt.variant('browser') {
442 role('production') { layers('main') }
443 }
444
445 variantSources.whenFinalized {
446 variantSources.lateConfigurationPolicy {
447 allowLateConfiguration()
448 }
449 }
450 """);
451
452 assertBuildFails("Lazy configuration policy already applied", "help");
453 }
431 454 }
General Comments 0
You need to be logged in to leave comments. Login now