| @@ -0,0 +1,75 | |||||
|
|
1 | package org.implab.gradle.common.core.lang; | |||
|
|
2 | ||||
|
|
3 | import java.nio.file.Path; | |||
|
|
4 | import java.nio.file.Paths; | |||
|
|
5 | import java.util.LinkedList; | |||
|
|
6 | import java.util.List; | |||
|
|
7 | import java.util.Set; | |||
|
|
8 | import java.util.function.BiConsumer; | |||
|
|
9 | import java.util.function.BinaryOperator; | |||
|
|
10 | import java.util.function.Function; | |||
|
|
11 | import java.util.function.Supplier; | |||
|
|
12 | import java.util.stream.Collector; | |||
|
|
13 | import java.util.stream.Stream; | |||
|
|
14 | ||||
|
|
15 | public final class FilePaths { | |||
|
|
16 | private static PathCollector defaultPathCollector = new PathCollector(); | |||
|
|
17 | ||||
|
|
18 | private FilePaths() { | |||
|
|
19 | } | |||
|
|
20 | ||||
|
|
21 | public static String cat(String fist, String... extra) { | |||
|
|
22 | return catPath(fist, extra).toString(); | |||
|
|
23 | } | |||
|
|
24 | ||||
|
|
25 | public static Path catPath(String first, String ...extra) { | |||
|
|
26 | return Stream.concat(Stream.of(first), Stream.of(extra)) | |||
|
|
27 | .collect(asPath()); | |||
|
|
28 | } | |||
|
|
29 | ||||
|
|
30 | public static Collector<String, List<String>, Path> asPath() { | |||
|
|
31 | return defaultPathCollector; | |||
|
|
32 | } | |||
|
|
33 | ||||
|
|
34 | static class PathCollector implements Collector<String, List<String>, Path> { | |||
|
|
35 | ||||
|
|
36 | private final Function<String, String> sanitizer; | |||
|
|
37 | ||||
|
|
38 | public PathCollector() { | |||
|
|
39 | sanitizer = Strings::sanitizeFileName; | |||
|
|
40 | } | |||
|
|
41 | ||||
|
|
42 | @Override | |||
|
|
43 | public BiConsumer<List<String>, String> accumulator() { | |||
|
|
44 | return (components, item) -> components.add(sanitizer.apply(item)); | |||
|
|
45 | } | |||
|
|
46 | ||||
|
|
47 | @Override | |||
|
|
48 | public Set<Characteristics> characteristics() { | |||
|
|
49 | return Set.of(); | |||
|
|
50 | } | |||
|
|
51 | ||||
|
|
52 | @Override | |||
|
|
53 | public BinaryOperator<List<String>> combiner() { | |||
|
|
54 | return (first, extra) -> { | |||
|
|
55 | first.addAll(extra); | |||
|
|
56 | return first; | |||
|
|
57 | }; | |||
|
|
58 | } | |||
|
|
59 | ||||
|
|
60 | @Override | |||
|
|
61 | public Function<List<String>, Path> finisher() { | |||
|
|
62 | return components -> Paths.get( | |||
|
|
63 | components.get(0), | |||
|
|
64 | components.subList(1, components.size()) | |||
|
|
65 | .toArray(String[]::new)); | |||
|
|
66 | ||||
|
|
67 | } | |||
|
|
68 | ||||
|
|
69 | @Override | |||
|
|
70 | public Supplier<List<String>> supplier() { | |||
|
|
71 | return () -> new LinkedList<>(); | |||
|
|
72 | } | |||
|
|
73 | ||||
|
|
74 | } | |||
|
|
75 | } | |||
| @@ -0,0 +1,100 | |||||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |||
|
|
2 | ||||
|
|
3 | import java.util.HashMap; | |||
|
|
4 | import java.util.Map; | |||
|
|
5 | ||||
|
|
6 | import org.gradle.api.Action; | |||
|
|
7 | import org.gradle.api.file.Directory; | |||
|
|
8 | import org.gradle.api.file.DirectoryProperty; | |||
|
|
9 | import org.gradle.api.model.ObjectFactory; | |||
|
|
10 | import org.gradle.api.provider.Provider; | |||
|
|
11 | import org.gradle.api.tasks.Copy; | |||
|
|
12 | import org.gradle.api.tasks.TaskContainer; | |||
|
|
13 | import org.gradle.language.base.plugins.LifecycleBasePlugin; | |||
|
|
14 | import org.implab.gradle.common.core.lang.FilePaths; | |||
|
|
15 | import org.implab.gradle.common.core.lang.Strings; | |||
|
|
16 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; | |||
|
|
17 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |||
|
|
18 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |||
|
|
19 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |||
|
|
20 | import org.implab.gradle.variants.sources.CompileUnitsView; | |||
|
|
21 | import org.implab.gradle.variants.sources.RoleProjectionsView; | |||
|
|
22 | import org.implab.gradle.variants.sources.SourceSetMaterializer; | |||
|
|
23 | ||||
|
|
24 | public class ArtifactConfigurationHandler { | |||
|
|
25 | private final ArtifactAssemblyRegistry registry; | |||
|
|
26 | ||||
|
|
27 | private final CompileUnitsView compileUnitsView; | |||
|
|
28 | ||||
|
|
29 | private final RoleProjectionsView roleProjectionsView; | |||
|
|
30 | ||||
|
|
31 | private final SourceSetMaterializer sourceSetMaterializer; | |||
|
|
32 | ||||
|
|
33 | private final Map<ArtifactSlot, SlotContributionVisitor> visitors = new HashMap<>(); | |||
|
|
34 | ||||
|
|
35 | private final ObjectFactory objectFactory; | |||
|
|
36 | ||||
|
|
37 | private final DirectoryProperty assembliesDirectory; | |||
|
|
38 | ||||
|
|
39 | private final TaskContainer tasks; | |||
|
|
40 | ||||
|
|
41 | public ArtifactConfigurationHandler( | |||
|
|
42 | ArtifactAssemblyRegistry registry, | |||
|
|
43 | CompileUnitsView compileUnitsView, | |||
|
|
44 | RoleProjectionsView roleProjectionsView, | |||
|
|
45 | SourceSetMaterializer sourceSetMaterializer, | |||
|
|
46 | ObjectFactory objectFactory, | |||
|
|
47 | TaskContainer tasks) { | |||
|
|
48 | this.registry = registry; | |||
|
|
49 | this.compileUnitsView = compileUnitsView; | |||
|
|
50 | this.roleProjectionsView = roleProjectionsView; | |||
|
|
51 | this.sourceSetMaterializer = sourceSetMaterializer; | |||
|
|
52 | this.objectFactory = objectFactory; | |||
|
|
53 | this.assembliesDirectory = objectFactory.directoryProperty(); | |||
|
|
54 | this.tasks = tasks; | |||
|
|
55 | } | |||
|
|
56 | ||||
|
|
57 | public SlotContributionVisitor slotAssembler(ArtifactSlot artifactSlot) { | |||
|
|
58 | return visitors.computeIfAbsent(artifactSlot, this::newVisitor); | |||
|
|
59 | } | |||
|
|
60 | ||||
|
|
61 | public void configureVariant(OutgoingVariant outgoingVariant, Action<? super VariantArtifactsSpec> action) { | |||
|
|
62 | var spec = new DefaultVariantArtifactSpec(outgoingVariant, objectFactory, this); | |||
|
|
63 | ||||
|
|
64 | action.execute(spec); | |||
|
|
65 | } | |||
|
|
66 | ||||
|
|
67 | private SlotContributionVisitor newVisitor(ArtifactSlot artifactSlot) { | |||
|
|
68 | var fileCollection = objectFactory.fileCollection(); | |||
|
|
69 | var outputDirectory = outputDirectory(artifactSlot); | |||
|
|
70 | ||||
|
|
71 | var task = tasks.register(assembleTaskName(artifactSlot), Copy.class, copy -> { | |||
|
|
72 | copy.setGroup(LifecycleBasePlugin.BUILD_GROUP); | |||
|
|
73 | copy.into(outputDirectory); | |||
|
|
74 | copy.from(fileCollection); | |||
|
|
75 | }); | |||
|
|
76 | ||||
|
|
77 | // ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ Π·Π°Π΄Π°ΡΠ° ΠΈ Π°ΡΠ΅ΡΠ°ΠΊΡ ΡΠ±ΠΎΡΠΊΠΈ ΡΠ»ΠΎΡΠ° | |||
|
|
78 | registry.register(artifactSlot, task, t -> outputDirectory); | |||
|
|
79 | ||||
|
|
80 | return new SlotInputsAssembler( | |||
|
|
81 | artifactSlot, | |||
|
|
82 | fileCollection, | |||
|
|
83 | compileUnitsView, | |||
|
|
84 | roleProjectionsView, | |||
|
|
85 | sourceSetMaterializer); | |||
|
|
86 | } | |||
|
|
87 | ||||
|
|
88 | private String assembleTaskName(ArtifactSlot artifactSlot) { | |||
|
|
89 | return "assemble" | |||
|
|
90 | + Strings.capitalize(artifactSlot.variant().getName()) | |||
|
|
91 | + Strings.capitalize(artifactSlot.slot().getName()); | |||
|
|
92 | } | |||
|
|
93 | ||||
|
|
94 | private Provider<Directory> outputDirectory(ArtifactSlot artifactSlot) { | |||
|
|
95 | return assembliesDirectory.dir( | |||
|
|
96 | FilePaths.cat( | |||
|
|
97 | artifactSlot.variant().getName(), | |||
|
|
98 | artifactSlot.slot().getName())); | |||
|
|
99 | } | |||
|
|
100 | } | |||
| @@ -0,0 +1,51 | |||||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |||
|
|
2 | ||||
|
|
3 | import org.gradle.api.Action; | |||
|
|
4 | import org.gradle.api.model.ObjectFactory; | |||
|
|
5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |||
|
|
6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |||
|
|
7 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |||
|
|
8 | import org.implab.gradle.variants.artifacts.Slot; | |||
|
|
9 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |||
|
|
10 | ||||
|
|
11 | class DefaultVariantArtifactSpec implements VariantArtifactsSpec { | |||
|
|
12 | ||||
|
|
13 | private final ObjectFactory objectFactory; | |||
|
|
14 | private final ArtifactConfigurationHandler assemblyBuilder; | |||
|
|
15 | ||||
|
|
16 | private final OutgoingVariant outgoingVariant; | |||
|
|
17 | ||||
|
|
18 | DefaultVariantArtifactSpec( | |||
|
|
19 | OutgoingVariant outgoingVariant, | |||
|
|
20 | ObjectFactory objectFactory, | |||
|
|
21 | ArtifactConfigurationHandler assemblyBuilder) { | |||
|
|
22 | this.objectFactory = objectFactory; | |||
|
|
23 | this.assemblyBuilder = assemblyBuilder; | |||
|
|
24 | this.outgoingVariant = outgoingVariant; | |||
|
|
25 | } | |||
|
|
26 | ||||
|
|
27 | @Override | |||
|
|
28 | public void slot(String name, Action<? super ArtifactAssemblySpec> action) { | |||
|
|
29 | var slot = outgoingVariant.getSlots().maybeCreate(name); | |||
|
|
30 | ||||
|
|
31 | configureSlot(slot, action); | |||
|
|
32 | } | |||
|
|
33 | ||||
|
|
34 | @Override | |||
|
|
35 | public void primarySlot(String name, Action<? super ArtifactAssemblySpec> action) { | |||
|
|
36 | var slot = outgoingVariant.getSlots().maybeCreate(name); | |||
|
|
37 | outgoingVariant.getPrimarySlot().set(slot); | |||
|
|
38 | ||||
|
|
39 | configureSlot(slot, action); | |||
|
|
40 | } | |||
|
|
41 | ||||
|
|
42 | private void configureSlot(Slot slot, Action<? super ArtifactAssemblySpec> action) { | |||
|
|
43 | var artifactSlot = new ArtifactSlot(outgoingVariant.getVariant(), slot); | |||
|
|
44 | ||||
|
|
45 | var inputsAssembler = assemblyBuilder.slotAssembler(artifactSlot); | |||
|
|
46 | ||||
|
|
47 | var spec = new DefaultArtifactAssemblySpec(objectFactory, inputsAssembler.consumer()); | |||
|
|
48 | action.execute(spec); | |||
|
|
49 | } | |||
|
|
50 | ||||
|
|
51 | } | |||
| @@ -1,70 +1,149 | |||||
| 1 | package org.implab.gradle.common.core.lang; |
|
1 | package org.implab.gradle.common.core.lang; | |
| 2 |
|
2 | |||
|
|
3 | import java.util.function.Consumer; | |||
|
|
4 | import java.util.function.Function; | |||
| 3 | import java.util.regex.Pattern; |
|
5 | import java.util.regex.Pattern; | |
| 4 |
|
6 | |||
| 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
7 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 6 | import org.gradle.api.provider.Provider; |
|
8 | import org.gradle.api.provider.Provider; | |
| 7 |
|
9 | |||
| 8 | @NonNullByDefault |
|
10 | @NonNullByDefault | |
| 9 | public class Strings { |
|
11 | public class Strings { | |
| 10 |
|
12 | |||
|
|
13 | private static final boolean[] ALLOWED_FILE_NAME_CHAR = new boolean[128]; | |||
|
|
14 | ||||
|
|
15 | private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); | |||
|
|
16 | ||||
| 11 | private static final Pattern firstLetter = Pattern.compile("^\\w"); |
|
17 | private static final Pattern firstLetter = Pattern.compile("^\\w"); | |
| 12 |
|
18 | |||
| 13 | private static final Pattern INVALID_NAME_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); |
|
19 | private static final Pattern INVALID_NAME_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); | |
| 14 |
|
20 | |||
|
|
21 | static { | |||
|
|
22 | for (char c = '0'; c <= '9'; c++) | |||
|
|
23 | ALLOWED_FILE_NAME_CHAR[c] = true; | |||
|
|
24 | for (char c = 'A'; c <= 'Z'; c++) | |||
|
|
25 | ALLOWED_FILE_NAME_CHAR[c] = true; | |||
|
|
26 | for (char c = 'a'; c <= 'z'; c++) | |||
|
|
27 | ALLOWED_FILE_NAME_CHAR[c] = true; | |||
|
|
28 | ALLOWED_FILE_NAME_CHAR['.'] = true; | |||
|
|
29 | ALLOWED_FILE_NAME_CHAR['_'] = true; | |||
|
|
30 | ALLOWED_FILE_NAME_CHAR['-'] = true; | |||
|
|
31 | } | |||
|
|
32 | ||||
| 15 | public static String capitalize(String string) { |
|
33 | public static String capitalize(String string) { | |
| 16 | return string == null ? null |
|
34 | return string == null ? null | |
| 17 | : string.length() == 0 ? string |
|
35 | : string.length() == 0 ? string | |
| 18 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); |
|
36 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); | |
| 19 | } |
|
37 | } | |
| 20 |
|
38 | |||
| 21 | public static String toCamelCase(String name) { |
|
39 | public static String toCamelCase(String name) { | |
| 22 | if (name == null || name.isEmpty()) |
|
40 | if (name == null || name.isEmpty()) | |
| 23 | return name; |
|
41 | return name; | |
| 24 | StringBuilder out = new StringBuilder(name.length()); |
|
42 | StringBuilder out = new StringBuilder(name.length()); | |
| 25 | boolean up = false; |
|
43 | boolean up = false; | |
| 26 | boolean first = true; |
|
44 | boolean first = true; | |
| 27 | for (int i = 0; i < name.length(); i++) { |
|
45 | for (int i = 0; i < name.length(); i++) { | |
| 28 | char c = name.charAt(i); |
|
46 | char c = name.charAt(i); | |
| 29 | switch (c) { |
|
47 | switch (c) { | |
| 30 | case '-', '_', ' ', '.' -> up = true; |
|
48 | case '-', '_', ' ', '.' -> up = true; | |
| 31 | default -> { |
|
49 | default -> { | |
| 32 | out.append( |
|
50 | out.append( | |
| 33 | first ? Character.toLowerCase(c) |
|
51 | first ? Character.toLowerCase(c) | |
| 34 | : up ? Character.toUpperCase(c): c); |
|
52 | : up ? Character.toUpperCase(c): c); | |
| 35 | up = false; |
|
53 | up = false; | |
| 36 | first = false; |
|
54 | first = false; | |
| 37 | } |
|
55 | } | |
| 38 | } |
|
56 | } | |
| 39 | } |
|
57 | } | |
| 40 | return out.toString(); |
|
58 | return out.toString(); | |
| 41 | } |
|
59 | } | |
| 42 |
|
60 | |||
| 43 | public static void argumentNotNullOrEmpty(String value, String argumentName) { |
|
61 | public static void argumentNotNullOrEmpty(String value, String argumentName) { | |
| 44 | if (value == null || value.length() == 0) |
|
62 | if (value == null || value.length() == 0) | |
| 45 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); |
|
63 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); | |
| 46 | } |
|
64 | } | |
| 47 |
|
65 | |||
| 48 | public static void argumentNotNullOrBlank(String value, String argumentName) { |
|
66 | public static void argumentNotNullOrBlank(String value, String argumentName) { | |
| 49 | if (value == null || value.trim().length() == 0) |
|
67 | if (value == null || value.trim().length() == 0) | |
| 50 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); |
|
68 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); | |
| 51 | } |
|
69 | } | |
| 52 |
|
70 | |||
| 53 | public static String requireNonBlank(String value) { |
|
71 | public static String requireNonBlank(String value) { | |
| 54 | argumentNotNullOrBlank(value, "value"); |
|
72 | argumentNotNullOrBlank(value, "value"); | |
| 55 | return value; |
|
73 | return value; | |
| 56 | } |
|
74 | } | |
| 57 |
|
75 | |||
|
|
76 | public static String requireNonEmpty(String value) { | |||
|
|
77 | argumentNotNullOrEmpty(value, "value"); | |||
|
|
78 | return value; | |||
|
|
79 | } | |||
|
|
80 | ||||
|
|
81 | ||||
| 58 | public static String sanitizeName(String value) { |
|
82 | public static String sanitizeName(String value) { | |
| 59 | return INVALID_NAME_CHAR.matcher(value).replaceAll("_"); |
|
83 | return INVALID_NAME_CHAR.matcher(value).replaceAll("_"); | |
| 60 | } |
|
84 | } | |
| 61 |
|
85 | |||
|
|
86 | public static String sanitizeFileName(String value) { | |||
|
|
87 | int length = value.length(); | |||
|
|
88 | for (int i = 0; i < length; i++) { | |||
|
|
89 | char c = value.charAt(i); | |||
|
|
90 | if (c >= ALLOWED_FILE_NAME_CHAR.length || !ALLOWED_FILE_NAME_CHAR[c]) | |||
|
|
91 | return sanitizeFileName(value, i); | |||
|
|
92 | } | |||
|
|
93 | return value; | |||
|
|
94 | } | |||
|
|
95 | ||||
| 62 | public static String asString(Object value) { |
|
96 | public static String asString(Object value) { | |
| 63 | if (value == null) |
|
97 | if (value == null) | |
| 64 | return null; |
|
98 | return null; | |
| 65 | if (value instanceof Provider<?> provider) |
|
99 | if (value instanceof Provider<?> provider) | |
| 66 | return asString(provider.get()); |
|
100 | return asString(provider.get()); | |
| 67 | else |
|
101 | else | |
| 68 | return value.toString(); |
|
102 | return value.toString(); | |
| 69 | } |
|
103 | } | |
|
|
104 | ||||
|
|
105 | private static String sanitizeFileName(String value, int invalidIndex) { | |||
|
|
106 | int length = value.length(); | |||
|
|
107 | StringBuilder out = new StringBuilder(length + 16); | |||
|
|
108 | out.append(value, 0, invalidIndex); | |||
|
|
109 | ||||
|
|
110 | for (int i = invalidIndex; i < length; i++) { | |||
|
|
111 | char c = value.charAt(i); | |||
|
|
112 | if (c < ALLOWED_FILE_NAME_CHAR.length && ALLOWED_FILE_NAME_CHAR[c]) { | |||
|
|
113 | out.append(c); | |||
|
|
114 | } else if (Character.isHighSurrogate(c) && i + 1 < length && Character.isLowSurrogate(value.charAt(i + 1))) { | |||
|
|
115 | appendUrlEncodedUtf8(out, Character.toCodePoint(c, value.charAt(++i))); | |||
|
|
116 | } else if (Character.isSurrogate(c)) { | |||
|
|
117 | appendUrlEncodedUtf8(out, 0xFFFD); | |||
|
|
118 | } else { | |||
|
|
119 | appendUrlEncodedUtf8(out, c); | |||
| 70 | } |
|
120 | } | |
|
|
121 | } | |||
|
|
122 | ||||
|
|
123 | return out.toString(); | |||
|
|
124 | } | |||
|
|
125 | ||||
|
|
126 | private static void appendUrlEncodedUtf8(StringBuilder out, int codePoint) { | |||
|
|
127 | if (codePoint <= 0x7F) { | |||
|
|
128 | appendUrlEncodedByte(out, codePoint); | |||
|
|
129 | } else if (codePoint <= 0x7FF) { | |||
|
|
130 | appendUrlEncodedByte(out, 0xC0 | (codePoint >>> 6)); | |||
|
|
131 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |||
|
|
132 | } else if (codePoint <= 0xFFFF) { | |||
|
|
133 | appendUrlEncodedByte(out, 0xE0 | (codePoint >>> 12)); | |||
|
|
134 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); | |||
|
|
135 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |||
|
|
136 | } else { | |||
|
|
137 | appendUrlEncodedByte(out, 0xF0 | (codePoint >>> 18)); | |||
|
|
138 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 12) & 0x3F)); | |||
|
|
139 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); | |||
|
|
140 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |||
|
|
141 | } | |||
|
|
142 | } | |||
|
|
143 | ||||
|
|
144 | private static void appendUrlEncodedByte(StringBuilder out, int value) { | |||
|
|
145 | out.append('%'); | |||
|
|
146 | out.append(HEX_DIGITS[(value >>> 4) & 0x0F]); | |||
|
|
147 | out.append(HEX_DIGITS[value & 0x0F]); | |||
|
|
148 | } | |||
|
|
149 | } | |||
| @@ -1,75 +1,78 | |||||
| 1 | package org.implab.gradle.variants; |
|
1 | package org.implab.gradle.variants; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Action; |
|
3 | import org.gradle.api.Action; | |
| 4 | import org.gradle.api.Plugin; |
|
4 | import org.gradle.api.Plugin; | |
| 5 | import org.gradle.api.Project; |
|
5 | import org.gradle.api.Project; | |
| 6 | import org.implab.gradle.common.core.lang.Deferred; |
|
6 | import org.implab.gradle.common.core.lang.Deferred; | |
| 7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; |
|
7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; | |
| 8 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
8 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; | |
| 9 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; |
|
9 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; | |
| 10 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; |
|
10 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; | |
| 11 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
11 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |
| 12 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBridge; |
|
12 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBridge; | |
|
|
13 | import org.implab.gradle.variants.artifacts.internal.DefaultVariantArtifactSpec; | |||
| 13 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; |
|
14 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; | |
| 14 | import org.implab.gradle.variants.core.Variant; |
|
15 | import org.implab.gradle.variants.core.Variant; | |
| 15 | import org.implab.gradle.variants.core.VariantsExtension; |
|
16 | import org.implab.gradle.variants.core.VariantsExtension; | |
| 16 |
|
17 | |||
| 17 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
|
18 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { | |
| 18 |
|
19 | |||
| 19 | @Override |
|
20 | @Override | |
| 20 | public void apply(Project target) { |
|
21 | public void apply(Project target) { | |
| 21 | var extensions = target.getExtensions(); |
|
22 | var extensions = target.getExtensions(); | |
| 22 | var objects = target.getObjects(); |
|
23 | var objects = target.getObjects(); | |
| 23 | var providers = target.getProviders(); |
|
24 | var providers = target.getProviders(); | |
| 24 | var configurations = target.getConfigurations(); |
|
25 | var configurations = target.getConfigurations(); | |
| 25 | var tasks = target.getTasks(); |
|
26 | var tasks = target.getTasks(); | |
| 26 |
|
27 | |||
| 27 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
28 | // Apply the main VariantsPlugin to ensure the core variant model is available. | |
| 28 | target.getPlugins().apply(VariantsPlugin.class); |
|
29 | target.getPlugins().apply(VariantsPlugin.class); | |
| 29 | // Access the VariantsExtension to configure variant sources. |
|
30 | // Access the VariantsExtension to configure variant sources. | |
| 30 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
31 | var variantsExtension = extensions.getByType(VariantsExtension.class); | |
| 31 |
|
32 | |||
| 32 | var outgoing = new OutgoingRegistry(configurations, objects, providers); |
|
33 | var outgoing = new OutgoingRegistry(configurations, objects, providers); | |
| 33 | var assemblies = new ArtifactAssemblyRegistry(objects, tasks); |
|
34 | var assemblies = new ArtifactAssemblyRegistry(objects, tasks); | |
| 34 |
|
35 | |||
|
|
36 | // wire artifact assemblies to configuration variants | |||
| 35 | var assembliesBridge = new ArtifactAssemblyBridge(assemblies); |
|
37 | var assembliesBridge = new ArtifactAssemblyBridge(assemblies); | |
| 36 |
|
38 | |||
| 37 | var deferred = new Deferred<OutgoingVariantsContext>(); |
|
39 | var deferred = new Deferred<OutgoingVariantsContext>(); | |
| 38 |
|
40 | |||
| 39 | deferred.whenResolved(context -> context.all(assembliesBridge)); |
|
41 | deferred.whenResolved(context -> context.all(assembliesBridge)); | |
| 40 |
|
42 | |||
| 41 | variantsExtension.whenFinalized(variants -> { |
|
43 | variantsExtension.whenFinalized(variants -> { | |
| 42 |
|
44 | |||
| 43 | }); |
|
45 | }); | |
| 44 |
|
46 | |||
| 45 | var variantArtifacts = new VariantArtifactsExtension() { |
|
47 | var variantArtifacts = new VariantArtifactsExtension() { | |
| 46 |
|
48 | |||
| 47 | @Override |
|
49 | @Override | |
| 48 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
50 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { | |
| 49 |
deferred.whenResolved(context -> |
|
51 | deferred.whenResolved(context -> { | |
| 50 | objects.named(Variant.class, variantName), action)); |
|
52 | new DefaultVariantArtifactSpec(); | |
|
|
53 | }); | |||
| 51 | } |
|
54 | } | |
| 52 |
|
55 | |||
| 53 | @Override |
|
56 | @Override | |
| 54 | public void whenFinalized(Action<? super OutgoingVariantsContext> action) { |
|
57 | public void whenFinalized(Action<? super OutgoingVariantsContext> action) { | |
| 55 | deferred.whenResolved(registry -> action.execute(registry.variantsContext())); |
|
58 | deferred.whenResolved(registry -> action.execute(registry.variantsContext())); | |
| 56 | } |
|
59 | } | |
| 57 |
|
60 | |||
| 58 | @Override |
|
61 | @Override | |
| 59 | public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) { |
|
62 | public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) { | |
| 60 | deferred.whenResolved(registry -> registry.configureOutgoing(action)); |
|
63 | deferred.whenResolved(registry -> registry.configureOutgoing(action)); | |
| 61 |
|
64 | |||
| 62 | } |
|
65 | } | |
| 63 |
|
66 | |||
| 64 | @Override |
|
67 | @Override | |
| 65 | public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) { |
|
68 | public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) { | |
| 66 | deferred.whenResolved(registry -> registry.configureOutgoingSlot(action)); |
|
69 | deferred.whenResolved(registry -> registry.configureOutgoingSlot(action)); | |
| 67 | } |
|
70 | } | |
| 68 |
|
71 | |||
| 69 | }; |
|
72 | }; | |
| 70 |
|
73 | |||
| 71 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); |
|
74 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); | |
| 72 |
|
75 | |||
| 73 | } |
|
76 | } | |
| 74 |
|
77 | |||
| 75 | } |
|
78 | } | |
| @@ -1,43 +1,37 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Task; |
|
3 | import org.gradle.api.Task; | |
| 4 | import org.gradle.api.file.FileCollection; |
|
|||
| 5 | import org.gradle.api.file.FileSystemLocation; |
|
4 | import org.gradle.api.file.FileSystemLocation; | |
| 6 | import org.gradle.api.provider.Provider; |
|
5 | import org.gradle.api.provider.Provider; | |
| 7 | import org.gradle.api.tasks.TaskProvider; |
|
6 | import org.gradle.api.tasks.TaskProvider; | |
| 8 |
|
7 | |||
| 9 | /** |
|
8 | /** | |
| 10 | * Materialized body of an {@link ArtifactSlot}. |
|
9 | * Materialized body of an {@link ArtifactSlot}. | |
| 11 | * |
|
10 | * | |
| 12 | * <p> |
|
11 | * <p> | |
| 13 | * An assembly is a stateful build object obtained on demand from |
|
12 | * An assembly is a stateful build object obtained on demand from | |
| 14 | * {@link ArtifactAssemblies#require(ArtifactSlot)}. It describes how the |
|
13 | * {@link ArtifactAssemblies#require(ArtifactSlot)}. It describes how the | |
| 15 | * slot artifact is produced and |
|
14 | * slot artifact is produced and | |
| 16 | * where that single published artifact will appear. |
|
15 | * where that single published artifact will appear. | |
| 17 | */ |
|
16 | */ | |
| 18 | public interface ArtifactAssembly { |
|
17 | public interface ArtifactAssembly { | |
| 19 |
|
18 | |||
| 20 | /** |
|
19 | /** | |
| 21 | * Returns the published artifact produced for the slot. |
|
20 | * Returns the published artifact produced for the slot. | |
| 22 | * |
|
21 | * | |
| 23 | * <p> |
|
22 | * <p> | |
| 24 | * A slot is expected to produce exactly one artifact represented by one file or |
|
23 | * A slot is expected to produce exactly one artifact represented by one file or | |
| 25 | * one directory. |
|
24 | * one directory. | |
| 26 | * |
|
25 | * | |
| 27 | * @return provider of the produced artifact location |
|
26 | * @return provider of the produced artifact location | |
| 28 | */ |
|
27 | */ | |
| 29 | Provider<? extends FileSystemLocation> getArtifact(); |
|
28 | Provider<? extends FileSystemLocation> getArtifact(); | |
| 30 |
|
29 | |||
| 31 | /** |
|
30 | /** | |
| 32 | * Returns the task that assembles the slot artifact. |
|
31 | * Returns the task that assembles the slot artifact. | |
| 33 | * |
|
32 | * | |
| 34 | * @return provider of the assembly task |
|
33 | * @return provider of the assembly task | |
| 35 | */ |
|
34 | */ | |
| 36 | TaskProvider<? extends Task> getAssemblyTask(); |
|
35 | TaskProvider<? extends Task> getAssemblyTask(); | |
| 37 |
|
36 | |||
| 38 | /** |
|
|||
| 39 | * File collection, contains {@link #getArtifact()} and build dependency on |
|
|||
| 40 | * {@link #getAssemblyTask()}. This is a conventional property. |
|
|||
| 41 | */ |
|
|||
| 42 | FileCollection getFileCollection(); |
|
|||
| 43 | } |
|
37 | } | |
| @@ -1,124 +1,87 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import java.util.LinkedHashMap; |
|
3 | import java.util.LinkedHashMap; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
| 6 | import java.util.function.Function; |
|
6 | import java.util.function.Function; | |
| 7 |
|
7 | |||
| 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 9 | import org.gradle.api.Action; |
|
9 | import org.gradle.api.Action; | |
| 10 | import org.gradle.api.InvalidUserDataException; |
|
10 | import org.gradle.api.InvalidUserDataException; | |
| 11 | import org.gradle.api.Task; |
|
11 | import org.gradle.api.Task; | |
| 12 | import org.gradle.api.file.Directory; |
|
|||
| 13 | import org.gradle.api.file.FileCollection; |
|
|||
| 14 | import org.gradle.api.file.FileSystemLocation; |
|
12 | import org.gradle.api.file.FileSystemLocation; | |
| 15 | import org.gradle.api.model.ObjectFactory; |
|
|||
| 16 | import org.gradle.api.provider.Provider; |
|
13 | import org.gradle.api.provider.Provider; | |
| 17 | import org.gradle.api.tasks.Copy; |
|
|||
| 18 | import org.gradle.api.tasks.TaskContainer; |
|
|||
| 19 | import org.gradle.api.tasks.TaskProvider; |
|
14 | import org.gradle.api.tasks.TaskProvider; | |
| 20 | import org.gradle.language.base.plugins.LifecycleBasePlugin; |
|
|||
| 21 | import org.implab.gradle.common.core.lang.Deferred; |
|
15 | import org.implab.gradle.common.core.lang.Deferred; | |
| 22 | import org.implab.gradle.internal.ReplayableQueue; |
|
16 | import org.implab.gradle.internal.ReplayableQueue; | |
| 23 |
|
17 | |||
| 24 | @NonNullByDefault |
|
18 | @NonNullByDefault | |
| 25 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
19 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { | |
| 26 | private final ObjectFactory objects; |
|
|||
| 27 | private final TaskContainer tasks; |
|
|||
| 28 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
20 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); | |
| 29 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
21 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); | |
| 30 |
|
22 | |||
| 31 |
public ArtifactAssemblyRegistry( |
|
23 | public ArtifactAssemblyRegistry() { | |
| 32 | this.objects = objects; |
|
|||
| 33 | this.tasks = tasks; |
|
|||
| 34 | } |
|
|||
| 35 |
|
||||
| 36 | public ArtifactAssembly register( |
|
|||
| 37 | ArtifactSlot slot, |
|
|||
| 38 | String taskName, |
|
|||
| 39 | Provider<Directory> outputDirectory, |
|
|||
| 40 | FileCollection sources) { |
|
|||
| 41 |
|
||||
| 42 | var task = tasks.register(taskName, Copy.class, copy -> { |
|
|||
| 43 | copy.setGroup(LifecycleBasePlugin.BUILD_GROUP); |
|
|||
| 44 | copy.into(outputDirectory); |
|
|||
| 45 | copy.from(sources); |
|
|||
| 46 | }); |
|
|||
| 47 |
|
||||
| 48 | return register(slot, task, t -> outputDirectory); |
|
|||
| 49 | } |
|
24 | } | |
| 50 |
|
25 | |||
| 51 | public <T extends Task> ArtifactAssembly register( |
|
26 | public <T extends Task> ArtifactAssembly register( | |
| 52 | ArtifactSlot slot, |
|
27 | ArtifactSlot slot, | |
| 53 | TaskProvider<T> task, |
|
28 | TaskProvider<T> task, | |
| 54 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { |
|
29 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { | |
| 55 |
|
30 | |||
| 56 | var deferred = getDeferred(slot); |
|
31 | var deferred = getDeferred(slot); | |
| 57 | if (deferred.resolved()) { |
|
32 | if (deferred.resolved()) { | |
| 58 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); |
|
33 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); | |
| 59 | } |
|
34 | } | |
| 60 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); |
|
35 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); | |
| 61 |
|
36 | |||
| 62 | var output = objects.fileCollection() |
|
37 | var assembly = new Assembly(outputArtifact, task); | |
| 63 | .from(outputArtifact) |
|
|||
| 64 | .builtBy(task); |
|
|||
| 65 |
|
||||
| 66 | var assembly = new Assembly(outputArtifact, task, output); |
|
|||
| 67 | deferred.resolve(assembly); |
|
38 | deferred.resolve(assembly); | |
| 68 | assemblies.add(assembly); |
|
39 | assemblies.add(assembly); | |
| 69 | return assembly; |
|
40 | return assembly; | |
| 70 | } |
|
41 | } | |
| 71 |
|
42 | |||
| 72 | @Override |
|
43 | @Override | |
| 73 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { |
|
44 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { | |
| 74 | // to prevent creation of map entries on lookup use the map directly |
|
45 | // to prevent creation of map entries on lookup use the map directly | |
| 75 | var deferred = assembliesBySlots.get(slot); |
|
46 | var deferred = assembliesBySlots.get(slot); | |
| 76 | return deferred != null && deferred.resolved() |
|
47 | return deferred != null && deferred.resolved() | |
| 77 | ? Optional.of(deferred.value()) |
|
48 | ? Optional.of(deferred.value()) | |
| 78 | : Optional.empty(); |
|
49 | : Optional.empty(); | |
| 79 | } |
|
50 | } | |
| 80 |
|
51 | |||
| 81 | @Override |
|
52 | @Override | |
| 82 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { |
|
53 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { | |
| 83 | getDeferred(slot).whenResolved(action::execute); |
|
54 | getDeferred(slot).whenResolved(action::execute); | |
| 84 | } |
|
55 | } | |
| 85 |
|
56 | |||
| 86 | @Override |
|
57 | @Override | |
| 87 | public void all(Action<? super ArtifactAssembly> action) { |
|
58 | public void all(Action<? super ArtifactAssembly> action) { | |
| 88 | assemblies.forEach(action::execute); |
|
59 | assemblies.forEach(action::execute); | |
| 89 | } |
|
60 | } | |
| 90 |
|
61 | |||
| 91 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { |
|
62 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { | |
| 92 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); |
|
63 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); | |
| 93 | } |
|
64 | } | |
| 94 |
|
65 | |||
| 95 | static class Assembly implements ArtifactAssembly { |
|
66 | static class Assembly implements ArtifactAssembly { | |
| 96 |
|
67 | |||
| 97 | private final Provider<? extends FileSystemLocation> artifact; |
|
68 | private final Provider<? extends FileSystemLocation> artifact; | |
| 98 | private final TaskProvider<? extends Task> task; |
|
69 | private final TaskProvider<? extends Task> task; | |
| 99 | private final FileCollection fileCollection; |
|
|||
| 100 |
|
70 | |||
| 101 |
Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task |
|
71 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { | |
| 102 | FileCollection fileCollection) { |
|
|||
| 103 | this.artifact = artifact; |
|
72 | this.artifact = artifact; | |
| 104 | this.task = task; |
|
73 | this.task = task; | |
| 105 | this.fileCollection = fileCollection; |
|
|||
| 106 | } |
|
74 | } | |
| 107 |
|
75 | |||
| 108 | @Override |
|
76 | @Override | |
| 109 | public Provider<? extends FileSystemLocation> getArtifact() { |
|
77 | public Provider<? extends FileSystemLocation> getArtifact() { | |
| 110 | return artifact; |
|
78 | return artifact; | |
| 111 | } |
|
79 | } | |
| 112 |
|
80 | |||
| 113 | @Override |
|
81 | @Override | |
| 114 | public TaskProvider<? extends Task> getAssemblyTask() { |
|
82 | public TaskProvider<? extends Task> getAssemblyTask() { | |
| 115 | return task; |
|
83 | return task; | |
| 116 | } |
|
84 | } | |
| 117 |
|
85 | |||
| 118 | @Override |
|
|||
| 119 | public FileCollection getFileCollection() { |
|
|||
| 120 | return fileCollection; |
|
|||
| 121 | } |
|
|||
| 122 |
|
||||
| 123 | } |
|
86 | } | |
| 124 | } |
|
87 | } | |
| @@ -1,82 +1,77 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 | import java.util.HashSet; |
|
3 | import java.util.HashSet; | |
| 4 | import java.util.LinkedList; |
|
|||
| 5 | import java.util.List; |
|
|||
| 6 | import java.util.Set; |
|
4 | import java.util.Set; | |
| 7 | import java.util.function.Consumer; |
|
5 | import java.util.function.Consumer; | |
| 8 | import java.util.stream.Stream; |
|
6 | import java.util.stream.Stream; | |
| 9 |
|
7 | |||
| 10 | import org.gradle.api.Action; |
|
8 | import org.gradle.api.Action; | |
| 11 | import org.gradle.api.model.ObjectFactory; |
|
9 | import org.gradle.api.model.ObjectFactory; | |
| 12 | import org.implab.gradle.common.core.lang.Strings; |
|
10 | import org.implab.gradle.common.core.lang.Strings; | |
| 13 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
11 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
| 14 | import org.implab.gradle.variants.core.Layer; |
|
12 | import org.implab.gradle.variants.core.Layer; | |
| 15 | import org.implab.gradle.variants.core.Role; |
|
13 | import org.implab.gradle.variants.core.Role; | |
| 16 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; |
|
14 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; | |
| 17 |
|
15 | |||
| 18 | /** |
|
16 | /** | |
| 19 | * Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ DSL ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΡΡΡΠΎΠΈΡ Π½Π°Π±ΠΎΡ {@link SlotContribution}. ΠΡΠΈ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠΈ Π½Π°Π±ΠΎΡΠ° |
|
17 | * Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ DSL ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΡΡΡΠΎΠΈΡ Π½Π°Π±ΠΎΡ {@link SlotContribution}. ΠΡΠΈ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠΈ Π½Π°Π±ΠΎΡΠ° | |
| 20 | * ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΡΡΡΡ. ΠΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½Ρ |
|
18 | * ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΡΡΡΡ. ΠΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½Ρ | |
| 21 | * Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄ {@link #process(Consumer)} Π΄Π»Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ². |
|
19 | * Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄ {@link #process(Consumer)} Π΄Π»Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ². | |
| 22 | * |
|
20 | * | |
| 23 | */ |
|
21 | */ | |
| 24 |
final class |
|
22 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { | |
| 25 |
private final |
|
23 | private final Consumer<? super SlotContribution> consumer; | |
| 26 | private final ObjectFactory objectFactory; |
|
24 | private final ObjectFactory objectFactory; | |
| 27 |
|
25 | |||
| 28 |
|
|
26 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { | |
|
|
27 | this.consumer = consumer; | |||
| 29 | this.objectFactory = objectFactory; |
|
28 | this.objectFactory = objectFactory; | |
| 30 | } |
|
29 | } | |
| 31 |
|
30 | |||
| 32 | @Override |
|
31 | @Override | |
| 33 | public void from(Object artifact) { |
|
32 | public void from(Object artifact) { | |
| 34 |
con |
|
33 | consumer.accept(new DirectContribution(artifact)); | |
| 35 | } |
|
34 | } | |
| 36 |
|
35 | |||
| 37 | @Override |
|
36 | @Override | |
| 38 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
37 | public void fromVariant(Action<? super OutputSelectionSpec> action) { | |
| 39 |
con |
|
38 | consumer.accept(new VariantOutputsContribution(outputs(action))); | |
| 40 | } |
|
39 | } | |
| 41 |
|
40 | |||
| 42 | @Override |
|
41 | @Override | |
| 43 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
42 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { | |
| 44 |
|
43 | |||
| 45 |
con |
|
44 | consumer.accept(new RoleOutputsContribution( | |
| 46 | objectFactory.named(Role.class, roleName), |
|
45 | objectFactory.named(Role.class, roleName), | |
| 47 | outputs(action))); |
|
46 | outputs(action))); | |
| 48 | } |
|
47 | } | |
| 49 |
|
48 | |||
| 50 | @Override |
|
49 | @Override | |
| 51 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
50 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { | |
| 52 |
con |
|
51 | consumer.accept(new LayerOutputsContribution( | |
| 53 | objectFactory.named(Layer.class, layerName), |
|
52 | objectFactory.named(Layer.class, layerName), | |
| 54 | outputs(action))); |
|
53 | outputs(action))); | |
| 55 | } |
|
54 | } | |
| 56 |
|
55 | |||
| 57 | void process(Consumer<? super SlotContribution> consumer) { |
|
|||
| 58 | contributions.forEach(consumer); |
|
|||
| 59 | } |
|
|||
| 60 |
|
||||
| 61 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
56 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { | |
| 62 | var spec = new OutputsSetSpec(); |
|
57 | var spec = new OutputsSetSpec(); | |
| 63 | action.execute(spec); |
|
58 | action.execute(spec); | |
| 64 | return spec.outputs(); |
|
59 | return spec.outputs(); | |
| 65 | } |
|
60 | } | |
| 66 |
|
61 | |||
| 67 | private static class OutputsSetSpec implements OutputSelectionSpec { |
|
62 | private static class OutputsSetSpec implements OutputSelectionSpec { | |
| 68 | private final Set<String> outputs = new HashSet<>(); |
|
63 | private final Set<String> outputs = new HashSet<>(); | |
| 69 |
|
64 | |||
| 70 | @Override |
|
65 | @Override | |
| 71 | public void output(String name, String... extra) { |
|
66 | public void output(String name, String... extra) { | |
| 72 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
67 | Stream.concat(Stream.of(name), Stream.of(extra)) | |
| 73 | .map(Strings::requireNonBlank) |
|
68 | .map(Strings::requireNonBlank) | |
| 74 | .forEach(outputs::add); |
|
69 | .forEach(outputs::add); | |
| 75 | } |
|
70 | } | |
| 76 |
|
71 | |||
| 77 | Set<String> outputs() { |
|
72 | Set<String> outputs() { | |
| 78 | return Set.copyOf(outputs); |
|
73 | return Set.copyOf(outputs); | |
| 79 | } |
|
74 | } | |
| 80 |
|
75 | |||
| 81 | } |
|
76 | } | |
| 82 | } |
|
77 | } | |
| @@ -1,55 +1,96 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 |
import java.util. |
|
3 | import java.util.HashSet; | |
| 4 | import java.util.Set; |
|
4 | import java.util.Set; | |
| 5 | import java.util.function.Consumer; |
|
|||
| 6 | import java.util.function.Function; |
|
|||
| 7 |
|
5 | |||
|
|
6 | import org.gradle.api.file.ConfigurableFileCollection; | |||
| 8 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
7 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
| 9 | import org.implab.gradle.variants.core.VariantsView; |
|
|||
| 10 | import org.implab.gradle.variants.sources.CompileUnit; |
|
8 | import org.implab.gradle.variants.sources.CompileUnit; | |
| 11 |
import org.implab.gradle.variants.sources. |
|
9 | import org.implab.gradle.variants.sources.CompileUnitsView; | |
|
|
10 | import org.implab.gradle.variants.sources.RoleProjectionsView; | |||
|
|
11 | import org.implab.gradle.variants.sources.SourceSetMaterializer; | |||
| 12 |
|
12 | |||
|
|
13 | /** | |||
|
|
14 | * Π‘Π±ΠΎΡΡΠΈΠΊ Π²Ρ ΠΎΠ΄ΡΡΠΈΡ ΡΠ»Π΅ΠΌΠ΅Π½ΡΠΎΠ² ΠΈΠ· ΡΠ°Π·Π½ΡΡ ΠΈΡΡΠΎΡΠ½ΠΈΠΊΠΎΠ² {@link SlotContribution}. | |||
|
|
15 | * Π₯ΡΠ°Π½ΠΈΡ {@link ConfigurableFileCollection} ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΠ΅Ρ Π² Π½Π΅Π³ΠΎ Π½ΠΎΠ²ΡΠ΅ ΡΠ»Π΅ΠΌΠ΅Π½ΡΡ, | |||
|
|
16 | * ΠΏΡΠΈ ΡΡΠΎΠΌ Π΄Π΅Π»Π°Π΅ΡΡΡ Π΄Π΅Π΄ΡΠΏΠ»ΠΈΠΊΠ°ΡΠΈΡ ΠΏΠΎ {@link SlotInputKey}. | |||
|
|
17 | * | |||
|
|
18 | */ | |||
| 13 | public class SlotInputsAssembler implements SlotContributionVisitor { |
|
19 | public class SlotInputsAssembler implements SlotContributionVisitor { | |
| 14 |
|
20 | |||
| 15 | VariantsView variantView; |
|
21 | // sources context | |
| 16 | VariantSourcesContext sources; |
|
22 | private final CompileUnitsView compileUnitsView; | |
| 17 | ArtifactSlot artifactSlot; |
|
23 | ||
|
|
24 | private final RoleProjectionsView roleProjectionsView; | |||
|
|
25 | ||||
|
|
26 | private final SourceSetMaterializer sourceSetMaterializer; | |||
|
|
27 | ||||
|
|
28 | // artifact slot for this assembly | |||
|
|
29 | private final ArtifactSlot artifactSlot; | |||
|
|
30 | ||||
|
|
31 | // content for this assembly | |||
|
|
32 | private final ConfigurableFileCollection artifactInputs; | |||
| 18 |
|
33 | |||
| 19 | Set<SlotInputKey> seen; |
|
34 | // seen inputs, used for deduplication | |
|
|
35 | private final Set<SlotInputKey> seen = new HashSet<>(); | |||
|
|
36 | ||||
|
|
37 | public SlotInputsAssembler( | |||
|
|
38 | ArtifactSlot artifactSlot, | |||
|
|
39 | ConfigurableFileCollection artifactInputs, | |||
|
|
40 | CompileUnitsView compileUnitsView, | |||
|
|
41 | RoleProjectionsView roleProjectionsView, | |||
|
|
42 | SourceSetMaterializer sourceSetMaterializer) { | |||
|
|
43 | this.compileUnitsView = compileUnitsView; | |||
|
|
44 | this.roleProjectionsView = roleProjectionsView; | |||
|
|
45 | this.sourceSetMaterializer = sourceSetMaterializer; | |||
|
|
46 | this.artifactSlot = artifactSlot; | |||
|
|
47 | this.artifactInputs = artifactInputs; | |||
|
|
48 | } | |||
| 20 |
|
49 | |||
| 21 | @Override |
|
50 | @Override | |
| 22 | public void visit(DirectContribution contribution) { |
|
51 | public void visit(DirectContribution contribution) { | |
| 23 | contribute( |
|
52 | contribute( | |
| 24 | SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), |
|
53 | SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), | |
| 25 | contribution.input()); |
|
54 | contribution.input()); | |
| 26 | } |
|
55 | } | |
| 27 |
|
56 | |||
| 28 | @Override |
|
57 | @Override | |
| 29 | public void visit(VariantOutputsContribution contribution) { |
|
58 | public void visit(VariantOutputsContribution contribution) { | |
| 30 |
|
|
59 | var units = compileUnitsView.getUnitsForVariant(artifactSlot.variant()); | |
|
|
60 | contributeCompileUnits(units, contribution.outputs()); | |||
| 31 | } |
|
61 | } | |
| 32 |
|
62 | |||
| 33 | @Override |
|
63 | @Override | |
| 34 | public void visit(RoleOutputsContribution contribution) { |
|
64 | public void visit(RoleOutputsContribution contribution) { | |
| 35 | // TODO Auto-generated method stub |
|
65 | var roleProjection = roleProjectionsView.requireProjection(artifactSlot.variant(), | |
| 36 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); |
|
66 | contribution.role()); | |
|
|
67 | var units = roleProjectionsView.getUnits(roleProjection); | |||
|
|
68 | ||||
|
|
69 | contributeCompileUnits(units, contribution.outputs()); | |||
|
|
70 | ||||
| 37 | } |
|
71 | } | |
| 38 |
|
72 | |||
| 39 | @Override |
|
73 | @Override | |
| 40 | public void visit(LayerOutputsContribution contribution) { |
|
74 | public void visit(LayerOutputsContribution contribution) { | |
| 41 | // TODO Auto-generated method stub |
|
75 | var unit = compileUnitsView.requireUnit(artifactSlot.variant(), contribution.layer()); | |
| 42 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); |
|
76 | contributeCompileUnits(Set.of(unit), contribution.outputs()); | |
| 43 | } |
|
77 | } | |
| 44 |
|
78 | |||
| 45 | private void contribute(SlotInputKey key, Object input) { |
|
79 | private void contributeCompileUnits(Set<CompileUnit> units, Set<String> outputs) { | |
| 46 |
|
80 | units.stream() | ||
|
|
81 | // expand variant compile units, make (compileUnit, outputName) pairs | |||
|
|
82 | .flatMap(unit -> outputs.stream() | |||
|
|
83 | .map(output -> new CompileUnitOutputKey(unit, output))) | |||
|
|
84 | .forEach(key -> contribute( | |||
|
|
85 | key, | |||
|
|
86 | sourceSetMaterializer.getSourceSet(key.unit()) | |||
|
|
87 | .map(s -> s.output(key.outputName())))); | |||
| 47 | } |
|
88 | } | |
| 48 |
|
89 | |||
| 49 | private Function<CompileUnit, Object> resolveOutputs(Collection<? extends String> outputs) { |
|
90 | private void contribute(SlotInputKey key, Object resolvedInput) { | |
| 50 | return unit -> { |
|
91 | if (!seen.add(key)) | |
| 51 |
|
92 | return; | ||
| 52 | }; |
|
93 | artifactInputs.from(resolvedInput); | |
| 53 | } |
|
94 | } | |
| 54 |
|
95 | |||
| 55 | } |
|
96 | } | |
| @@ -1,83 +1,83 | |||||
| 1 | package org.implab.gradle.variants.sources; |
|
1 | package org.implab.gradle.variants.sources; | |
| 2 |
|
2 | |||
| 3 | import java.util.HashMap; |
|
3 | import java.util.HashMap; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.Objects; |
|
5 | import java.util.Objects; | |
| 6 | import java.util.Optional; |
|
6 | import java.util.Optional; | |
| 7 | import java.util.Set; |
|
7 | import java.util.Set; | |
| 8 | import java.util.stream.Collectors; |
|
8 | import java.util.stream.Collectors; | |
| 9 |
|
9 | |||
| 10 | import org.implab.gradle.variants.core.Layer; |
|
10 | import org.implab.gradle.variants.core.Layer; | |
| 11 | import org.implab.gradle.variants.core.Role; |
|
11 | import org.implab.gradle.variants.core.Role; | |
| 12 | import org.implab.gradle.variants.core.Variant; |
|
12 | import org.implab.gradle.variants.core.Variant; | |
| 13 | import org.implab.gradle.variants.core.VariantsView; |
|
13 | import org.implab.gradle.variants.core.VariantsView; | |
| 14 |
|
14 | |||
| 15 | public final class RoleProjectionsView { |
|
15 | public final class RoleProjectionsView { | |
| 16 | private final VariantsView variants; |
|
16 | private final VariantsView variants; | |
| 17 |
|
17 | |||
| 18 | private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>(); |
|
18 | private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>(); | |
| 19 |
|
19 | |||
| 20 | private RoleProjectionsView(VariantsView variants) { |
|
20 | private RoleProjectionsView(VariantsView variants) { | |
| 21 | this.variants = variants; |
|
21 | this.variants = variants; | |
| 22 | } |
|
22 | } | |
| 23 |
|
23 | |||
| 24 | public Set<RoleProjection> getProjections() { |
|
24 | public Set<RoleProjection> getProjections() { | |
| 25 | return variants.getEntries().stream() |
|
25 | return variants.getEntries().stream() | |
| 26 | .map(RoleProjection::of) |
|
26 | .map(RoleProjection::of) | |
| 27 | .collect(Collectors.toUnmodifiableSet()); |
|
27 | .collect(Collectors.toUnmodifiableSet()); | |
| 28 | } |
|
28 | } | |
| 29 |
|
29 | |||
| 30 | public Set<RoleProjection> getProjectionsForVariant(Variant variant) { |
|
30 | public Set<RoleProjection> getProjectionsForVariant(Variant variant) { | |
| 31 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
31 | Objects.requireNonNull(variant, "Variant can't be null"); | |
| 32 | return projectionsByVariant.computeIfAbsent(variant, key -> variants |
|
32 | return projectionsByVariant.computeIfAbsent(variant, key -> variants | |
| 33 | .getEntriesForVariant(variant).stream() |
|
33 | .getEntriesForVariant(variant).stream() | |
| 34 | .map(RoleProjection::of) |
|
34 | .map(RoleProjection::of) | |
| 35 | .collect(Collectors.toUnmodifiableSet())); |
|
35 | .collect(Collectors.toUnmodifiableSet())); | |
| 36 | } |
|
36 | } | |
| 37 |
|
37 | |||
| 38 | public Set<RoleProjection> getProjectionsForRole(Role role) { |
|
38 | public Set<RoleProjection> getProjectionsForRole(Role role) { | |
| 39 | Objects.requireNonNull(role, "Role can't be null"); |
|
39 | Objects.requireNonNull(role, "Role can't be null"); | |
| 40 | return variants.getEntriesForRole(role).stream() |
|
40 | return variants.getEntriesForRole(role).stream() | |
| 41 | .map(RoleProjection::of) |
|
41 | .map(RoleProjection::of) | |
| 42 | .collect(Collectors.toUnmodifiableSet()); |
|
42 | .collect(Collectors.toUnmodifiableSet()); | |
| 43 | } |
|
43 | } | |
| 44 |
|
44 | |||
| 45 | public Optional<RoleProjection> findProjection(Variant variant, Role role) { |
|
45 | public Optional<RoleProjection> findProjection(Variant variant, Role role) { | |
| 46 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
46 | Objects.requireNonNull(variant, "Variant can't be null"); | |
| 47 | Objects.requireNonNull(role, "Role can't be null"); |
|
47 | Objects.requireNonNull(role, "Role can't be null"); | |
| 48 | return variants.getEntriesForVariant(variant).stream() |
|
48 | return variants.getEntriesForVariant(variant).stream() | |
| 49 | .filter(entry -> entry.role().equals(role)) |
|
49 | .filter(entry -> entry.role().equals(role)) | |
| 50 | .map(RoleProjection::of) |
|
50 | .map(RoleProjection::of) | |
| 51 | .findAny(); |
|
51 | .findAny(); | |
| 52 | } |
|
52 | } | |
| 53 |
|
53 | |||
| 54 | public boolean contains(Variant variant, Role role) { |
|
54 | public boolean contains(Variant variant, Role role) { | |
| 55 | return findProjection(variant, role).isPresent(); |
|
55 | return findProjection(variant, role).isPresent(); | |
| 56 | } |
|
56 | } | |
| 57 |
|
57 | |||
| 58 | public Set<CompileUnit> getUnits(RoleProjection projection) { |
|
58 | public Set<CompileUnit> getUnits(RoleProjection projection) { | |
| 59 | Objects.requireNonNull(projection, "Role projection can't be null"); |
|
59 | Objects.requireNonNull(projection, "Role projection can't be null"); | |
| 60 | return variants.getEntriesForVariant(projection.variant()).stream() |
|
60 | return variants.getEntriesForVariant(projection.variant()).stream() | |
| 61 | .filter(entry -> entry.role().equals(projection.role())) |
|
61 | .filter(entry -> entry.role().equals(projection.role())) | |
| 62 | .map(CompileUnit::of) |
|
62 | .map(CompileUnit::of) | |
| 63 | .collect(Collectors.toUnmodifiableSet()); |
|
63 | .collect(Collectors.toUnmodifiableSet()); | |
| 64 |
|
64 | |||
| 65 | } |
|
65 | } | |
| 66 |
|
66 | |||
| 67 |
public RoleProjection |
|
67 | public RoleProjection requireProjection(Variant variant, Role role) { | |
| 68 | return findProjection(variant, role) |
|
68 | return findProjection(variant, role) | |
| 69 | .orElseThrow(() -> new IllegalArgumentException( |
|
69 | .orElseThrow(() -> new IllegalArgumentException( | |
| 70 | "Role projection for variant '" + variant.getName() |
|
70 | "Role projection for variant '" + variant.getName() | |
| 71 | + "' and role '" + role.getName() + "' not found")); |
|
71 | + "' and role '" + role.getName() + "' not found")); | |
| 72 | } |
|
72 | } | |
| 73 |
|
73 | |||
| 74 | public Set<Layer> getLayers(RoleProjection projection) { |
|
74 | public Set<Layer> getLayers(RoleProjection projection) { | |
| 75 | return getUnits(projection).stream() |
|
75 | return getUnits(projection).stream() | |
| 76 | .map(CompileUnit::layer) |
|
76 | .map(CompileUnit::layer) | |
| 77 | .collect(java.util.stream.Collectors.toUnmodifiableSet()); |
|
77 | .collect(java.util.stream.Collectors.toUnmodifiableSet()); | |
| 78 | } |
|
78 | } | |
| 79 |
|
79 | |||
| 80 | public static RoleProjectionsView of(VariantsView variantsView) { |
|
80 | public static RoleProjectionsView of(VariantsView variantsView) { | |
| 81 | return new RoleProjectionsView(variantsView); |
|
81 | return new RoleProjectionsView(variantsView); | |
| 82 | } |
|
82 | } | |
| 83 | } No newline at end of file |
|
83 | } | |
| @@ -1,105 +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.implab.gradle.variants.core.Layer; |
|
10 | import org.implab.gradle.variants.core.Layer; | |
| 11 | import org.implab.gradle.variants.core.Role; |
|
11 | import org.implab.gradle.variants.core.Role; | |
| 12 | import org.implab.gradle.variants.core.Variant; |
|
12 | import org.implab.gradle.variants.core.Variant; | |
| 13 | import org.implab.gradle.variants.core.VariantsView; |
|
13 | import org.implab.gradle.variants.core.VariantsView; | |
| 14 | import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer; |
|
14 | import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer; | |
| 15 | import org.junit.jupiter.api.Test; |
|
15 | import org.junit.jupiter.api.Test; | |
| 16 |
|
16 | |||
| 17 | class RoleProjectionsViewTest { |
|
17 | class RoleProjectionsViewTest { | |
| 18 | @Test |
|
18 | @Test | |
| 19 | void exposesRoleProjectionsAndTheirCompileUnits() { |
|
19 | void exposesRoleProjectionsAndTheirCompileUnits() { | |
| 20 | var browser = new TestVariant("browser"); |
|
20 | var browser = new TestVariant("browser"); | |
| 21 | var main = new TestLayer("main"); |
|
21 | var main = new TestLayer("main"); | |
| 22 | var test = new TestLayer("test"); |
|
22 | var test = new TestLayer("test"); | |
| 23 | var production = new TestRole("production"); |
|
23 | var production = new TestRole("production"); | |
| 24 | var qa = new TestRole("test"); |
|
24 | var qa = new TestRole("test"); | |
| 25 |
|
25 | |||
| 26 | var view = view( |
|
26 | var view = view( | |
| 27 | Set.of(main, test), |
|
27 | Set.of(main, test), | |
| 28 | Set.of(production, qa), |
|
28 | Set.of(production, qa), | |
| 29 | Set.of(browser), |
|
29 | Set.of(browser), | |
| 30 | Set.of( |
|
30 | Set.of( | |
| 31 | new VariantRoleLayer(browser, production, main), |
|
31 | new VariantRoleLayer(browser, production, main), | |
| 32 | new VariantRoleLayer(browser, qa, main), |
|
32 | new VariantRoleLayer(browser, qa, main), | |
| 33 | new VariantRoleLayer(browser, qa, test))); |
|
33 | new VariantRoleLayer(browser, qa, test))); | |
| 34 |
|
34 | |||
| 35 | var projections = RoleProjectionsView.of(view); |
|
35 | var projections = RoleProjectionsView.of(view); | |
| 36 |
var productionProjection = projections. |
|
36 | var productionProjection = projections.requireProjection(browser, production); | |
| 37 |
var qaProjection = projections. |
|
37 | var qaProjection = projections.requireProjection(browser, qa); | |
| 38 |
|
38 | |||
| 39 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections()); |
|
39 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections()); | |
| 40 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser)); |
|
40 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser)); | |
| 41 | assertEquals(Set.of(qaProjection), projections.getProjectionsForRole(qa)); |
|
41 | assertEquals(Set.of(qaProjection), projections.getProjectionsForRole(qa)); | |
| 42 | assertEquals(Set.of(new CompileUnit(browser, main)), projections.getUnits(productionProjection)); |
|
42 | 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)); |
|
43 | assertEquals(Set.of(new CompileUnit(browser, main), new CompileUnit(browser, test)), projections.getUnits(qaProjection)); | |
| 44 | assertEquals(Set.of(main, test), projections.getLayers(qaProjection)); |
|
44 | assertEquals(Set.of(main, test), projections.getLayers(qaProjection)); | |
| 45 | assertTrue(projections.contains(browser, production)); |
|
45 | assertTrue(projections.contains(browser, production)); | |
| 46 | } |
|
46 | } | |
| 47 |
|
47 | |||
| 48 | @Test |
|
48 | @Test | |
| 49 | void rejectsMissingProjectionLookup() { |
|
49 | void rejectsMissingProjectionLookup() { | |
| 50 | var browser = new TestVariant("browser"); |
|
50 | var browser = new TestVariant("browser"); | |
| 51 | var node = new TestVariant("node"); |
|
51 | var node = new TestVariant("node"); | |
| 52 | var main = new TestLayer("main"); |
|
52 | var main = new TestLayer("main"); | |
| 53 | var production = new TestRole("production"); |
|
53 | var production = new TestRole("production"); | |
| 54 |
|
54 | |||
| 55 | var view = view( |
|
55 | var view = view( | |
| 56 | Set.of(main), |
|
56 | Set.of(main), | |
| 57 | Set.of(production), |
|
57 | Set.of(production), | |
| 58 | Set.of(browser, node), |
|
58 | Set.of(browser, node), | |
| 59 | Set.of(new VariantRoleLayer(browser, production, main))); |
|
59 | Set.of(new VariantRoleLayer(browser, production, main))); | |
| 60 |
|
60 | |||
| 61 | var projections = RoleProjectionsView.of(view); |
|
61 | var projections = RoleProjectionsView.of(view); | |
| 62 |
|
62 | |||
| 63 |
var ex = assertThrows(IllegalArgumentException.class, () -> projections. |
|
63 | var ex = assertThrows(IllegalArgumentException.class, () -> projections.requireProjection(node, production)); | |
| 64 | assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found")); |
|
64 | assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found")); | |
| 65 | } |
|
65 | } | |
| 66 |
|
66 | |||
| 67 | private static VariantsView view( |
|
67 | private static VariantsView view( | |
| 68 | Set<Layer> layers, |
|
68 | Set<Layer> layers, | |
| 69 | Set<Role> roles, |
|
69 | Set<Role> roles, | |
| 70 | Set<Variant> variants, |
|
70 | Set<Variant> variants, | |
| 71 | Set<VariantRoleLayer> entries) { |
|
71 | Set<VariantRoleLayer> entries) { | |
| 72 | try { |
|
72 | try { | |
| 73 | Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor( |
|
73 | Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor( | |
| 74 | Set.class, |
|
74 | Set.class, | |
| 75 | Set.class, |
|
75 | Set.class, | |
| 76 | Set.class, |
|
76 | Set.class, | |
| 77 | Set.class); |
|
77 | Set.class); | |
| 78 | ctor.setAccessible(true); |
|
78 | ctor.setAccessible(true); | |
| 79 | return ctor.newInstance(layers, roles, variants, entries); |
|
79 | return ctor.newInstance(layers, roles, variants, entries); | |
| 80 | } catch (Exception e) { |
|
80 | } catch (Exception e) { | |
| 81 | throw new RuntimeException("Unable to create VariantsView fixture", e); |
|
81 | throw new RuntimeException("Unable to create VariantsView fixture", e); | |
| 82 | } |
|
82 | } | |
| 83 | } |
|
83 | } | |
| 84 |
|
84 | |||
| 85 | private record TestVariant(String value) implements Variant { |
|
85 | private record TestVariant(String value) implements Variant { | |
| 86 | @Override |
|
86 | @Override | |
| 87 | public String getName() { |
|
87 | public String getName() { | |
| 88 | return value; |
|
88 | return value; | |
| 89 | } |
|
89 | } | |
| 90 | } |
|
90 | } | |
| 91 |
|
91 | |||
| 92 | private record TestLayer(String value) implements Layer { |
|
92 | private record TestLayer(String value) implements Layer { | |
| 93 | @Override |
|
93 | @Override | |
| 94 | public String getName() { |
|
94 | public String getName() { | |
| 95 | return value; |
|
95 | return value; | |
| 96 | } |
|
96 | } | |
| 97 | } |
|
97 | } | |
| 98 |
|
98 | |||
| 99 | private record TestRole(String value) implements Role { |
|
99 | private record TestRole(String value) implements Role { | |
| 100 | @Override |
|
100 | @Override | |
| 101 | public String getName() { |
|
101 | public String getName() { | |
| 102 | return value; |
|
102 | return value; | |
| 103 | } |
|
103 | } | |
| 104 | } |
|
104 | } | |
| 105 | } |
|
105 | } | |
General Comments 0
You need to be logged in to leave comments.
Login now
