##// END OF EJS Templates
variants: validate source context identities
cin -
r57:1abf7dba60ee default
parent child
Show More
@@ -1,227 +1,259
1 package org.implab.gradle.variants.core;
1 package org.implab.gradle.variants.core;
2
2
3 import java.util.ArrayList;
3 import java.util.ArrayList;
4 import java.util.LinkedHashMap;
4 import java.util.LinkedHashMap;
5 import java.util.List;
5 import java.util.List;
6 import java.util.Map;
6 import java.util.Map;
7 import java.util.Objects;
7 import java.util.Objects;
8 import java.util.Optional;
8 import java.util.Optional;
9 import java.util.Set;
9 import java.util.Set;
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.InvalidUserDataException;
13 import org.gradle.api.InvalidUserDataException;
14
14
15 /**
15 /**
16 * A resolved view of declared variants, roles, layers, and their bindings.
16 * A resolved view of declared variants, roles, layers, and their bindings.
17 *
17 *
18 * Built from {@link VariantDefinition} instances, this class materializes validated
18 * Built from {@link VariantDefinition} instances, this class materializes validated
19 * {@link VariantRoleLayer} entries and provides lookup APIs grouped by variant,
19 * {@link VariantRoleLayer} entries and provides lookup APIs grouped by variant,
20 * role, or layer.
20 * role, or layer.
21 *
21 *
22 * Typical usage is to collect identities and variant definitions through
22 * Typical usage is to collect identities and variant definitions through
23 * {@link Builder}, then use the resulting view to traverse resolved bindings.
23 * {@link Builder}, then use the resulting view to traverse resolved bindings.
24 */
24 */
25 @NonNullByDefault
25 @NonNullByDefault
26 public class VariantsView {
26 public class VariantsView {
27 private final Set<Layer> layers;
27 private final Set<Layer> layers;
28 private final Set<Role> roles;
28 private final Set<Role> roles;
29 private final Set<Variant> variants;
29 private final Set<Variant> variants;
30 private final Set<VariantRoleLayer> entries;
30 private final Set<VariantRoleLayer> entries;
31
31
32 private final Map<Variant, Set<VariantRoleLayer>> entriesByVariant;
32 private final Map<Variant, Set<VariantRoleLayer>> entriesByVariant;
33 private final Map<Role, Set<VariantRoleLayer>> entriesByRole;
33 private final Map<Role, Set<VariantRoleLayer>> entriesByRole;
34 private final Map<Layer, Set<VariantRoleLayer>> entriesByLayer;
34 private final Map<Layer, Set<VariantRoleLayer>> entriesByLayer;
35
35
36 private VariantsView(Set<Layer> layers, Set<Role> roles, Set<Variant> variants, Set<VariantRoleLayer> entries) {
36 private VariantsView(Set<Layer> layers, Set<Role> roles, Set<Variant> variants, Set<VariantRoleLayer> entries) {
37 this.layers = layers;
37 this.layers = layers;
38 this.roles = roles;
38 this.roles = roles;
39 this.variants = variants;
39 this.variants = variants;
40 this.entries = entries;
40 this.entries = entries;
41 this.entriesByVariant = entries.stream()
41 this.entriesByVariant = entries.stream()
42 .collect(Collectors.groupingBy(VariantRoleLayer::variant, Collectors.toSet()));
42 .collect(Collectors.groupingBy(VariantRoleLayer::variant, Collectors.toSet()));
43 this.entriesByRole = entries.stream()
43 this.entriesByRole = entries.stream()
44 .collect(Collectors.groupingBy(VariantRoleLayer::role, Collectors.toSet()));
44 .collect(Collectors.groupingBy(VariantRoleLayer::role, Collectors.toSet()));
45 this.entriesByLayer = entries.stream()
45 this.entriesByLayer = entries.stream()
46 .collect(Collectors.groupingBy(VariantRoleLayer::layer, Collectors.toSet()));
46 .collect(Collectors.groupingBy(VariantRoleLayer::layer, Collectors.toSet()));
47 }
47 }
48
48
49 /**
49 /**
50 * Returns all declared layers included in this view.
50 * Returns all declared layers included in this view.
51 */
51 */
52 public Set<Layer> getLayers() {
52 public Set<Layer> getLayers() {
53 return layers;
53 return layers;
54 }
54 }
55
55
56 /**
56 /**
57 * Returns all declared roles included in this view.
57 * Returns all declared roles included in this view.
58 */
58 */
59 public Set<Role> getRoles() {
59 public Set<Role> getRoles() {
60 return roles;
60 return roles;
61 }
61 }
62
62
63 /**
64 * Requires the layer to be declared in this view.
65 *
66 * @param layer layer identity
67 * @throws InvalidUserDataException if the layer is not declared
68 */
69 public void assertLayer(Layer layer) {
70 Objects.requireNonNull(layer, "The layer can't be null");
71
72 if (!layers.contains(layer))
73 throw new InvalidUserDataException("The specified layer '" + layer.getName() + "' isn't declared");
74 }
75
76 /**
77 * Requires the role to be declared in this view.
78 *
79 * @param role role identity
80 * @throws InvalidUserDataException if the role is not declared
81 */
63 public void assertRole(Role role) {
82 public void assertRole(Role role) {
64 Objects.requireNonNull(role, "The role can't be null");
83 Objects.requireNonNull(role, "The role can't be null");
65
84
66 if (!roles.contains(role))
85 if (!roles.contains(role))
67 throw new InvalidUserDataException("The specified role '" + role.getName() + "' isn't declared");
86 throw new InvalidUserDataException("The specified role '" + role.getName() + "' isn't declared");
68 }
87 }
69
88
70 /**
89 /**
71 * Returns all declared variants included in this view.
90 * Returns all declared variants included in this view.
72 */
91 */
73 public Set<Variant> getVariants() {
92 public Set<Variant> getVariants() {
74 return variants;
93 return variants;
75 }
94 }
76
95
77 /**
96 /**
97 * Requires the variant to be declared in this view.
98 *
99 * @param variant variant identity
100 * @throws InvalidUserDataException if the variant is not declared
101 */
102 public void assertVariant(Variant variant) {
103 Objects.requireNonNull(variant, "The variant can't be null");
104
105 if (!variants.contains(variant))
106 throw new InvalidUserDataException("The specified variant '" + variant.getName() + "' isn't declared");
107 }
108
109 /**
78 * Returns all resolved variant-role-layer bindings.
110 * Returns all resolved variant-role-layer bindings.
79 */
111 */
80 public Set<VariantRoleLayer> getEntries() {
112 public Set<VariantRoleLayer> getEntries() {
81 return entries;
113 return entries;
82 }
114 }
83
115
84 /**
116 /**
85 * Returns all bindings associated with the specified variant.
117 * Returns all bindings associated with the specified variant.
86 *
118 *
87 * An empty set is returned when the variant has no bindings in this view.
119 * An empty set is returned when the variant has no bindings in this view.
88 */
120 */
89 public Set<VariantRoleLayer> getEntriesForVariant(Variant variant) {
121 public Set<VariantRoleLayer> getEntriesForVariant(Variant variant) {
90 return entriesByVariant.getOrDefault(variant, Set.of());
122 return entriesByVariant.getOrDefault(variant, Set.of());
91 }
123 }
92
124
93 /**
125 /**
94 * Returns all bindings associated with the specified layer.
126 * Returns all bindings associated with the specified layer.
95 *
127 *
96 * An empty set is returned when the layer has no bindings in this view.
128 * An empty set is returned when the layer has no bindings in this view.
97 */
129 */
98 public Set<VariantRoleLayer> getEntriesForLayer(Layer layer) {
130 public Set<VariantRoleLayer> getEntriesForLayer(Layer layer) {
99 return entriesByLayer.getOrDefault(layer, Set.of());
131 return entriesByLayer.getOrDefault(layer, Set.of());
100 }
132 }
101
133
102 /**
134 /**
103 * Returns all bindings associated with the specified role.
135 * Returns all bindings associated with the specified role.
104 *
136 *
105 * An empty set is returned when the role has no bindings in this view.
137 * An empty set is returned when the role has no bindings in this view.
106 */
138 */
107 public Set<VariantRoleLayer> getEntriesForRole(Role role) {
139 public Set<VariantRoleLayer> getEntriesForRole(Role role) {
108 return entriesByRole.getOrDefault(role, Set.of());
140 return entriesByRole.getOrDefault(role, Set.of());
109 }
141 }
110
142
111 /**
143 /**
112 * A resolved binding between a variant, a role, and a layer.
144 * A resolved binding between a variant, a role, and a layer.
113 *
145 *
114 * @param variant the resolved variant
146 * @param variant the resolved variant
115 * @param role the resolved role
147 * @param role the resolved role
116 * @param layer the resolved layer
148 * @param layer the resolved layer
117 */
149 */
118 public record VariantRoleLayer(Variant variant, Role role, Layer layer) {
150 public record VariantRoleLayer(Variant variant, Role role, Layer layer) {
119 }
151 }
120
152
121 /**
153 /**
122 * Creates a builder for assembling a {@link VariantsView}.
154 * Creates a builder for assembling a {@link VariantsView}.
123 */
155 */
124 public static Builder builder() {
156 public static Builder builder() {
125 return new Builder();
157 return new Builder();
126 }
158 }
127
159
128 /**
160 /**
129 * Collects declared identities and variant definitions, then resolves them
161 * Collects declared identities and variant definitions, then resolves them
130 * into a {@link VariantsView}.
162 * into a {@link VariantsView}.
131 */
163 */
132 public static class Builder {
164 public static class Builder {
133
165
134 private final Map<String, Layer> layers = new LinkedHashMap<>();
166 private final Map<String, Layer> layers = new LinkedHashMap<>();
135 private final Map<String, Role> roles = new LinkedHashMap<>();
167 private final Map<String, Role> roles = new LinkedHashMap<>();
136 private final Map<String, Variant> variants = new LinkedHashMap<>();
168 private final Map<String, Variant> variants = new LinkedHashMap<>();
137 private final List<VariantDefinition> definitions = new ArrayList<>();
169 private final List<VariantDefinition> definitions = new ArrayList<>();
138
170
139 private Builder() {
171 private Builder() {
140 }
172 }
141
173
142 /**
174 /**
143 * Adds or replaces a role by its name.
175 * Adds or replaces a role by its name.
144 */
176 */
145 public Builder addRole(Role role) {
177 public Builder addRole(Role role) {
146 Objects.requireNonNull(role, "role can't be null");
178 Objects.requireNonNull(role, "role can't be null");
147 roles.put(role.getName(), role);
179 roles.put(role.getName(), role);
148 return this;
180 return this;
149 }
181 }
150
182
151 /**
183 /**
152 * Adds or replaces a layer by its name.
184 * Adds or replaces a layer by its name.
153 */
185 */
154 public Builder addLayer(Layer layer) {
186 public Builder addLayer(Layer layer) {
155 Objects.requireNonNull(layer, "layer can't be null");
187 Objects.requireNonNull(layer, "layer can't be null");
156 layers.put(layer.getName(), layer);
188 layers.put(layer.getName(), layer);
157 return this;
189 return this;
158 }
190 }
159
191
160 /**
192 /**
161 * Adds or replaces a variant by its name.
193 * Adds or replaces a variant by its name.
162 */
194 */
163 public Builder addVariant(Variant variant) {
195 public Builder addVariant(Variant variant) {
164 Objects.requireNonNull(variant, "variant can't be null");
196 Objects.requireNonNull(variant, "variant can't be null");
165 variants.put(variant.getName(), variant);
197 variants.put(variant.getName(), variant);
166 return this;
198 return this;
167 }
199 }
168
200
169 /**
201 /**
170 * Adds a variant definition to be resolved during {@link #build()}.
202 * Adds a variant definition to be resolved during {@link #build()}.
171 */
203 */
172 public Builder addDefinition(VariantDefinition definition) {
204 public Builder addDefinition(VariantDefinition definition) {
173 Objects.requireNonNull(definition, "definition can't be null");
205 Objects.requireNonNull(definition, "definition can't be null");
174 definitions.add(definition);
206 definitions.add(definition);
175 return this;
207 return this;
176 }
208 }
177
209
178 /**
210 /**
179 * Resolves collected identities and definitions into an immutable view.
211 * Resolves collected identities and definitions into an immutable view.
180 *
212 *
181 * Missing variant, role, or layer declarations referenced by definitions
213 * Missing variant, role, or layer declarations referenced by definitions
182 * cause {@link InvalidUserDataException}.
214 * cause {@link InvalidUserDataException}.
183 */
215 */
184 public VariantsView build() {
216 public VariantsView build() {
185
217
186 var entries = definitions.stream()
218 var entries = definitions.stream()
187 .flatMap(def -> def.getRoleBindings().get().stream()
219 .flatMap(def -> def.getRoleBindings().get().stream()
188 .map(layerRole -> createVariantRoleLayer(
220 .map(layerRole -> createVariantRoleLayer(
189 def.getName(), // variantName
221 def.getName(), // variantName
190 layerRole.roleName(),
222 layerRole.roleName(),
191 layerRole.layerName())))
223 layerRole.layerName())))
192 .collect(Collectors.toSet());
224 .collect(Collectors.toSet());
193
225
194 return new VariantsView(
226 return new VariantsView(
195 Set.copyOf(layers.values()),
227 Set.copyOf(layers.values()),
196 Set.copyOf(roles.values()),
228 Set.copyOf(roles.values()),
197 Set.copyOf(variants.values()),
229 Set.copyOf(variants.values()),
198 entries);
230 entries);
199 }
231 }
200
232
201 private VariantRoleLayer createVariantRoleLayer(String variantName, String roleName, String layerName) {
233 private VariantRoleLayer createVariantRoleLayer(String variantName, String roleName, String layerName) {
202 return new VariantRoleLayer(
234 return new VariantRoleLayer(
203 resolveVariant(variantName,
235 resolveVariant(variantName,
204 "Variant '" + variantName + "' isn't declared"),
236 "Variant '" + variantName + "' isn't declared"),
205 resolveRole(roleName,
237 resolveRole(roleName,
206 "Role '" + roleName + "' isn't declared, referenced in '" + variantName + "' variant"),
238 "Role '" + roleName + "' isn't declared, referenced in '" + variantName + "' variant"),
207 resolveLayer(layerName,
239 resolveLayer(layerName,
208 "Layer '" + layerName + "' isn't declared, referenced in '" + variantName
240 "Layer '" + layerName + "' isn't declared, referenced in '" + variantName
209 + "' variant with '" + roleName + "' role"));
241 + "' variant with '" + roleName + "' role"));
210 }
242 }
211
243
212 private Layer resolveLayer(String layerName, String errorMessage) {
244 private Layer resolveLayer(String layerName, String errorMessage) {
213 return Optional.ofNullable(layers.get(layerName))
245 return Optional.ofNullable(layers.get(layerName))
214 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
246 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
215 }
247 }
216
248
217 private Variant resolveVariant(String variantName, String errorMessage) {
249 private Variant resolveVariant(String variantName, String errorMessage) {
218 return Optional.ofNullable(variants.get(variantName))
250 return Optional.ofNullable(variants.get(variantName))
219 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
251 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
220 }
252 }
221
253
222 private Role resolveRole(String roleName, String errorMessage) {
254 private Role resolveRole(String roleName, String errorMessage) {
223 return Optional.ofNullable(roles.get(roleName))
255 return Optional.ofNullable(roles.get(roleName))
224 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
256 .orElseThrow(() -> new InvalidUserDataException(errorMessage));
225 }
257 }
226 }
258 }
227 }
259 }
@@ -1,78 +1,79
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.gradle.api.InvalidUserDataException;
11 import org.implab.gradle.variants.core.Layer;
12 import org.implab.gradle.variants.core.Layer;
12 import org.implab.gradle.variants.core.Role;
13 import org.implab.gradle.variants.core.Role;
13 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
16 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
16
17
17 @NonNullByDefault
18 @NonNullByDefault
18 public final class CompileUnitsView {
19 public final class CompileUnitsView {
19
20
20 private final VariantsView variants;
21 private final VariantsView variants;
21 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
22 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
22
23
23 private CompileUnitsView(VariantsView variants) {
24 private CompileUnitsView(VariantsView variants) {
24 this.variants = variants;
25 this.variants = variants;
25 }
26 }
26
27
27 public Set<CompileUnit> getUnits() {
28 public Set<CompileUnit> getUnits() {
28 return variants.getEntries().stream()
29 return variants.getEntries().stream()
29 .map(CompileUnit::of)
30 .map(CompileUnit::of)
30 .collect(Collectors.toUnmodifiableSet());
31 .collect(Collectors.toUnmodifiableSet());
31 }
32 }
32
33
33 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
34 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
34 Objects.requireNonNull(variant, "Variant can't be null");
35 Objects.requireNonNull(variant, "Variant can't be null");
35
36
36 return unitsByVariant.computeIfAbsent(variant, key -> variants
37 return unitsByVariant.computeIfAbsent(variant, key -> variants
37 .getEntriesForVariant(variant).stream()
38 .getEntriesForVariant(variant).stream()
38 .map(CompileUnit::of)
39 .map(CompileUnit::of)
39 .collect(Collectors.toUnmodifiableSet()));
40 .collect(Collectors.toUnmodifiableSet()));
40 }
41 }
41
42
42 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
43 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
43 Objects.requireNonNull(variant, "Variant can't be null");
44 Objects.requireNonNull(variant, "Variant can't be null");
44 Objects.requireNonNull(layer, "Layer can't be null");
45 Objects.requireNonNull(layer, "Layer can't be null");
45
46
46 return getUnitsForVariant(variant).stream()
47 return getUnitsForVariant(variant).stream()
47 .filter(u -> u.layer().equals(layer))
48 .filter(u -> u.layer().equals(layer))
48 .findAny();
49 .findAny();
49 }
50 }
50
51
51 public boolean contains(Variant variant, Layer layer) {
52 public boolean contains(Variant variant, Layer layer) {
52 return findUnit(variant, layer).isPresent();
53 return findUnit(variant, layer).isPresent();
53 }
54 }
54
55
55 /**
56 /**
56 * In which logical roles this compile unit participates.
57 * In which logical roles this compile unit participates.
57 */
58 */
58
59
59 public Set<Role> getRoles(CompileUnit unit) {
60 public Set<Role> getRoles(CompileUnit unit) {
60 Objects.requireNonNull(unit, "Compile unit can't be null");
61 Objects.requireNonNull(unit, "Compile unit can't be null");
61 return variants.getEntriesForVariant(unit.variant()).stream()
62 return variants.getEntriesForVariant(unit.variant()).stream()
62 .filter(entry -> entry.layer().equals(unit.layer()))
63 .filter(entry -> entry.layer().equals(unit.layer()))
63 .map(VariantRoleLayer::role)
64 .map(VariantRoleLayer::role)
64 .collect(Collectors.toUnmodifiableSet());
65 .collect(Collectors.toUnmodifiableSet());
65 }
66 }
66
67
67 public CompileUnit requireUnit(Variant variant, Layer layer) {
68 public CompileUnit requireUnit(Variant variant, Layer layer) {
68 return findUnit(variant, layer)
69 return findUnit(variant, layer)
69 .orElseThrow(() -> new IllegalArgumentException(
70 .orElseThrow(() -> new InvalidUserDataException(
70 "Compile unit for variant '" + variant.getName()
71 "Compile unit for variant '" + variant.getName()
71 + "' and layer '" + layer.getName() + "' not found"));
72 + "' and layer '" + layer.getName() + "' not found"));
72 }
73 }
73
74
74 public static CompileUnitsView of(VariantsView variantsView) {
75 public static CompileUnitsView of(VariantsView variantsView) {
75 Objects.requireNonNull(variantsView, "variantsView can't be null");
76 Objects.requireNonNull(variantsView, "variantsView can't be null");
76 return new CompileUnitsView(variantsView);
77 return new CompileUnitsView(variantsView);
77 }
78 }
78 }
79 }
@@ -1,83 +1,85
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.gradle.api.InvalidUserDataException;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
14
15
15 public final class RoleProjectionsView {
16 public final class RoleProjectionsView {
16 private final VariantsView variants;
17 private final VariantsView variants;
17
18
18 private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>();
19 private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>();
19
20
20 private RoleProjectionsView(VariantsView variants) {
21 private RoleProjectionsView(VariantsView variants) {
21 this.variants = variants;
22 this.variants = variants;
22 }
23 }
23
24
24 public Set<RoleProjection> getProjections() {
25 public Set<RoleProjection> getProjections() {
25 return variants.getEntries().stream()
26 return variants.getEntries().stream()
26 .map(RoleProjection::of)
27 .map(RoleProjection::of)
27 .collect(Collectors.toUnmodifiableSet());
28 .collect(Collectors.toUnmodifiableSet());
28 }
29 }
29
30
30 public Set<RoleProjection> getProjectionsForVariant(Variant variant) {
31 public Set<RoleProjection> getProjectionsForVariant(Variant variant) {
31 Objects.requireNonNull(variant, "Variant can't be null");
32 Objects.requireNonNull(variant, "Variant can't be null");
32 return projectionsByVariant.computeIfAbsent(variant, key -> variants
33 return projectionsByVariant.computeIfAbsent(variant, key -> variants
33 .getEntriesForVariant(variant).stream()
34 .getEntriesForVariant(variant).stream()
34 .map(RoleProjection::of)
35 .map(RoleProjection::of)
35 .collect(Collectors.toUnmodifiableSet()));
36 .collect(Collectors.toUnmodifiableSet()));
36 }
37 }
37
38
38 public Set<RoleProjection> getProjectionsForRole(Role role) {
39 public Set<RoleProjection> getProjectionsForRole(Role role) {
39 Objects.requireNonNull(role, "Role can't be null");
40 Objects.requireNonNull(role, "Role can't be null");
40 return variants.getEntriesForRole(role).stream()
41 return variants.getEntriesForRole(role).stream()
41 .map(RoleProjection::of)
42 .map(RoleProjection::of)
42 .collect(Collectors.toUnmodifiableSet());
43 .collect(Collectors.toUnmodifiableSet());
43 }
44 }
44
45
45 public Optional<RoleProjection> findProjection(Variant variant, Role role) {
46 public Optional<RoleProjection> findProjection(Variant variant, Role role) {
46 Objects.requireNonNull(variant, "Variant can't be null");
47 Objects.requireNonNull(variant, "Variant can't be null");
47 Objects.requireNonNull(role, "Role can't be null");
48 Objects.requireNonNull(role, "Role can't be null");
48 return variants.getEntriesForVariant(variant).stream()
49 return variants.getEntriesForVariant(variant).stream()
49 .filter(entry -> entry.role().equals(role))
50 .filter(entry -> entry.role().equals(role))
50 .map(RoleProjection::of)
51 .map(RoleProjection::of)
51 .findAny();
52 .findAny();
52 }
53 }
53
54
54 public boolean contains(Variant variant, Role role) {
55 public boolean contains(Variant variant, Role role) {
55 return findProjection(variant, role).isPresent();
56 return findProjection(variant, role).isPresent();
56 }
57 }
57
58
58 public Set<CompileUnit> getUnits(RoleProjection projection) {
59 public Set<CompileUnit> getUnits(RoleProjection projection) {
59 Objects.requireNonNull(projection, "Role projection can't be null");
60 Objects.requireNonNull(projection, "Role projection can't be null");
60 return variants.getEntriesForVariant(projection.variant()).stream()
61 return variants.getEntriesForVariant(projection.variant()).stream()
61 .filter(entry -> entry.role().equals(projection.role()))
62 .filter(entry -> entry.role().equals(projection.role()))
62 .map(CompileUnit::of)
63 .map(CompileUnit::of)
63 .collect(Collectors.toUnmodifiableSet());
64 .collect(Collectors.toUnmodifiableSet());
64
65
65 }
66 }
66
67
67 public RoleProjection requireProjection(Variant variant, Role role) {
68 public RoleProjection requireProjection(Variant variant, Role role) {
68 return findProjection(variant, role)
69 return findProjection(variant, role)
69 .orElseThrow(() -> new IllegalArgumentException(
70 .orElseThrow(() -> new InvalidUserDataException(
70 "Role projection for variant '" + variant.getName()
71 "Role projection for variant '" + variant.getName()
71 + "' and role '" + role.getName() + "' not found"));
72 + "' and role '" + role.getName() + "' not found"));
72 }
73 }
73
74
74 public Set<Layer> getLayers(RoleProjection projection) {
75 public Set<Layer> getLayers(RoleProjection projection) {
75 return getUnits(projection).stream()
76 return getUnits(projection).stream()
76 .map(CompileUnit::layer)
77 .map(CompileUnit::layer)
77 .collect(java.util.stream.Collectors.toUnmodifiableSet());
78 .collect(java.util.stream.Collectors.toUnmodifiableSet());
78 }
79 }
79
80
80 public static RoleProjectionsView of(VariantsView variantsView) {
81 public static RoleProjectionsView of(VariantsView variantsView) {
82 Objects.requireNonNull(variantsView, "variantsView can't be null");
81 return new RoleProjectionsView(variantsView);
83 return new RoleProjectionsView(variantsView);
82 }
84 }
83 }
85 }
@@ -1,20 +1,23
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
4
5 /**
5 /**
6 * Materializes symbolic source set names into actual {@link GenericSourceSet}
6 * Materializes symbolic source set names into actual {@link GenericSourceSet}
7 * instances.
7 * instances.
8 *
8 *
9 * <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
10 * the selected
10 * the selected
11 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
11 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
12 */
12 */
13 public interface SourceSetMaterializer {
13 public interface SourceSetMaterializer {
14 /**
14 /**
15 * 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.
16 *
16 *
17 * <p>The provider is stable and cached per compile unit.
17 * <p>The provider is stable and cached per compile unit.
18 *
19 * @throws org.gradle.api.InvalidUserDataException if the compile unit is not
20 * part of the finalized variant model
18 */
21 */
19 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
22 NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit);
20 }
23 }
@@ -1,72 +1,81
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.variants.core.Layer;
4 import org.implab.gradle.variants.core.Layer;
5 import org.implab.gradle.variants.core.Variant;
5 import org.implab.gradle.variants.core.Variant;
6 import org.implab.gradle.variants.core.VariantsView;
6 import org.implab.gradle.variants.core.VariantsView;
7
7
8 /**
8 /**
9 * Registry of symbolic source set names produced by sources projection.
9 * Registry of symbolic source set names produced by sources projection.
10 *
10 *
11 * <p>Identity in this registry is the {@link GenericSourceSet} name assigned
11 * <p>Identity in this registry is the {@link GenericSourceSet} name assigned
12 * by the finalized
12 * by the finalized
13 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
13 * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}.
14 */
14 */
15 public interface VariantSourcesContext {
15 public interface VariantSourcesContext {
16
16
17 /**
17 /**
18 * Finalized core model.
18 * Finalized core model.
19 */
19 */
20 VariantsView getVariants();
20 VariantsView getVariants();
21
21
22 /**
22 /**
23 * Derived compile-side view.
23 * Derived compile-side view.
24 */
24 */
25 CompileUnitsView getCompileUnits();
25 CompileUnitsView getCompileUnits();
26
26
27 /**
27 /**
28 * Derived role-side view.
28 * Derived role-side view.
29 */
29 */
30 RoleProjectionsView getRoleProjections();
30 RoleProjectionsView getRoleProjections();
31
31
32 /**
32 /**
33 * Lazy source set provider service.
33 * Lazy source set provider service.
34 */
34 */
35 SourceSetMaterializer getSourceSets();
35 SourceSetMaterializer getSourceSets();
36
36
37 /**
37 /**
38 * Configures all GenericSourceSets produced from the given layer.
38 * Configures all GenericSourceSets produced from the given layer.
39 *
39 *
40 * The action is applied:
40 * The action is applied:
41 * - to already materialized source sets of this layer
41 * - to already materialized source sets of this layer
42 * - to all future source sets of this layer
42 * - to all future source sets of this layer
43 *
43 *
44 * <p>For future source sets, selector precedence and registration order are
44 * <p>For future source sets, selector precedence and registration order are
45 * preserved by the materializer.
45 * preserved by the materializer.
46 *
46 *
47 * <p>For already materialized source sets, behavior is governed by
47 * <p>For already materialized source sets, behavior is governed by
48 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
48 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
49 * 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
50 * not retroactively restore selector precedence.
50 * not retroactively restore selector precedence.
51 *
52 * @throws org.gradle.api.InvalidUserDataException if the layer is not part of
53 * the finalized variant model
51 */
54 */
52 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
55 void configureLayer(Layer layer, Action<? super GenericSourceSet> action);
53
56
54 /**
57 /**
55 * Configures all GenericSourceSets produced from the given variant.
58 * Configures all GenericSourceSets produced from the given variant.
56 *
59 *
57 * <p>Late application semantics for already materialized source sets are
60 * <p>Late application semantics for already materialized source sets are
58 * governed by
61 * governed by
59 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
62 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
63 *
64 * @throws org.gradle.api.InvalidUserDataException if the variant is not part
65 * of the finalized variant model
60 */
66 */
61 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
67 void configureVariant(Variant variant, Action<? super GenericSourceSet> action);
62
68
63 /**
69 /**
64 * Configures the GenericSourceSet produced from the given compile unit.
70 * Configures the GenericSourceSet produced from the given compile unit.
65 *
71 *
66 * <p>Late application semantics for already materialized source sets are
72 * <p>Late application semantics for already materialized source sets are
67 * governed by
73 * governed by
68 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
74 * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}.
75 *
76 * @throws org.gradle.api.InvalidUserDataException if the compile unit is not
77 * part of the finalized variant model
69 */
78 */
70 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
79 void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action);
71
80
72 }
81 }
@@ -1,170 +1,170
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 groovy.lang.Closure;
6 import groovy.lang.Closure;
7
7
8 @NonNullByDefault
8 @NonNullByDefault
9 public interface VariantSourcesExtension {
9 public interface VariantSourcesExtension {
10
10
11 /**
11 /**
12 * Selects how selector rules behave when they target an already materialized
12 * Selects how selector rules behave when they target an already materialized
13 * {@link GenericSourceSet}.
13 * {@link GenericSourceSet}.
14 *
14 *
15 * <p>This policy is single-valued:
15 * <p>This policy is single-valued:
16 * <ul>
16 * <ul>
17 * <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
18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
19 * {@link #unit(String, String, Action)};</li>
19 * {@link #unit(String, String, Action)};</li>
20 * <li>it must be selected before the source context becomes observable via
20 * <li>it must be selected before the source context becomes observable via
21 * {@link #whenAvailable(Action)};</li>
21 * {@link #whenAvailable(Action)};</li>
22 * <li>once selected or once source context creation begins, it cannot
22 * <li>once selected or once source context creation begins, it cannot
23 * be changed later;</li>
23 * be changed later;</li>
24 * <li>the policy controls both diagnostics and late-application semantics.</li>
24 * <li>the policy controls both diagnostics and late-application semantics.</li>
25 * </ul>
25 * </ul>
26 *
26 *
27 * <p>If not selected explicitly, the default is
27 * <p>If not selected explicitly, the default is
28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
29 */
29 */
30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
31
31
32 default void lateConfigurationPolicy(Closure<?> closure) {
32 default void lateConfigurationPolicy(Closure<?> closure) {
33 lateConfigurationPolicy(Closures.action(closure));
33 lateConfigurationPolicy(Closures.action(closure));
34 }
34 }
35
35
36 /**
36 /**
37 * Selects how compile-unit name collisions are handled when the source
37 * Selects how compile-unit name collisions are handled when the source
38 * context is created from the finalized variant model.
38 * context is created from the finalized variant model.
39 *
39 *
40 * <p>This policy is single-valued:
40 * <p>This policy is single-valued:
41 * <ul>
41 * <ul>
42 * <li>it must be selected before the finalized
42 * <li>it must be selected before the
43 * {@link VariantSourcesContext} becomes observable through
43 * {@link VariantSourcesContext} becomes observable through
44 * {@link #whenAvailable(Action)};</li>
44 * {@link #whenAvailable(Action)};</li>
45 * <li>once the context is being created, the policy is fixed and cannot be
45 * <li>once the context is being created, the policy is fixed and cannot be
46 * changed later;</li>
46 * changed later;</li>
47 * <li>the policy governs validation of compile-unit names produced by the
47 * <li>the policy governs validation of compile-unit names produced by the
48 * source-set materializer.</li>
48 * source-set materializer.</li>
49 * </ul>
49 * </ul>
50 *
50 *
51 * <p>If not selected explicitly, the default is
51 * <p>If not selected explicitly, the default is
52 * {@link NamingPolicySpec#failOnNameCollision()}.
52 * {@link NamingPolicySpec#failOnNameCollision()}.
53 */
53 */
54 void namingPolicy(Action<? super NamingPolicySpec> action);
54 void namingPolicy(Action<? super NamingPolicySpec> action);
55
55
56 default void namingPolicy(Closure<?> closure) {
56 default void namingPolicy(Closure<?> closure) {
57 namingPolicy(Closures.action(closure));
57 namingPolicy(Closures.action(closure));
58 }
58 }
59
59
60 /**
60 /**
61 * Registers a selector rule for all compile units of the given layer.
61 * Registers a selector rule for all compile units of the given layer.
62 *
62 *
63 * <p>Registering the first selector rule fixes the selected
63 * <p>Registering the first selector rule fixes the selected
64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
65 * lifecycle.
65 * lifecycle.
66 */
66 */
67 void layer(String layerName, Action<? super GenericSourceSet> action);
67 void layer(String layerName, Action<? super GenericSourceSet> action);
68
68
69 default void layer(String layerName, Closure<?> closure) {
69 default void layer(String layerName, Closure<?> closure) {
70 layer(layerName, Closures.action(closure));
70 layer(layerName, Closures.action(closure));
71 }
71 }
72
72
73 /**
73 /**
74 * Registers a selector rule for all compile units of the given variant.
74 * Registers a selector rule for all compile units of the given variant.
75 *
75 *
76 * <p>Registering the first selector rule fixes the selected
76 * <p>Registering the first selector rule fixes the selected
77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
78 * lifecycle.
78 * lifecycle.
79 */
79 */
80 void variant(String variantName, Action<? super GenericSourceSet> action);
80 void variant(String variantName, Action<? super GenericSourceSet> action);
81
81
82 default void variant(String variantName, Closure<?> closure) {
82 default void variant(String variantName, Closure<?> closure) {
83 variant(variantName, Closures.action(closure));
83 variant(variantName, Closures.action(closure));
84 }
84 }
85
85
86 /**
86 /**
87 * Registers a selector rule for one exact compile unit.
87 * Registers a selector rule for one exact compile unit.
88 *
88 *
89 * <p>Registering the first selector rule fixes the selected
89 * <p>Registering the first selector rule fixes the selected
90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
91 * lifecycle.
91 * lifecycle.
92 */
92 */
93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
94
94
95 default void unit(String variantName, String layerName, Closure<?> closure) {
95 default void unit(String variantName, String layerName, Closure<?> closure) {
96 unit(variantName, layerName, Closures.action(closure));
96 unit(variantName, layerName, Closures.action(closure));
97 }
97 }
98
98
99 /**
99 /**
100 * Invoked when the variants-derived source context becomes available.
100 * Invoked when the variants-derived source context becomes available.
101 *
101 *
102 * Replayable:
102 * Replayable:
103 * <ul>
103 * <ul>
104 * <li>if called before variants finalization, action is queued
104 * <li>if called before variants finalization, action is queued
105 * <li>if called after variants finalization, action is invoked immediately
105 * <li>if called after variants finalization, action is invoked immediately
106 * </ul>
106 * </ul>
107 *
107 *
108 * <p>By the time this callback becomes observable, compile-unit naming
108 * <p>By the time this callback becomes observable, compile-unit naming
109 * policy has already been fixed and symbolic source-set names for finalized
109 * policy has already been fixed and symbolic source-set names for finalized
110 * compile units are determined.
110 * compile units are determined.
111 */
111 */
112 void whenAvailable(Action<? super VariantSourcesContext> action);
112 void whenAvailable(Action<? super VariantSourcesContext> action);
113
113
114 default void whenAvailable(Closure<?> closure) {
114 default void whenAvailable(Closure<?> closure) {
115 whenAvailable(Closures.action(closure));
115 whenAvailable(Closures.action(closure));
116 }
116 }
117
117
118
118
119 /**
119 /**
120 * Imperative selector for the late-configuration mode.
120 * Imperative selector for the late-configuration mode.
121 *
121 *
122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
123 */
123 */
124 interface LateConfigurationPolicySpec {
124 interface LateConfigurationPolicySpec {
125 /**
125 /**
126 * Rejects selector registration if it targets any already materialized
126 * Rejects selector registration if it targets any already materialized
127 * source set.
127 * source set.
128 */
128 */
129 void failOnLateConfiguration();
129 void failOnLateConfiguration();
130
130
131 /**
131 /**
132 * Allows late selector registration, but emits a warning when it targets an
132 * Allows late selector registration, but emits a warning when it targets an
133 * already materialized source set.
133 * already materialized source set.
134 *
134 *
135 * <p>For such targets, selector precedence is not re-established
135 * <p>For such targets, selector precedence is not re-established
136 * retroactively. The action is applied as a late imperative step, after the
136 * retroactively. The action is applied as a late imperative step, after the
137 * state already produced at the materialization moment.
137 * state already produced at the materialization moment.
138 */
138 */
139 void warnOnLateConfiguration();
139 void warnOnLateConfiguration();
140
140
141 /**
141 /**
142 * Allows late selector registration without a warning when it targets an
142 * Allows late selector registration without a warning when it targets an
143 * already materialized source set.
143 * already materialized source set.
144 *
144 *
145 * <p>For such targets, selector precedence is not re-established
145 * <p>For such targets, selector precedence is not re-established
146 * retroactively. The action is applied as a late imperative step, after the
146 * retroactively. The action is applied as a late imperative step, after the
147 * state already produced at the materialization moment.
147 * state already produced at the materialization moment.
148 */
148 */
149 void allowLateConfiguration();
149 void allowLateConfiguration();
150 }
150 }
151
151
152 interface NamingPolicySpec {
152 interface NamingPolicySpec {
153 /**
153 /**
154 * Rejects finalized compile-unit models that project the same source-set
154 * Rejects finalized compile-unit models that project the same source-set
155 * name for different compile units.
155 * name for different compile units.
156 */
156 */
157 void failOnNameCollision();
157 void failOnNameCollision();
158
158
159 /**
159 /**
160 * Resolves name collisions deterministically for the finalized
160 * Resolves name collisions deterministically for the finalized
161 * compile-unit model.
161 * compile-unit model.
162 *
162 *
163 * <p>Conflicting compile units are ordered canonically by
163 * <p>Conflicting compile units are ordered canonically by
164 * {@code (variant.name, layer.name)}. The first unit keeps the base
164 * {@code (variant.name, layer.name)}. The first unit keeps the base
165 * projected name, and each next unit receives a numeric suffix
165 * projected name, and each next unit receives a numeric suffix
166 * ({@code 2}, {@code 3}, ...).
166 * ({@code 2}, {@code 3}, ...).
167 */
167 */
168 void resolveNameCollision();
168 void resolveNameCollision();
169 }
169 }
170 }
170 }
@@ -1,91 +1,91
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.Collection;
4 import java.util.Collection;
5 import java.util.Comparator;
5 import java.util.Comparator;
6 import java.util.HashMap;
6 import java.util.HashMap;
7 import java.util.HashSet;
7 import java.util.HashSet;
8 import java.util.Map;
8 import java.util.Map;
9 import java.util.Optional;
9 import java.util.Optional;
10 import java.util.Set;
10 import java.util.Set;
11 import java.util.function.Function;
11 import java.util.function.Function;
12 import java.util.stream.Collectors;
12 import java.util.stream.Collectors;
13
13
14 import org.gradle.api.InvalidUserDataException;
14 import org.gradle.api.InvalidUserDataException;
15 import org.implab.gradle.common.core.lang.Strings;
15 import org.implab.gradle.common.core.lang.Strings;
16 import org.implab.gradle.variants.sources.CompileUnit;
16 import org.implab.gradle.variants.sources.CompileUnit;
17
17
18 public interface CompileUnitNamer {
18 public interface CompileUnitNamer {
19
19
20 String resolveName(CompileUnit unit);
20 String resolveName(CompileUnit unit);
21
21
22 public static Builder builder() {
22 public static Builder builder() {
23 return new Builder();
23 return new Builder();
24 }
24 }
25
25
26 static class Builder {
26 static class Builder {
27 private final Set<CompileUnit> units = new HashSet<>();
27 private final Set<CompileUnit> units = new HashSet<>();
28 private NameCollisionPolicy nameCollisionPolicy = NameCollisionPolicy.FAIL;
28 private NameCollisionPolicy nameCollisionPolicy = NameCollisionPolicy.FAIL;
29
29
30 private Builder() {
30 private Builder() {
31 }
31 }
32
32
33 public Builder addUnits(Collection<CompileUnit> other) {
33 public Builder addUnits(Collection<CompileUnit> other) {
34 units.addAll(other);
34 units.addAll(other);
35 return this;
35 return this;
36 }
36 }
37
37
38 public Builder nameCollisionPolicy(NameCollisionPolicy policy) {
38 public Builder nameCollisionPolicy(NameCollisionPolicy policy) {
39 nameCollisionPolicy = policy;
39 nameCollisionPolicy = policy;
40 return this;
40 return this;
41 }
41 }
42
42
43 public CompileUnitNamer build() {
43 public CompileUnitNamer build() {
44 Map<String, Integer> seen = new HashMap<>();
44 Map<String, Integer> seen = new HashMap<>();
45
45
46 if (nameCollisionPolicy == NameCollisionPolicy.FAIL) {
46 if (nameCollisionPolicy == NameCollisionPolicy.FAIL) {
47 var collisions = units.stream()
47 var collisions = units.stream()
48 .collect(Collectors.groupingBy(this::projectName))
48 .collect(Collectors.groupingBy(this::projectName))
49 .entrySet().stream()
49 .entrySet().stream()
50 .filter(pair -> pair.getValue().size() > 1)
50 .filter(pair -> pair.getValue().size() > 1)
51 .map(pair -> MessageFormat.format(
51 .map(pair -> MessageFormat.format(
52 "({0}: {1})",
52 "({0}: {1})",
53 pair.getKey(),
53 pair.getKey(),
54 pair.getValue().stream()
54 pair.getValue().stream()
55 .map(Object::toString)
55 .map(Object::toString)
56 .collect(Collectors.joining(","))))
56 .collect(Collectors.joining(","))))
57 .collect(Collectors.joining(","));
57 .collect(Collectors.joining(","));
58 if (!collisions.isEmpty())
58 if (!collisions.isEmpty())
59 throw new InvalidUserDataException(
59 throw new InvalidUserDataException(
60 "The same source set names are produced by different compile units: " + collisions);
60 "The same source set names are produced by different compile units: " + collisions);
61 }
61 }
62
62
63 var unitNames = units.stream()
63 var unitNames = units.stream()
64 .sorted(Comparator
64 .sorted(Comparator
65 .comparing((CompileUnit unit) -> unit.variant().getName())
65 .comparing((CompileUnit unit) -> unit.variant().getName())
66 .thenComparing(unit -> unit.layer().getName()))
66 .thenComparing(unit -> unit.layer().getName()))
67 .collect(Collectors.toUnmodifiableMap(Function.identity(), unit -> {
67 .collect(Collectors.toUnmodifiableMap(Function.identity(), unit -> {
68 var baseName = projectName(unit);
68 var baseName = projectName(unit);
69
69
70 var c = seen.compute(baseName, (key, count) -> count == null ? 1 : count + 1);
70 var c = seen.compute(baseName, (key, count) -> count == null ? 1 : count + 1);
71 return c == 1 ? baseName : baseName + String.valueOf(c);
71 return c == 1 ? baseName : baseName + String.valueOf(c);
72 }));
72 }));
73
73
74 return new CompileUnitNamer() {
74 return new CompileUnitNamer() {
75
75
76 @Override
76 @Override
77 public String resolveName(CompileUnit unit) {
77 public String resolveName(CompileUnit unit) {
78 return Optional.ofNullable(unitNames.get(unit)).orElseThrow(
78 return Optional.ofNullable(unitNames.get(unit)).orElseThrow(
79 () -> new IllegalArgumentException(MessageFormat.format(
79 () -> new InvalidUserDataException(MessageFormat.format(
80 "Compile unit {0} doesn't have an associated name",
80 "Compile unit {0} doesn't have an associated name",
81 unit)));
81 unit)));
82 }
82 }
83
83
84 };
84 };
85 }
85 }
86
86
87 private String projectName(CompileUnit unit) {
87 private String projectName(CompileUnit unit) {
88 return unit.variant().getName() + Strings.capitalize(unit.layer().getName());
88 return unit.variant().getName() + Strings.capitalize(unit.layer().getName());
89 }
89 }
90 }
90 }
91 }
91 }
@@ -1,36 +1,37
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import org.gradle.api.InvalidUserDataException;
3 import org.implab.gradle.variants.sources.VariantSourcesExtension.NamingPolicySpec;
4 import org.implab.gradle.variants.sources.VariantSourcesExtension.NamingPolicySpec;
4
5
5 public class DefaultCompileUnitNamingPolicy implements NamingPolicySpec {
6 public class DefaultCompileUnitNamingPolicy implements NamingPolicySpec {
6 private NameCollisionPolicy policy = NameCollisionPolicy.FAIL;
7 private NameCollisionPolicy policy = NameCollisionPolicy.FAIL;
7 private boolean policyApplied = false;
8 private boolean policyApplied = false;
8
9
9 public NameCollisionPolicy policy() {
10 public NameCollisionPolicy policy() {
10 finalizePolicy();
11 finalizePolicy();
11 return policy;
12 return policy;
12 }
13 }
13
14
14 public void finalizePolicy() {
15 public void finalizePolicy() {
15 policyApplied = true;
16 policyApplied = true;
16 }
17 }
17
18
18 @Override
19 @Override
19 public void failOnNameCollision() {
20 public void failOnNameCollision() {
20 assertApplyOnce();
21 assertApplyOnce();
21 policy = NameCollisionPolicy.FAIL;
22 policy = NameCollisionPolicy.FAIL;
22 }
23 }
23
24
24 @Override
25 @Override
25 public void resolveNameCollision() {
26 public void resolveNameCollision() {
26 assertApplyOnce();
27 assertApplyOnce();
27 policy = NameCollisionPolicy.RESOLVE;
28 policy = NameCollisionPolicy.RESOLVE;
28 }
29 }
29
30
30 private void assertApplyOnce() {
31 private void assertApplyOnce() {
31 if (policyApplied)
32 if (policyApplied)
32 throw new IllegalStateException("Naming policy already applied");
33 throw new InvalidUserDataException("Naming policy already applied");
33 policyApplied = true;
34 policyApplied = true;
34
35
35 }
36 }
36 }
37 }
@@ -1,43 +1,44
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import org.gradle.api.InvalidUserDataException;
3 import org.implab.gradle.variants.sources.VariantSourcesExtension.LateConfigurationPolicySpec;
4 import org.implab.gradle.variants.sources.VariantSourcesExtension.LateConfigurationPolicySpec;
4
5
5 public class DefaultLateConfigurationPolicySpec implements LateConfigurationPolicySpec {
6 public class DefaultLateConfigurationPolicySpec implements LateConfigurationPolicySpec {
6
7
7 private LateConfigurationMode policyMode = LateConfigurationMode.FAIL;
8 private LateConfigurationMode policyMode = LateConfigurationMode.FAIL;
8 private boolean policyApplied = false;
9 private boolean policyApplied = false;
9
10
10 public LateConfigurationMode mode() {
11 public LateConfigurationMode mode() {
11 finalizePolicy();
12 finalizePolicy();
12 return policyMode;
13 return policyMode;
13 }
14 }
14
15
15 public void finalizePolicy() {
16 public void finalizePolicy() {
16 policyApplied = true;
17 policyApplied = true;
17 }
18 }
18
19
19 @Override
20 @Override
20 public void failOnLateConfiguration() {
21 public void failOnLateConfiguration() {
21 assertApplyOnce();
22 assertApplyOnce();
22 policyMode = LateConfigurationMode.FAIL;
23 policyMode = LateConfigurationMode.FAIL;
23 }
24 }
24
25
25 @Override
26 @Override
26 public void warnOnLateConfiguration() {
27 public void warnOnLateConfiguration() {
27 assertApplyOnce();
28 assertApplyOnce();
28 policyMode = LateConfigurationMode.WARN;
29 policyMode = LateConfigurationMode.WARN;
29 }
30 }
30
31
31 @Override
32 @Override
32 public void allowLateConfiguration() {
33 public void allowLateConfiguration() {
33 assertApplyOnce();
34 assertApplyOnce();
34 policyMode = LateConfigurationMode.APPLY;
35 policyMode = LateConfigurationMode.APPLY;
35 }
36 }
36
37
37 private void assertApplyOnce() {
38 private void assertApplyOnce() {
38 if (policyApplied)
39 if (policyApplied)
39 throw new IllegalStateException("Lazy configuration policy already applied");
40 throw new InvalidUserDataException("Lazy configuration policy already applied");
40 policyApplied = true;
41 policyApplied = true;
41 }
42 }
42
43
43 }
44 }
@@ -1,92 +1,107
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.Objects;
5 import org.gradle.api.Action;
6 import org.gradle.api.Action;
6 import org.gradle.api.NamedDomainObjectProvider;
7 import org.gradle.api.NamedDomainObjectProvider;
7 import org.implab.gradle.variants.core.Layer;
8 import org.implab.gradle.variants.core.Layer;
8 import org.implab.gradle.variants.core.Variant;
9 import org.implab.gradle.variants.core.Variant;
9 import org.implab.gradle.variants.core.VariantsView;
10 import org.implab.gradle.variants.core.VariantsView;
10 import org.implab.gradle.variants.sources.CompileUnit;
11 import org.implab.gradle.variants.sources.CompileUnit;
11 import org.implab.gradle.variants.sources.CompileUnitsView;
12 import org.implab.gradle.variants.sources.CompileUnitsView;
12 import org.implab.gradle.variants.sources.GenericSourceSet;
13 import org.implab.gradle.variants.sources.GenericSourceSet;
13 import org.implab.gradle.variants.sources.RoleProjectionsView;
14 import org.implab.gradle.variants.sources.RoleProjectionsView;
14 import org.implab.gradle.variants.sources.SourceSetMaterializer;
15 import org.implab.gradle.variants.sources.SourceSetMaterializer;
15 import org.implab.gradle.variants.sources.VariantSourcesContext;
16 import org.implab.gradle.variants.sources.VariantSourcesContext;
16
17
17 public class DefaultVariantSourcesContext implements VariantSourcesContext {
18 public class DefaultVariantSourcesContext implements VariantSourcesContext {
18 private final VariantsView variantsView;
19 private final VariantsView variantsView;
19 private final CompileUnitsView compileUnitsView;
20 private final CompileUnitsView compileUnitsView;
20 private final RoleProjectionsView roleProjectionsView;
21 private final RoleProjectionsView roleProjectionsView;
21 private final SourceSetMaterializer sourceSetMaterializer;
22 private final SourceSetMaterializer sourceSetMaterializer;
22 private final SourceSetRegistry sourceSetRegistry;
23 private final SourceSetRegistry sourceSetRegistry;
23 private final CompileUnitNamer compileUnitNamer;
24 private final CompileUnitNamer compileUnitNamer;
24 private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry;
25 private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry;
25
26
26 public DefaultVariantSourcesContext(
27 public DefaultVariantSourcesContext(
27 VariantsView variantsView,
28 VariantsView variantsView,
28 CompileUnitsView compileUnitsView,
29 CompileUnitsView compileUnitsView,
29 RoleProjectionsView roleProjectionsView,
30 RoleProjectionsView roleProjectionsView,
30 CompileUnitNamer compileUnitNamer,
31 CompileUnitNamer compileUnitNamer,
31 SourceSetRegistry sourceSetRegistry,
32 SourceSetRegistry sourceSetRegistry,
32 SourceSetConfigurationRegistry sourceSetConfigurationRegistry) {
33 SourceSetConfigurationRegistry sourceSetConfigurationRegistry) {
33 this.variantsView = variantsView;
34 this.variantsView = variantsView;
34 this.compileUnitNamer = compileUnitNamer;
35 this.compileUnitNamer = compileUnitNamer;
35 this.compileUnitsView = compileUnitsView;
36 this.compileUnitsView = compileUnitsView;
36 this.roleProjectionsView = roleProjectionsView;
37 this.roleProjectionsView = roleProjectionsView;
37 this.sourceSetRegistry = sourceSetRegistry;
38 this.sourceSetRegistry = sourceSetRegistry;
38 this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry;
39 this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry;
39
40
40 sourceSetMaterializer = new LocalSourceSetMaterializer();
41 sourceSetMaterializer = new LocalSourceSetMaterializer();
41 }
42 }
42
43
43 @Override
44 @Override
44 public VariantsView getVariants() {
45 public VariantsView getVariants() {
45 return variantsView;
46 return variantsView;
46 }
47 }
47
48
48 @Override
49 @Override
49 public CompileUnitsView getCompileUnits() {
50 public CompileUnitsView getCompileUnits() {
50 return compileUnitsView;
51 return compileUnitsView;
51 }
52 }
52
53
53 @Override
54 @Override
54 public RoleProjectionsView getRoleProjections() {
55 public RoleProjectionsView getRoleProjections() {
55 return roleProjectionsView;
56 return roleProjectionsView;
56 }
57 }
57
58
58 @Override
59 @Override
59 public SourceSetMaterializer getSourceSets() {
60 public SourceSetMaterializer getSourceSets() {
60 return sourceSetMaterializer;
61 return sourceSetMaterializer;
61 }
62 }
62
63
63 @Override
64 @Override
64 public void configureLayer(Layer layer, Action<? super GenericSourceSet> action) {
65 public void configureLayer(Layer layer, Action<? super GenericSourceSet> action) {
66 variantsView.assertLayer(layer);
67 Objects.requireNonNull(action, "action can't be null");
65 sourceSetConfigurationRegistry.addLayerAction(layer, action);
68 sourceSetConfigurationRegistry.addLayerAction(layer, action);
66 }
69 }
67
70
68 @Override
71 @Override
69 public void configureVariant(Variant variant, Action<? super GenericSourceSet> action) {
72 public void configureVariant(Variant variant, Action<? super GenericSourceSet> action) {
73 variantsView.assertVariant(variant);
74 Objects.requireNonNull(action, "action can't be null");
70 sourceSetConfigurationRegistry.addVariantAction(variant, action);
75 sourceSetConfigurationRegistry.addVariantAction(variant, action);
71 }
76 }
72
77
73 @Override
78 @Override
74 public void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action) {
79 public void configureUnit(CompileUnit unit, Action<? super GenericSourceSet> action) {
80 assertCompileUnit(unit);
81 Objects.requireNonNull(action, "action can't be null");
75 sourceSetConfigurationRegistry.addCompileUnitAction(unit, action);
82 sourceSetConfigurationRegistry.addCompileUnitAction(unit, action);
76 }
83 }
77
84
85 private void assertCompileUnit(CompileUnit unit) {
86 Objects.requireNonNull(unit, "unit can't be null");
87 variantsView.assertVariant(unit.variant());
88 variantsView.assertLayer(unit.layer());
89 compileUnitsView.requireUnit(unit.variant(), unit.layer());
90 }
91
78 class LocalSourceSetMaterializer implements SourceSetMaterializer {
92 class LocalSourceSetMaterializer implements SourceSetMaterializer {
79 private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>();
93 private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>();
80
94
81 @Override
95 @Override
82 public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) {
96 public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) {
97 assertCompileUnit(unit);
83 return registeredSources.computeIfAbsent(unit, k -> {
98 return registeredSources.computeIfAbsent(unit, k -> {
84 var sourcesName = compileUnitNamer.resolveName(unit);
99 var sourcesName = compileUnitNamer.resolveName(unit);
85 sourceSetRegistry.whenMaterialized(sourcesName,
100 sourceSetRegistry.whenMaterialized(sourcesName,
86 sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet));
101 sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet));
87 return sourceSetRegistry.sourceSets().register(sourcesName);
102 return sourceSetRegistry.sourceSets().register(sourcesName);
88 });
103 });
89 }
104 }
90
105
91 }
106 }
92 }
107 }
@@ -1,94 +1,95
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.List;
5 import java.util.List;
6 import java.util.Map;
6 import java.util.Map;
7 import java.util.function.Supplier;
7 import java.util.function.Supplier;
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.gradle.api.Action;
11 import org.gradle.api.Action;
12 import org.gradle.api.InvalidUserDataException;
12 import org.gradle.api.Named;
13 import org.gradle.api.Named;
13 import org.gradle.api.logging.Logger;
14 import org.gradle.api.logging.Logger;
14 import org.gradle.api.logging.Logging;
15 import org.gradle.api.logging.Logging;
15 import org.implab.gradle.common.core.lang.ReplayableQueue;
16 import org.implab.gradle.common.core.lang.ReplayableQueue;
16 import org.implab.gradle.variants.core.Layer;
17 import org.implab.gradle.variants.core.Layer;
17 import org.implab.gradle.variants.core.Variant;
18 import org.implab.gradle.variants.core.Variant;
18 import org.implab.gradle.variants.sources.CompileUnit;
19 import org.implab.gradle.variants.sources.CompileUnit;
19 import org.implab.gradle.variants.sources.GenericSourceSet;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
20
21
21 @NonNullByDefault
22 @NonNullByDefault
22 public class SourceSetConfigurationRegistry {
23 public class SourceSetConfigurationRegistry {
23 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
24 private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class);
24
25
25 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
26 private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>();
26 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
27 private final Map<Variant, ReplayableQueue<GenericSourceSet>> sourcesByVariant = new LinkedHashMap<>();
27 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
28 private final Map<CompileUnit, ReplayableQueue<GenericSourceSet>> sourcesByUnit = new LinkedHashMap<>();
28
29
29 private final Supplier<LateConfigurationMode> lateConfigurationMode;
30 private final Supplier<LateConfigurationMode> lateConfigurationMode;
30
31
31 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
32 public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) {
32 this.lateConfigurationMode = lateConfigurationMode;
33 this.lateConfigurationMode = lateConfigurationMode;
33 }
34 }
34
35
35 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
36 public void addLayerAction(Layer layer, Action<? super GenericSourceSet> action) {
36 addToActions(
37 addToActions(
37 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
38 sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()),
38 action,
39 action,
39 MessageFormat.format(
40 MessageFormat.format(
40 "Source sets for [layer={0}] layer already materialized",
41 "Source sets for [layer={0}] layer already materialized",
41 layer.getName()));
42 layer.getName()));
42 }
43 }
43
44
44 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
45 public void addVariantAction(Variant variant, Action<? super GenericSourceSet> action) {
45 addToActions(
46 addToActions(
46 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
47 sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()),
47 action,
48 action,
48 MessageFormat.format(
49 MessageFormat.format(
49 "Source sets for [variant={0}] variant already materialized",
50 "Source sets for [variant={0}] variant already materialized",
50 variant.getName()));
51 variant.getName()));
51
52
52 }
53 }
53
54
54 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
55 public void addCompileUnitAction(CompileUnit unit, Action<? super GenericSourceSet> action) {
55 addToActions(
56 addToActions(
56 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
57 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()),
57 action,
58 action,
58 MessageFormat.format(
59 MessageFormat.format(
59 "Source set for [variant={0}, layer={1}] already materialized",
60 "Source set for [variant={0}, layer={1}] already materialized",
60 unit.variant().getName(),
61 unit.variant().getName(),
61 unit.layer().getName()));
62 unit.layer().getName()));
62 }
63 }
63
64
64 private void addToActions(
65 private void addToActions(
65 ReplayableQueue<GenericSourceSet> actions,
66 ReplayableQueue<GenericSourceSet> actions,
66 Action<? super GenericSourceSet> action,
67 Action<? super GenericSourceSet> action,
67 String assertMessage) {
68 String assertMessage) {
68 assertLazyConfiguration(actions.values(), assertMessage);
69 assertLazyConfiguration(actions.values(), assertMessage);
69 actions.forEach(action::execute);
70 actions.forEach(action::execute);
70 }
71 }
71
72
72 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
73 void assertLazyConfiguration(List<GenericSourceSet> sets, String message) {
73 if (sets.size() == 0)
74 if (sets.size() == 0)
74 return;
75 return;
75
76
76 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
77 var names = sets.stream().map(Named::getName).collect(Collectors.joining(", "));
77
78
78 switch (lateConfigurationMode.get()) {
79 switch (lateConfigurationMode.get()) {
79 case FAIL:
80 case FAIL:
80 throw new IllegalStateException(message + " [" + names + "]");
81 throw new InvalidUserDataException(message + " [" + names + "]");
81 case WARN:
82 case WARN:
82 logger.warn(message + "\n\t" + names);
83 logger.warn(message + "\n\t" + names);
83 break;
84 break;
84 default:
85 default:
85 break;
86 break;
86 }
87 }
87 }
88 }
88
89
89 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
90 public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) {
90 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
91 sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet);
91 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet);
92 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
93 sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(sourceSet);
93 }
94 }
94 }
95 }
@@ -1,454 +1,554
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.whenAvailable { ctx ->
29 variantSources.whenAvailable { 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.requireProjection(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.whenAvailable { ctx ->
53 variantSources.whenAvailable { 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.whenAvailable { ctx ->
110 variantSources.whenAvailable { 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.whenAvailable { ctx ->
149 variantSources.whenAvailable { 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.whenAvailable { ctx ->
185 variantSources.whenAvailable { 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.whenAvailable { ctx ->
224 variantSources.whenAvailable { 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.whenAvailable { ctx ->
318 variantSources.whenAvailable { 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' isn't declared", "help");
359 assertBuildFails("Variant 'missing' isn't declared", "help");
360 }
360 }
361
361
362 @Test
362 @Test
363 void failsOnUnknownVariantContextSelectorTarget() throws Exception {
364 writeSettings("variant-sources-context-missing-variant");
365 writeBuildFile("""
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 variantsExt.layers.create('main')
370 variantsExt.roles.create('production')
371 variantsExt.variant('browser') {
372 role('production') { layers('main') }
373 }
374
375 variantSources.whenAvailable { ctx ->
376 def missing = objects.named(org.implab.gradle.variants.core.Variant, 'missing')
377 ctx.configureVariant(missing) {
378 declareOutputs('js')
379 }
380 }
381 """);
382
383 assertBuildFails("The specified variant 'missing' isn't declared", "help");
384 }
385
386 @Test
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
387 void failsOnUnknownLayerSelectorTarget() throws Exception {
364 writeSettings("variant-sources-missing-layer");
388 writeSettings("variant-sources-missing-layer");
365 writeBuildFile("""
389 writeBuildFile("""
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367
391
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 variantsExt.layers.create('main')
393 variantsExt.layers.create('main')
370 variantsExt.roles.create('production')
394 variantsExt.roles.create('production')
371 variantsExt.variant('browser') {
395 variantsExt.variant('browser') {
372 role('production') { layers('main') }
396 role('production') { layers('main') }
373 }
397 }
374
398
375 variantSources {
399 variantSources {
376 layer('missing') {
400 layer('missing') {
377 declareOutputs('js')
401 declareOutputs('js')
378 }
402 }
379 }
403 }
380 """);
404 """);
381
405
382 assertBuildFails("Layer 'missing' isn't declared", "help");
406 assertBuildFails("Layer 'missing' isn't declared", "help");
383 }
407 }
384
408
385 @Test
409 @Test
410 void failsOnUnknownLayerContextSelectorTarget() throws Exception {
411 writeSettings("variant-sources-context-missing-layer");
412 writeBuildFile("""
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 variantsExt.layers.create('main')
417 variantsExt.roles.create('production')
418 variantsExt.variant('browser') {
419 role('production') { layers('main') }
420 }
421
422 variantSources.whenAvailable { ctx ->
423 def missing = objects.named(org.implab.gradle.variants.core.Layer, 'missing')
424 ctx.configureLayer(missing) {
425 declareOutputs('js')
426 }
427 }
428 """);
429
430 assertBuildFails("The specified layer 'missing' isn't declared", "help");
431 }
432
433 @Test
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
434 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
387 writeSettings("variant-sources-missing-unit");
435 writeSettings("variant-sources-missing-unit");
388 writeBuildFile("""
436 writeBuildFile("""
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
437 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390
438
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
439 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 variantsExt.layers.create('main')
440 variantsExt.layers.create('main')
393 variantsExt.layers.create('test')
441 variantsExt.layers.create('test')
394 variantsExt.roles.create('production')
442 variantsExt.roles.create('production')
395 variantsExt.variant('browser') {
443 variantsExt.variant('browser') {
396 role('production') { layers('main') }
444 role('production') { layers('main') }
397 }
445 }
398
446
399 variantSources {
447 variantSources {
400 unit('browser', 'test') {
448 unit('browser', 'test') {
401 declareOutputs('js')
449 declareOutputs('js')
402 }
450 }
403 }
451 }
404 """);
452 """);
405
453
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
454 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
407 }
455 }
408
456
409 @Test
457 @Test
458 void failsOnUndeclaredCompileUnitContextSelectorTarget() throws Exception {
459 writeSettings("variant-sources-context-missing-unit");
460 writeBuildFile("""
461 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
462
463 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
464 variantsExt.layers.create('main')
465 variantsExt.layers.create('test')
466 variantsExt.roles.create('production')
467 variantsExt.variant('browser') {
468 role('production') { layers('main') }
469 }
470
471 variantSources.whenAvailable { ctx ->
472 def browser = ctx.variants.variants.find { it.name == 'browser' }
473 def testLayer = ctx.variants.layers.find { it.name == 'test' }
474 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
475 ctx.configureUnit(unit) {
476 declareOutputs('js')
477 }
478 }
479 """);
480
481 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
482 }
483
484 @Test
485 void failsOnUndeclaredCompileUnitMaterializerTarget() throws Exception {
486 writeSettings("variant-sources-materializer-missing-unit");
487 writeBuildFile("""
488 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
489
490 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
491 variantsExt.layers.create('main')
492 variantsExt.layers.create('test')
493 variantsExt.roles.create('production')
494 variantsExt.variant('browser') {
495 role('production') { layers('main') }
496 }
497
498 variantSources.whenAvailable { ctx ->
499 def browser = ctx.variants.variants.find { it.name == 'browser' }
500 def testLayer = ctx.variants.layers.find { it.name == 'test' }
501 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
502 ctx.sourceSets.getSourceSet(unit)
503 }
504 """);
505
506 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
507 }
508
509 @Test
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
510 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
411 writeSettings("variant-sources-name-policy-fixed");
511 writeSettings("variant-sources-name-policy-fixed");
412 writeBuildFile("""
512 writeBuildFile("""
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
513 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414
514
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
515 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 variantsExt.layers.create('main')
516 variantsExt.layers.create('main')
417 variantsExt.roles.create('production')
517 variantsExt.roles.create('production')
418 variantsExt.variant('browser') {
518 variantsExt.variant('browser') {
419 role('production') { layers('main') }
519 role('production') { layers('main') }
420 }
520 }
421
521
422 variantSources.whenAvailable {
522 variantSources.whenAvailable {
423 variantSources.namingPolicy {
523 variantSources.namingPolicy {
424 resolveNameCollision()
524 resolveNameCollision()
425 }
525 }
426 }
526 }
427 """);
527 """);
428
528
429 assertBuildFails("Naming policy already applied", "help");
529 assertBuildFails("Naming policy already applied", "help");
430 }
530 }
431
531
432 @Test
532 @Test
433 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
533 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
434 writeSettings("variant-sources-late-policy-context-fixed");
534 writeSettings("variant-sources-late-policy-context-fixed");
435 writeBuildFile("""
535 writeBuildFile("""
436 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
536 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
437
537
438 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
538 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
439 variantsExt.layers.create('main')
539 variantsExt.layers.create('main')
440 variantsExt.roles.create('production')
540 variantsExt.roles.create('production')
441 variantsExt.variant('browser') {
541 variantsExt.variant('browser') {
442 role('production') { layers('main') }
542 role('production') { layers('main') }
443 }
543 }
444
544
445 variantSources.whenAvailable {
545 variantSources.whenAvailable {
446 variantSources.lateConfigurationPolicy {
546 variantSources.lateConfigurationPolicy {
447 allowLateConfiguration()
547 allowLateConfiguration()
448 }
548 }
449 }
549 }
450 """);
550 """);
451
551
452 assertBuildFails("Lazy configuration policy already applied", "help");
552 assertBuildFails("Lazy configuration policy already applied", "help");
453 }
553 }
454 }
554 }
@@ -1,104 +1,105
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
6
7 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Constructor;
8 import java.util.Set;
8 import java.util.Set;
9
9
10 import org.gradle.api.InvalidUserDataException;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.junit.jupiter.api.Test;
16 import org.junit.jupiter.api.Test;
16
17
17 class CompileUnitsViewTest {
18 class CompileUnitsViewTest {
18 @Test
19 @Test
19 void deduplicatesCompileUnitsAcrossRolesAndExposesParticipatingRoles() {
20 void deduplicatesCompileUnitsAcrossRolesAndExposesParticipatingRoles() {
20 var browser = new TestVariant("browser");
21 var browser = new TestVariant("browser");
21 var main = new TestLayer("main");
22 var main = new TestLayer("main");
22 var test = new TestLayer("test");
23 var test = new TestLayer("test");
23 var production = new TestRole("production");
24 var production = new TestRole("production");
24 var qa = new TestRole("test");
25 var qa = new TestRole("test");
25
26
26 var view = view(
27 var view = view(
27 Set.of(main, test),
28 Set.of(main, test),
28 Set.of(production, qa),
29 Set.of(production, qa),
29 Set.of(browser),
30 Set.of(browser),
30 Set.of(
31 Set.of(
31 new VariantRoleLayer(browser, production, main),
32 new VariantRoleLayer(browser, production, main),
32 new VariantRoleLayer(browser, qa, main),
33 new VariantRoleLayer(browser, qa, main),
33 new VariantRoleLayer(browser, qa, test)));
34 new VariantRoleLayer(browser, qa, test)));
34
35
35 var units = CompileUnitsView.of(view);
36 var units = CompileUnitsView.of(view);
36 var browserMain = units.requireUnit(browser, main);
37 var browserMain = units.requireUnit(browser, main);
37 var browserTest = units.requireUnit(browser, test);
38 var browserTest = units.requireUnit(browser, test);
38
39
39 assertEquals(2, units.getUnits().size());
40 assertEquals(2, units.getUnits().size());
40 assertEquals(Set.of(browserMain, browserTest), units.getUnitsForVariant(browser));
41 assertEquals(Set.of(browserMain, browserTest), units.getUnitsForVariant(browser));
41 assertEquals(Set.of(production, qa), units.getRoles(browserMain));
42 assertEquals(Set.of(production, qa), units.getRoles(browserMain));
42 assertEquals(Set.of(qa), units.getRoles(browserTest));
43 assertEquals(Set.of(qa), units.getRoles(browserTest));
43 assertTrue(units.contains(browser, main));
44 assertTrue(units.contains(browser, main));
44 assertTrue(units.contains(browser, test));
45 assertTrue(units.contains(browser, test));
45 }
46 }
46
47
47 @Test
48 @Test
48 void rejectsMissingCompileUnitLookup() {
49 void rejectsMissingCompileUnitLookup() {
49 var browser = new TestVariant("browser");
50 var browser = new TestVariant("browser");
50 var main = new TestLayer("main");
51 var main = new TestLayer("main");
51 var missing = new TestLayer("missing");
52 var missing = new TestLayer("missing");
52 var production = new TestRole("production");
53 var production = new TestRole("production");
53
54
54 var view = view(
55 var view = view(
55 Set.of(main),
56 Set.of(main),
56 Set.of(production),
57 Set.of(production),
57 Set.of(browser),
58 Set.of(browser),
58 Set.of(new VariantRoleLayer(browser, production, main)));
59 Set.of(new VariantRoleLayer(browser, production, main)));
59
60
60 var units = CompileUnitsView.of(view);
61 var units = CompileUnitsView.of(view);
61
62
62 var ex = assertThrows(IllegalArgumentException.class, () -> units.requireUnit(browser, missing));
63 var ex = assertThrows(InvalidUserDataException.class, () -> units.requireUnit(browser, missing));
63 assertTrue(ex.getMessage().contains("Compile unit for variant 'browser' and layer 'missing' not found"));
64 assertTrue(ex.getMessage().contains("Compile unit for variant 'browser' and layer 'missing' not found"));
64 }
65 }
65
66
66 private static VariantsView view(
67 private static VariantsView view(
67 Set<Layer> layers,
68 Set<Layer> layers,
68 Set<Role> roles,
69 Set<Role> roles,
69 Set<Variant> variants,
70 Set<Variant> variants,
70 Set<VariantRoleLayer> entries) {
71 Set<VariantRoleLayer> entries) {
71 try {
72 try {
72 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
73 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
73 Set.class,
74 Set.class,
74 Set.class,
75 Set.class,
75 Set.class,
76 Set.class,
76 Set.class);
77 Set.class);
77 ctor.setAccessible(true);
78 ctor.setAccessible(true);
78 return ctor.newInstance(layers, roles, variants, entries);
79 return ctor.newInstance(layers, roles, variants, entries);
79 } catch (Exception e) {
80 } catch (Exception e) {
80 throw new RuntimeException("Unable to create VariantsView fixture", e);
81 throw new RuntimeException("Unable to create VariantsView fixture", e);
81 }
82 }
82 }
83 }
83
84
84 private record TestVariant(String value) implements Variant {
85 private record TestVariant(String value) implements Variant {
85 @Override
86 @Override
86 public String getName() {
87 public String getName() {
87 return value;
88 return value;
88 }
89 }
89 }
90 }
90
91
91 private record TestLayer(String value) implements Layer {
92 private record TestLayer(String value) implements Layer {
92 @Override
93 @Override
93 public String getName() {
94 public String getName() {
94 return value;
95 return value;
95 }
96 }
96 }
97 }
97
98
98 private record TestRole(String value) implements Role {
99 private record TestRole(String value) implements Role {
99 @Override
100 @Override
100 public String getName() {
101 public String getName() {
101 return value;
102 return value;
102 }
103 }
103 }
104 }
104 }
105 }
@@ -1,105 +1,106
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
6
7 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Constructor;
8 import java.util.Set;
8 import java.util.Set;
9
9
10 import org.gradle.api.InvalidUserDataException;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.junit.jupiter.api.Test;
16 import org.junit.jupiter.api.Test;
16
17
17 class RoleProjectionsViewTest {
18 class RoleProjectionsViewTest {
18 @Test
19 @Test
19 void exposesRoleProjectionsAndTheirCompileUnits() {
20 void exposesRoleProjectionsAndTheirCompileUnits() {
20 var browser = new TestVariant("browser");
21 var browser = new TestVariant("browser");
21 var main = new TestLayer("main");
22 var main = new TestLayer("main");
22 var test = new TestLayer("test");
23 var test = new TestLayer("test");
23 var production = new TestRole("production");
24 var production = new TestRole("production");
24 var qa = new TestRole("test");
25 var qa = new TestRole("test");
25
26
26 var view = view(
27 var view = view(
27 Set.of(main, test),
28 Set.of(main, test),
28 Set.of(production, qa),
29 Set.of(production, qa),
29 Set.of(browser),
30 Set.of(browser),
30 Set.of(
31 Set.of(
31 new VariantRoleLayer(browser, production, main),
32 new VariantRoleLayer(browser, production, main),
32 new VariantRoleLayer(browser, qa, main),
33 new VariantRoleLayer(browser, qa, main),
33 new VariantRoleLayer(browser, qa, test)));
34 new VariantRoleLayer(browser, qa, test)));
34
35
35 var projections = RoleProjectionsView.of(view);
36 var projections = RoleProjectionsView.of(view);
36 var productionProjection = projections.requireProjection(browser, production);
37 var productionProjection = projections.requireProjection(browser, production);
37 var qaProjection = projections.requireProjection(browser, qa);
38 var qaProjection = projections.requireProjection(browser, qa);
38
39
39 assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections());
40 assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections());
40 assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser));
41 assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser));
41 assertEquals(Set.of(qaProjection), projections.getProjectionsForRole(qa));
42 assertEquals(Set.of(qaProjection), projections.getProjectionsForRole(qa));
42 assertEquals(Set.of(new CompileUnit(browser, main)), projections.getUnits(productionProjection));
43 assertEquals(Set.of(new CompileUnit(browser, main)), projections.getUnits(productionProjection));
43 assertEquals(Set.of(new CompileUnit(browser, main), new CompileUnit(browser, test)), projections.getUnits(qaProjection));
44 assertEquals(Set.of(new CompileUnit(browser, main), new CompileUnit(browser, test)), projections.getUnits(qaProjection));
44 assertEquals(Set.of(main, test), projections.getLayers(qaProjection));
45 assertEquals(Set.of(main, test), projections.getLayers(qaProjection));
45 assertTrue(projections.contains(browser, production));
46 assertTrue(projections.contains(browser, production));
46 }
47 }
47
48
48 @Test
49 @Test
49 void rejectsMissingProjectionLookup() {
50 void rejectsMissingProjectionLookup() {
50 var browser = new TestVariant("browser");
51 var browser = new TestVariant("browser");
51 var node = new TestVariant("node");
52 var node = new TestVariant("node");
52 var main = new TestLayer("main");
53 var main = new TestLayer("main");
53 var production = new TestRole("production");
54 var production = new TestRole("production");
54
55
55 var view = view(
56 var view = view(
56 Set.of(main),
57 Set.of(main),
57 Set.of(production),
58 Set.of(production),
58 Set.of(browser, node),
59 Set.of(browser, node),
59 Set.of(new VariantRoleLayer(browser, production, main)));
60 Set.of(new VariantRoleLayer(browser, production, main)));
60
61
61 var projections = RoleProjectionsView.of(view);
62 var projections = RoleProjectionsView.of(view);
62
63
63 var ex = assertThrows(IllegalArgumentException.class, () -> projections.requireProjection(node, production));
64 var ex = assertThrows(InvalidUserDataException.class, () -> projections.requireProjection(node, production));
64 assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found"));
65 assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found"));
65 }
66 }
66
67
67 private static VariantsView view(
68 private static VariantsView view(
68 Set<Layer> layers,
69 Set<Layer> layers,
69 Set<Role> roles,
70 Set<Role> roles,
70 Set<Variant> variants,
71 Set<Variant> variants,
71 Set<VariantRoleLayer> entries) {
72 Set<VariantRoleLayer> entries) {
72 try {
73 try {
73 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
74 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
74 Set.class,
75 Set.class,
75 Set.class,
76 Set.class,
76 Set.class,
77 Set.class,
77 Set.class);
78 Set.class);
78 ctor.setAccessible(true);
79 ctor.setAccessible(true);
79 return ctor.newInstance(layers, roles, variants, entries);
80 return ctor.newInstance(layers, roles, variants, entries);
80 } catch (Exception e) {
81 } catch (Exception e) {
81 throw new RuntimeException("Unable to create VariantsView fixture", e);
82 throw new RuntimeException("Unable to create VariantsView fixture", e);
82 }
83 }
83 }
84 }
84
85
85 private record TestVariant(String value) implements Variant {
86 private record TestVariant(String value) implements Variant {
86 @Override
87 @Override
87 public String getName() {
88 public String getName() {
88 return value;
89 return value;
89 }
90 }
90 }
91 }
91
92
92 private record TestLayer(String value) implements Layer {
93 private record TestLayer(String value) implements Layer {
93 @Override
94 @Override
94 public String getName() {
95 public String getName() {
95 return value;
96 return value;
96 }
97 }
97 }
98 }
98
99
99 private record TestRole(String value) implements Role {
100 private record TestRole(String value) implements Role {
100 @Override
101 @Override
101 public String getName() {
102 public String getName() {
102 return value;
103 return value;
103 }
104 }
104 }
105 }
105 }
106 }
@@ -1,90 +1,90
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
6
7 import java.util.List;
7 import java.util.List;
8
8
9 import org.gradle.api.InvalidUserDataException;
9 import org.gradle.api.InvalidUserDataException;
10 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Variant;
11 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.sources.CompileUnit;
12 import org.implab.gradle.variants.sources.CompileUnit;
13 import org.junit.jupiter.api.Test;
13 import org.junit.jupiter.api.Test;
14
14
15 class CompileUnitNamerTest {
15 class CompileUnitNamerTest {
16 @Test
16 @Test
17 void resolvesProjectedNameForUniqueCompileUnit() {
17 void resolvesProjectedNameForUniqueCompileUnit() {
18 var unit = unit("browser", "main");
18 var unit = unit("browser", "main");
19
19
20 var namer = CompileUnitNamer.builder()
20 var namer = CompileUnitNamer.builder()
21 .addUnits(List.of(unit))
21 .addUnits(List.of(unit))
22 .build();
22 .build();
23
23
24 assertEquals("browserMain", namer.resolveName(unit));
24 assertEquals("browserMain", namer.resolveName(unit));
25 }
25 }
26
26
27 @Test
27 @Test
28 void failsOnProjectedNameCollisionInFailMode() {
28 void failsOnProjectedNameCollisionInFailMode() {
29 var left = unit("foo", "variantBar");
29 var left = unit("foo", "variantBar");
30 var right = unit("fooVariant", "bar");
30 var right = unit("fooVariant", "bar");
31
31
32 var ex = assertThrows(
32 var ex = assertThrows(
33 InvalidUserDataException.class,
33 InvalidUserDataException.class,
34 () -> CompileUnitNamer.builder()
34 () -> CompileUnitNamer.builder()
35 .addUnits(List.of(left, right))
35 .addUnits(List.of(left, right))
36 .nameCollisionPolicy(NameCollisionPolicy.FAIL)
36 .nameCollisionPolicy(NameCollisionPolicy.FAIL)
37 .build());
37 .build());
38
38
39 assertTrue(ex.getMessage().contains("The same source set names are produced by different compile units"));
39 assertTrue(ex.getMessage().contains("The same source set names are produced by different compile units"));
40 assertTrue(ex.getMessage().contains("fooVariantBar"));
40 assertTrue(ex.getMessage().contains("fooVariantBar"));
41 }
41 }
42
42
43 @Test
43 @Test
44 void resolvesProjectedNameCollisionDeterministicallyInCanonicalOrder() {
44 void resolvesProjectedNameCollisionDeterministicallyInCanonicalOrder() {
45 var earlier = unit("foo", "variantBar");
45 var earlier = unit("foo", "variantBar");
46 var later = unit("fooVariant", "bar");
46 var later = unit("fooVariant", "bar");
47
47
48 var namer = CompileUnitNamer.builder()
48 var namer = CompileUnitNamer.builder()
49 .addUnits(List.of(later, earlier))
49 .addUnits(List.of(later, earlier))
50 .nameCollisionPolicy(NameCollisionPolicy.RESOLVE)
50 .nameCollisionPolicy(NameCollisionPolicy.RESOLVE)
51 .build();
51 .build();
52
52
53 assertEquals("fooVariantBar", namer.resolveName(earlier));
53 assertEquals("fooVariantBar", namer.resolveName(earlier));
54 assertEquals("fooVariantBar2", namer.resolveName(later));
54 assertEquals("fooVariantBar2", namer.resolveName(later));
55 }
55 }
56
56
57 @Test
57 @Test
58 void rejectsUnknownCompileUnitLookup() {
58 void rejectsUnknownCompileUnitLookup() {
59 var known = unit("browser", "main");
59 var known = unit("browser", "main");
60 var unknown = unit("browser", "test");
60 var unknown = unit("browser", "test");
61
61
62 var namer = CompileUnitNamer.builder()
62 var namer = CompileUnitNamer.builder()
63 .addUnits(List.of(known))
63 .addUnits(List.of(known))
64 .build();
64 .build();
65
65
66 var ex = assertThrows(IllegalArgumentException.class, () -> namer.resolveName(unknown));
66 var ex = assertThrows(InvalidUserDataException.class, () -> namer.resolveName(unknown));
67 assertTrue(ex.getMessage().contains("Compile unit"));
67 assertTrue(ex.getMessage().contains("Compile unit"));
68 assertTrue(ex.getMessage().contains("associated name"));
68 assertTrue(ex.getMessage().contains("associated name"));
69 }
69 }
70
70
71 private static CompileUnit unit(String variantName, String layerName) {
71 private static CompileUnit unit(String variantName, String layerName) {
72 return new CompileUnit(
72 return new CompileUnit(
73 new TestVariant(variantName),
73 new TestVariant(variantName),
74 new TestLayer(layerName));
74 new TestLayer(layerName));
75 }
75 }
76
76
77 private record TestVariant(String value) implements Variant {
77 private record TestVariant(String value) implements Variant {
78 @Override
78 @Override
79 public String getName() {
79 public String getName() {
80 return value;
80 return value;
81 }
81 }
82 }
82 }
83
83
84 private record TestLayer(String value) implements Layer {
84 private record TestLayer(String value) implements Layer {
85 @Override
85 @Override
86 public String getName() {
86 public String getName() {
87 return value;
87 return value;
88 }
88 }
89 }
89 }
90 }
90 }
@@ -1,42 +1,43
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5
5
6 import org.gradle.api.InvalidUserDataException;
6 import org.junit.jupiter.api.Test;
7 import org.junit.jupiter.api.Test;
7
8
8 class DefaultCompileUnitNamingPolicyTest {
9 class DefaultCompileUnitNamingPolicyTest {
9 @Test
10 @Test
10 void usesFailPolicyByDefault() {
11 void usesFailPolicyByDefault() {
11 var policy = new DefaultCompileUnitNamingPolicy();
12 var policy = new DefaultCompileUnitNamingPolicy();
12
13
13 assertEquals(NameCollisionPolicy.FAIL, policy.policy());
14 assertEquals(NameCollisionPolicy.FAIL, policy.policy());
14 }
15 }
15
16
16 @Test
17 @Test
17 void switchesToResolvePolicyWhenSelected() {
18 void switchesToResolvePolicyWhenSelected() {
18 var policy = new DefaultCompileUnitNamingPolicy();
19 var policy = new DefaultCompileUnitNamingPolicy();
19
20
20 policy.resolveNameCollision();
21 policy.resolveNameCollision();
21
22
22 assertEquals(NameCollisionPolicy.RESOLVE, policy.policy());
23 assertEquals(NameCollisionPolicy.RESOLVE, policy.policy());
23 }
24 }
24
25
25 @Test
26 @Test
26 void rejectsChangingPolicyAfterItWasSelected() {
27 void rejectsChangingPolicyAfterItWasSelected() {
27 var policy = new DefaultCompileUnitNamingPolicy();
28 var policy = new DefaultCompileUnitNamingPolicy();
28
29
29 policy.resolveNameCollision();
30 policy.resolveNameCollision();
30
31
31 assertThrows(IllegalStateException.class, policy::failOnNameCollision);
32 assertThrows(InvalidUserDataException.class, policy::failOnNameCollision);
32 }
33 }
33
34
34 @Test
35 @Test
35 void rejectsChangingPolicyAfterItWasFinalized() {
36 void rejectsChangingPolicyAfterItWasFinalized() {
36 var policy = new DefaultCompileUnitNamingPolicy();
37 var policy = new DefaultCompileUnitNamingPolicy();
37
38
38 policy.finalizePolicy();
39 policy.finalizePolicy();
39
40
40 assertThrows(IllegalStateException.class, policy::resolveNameCollision);
41 assertThrows(InvalidUserDataException.class, policy::resolveNameCollision);
41 }
42 }
42 }
43 }
@@ -1,51 +1,52
1 package org.implab.gradle.variants.sources.internal;
1 package org.implab.gradle.variants.sources.internal;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5
5
6 import org.gradle.api.InvalidUserDataException;
6 import org.junit.jupiter.api.Test;
7 import org.junit.jupiter.api.Test;
7
8
8 class DefaultLateConfigurationPolicySpecTest {
9 class DefaultLateConfigurationPolicySpecTest {
9 @Test
10 @Test
10 void usesFailModeByDefault() {
11 void usesFailModeByDefault() {
11 var policy = new DefaultLateConfigurationPolicySpec();
12 var policy = new DefaultLateConfigurationPolicySpec();
12
13
13 assertEquals(LateConfigurationMode.FAIL, policy.mode());
14 assertEquals(LateConfigurationMode.FAIL, policy.mode());
14 }
15 }
15
16
16 @Test
17 @Test
17 void switchesToWarnModeWhenSelected() {
18 void switchesToWarnModeWhenSelected() {
18 var policy = new DefaultLateConfigurationPolicySpec();
19 var policy = new DefaultLateConfigurationPolicySpec();
19
20
20 policy.warnOnLateConfiguration();
21 policy.warnOnLateConfiguration();
21
22
22 assertEquals(LateConfigurationMode.WARN, policy.mode());
23 assertEquals(LateConfigurationMode.WARN, policy.mode());
23 }
24 }
24
25
25 @Test
26 @Test
26 void switchesToApplyModeWhenSelected() {
27 void switchesToApplyModeWhenSelected() {
27 var policy = new DefaultLateConfigurationPolicySpec();
28 var policy = new DefaultLateConfigurationPolicySpec();
28
29
29 policy.allowLateConfiguration();
30 policy.allowLateConfiguration();
30
31
31 assertEquals(LateConfigurationMode.APPLY, policy.mode());
32 assertEquals(LateConfigurationMode.APPLY, policy.mode());
32 }
33 }
33
34
34 @Test
35 @Test
35 void rejectsChangingPolicyAfterItWasSelected() {
36 void rejectsChangingPolicyAfterItWasSelected() {
36 var policy = new DefaultLateConfigurationPolicySpec();
37 var policy = new DefaultLateConfigurationPolicySpec();
37
38
38 policy.warnOnLateConfiguration();
39 policy.warnOnLateConfiguration();
39
40
40 assertThrows(IllegalStateException.class, policy::failOnLateConfiguration);
41 assertThrows(InvalidUserDataException.class, policy::failOnLateConfiguration);
41 }
42 }
42
43
43 @Test
44 @Test
44 void rejectsChangingPolicyAfterItWasFinalized() {
45 void rejectsChangingPolicyAfterItWasFinalized() {
45 var policy = new DefaultLateConfigurationPolicySpec();
46 var policy = new DefaultLateConfigurationPolicySpec();
46
47
47 policy.finalizePolicy();
48 policy.finalizePolicy();
48
49
49 assertThrows(IllegalStateException.class, policy::allowLateConfiguration);
50 assertThrows(InvalidUserDataException.class, policy::allowLateConfiguration);
50 }
51 }
51 }
52 }
General Comments 0
You need to be logged in to leave comments. Login now