| @@ -120,7 +120,10 public abstract class BuildVariant imple | |||||
| 120 |
|
120 | |||
| 121 | public LayerLink link(String from, String to, String kind) { |
|
121 | public LayerLink link(String from, String to, String kind) { | |
| 122 | ensureMutable("add links"); |
|
122 | ensureMutable("add links"); | |
| 123 |
var link = new LayerLink( |
|
123 | var link = new LayerLink( | |
|
|
124 | requireLinkValue("from", from), | |||
|
|
125 | requireLinkValue("to", to), | |||
|
|
126 | requireLinkValue("kind", kind)); | |||
| 124 | links.add(link); |
|
127 | links.add(link); | |
| 125 | return link; |
|
128 | return link; | |
| 126 | } |
|
129 | } | |
| @@ -191,6 +194,13 public abstract class BuildVariant imple | |||||
| 191 | throw new InvalidUserDataException("Variant '" + name + "' is finalized and cannot " + operation); |
|
194 | throw new InvalidUserDataException("Variant '" + name + "' is finalized and cannot " + operation); | |
| 192 | } |
|
195 | } | |
| 193 |
|
196 | |||
|
|
197 | private static String requireLinkValue(String field, String value) { | |||
|
|
198 | if (value == null || value.trim().isEmpty()) | |||
|
|
199 | throw new InvalidUserDataException("Link '" + field + "' must not be null or blank"); | |||
|
|
200 | ||||
|
|
201 | return value.trim(); | |||
|
|
202 | } | |||
|
|
203 | ||||
| 194 | public final class RolesSpec { |
|
204 | public final class RolesSpec { | |
| 195 | public BuildRole role(String name, Action<? super BuildRole> configure) { |
|
205 | public BuildRole role(String name, Action<? super BuildRole> configure) { | |
| 196 | return BuildVariant.this.role(name, configure); |
|
206 | return BuildVariant.this.role(name, configure); | |
| @@ -158,8 +158,18 public abstract class BuildVariantsExten | |||||
| 158 | var errors = new ArrayList<String>(); |
|
158 | var errors = new ArrayList<String>(); | |
| 159 |
|
159 | |||
| 160 | var layersByName = new LinkedHashMap<String, BuildLayer>(); |
|
160 | var layersByName = new LinkedHashMap<String, BuildLayer>(); | |
| 161 | for (var layer : layers) |
|
161 | for (var layer : layers) { | |
| 162 |
|
|
162 | var layerName = normalize(layer.getName()); | |
|
|
163 | if (layerName == null) { | |||
|
|
164 | errors.add("Layer name must not be blank"); | |||
|
|
165 | continue; | |||
|
|
166 | } | |||
|
|
167 | ||||
|
|
168 | var previous = layersByName.putIfAbsent(layerName, layer); | |||
|
|
169 | if (previous != null) { | |||
|
|
170 | errors.add("Layer '" + layerName + "' is declared more than once"); | |||
|
|
171 | } | |||
|
|
172 | } | |||
| 163 |
|
173 | |||
| 164 | for (var variant : variants) |
|
174 | for (var variant : variants) | |
| 165 | validateVariant(variant, layersByName, errors); |
|
175 | validateVariant(variant, layersByName, errors); | |
| @@ -174,28 +184,69 public abstract class BuildVariantsExten | |||||
| 174 | } |
|
184 | } | |
| 175 |
|
185 | |||
| 176 | private static void validateVariant(BuildVariant variant, Map<String, BuildLayer> layersByName, List<String> errors) { |
|
186 | private static void validateVariant(BuildVariant variant, Map<String, BuildLayer> layersByName, List<String> errors) { | |
|
|
187 | var variantName = normalize(variant.getName()); | |||
|
|
188 | if (variantName == null) { | |||
|
|
189 | errors.add("Variant name must not be blank"); | |||
|
|
190 | return; | |||
|
|
191 | } | |||
|
|
192 | ||||
|
|
193 | validateRoleAndArtifactNames(variant, errors); | |||
| 177 | var variantLayers = validateRoleMappings(variant, layersByName, errors); |
|
194 | var variantLayers = validateRoleMappings(variant, layersByName, errors); | |
| 178 | validateLinks(variant, variantLayers, errors); |
|
195 | validateLinks(variant, variantLayers, errors); | |
| 179 | } |
|
196 | } | |
| 180 |
|
197 | |||
|
|
198 | private static void validateRoleAndArtifactNames(BuildVariant variant, List<String> errors) { | |||
|
|
199 | var roleNames = new LinkedHashSet<String>(); | |||
|
|
200 | for (var role : variant.getRoles()) { | |||
|
|
201 | var roleName = normalize(role.getName()); | |||
|
|
202 | if (roleName == null) { | |||
|
|
203 | errors.add("Variant '" + variant.getName() + "' contains blank role name"); | |||
|
|
204 | continue; | |||
|
|
205 | } | |||
|
|
206 | if (!roleNames.add(roleName)) { | |||
|
|
207 | errors.add("Variant '" + variant.getName() + "' contains duplicated role name '" + roleName + "'"); | |||
|
|
208 | } | |||
|
|
209 | } | |||
|
|
210 | ||||
|
|
211 | var slotNames = new LinkedHashSet<String>(); | |||
|
|
212 | for (var slot : variant.getArtifactSlots()) { | |||
|
|
213 | var slotName = normalize(slot.getName()); | |||
|
|
214 | if (slotName == null) { | |||
|
|
215 | errors.add("Variant '" + variant.getName() + "' contains blank artifact slot name"); | |||
|
|
216 | continue; | |||
|
|
217 | } | |||
|
|
218 | if (!slotNames.add(slotName)) { | |||
|
|
219 | errors.add("Variant '" + variant.getName() + "' contains duplicated artifact slot name '" + slotName + "'"); | |||
|
|
220 | } | |||
|
|
221 | } | |||
|
|
222 | } | |||
|
|
223 | ||||
| 181 | private static Set<String> validateRoleMappings(BuildVariant variant, Map<String, BuildLayer> layersByName, |
|
224 | private static Set<String> validateRoleMappings(BuildVariant variant, Map<String, BuildLayer> layersByName, | |
| 182 | List<String> errors) { |
|
225 | List<String> errors) { | |
| 183 | var variantLayers = new LinkedHashSet<String>(); |
|
226 | var variantLayers = new LinkedHashSet<String>(); | |
| 184 |
|
227 | |||
| 185 | for (var role : variant.getRoles()) { |
|
228 | for (var role : variant.getRoles()) { | |
|
|
229 | var seenLayers = new LinkedHashSet<String>(); | |||
| 186 | for (var layerName : role.getLayers().getOrElse(List.of())) { |
|
230 | for (var layerName : role.getLayers().getOrElse(List.of())) { | |
| 187 | if (isBlank(layerName)) { |
|
231 | var normalizedLayerName = normalize(layerName); | |
|
|
232 | if (normalizedLayerName == null) { | |||
| 188 | errors.add("Variant '" + variant.getName() + "', role '" + role.getName() + "' contains blank layer name"); |
|
233 | errors.add("Variant '" + variant.getName() + "', role '" + role.getName() + "' contains blank layer name"); | |
| 189 | continue; |
|
234 | continue; | |
| 190 | } |
|
235 | } | |
| 191 |
|
236 | |||
| 192 | var layer = layersByName.get(layerName); |
|
237 | var layer = layersByName.get(normalizedLayerName); | |
| 193 | if (layer == null) { |
|
238 | if (layer == null) { | |
| 194 | errors.add("Variant '" + variant.getName() + "' references unknown layer '" + layerName + "'"); |
|
239 | errors.add("Variant '" + variant.getName() + "' references unknown layer '" + normalizedLayerName + "'"); | |
| 195 | continue; |
|
240 | continue; | |
| 196 | } |
|
241 | } | |
| 197 |
|
242 | |||
| 198 |
|
|
243 | if (!seenLayers.add(normalizedLayerName)) { | |
|
|
244 | errors.add("Variant '" + variant.getName() + "', role '" + role.getName() | |||
|
|
245 | + "' contains duplicated layer reference '" + normalizedLayerName + "'"); | |||
|
|
246 | continue; | |||
|
|
247 | } | |||
|
|
248 | ||||
|
|
249 | variantLayers.add(normalizedLayerName); | |||
| 199 | } |
|
250 | } | |
| 200 | } |
|
251 | } | |
| 201 |
|
252 | |||
| @@ -40,6 +40,7 public abstract class VariantSourcesExte | |||||
| 40 | private final List<SourceSetContext> boundContexts = new ArrayList<>(); |
|
40 | private final List<SourceSetContext> boundContexts = new ArrayList<>(); | |
| 41 | private final LinkedHashMap<String, NamedDomainObjectProvider<GenericSourceSet>> sourceSetsByName = new LinkedHashMap<>(); |
|
41 | private final LinkedHashMap<String, NamedDomainObjectProvider<GenericSourceSet>> sourceSetsByName = new LinkedHashMap<>(); | |
| 42 | private final LinkedHashMap<String, String> sourceSetLayersByName = new LinkedHashMap<>(); |
|
42 | private final LinkedHashMap<String, String> sourceSetLayersByName = new LinkedHashMap<>(); | |
|
|
43 | private boolean sourceSetsRegistered; | |||
| 43 |
|
44 | |||
| 44 | @Inject |
|
45 | @Inject | |
| 45 | public VariantSourcesExtension(ObjectFactory objects) { |
|
46 | public VariantSourcesExtension(ObjectFactory objects) { | |
| @@ -137,6 +138,10 public abstract class VariantSourcesExte | |||||
| 137 | } |
|
138 | } | |
| 138 |
|
139 | |||
| 139 | void registerSourceSets(BuildVariantsExtension variants, NamedDomainObjectContainer<GenericSourceSet> sources) { |
|
140 | void registerSourceSets(BuildVariantsExtension variants, NamedDomainObjectContainer<GenericSourceSet> sources) { | |
|
|
141 | if (sourceSetsRegistered) { | |||
|
|
142 | throw new InvalidUserDataException("variantSources source sets are already registered"); | |||
|
|
143 | } | |||
|
|
144 | ||||
| 140 | validateBindings(variants); |
|
145 | validateBindings(variants); | |
| 141 |
|
146 | |||
| 142 | var usages = layerUsages(variants).toList(); |
|
147 | var usages = layerUsages(variants).toList(); | |
| @@ -157,6 +162,8 public abstract class VariantSourcesExte | |||||
| 157 | registeredContexts.size() - registeredBefore, |
|
162 | registeredContexts.size() - registeredBefore, | |
| 158 | boundContexts.size() - boundBefore, |
|
163 | boundContexts.size() - boundBefore, | |
| 159 | sourceSetsByName.size()); |
|
164 | sourceSetsByName.size()); | |
|
|
165 | ||||
|
|
166 | sourceSetsRegistered = true; | |||
| 160 | } |
|
167 | } | |
| 161 |
|
168 | |||
| 162 | private Stream<LayerUsage> layerUsages(BuildVariantsExtension variants) { |
|
169 | private Stream<LayerUsage> layerUsages(BuildVariantsExtension variants) { | |
| @@ -156,7 +156,7 class VariantsPluginFunctionalTest { | |||||
| 156 | link('a', 'b', null) |
|
156 | link('a', 'b', null) | |
| 157 | } |
|
157 | } | |
| 158 | } |
|
158 | } | |
| 159 | """, "has incomplete link (from/to/kind are required)"); |
|
159 | """, "Link 'kind' must not be null or blank"); | |
| 160 | } |
|
160 | } | |
| 161 |
|
161 | |||
| 162 | @Test |
|
162 | @Test | |
| @@ -224,6 +224,25 class VariantsPluginFunctionalTest { | |||||
| 224 | } |
|
224 | } | |
| 225 |
|
225 | |||
| 226 | @Test |
|
226 | @Test | |
|
|
227 | void failsOnDuplicatedLayerReferenceInRole() throws Exception { | |||
|
|
228 | assertBuildFails(""" | |||
|
|
229 | plugins { | |||
|
|
230 | id 'org.implab.gradle-variants' | |||
|
|
231 | } | |||
|
|
232 | ||||
|
|
233 | variants { | |||
|
|
234 | layer('a') | |||
|
|
235 | ||||
|
|
236 | variant('browser') { | |||
|
|
237 | role('main') { | |||
|
|
238 | layers('a', 'a') | |||
|
|
239 | } | |||
|
|
240 | } | |||
|
|
241 | } | |||
|
|
242 | """, "contains duplicated layer reference 'a'"); | |||
|
|
243 | } | |||
|
|
244 | ||||
|
|
245 | @Test | |||
| 227 | void failsOnLateLayerMutationAfterFinalize() throws Exception { |
|
246 | void failsOnLateLayerMutationAfterFinalize() throws Exception { | |
| 228 | assertBuildFails(""" |
|
247 | assertBuildFails(""" | |
| 229 | plugins { |
|
248 | plugins { | |
General Comments 0
You need to be logged in to leave comments.
Login now
