| @@ -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 | 1 | package org.implab.gradle.common.core.lang; |
|
|
2 | 2 | |
|
|
3 | import java.util.function.Consumer; | |
|
|
4 | import java.util.function.Function; | |
|
|
3 | 5 | import java.util.regex.Pattern; |
|
|
4 | 6 | |
|
|
5 | 7 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
6 | 8 | import org.gradle.api.provider.Provider; |
|
|
7 | 9 | |
|
|
8 | 10 | @NonNullByDefault |
|
|
9 | 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 | 17 | private static final Pattern firstLetter = Pattern.compile("^\\w"); |
|
|
12 | 18 | |
|
|
13 | 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 | 33 | public static String capitalize(String string) { |
|
|
16 | 34 | return string == null ? null |
|
|
17 | 35 | : string.length() == 0 ? string |
|
|
18 | 36 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); |
|
|
19 | 37 | } |
|
|
20 | 38 | |
|
|
21 | 39 | public static String toCamelCase(String name) { |
|
|
22 | 40 | if (name == null || name.isEmpty()) |
|
|
23 | 41 | return name; |
|
|
24 | 42 | StringBuilder out = new StringBuilder(name.length()); |
|
|
25 | 43 | boolean up = false; |
|
|
26 | 44 | boolean first = true; |
|
|
27 | 45 | for (int i = 0; i < name.length(); i++) { |
|
|
28 | 46 | char c = name.charAt(i); |
|
|
29 | 47 | switch (c) { |
|
|
30 | 48 | case '-', '_', ' ', '.' -> up = true; |
|
|
31 | 49 | default -> { |
|
|
32 | 50 | out.append( |
|
|
33 | 51 | first ? Character.toLowerCase(c) |
|
|
34 | 52 | : up ? Character.toUpperCase(c): c); |
|
|
35 | 53 | up = false; |
|
|
36 | 54 | first = false; |
|
|
37 | 55 | } |
|
|
38 | 56 | } |
|
|
39 | 57 | } |
|
|
40 | 58 | return out.toString(); |
|
|
41 | 59 | } |
|
|
42 | 60 | |
|
|
43 | 61 | public static void argumentNotNullOrEmpty(String value, String argumentName) { |
|
|
44 | 62 | if (value == null || value.length() == 0) |
|
|
45 | 63 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); |
|
|
46 | 64 | } |
|
|
47 | 65 | |
|
|
48 | 66 | public static void argumentNotNullOrBlank(String value, String argumentName) { |
|
|
49 | 67 | if (value == null || value.trim().length() == 0) |
|
|
50 | 68 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); |
|
|
51 | 69 | } |
|
|
52 | 70 | |
|
|
53 | 71 | public static String requireNonBlank(String value) { |
|
|
54 | 72 | argumentNotNullOrBlank(value, "value"); |
|
|
55 | 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 | 82 | public static String sanitizeName(String value) { |
|
|
59 | 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 | 96 | public static String asString(Object value) { |
|
|
63 | 97 | if (value == null) |
|
|
64 | 98 | return null; |
|
|
65 | 99 | if (value instanceof Provider<?> provider) |
|
|
66 | 100 | return asString(provider.get()); |
|
|
67 | 101 | else |
|
|
68 | 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 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.Plugin; |
|
|
5 | 5 | import org.gradle.api.Project; |
|
|
6 | 6 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
7 | 7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; |
|
|
8 | 8 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
|
9 | 9 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; |
|
|
10 | 10 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; |
|
|
11 | 11 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
|
12 | 12 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBridge; |
|
|
13 | import org.implab.gradle.variants.artifacts.internal.DefaultVariantArtifactSpec; | |
|
|
13 | 14 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; |
|
|
14 | 15 | import org.implab.gradle.variants.core.Variant; |
|
|
15 | 16 | import org.implab.gradle.variants.core.VariantsExtension; |
|
|
16 | 17 | |
|
|
17 | 18 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
|
|
18 | 19 | |
|
|
19 | 20 | @Override |
|
|
20 | 21 | public void apply(Project target) { |
|
|
21 | 22 | var extensions = target.getExtensions(); |
|
|
22 | 23 | var objects = target.getObjects(); |
|
|
23 | 24 | var providers = target.getProviders(); |
|
|
24 | 25 | var configurations = target.getConfigurations(); |
|
|
25 | 26 | var tasks = target.getTasks(); |
|
|
26 | 27 | |
|
|
27 | 28 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
|
28 | 29 | target.getPlugins().apply(VariantsPlugin.class); |
|
|
29 | 30 | // Access the VariantsExtension to configure variant sources. |
|
|
30 | 31 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
|
31 | 32 | |
|
|
32 | 33 | var outgoing = new OutgoingRegistry(configurations, objects, providers); |
|
|
33 | 34 | var assemblies = new ArtifactAssemblyRegistry(objects, tasks); |
|
|
34 | 35 | |
|
|
36 | // wire artifact assemblies to configuration variants | |
|
|
35 | 37 | var assembliesBridge = new ArtifactAssemblyBridge(assemblies); |
|
|
36 | 38 | |
|
|
37 | 39 | var deferred = new Deferred<OutgoingVariantsContext>(); |
|
|
38 | 40 | |
|
|
39 | 41 | deferred.whenResolved(context -> context.all(assembliesBridge)); |
|
|
40 | 42 | |
|
|
41 | 43 | variantsExtension.whenFinalized(variants -> { |
|
|
42 | 44 | |
|
|
43 | 45 | }); |
|
|
44 | 46 | |
|
|
45 | 47 | var variantArtifacts = new VariantArtifactsExtension() { |
|
|
46 | 48 | |
|
|
47 | 49 | @Override |
|
|
48 | 50 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
|
49 |
deferred.whenResolved(context -> |
|
|
|
50 | objects.named(Variant.class, variantName), action)); | |
|
|
51 | deferred.whenResolved(context -> { | |
|
|
52 | new DefaultVariantArtifactSpec(); | |
|
|
53 | }); | |
|
|
51 | 54 | } |
|
|
52 | 55 | |
|
|
53 | 56 | @Override |
|
|
54 | 57 | public void whenFinalized(Action<? super OutgoingVariantsContext> action) { |
|
|
55 | 58 | deferred.whenResolved(registry -> action.execute(registry.variantsContext())); |
|
|
56 | 59 | } |
|
|
57 | 60 | |
|
|
58 | 61 | @Override |
|
|
59 | 62 | public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) { |
|
|
60 | 63 | deferred.whenResolved(registry -> registry.configureOutgoing(action)); |
|
|
61 | 64 | |
|
|
62 | 65 | } |
|
|
63 | 66 | |
|
|
64 | 67 | @Override |
|
|
65 | 68 | public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) { |
|
|
66 | 69 | deferred.whenResolved(registry -> registry.configureOutgoingSlot(action)); |
|
|
67 | 70 | } |
|
|
68 | 71 | |
|
|
69 | 72 | }; |
|
|
70 | 73 | |
|
|
71 | 74 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); |
|
|
72 | 75 | |
|
|
73 | 76 | } |
|
|
74 | 77 | |
|
|
75 | 78 | } |
| @@ -1,43 +1,37 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Task; |
|
|
4 | import org.gradle.api.file.FileCollection; | |
|
|
5 | 4 | import org.gradle.api.file.FileSystemLocation; |
|
|
6 | 5 | import org.gradle.api.provider.Provider; |
|
|
7 | 6 | import org.gradle.api.tasks.TaskProvider; |
|
|
8 | 7 | |
|
|
9 | 8 | /** |
|
|
10 | 9 | * Materialized body of an {@link ArtifactSlot}. |
|
|
11 | 10 | * |
|
|
12 | 11 | * <p> |
|
|
13 | 12 | * An assembly is a stateful build object obtained on demand from |
|
|
14 | 13 | * {@link ArtifactAssemblies#require(ArtifactSlot)}. It describes how the |
|
|
15 | 14 | * slot artifact is produced and |
|
|
16 | 15 | * where that single published artifact will appear. |
|
|
17 | 16 | */ |
|
|
18 | 17 | public interface ArtifactAssembly { |
|
|
19 | 18 | |
|
|
20 | 19 | /** |
|
|
21 | 20 | * Returns the published artifact produced for the slot. |
|
|
22 | 21 | * |
|
|
23 | 22 | * <p> |
|
|
24 | 23 | * A slot is expected to produce exactly one artifact represented by one file or |
|
|
25 | 24 | * one directory. |
|
|
26 | 25 | * |
|
|
27 | 26 | * @return provider of the produced artifact location |
|
|
28 | 27 | */ |
|
|
29 | 28 | Provider<? extends FileSystemLocation> getArtifact(); |
|
|
30 | 29 | |
|
|
31 | 30 | /** |
|
|
32 | 31 | * Returns the task that assembles the slot artifact. |
|
|
33 | 32 | * |
|
|
34 | 33 | * @return provider of the assembly task |
|
|
35 | 34 | */ |
|
|
36 | 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 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.LinkedHashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Optional; |
|
|
6 | 6 | import java.util.function.Function; |
|
|
7 | 7 | |
|
|
8 | 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
9 | 9 | import org.gradle.api.Action; |
|
|
10 | 10 | import org.gradle.api.InvalidUserDataException; |
|
|
11 | 11 | import org.gradle.api.Task; |
|
|
12 | import org.gradle.api.file.Directory; | |
|
|
13 | import org.gradle.api.file.FileCollection; | |
|
|
14 | 12 | import org.gradle.api.file.FileSystemLocation; |
|
|
15 | import org.gradle.api.model.ObjectFactory; | |
|
|
16 | 13 | import org.gradle.api.provider.Provider; |
|
|
17 | import org.gradle.api.tasks.Copy; | |
|
|
18 | import org.gradle.api.tasks.TaskContainer; | |
|
|
19 | 14 | import org.gradle.api.tasks.TaskProvider; |
|
|
20 | import org.gradle.language.base.plugins.LifecycleBasePlugin; | |
|
|
21 | 15 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
22 | 16 | import org.implab.gradle.internal.ReplayableQueue; |
|
|
23 | 17 | |
|
|
24 | 18 | @NonNullByDefault |
|
|
25 | 19 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
|
26 | private final ObjectFactory objects; | |
|
|
27 | private final TaskContainer tasks; | |
|
|
28 | 20 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
|
29 | 21 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
|
30 | 22 | |
|
|
31 |
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); | |
|
|
23 | public ArtifactAssemblyRegistry() { | |
|
|
49 | 24 | } |
|
|
50 | 25 | |
|
|
51 | 26 | public <T extends Task> ArtifactAssembly register( |
|
|
52 | 27 | ArtifactSlot slot, |
|
|
53 | 28 | TaskProvider<T> task, |
|
|
54 | 29 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { |
|
|
55 | 30 | |
|
|
56 | 31 | var deferred = getDeferred(slot); |
|
|
57 | 32 | if (deferred.resolved()) { |
|
|
58 | 33 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); |
|
|
59 | 34 | } |
|
|
60 | 35 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); |
|
|
61 | 36 | |
|
|
62 | var output = objects.fileCollection() | |
|
|
63 | .from(outputArtifact) | |
|
|
64 | .builtBy(task); | |
|
|
65 | ||
|
|
66 | var assembly = new Assembly(outputArtifact, task, output); | |
|
|
37 | var assembly = new Assembly(outputArtifact, task); | |
|
|
67 | 38 | deferred.resolve(assembly); |
|
|
68 | 39 | assemblies.add(assembly); |
|
|
69 | 40 | return assembly; |
|
|
70 | 41 | } |
|
|
71 | 42 | |
|
|
72 | 43 | @Override |
|
|
73 | 44 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { |
|
|
74 | 45 | // to prevent creation of map entries on lookup use the map directly |
|
|
75 | 46 | var deferred = assembliesBySlots.get(slot); |
|
|
76 | 47 | return deferred != null && deferred.resolved() |
|
|
77 | 48 | ? Optional.of(deferred.value()) |
|
|
78 | 49 | : Optional.empty(); |
|
|
79 | 50 | } |
|
|
80 | 51 | |
|
|
81 | 52 | @Override |
|
|
82 | 53 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { |
|
|
83 | 54 | getDeferred(slot).whenResolved(action::execute); |
|
|
84 | 55 | } |
|
|
85 | 56 | |
|
|
86 | 57 | @Override |
|
|
87 | 58 | public void all(Action<? super ArtifactAssembly> action) { |
|
|
88 | 59 | assemblies.forEach(action::execute); |
|
|
89 | 60 | } |
|
|
90 | 61 | |
|
|
91 | 62 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { |
|
|
92 | 63 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); |
|
|
93 | 64 | } |
|
|
94 | 65 | |
|
|
95 | 66 | static class Assembly implements ArtifactAssembly { |
|
|
96 | 67 | |
|
|
97 | 68 | private final Provider<? extends FileSystemLocation> artifact; |
|
|
98 | 69 | private final TaskProvider<? extends Task> task; |
|
|
99 | private final FileCollection fileCollection; | |
|
|
100 | 70 | |
|
|
101 |
Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task |
|
|
|
102 | FileCollection fileCollection) { | |
|
|
71 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { | |
|
|
103 | 72 | this.artifact = artifact; |
|
|
104 | 73 | this.task = task; |
|
|
105 | this.fileCollection = fileCollection; | |
|
|
106 | 74 | } |
|
|
107 | 75 | |
|
|
108 | 76 | @Override |
|
|
109 | 77 | public Provider<? extends FileSystemLocation> getArtifact() { |
|
|
110 | 78 | return artifact; |
|
|
111 | 79 | } |
|
|
112 | 80 | |
|
|
113 | 81 | @Override |
|
|
114 | 82 | public TaskProvider<? extends Task> getAssemblyTask() { |
|
|
115 | 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 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashSet; |
|
|
4 | import java.util.LinkedList; | |
|
|
5 | import java.util.List; | |
|
|
6 | 4 | import java.util.Set; |
|
|
7 | 5 | import java.util.function.Consumer; |
|
|
8 | 6 | import java.util.stream.Stream; |
|
|
9 | 7 | |
|
|
10 | 8 | import org.gradle.api.Action; |
|
|
11 | 9 | import org.gradle.api.model.ObjectFactory; |
|
|
12 | 10 | import org.implab.gradle.common.core.lang.Strings; |
|
|
13 | 11 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
|
14 | 12 | import org.implab.gradle.variants.core.Layer; |
|
|
15 | 13 | import org.implab.gradle.variants.core.Role; |
|
|
16 | 14 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; |
|
|
17 | 15 | |
|
|
18 | 16 | /** |
|
|
19 | 17 | * Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ DSL ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΡΡΡΠΎΠΈΡ Π½Π°Π±ΠΎΡ {@link SlotContribution}. ΠΡΠΈ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠΈ Π½Π°Π±ΠΎΡΠ° |
|
|
20 | 18 | * ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΡΡΡΡ. ΠΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½Ρ |
|
|
21 | 19 | * Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄ {@link #process(Consumer)} Π΄Π»Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ². |
|
|
22 | 20 | * |
|
|
23 | 21 | */ |
|
|
24 |
final class |
|
|
|
25 |
private final |
|
|
|
22 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { | |
|
|
23 | private final Consumer<? super SlotContribution> consumer; | |
|
|
26 | 24 | private final ObjectFactory objectFactory; |
|
|
27 | 25 | |
|
|
28 |
|
|
|
|
26 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { | |
|
|
27 | this.consumer = consumer; | |
|
|
29 | 28 | this.objectFactory = objectFactory; |
|
|
30 | 29 | } |
|
|
31 | 30 | |
|
|
32 | 31 | @Override |
|
|
33 | 32 | public void from(Object artifact) { |
|
|
34 |
con |
|
|
|
33 | consumer.accept(new DirectContribution(artifact)); | |
|
|
35 | 34 | } |
|
|
36 | 35 | |
|
|
37 | 36 | @Override |
|
|
38 | 37 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
|
39 |
con |
|
|
|
38 | consumer.accept(new VariantOutputsContribution(outputs(action))); | |
|
|
40 | 39 | } |
|
|
41 | 40 | |
|
|
42 | 41 | @Override |
|
|
43 | 42 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
|
44 | 43 | |
|
|
45 |
con |
|
|
|
44 | consumer.accept(new RoleOutputsContribution( | |
|
|
46 | 45 | objectFactory.named(Role.class, roleName), |
|
|
47 | 46 | outputs(action))); |
|
|
48 | 47 | } |
|
|
49 | 48 | |
|
|
50 | 49 | @Override |
|
|
51 | 50 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
|
52 |
con |
|
|
|
51 | consumer.accept(new LayerOutputsContribution( | |
|
|
53 | 52 | objectFactory.named(Layer.class, layerName), |
|
|
54 | 53 | outputs(action))); |
|
|
55 | 54 | } |
|
|
56 | 55 | |
|
|
57 | void process(Consumer<? super SlotContribution> consumer) { | |
|
|
58 | contributions.forEach(consumer); | |
|
|
59 | } | |
|
|
60 | ||
|
|
61 | 56 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
|
62 | 57 | var spec = new OutputsSetSpec(); |
|
|
63 | 58 | action.execute(spec); |
|
|
64 | 59 | return spec.outputs(); |
|
|
65 | 60 | } |
|
|
66 | 61 | |
|
|
67 | 62 | private static class OutputsSetSpec implements OutputSelectionSpec { |
|
|
68 | 63 | private final Set<String> outputs = new HashSet<>(); |
|
|
69 | 64 | |
|
|
70 | 65 | @Override |
|
|
71 | 66 | public void output(String name, String... extra) { |
|
|
72 | 67 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
|
73 | 68 | .map(Strings::requireNonBlank) |
|
|
74 | 69 | .forEach(outputs::add); |
|
|
75 | 70 | } |
|
|
76 | 71 | |
|
|
77 | 72 | Set<String> outputs() { |
|
|
78 | 73 | return Set.copyOf(outputs); |
|
|
79 | 74 | } |
|
|
80 | 75 | |
|
|
81 | 76 | } |
|
|
82 | 77 | } |
| @@ -1,55 +1,96 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 |
import java.util. |
|
|
|
3 | import java.util.HashSet; | |
|
|
4 | 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 | 7 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
9 | import org.implab.gradle.variants.core.VariantsView; | |
|
|
10 | 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 | 19 | public class SlotInputsAssembler implements SlotContributionVisitor { |
|
|
14 | 20 | |
|
|
15 | VariantsView variantView; | |
|
|
16 | VariantSourcesContext sources; | |
|
|
17 | ArtifactSlot artifactSlot; | |
|
|
21 | // sources context | |
|
|
22 | private final CompileUnitsView compileUnitsView; | |
|
|
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 | 50 | @Override |
|
|
22 | 51 | public void visit(DirectContribution contribution) { |
|
|
23 | 52 | contribute( |
|
|
24 | 53 | SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), |
|
|
25 | 54 | contribution.input()); |
|
|
26 | 55 | } |
|
|
27 | 56 | |
|
|
28 | 57 | @Override |
|
|
29 | 58 | public void visit(VariantOutputsContribution contribution) { |
|
|
30 |
|
|
|
|
59 | var units = compileUnitsView.getUnitsForVariant(artifactSlot.variant()); | |
|
|
60 | contributeCompileUnits(units, contribution.outputs()); | |
|
|
31 | 61 | } |
|
|
32 | 62 | |
|
|
33 | 63 | @Override |
|
|
34 | 64 | public void visit(RoleOutputsContribution contribution) { |
|
|
35 | // TODO Auto-generated method stub | |
|
|
36 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); | |
|
|
65 | var roleProjection = roleProjectionsView.requireProjection(artifactSlot.variant(), | |
|
|
66 | contribution.role()); | |
|
|
67 | var units = roleProjectionsView.getUnits(roleProjection); | |
|
|
68 | ||
|
|
69 | contributeCompileUnits(units, contribution.outputs()); | |
|
|
70 | ||
|
|
37 | 71 | } |
|
|
38 | 72 | |
|
|
39 | 73 | @Override |
|
|
40 | 74 | public void visit(LayerOutputsContribution contribution) { |
|
|
41 | // TODO Auto-generated method stub | |
|
|
42 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); | |
|
|
75 | var unit = compileUnitsView.requireUnit(artifactSlot.variant(), contribution.layer()); | |
|
|
76 | contributeCompileUnits(Set.of(unit), contribution.outputs()); | |
|
|
43 | 77 | } |
|
|
44 | 78 | |
|
|
45 | private void contribute(SlotInputKey key, Object input) { | |
|
|
46 | ||
|
|
79 | private void contributeCompileUnits(Set<CompileUnit> units, Set<String> outputs) { | |
|
|
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) { | |
|
|
50 | return unit -> { | |
|
|
51 | ||
|
|
52 | }; | |
|
|
90 | private void contribute(SlotInputKey key, Object resolvedInput) { | |
|
|
91 | if (!seen.add(key)) | |
|
|
92 | return; | |
|
|
93 | artifactInputs.from(resolvedInput); | |
|
|
53 | 94 | } |
|
|
54 | 95 | |
|
|
55 | 96 | } |
| @@ -1,83 +1,83 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Objects; |
|
|
6 | 6 | import java.util.Optional; |
|
|
7 | 7 | import java.util.Set; |
|
|
8 | 8 | import java.util.stream.Collectors; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.implab.gradle.variants.core.Layer; |
|
|
11 | 11 | import org.implab.gradle.variants.core.Role; |
|
|
12 | 12 | import org.implab.gradle.variants.core.Variant; |
|
|
13 | 13 | import org.implab.gradle.variants.core.VariantsView; |
|
|
14 | 14 | |
|
|
15 | 15 | public final class RoleProjectionsView { |
|
|
16 | 16 | private final VariantsView variants; |
|
|
17 | 17 | |
|
|
18 | 18 | private final Map<Variant, Set<RoleProjection>> projectionsByVariant = new HashMap<>(); |
|
|
19 | 19 | |
|
|
20 | 20 | private RoleProjectionsView(VariantsView variants) { |
|
|
21 | 21 | this.variants = variants; |
|
|
22 | 22 | } |
|
|
23 | 23 | |
|
|
24 | 24 | public Set<RoleProjection> getProjections() { |
|
|
25 | 25 | return variants.getEntries().stream() |
|
|
26 | 26 | .map(RoleProjection::of) |
|
|
27 | 27 | .collect(Collectors.toUnmodifiableSet()); |
|
|
28 | 28 | } |
|
|
29 | 29 | |
|
|
30 | 30 | public Set<RoleProjection> getProjectionsForVariant(Variant variant) { |
|
|
31 | 31 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
|
32 | 32 | return projectionsByVariant.computeIfAbsent(variant, key -> variants |
|
|
33 | 33 | .getEntriesForVariant(variant).stream() |
|
|
34 | 34 | .map(RoleProjection::of) |
|
|
35 | 35 | .collect(Collectors.toUnmodifiableSet())); |
|
|
36 | 36 | } |
|
|
37 | 37 | |
|
|
38 | 38 | public Set<RoleProjection> getProjectionsForRole(Role role) { |
|
|
39 | 39 | Objects.requireNonNull(role, "Role can't be null"); |
|
|
40 | 40 | return variants.getEntriesForRole(role).stream() |
|
|
41 | 41 | .map(RoleProjection::of) |
|
|
42 | 42 | .collect(Collectors.toUnmodifiableSet()); |
|
|
43 | 43 | } |
|
|
44 | 44 | |
|
|
45 | 45 | public Optional<RoleProjection> findProjection(Variant variant, Role role) { |
|
|
46 | 46 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
|
47 | 47 | Objects.requireNonNull(role, "Role can't be null"); |
|
|
48 | 48 | return variants.getEntriesForVariant(variant).stream() |
|
|
49 | 49 | .filter(entry -> entry.role().equals(role)) |
|
|
50 | 50 | .map(RoleProjection::of) |
|
|
51 | 51 | .findAny(); |
|
|
52 | 52 | } |
|
|
53 | 53 | |
|
|
54 | 54 | public boolean contains(Variant variant, Role role) { |
|
|
55 | 55 | return findProjection(variant, role).isPresent(); |
|
|
56 | 56 | } |
|
|
57 | 57 | |
|
|
58 | 58 | public Set<CompileUnit> getUnits(RoleProjection projection) { |
|
|
59 | 59 | Objects.requireNonNull(projection, "Role projection can't be null"); |
|
|
60 | 60 | return variants.getEntriesForVariant(projection.variant()).stream() |
|
|
61 | 61 | .filter(entry -> entry.role().equals(projection.role())) |
|
|
62 | 62 | .map(CompileUnit::of) |
|
|
63 | 63 | .collect(Collectors.toUnmodifiableSet()); |
|
|
64 | 64 | |
|
|
65 | 65 | } |
|
|
66 | 66 | |
|
|
67 |
public RoleProjection |
|
|
|
67 | public RoleProjection requireProjection(Variant variant, Role role) { | |
|
|
68 | 68 | return findProjection(variant, role) |
|
|
69 | 69 | .orElseThrow(() -> new IllegalArgumentException( |
|
|
70 | 70 | "Role projection for variant '" + variant.getName() |
|
|
71 | 71 | + "' and role '" + role.getName() + "' not found")); |
|
|
72 | 72 | } |
|
|
73 | 73 | |
|
|
74 | 74 | public Set<Layer> getLayers(RoleProjection projection) { |
|
|
75 | 75 | return getUnits(projection).stream() |
|
|
76 | 76 | .map(CompileUnit::layer) |
|
|
77 | 77 | .collect(java.util.stream.Collectors.toUnmodifiableSet()); |
|
|
78 | 78 | } |
|
|
79 | 79 | |
|
|
80 | 80 | public static RoleProjectionsView of(VariantsView variantsView) { |
|
|
81 | 81 | return new RoleProjectionsView(variantsView); |
|
|
82 | 82 | } |
|
|
83 | 83 | } No newline at end of file |
| @@ -1,105 +1,105 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; |
|
|
4 | 4 | import static org.junit.jupiter.api.Assertions.assertThrows; |
|
|
5 | 5 | import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
6 | 6 | |
|
|
7 | 7 | import java.lang.reflect.Constructor; |
|
|
8 | 8 | import java.util.Set; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.implab.gradle.variants.core.Layer; |
|
|
11 | 11 | import org.implab.gradle.variants.core.Role; |
|
|
12 | 12 | import org.implab.gradle.variants.core.Variant; |
|
|
13 | 13 | import org.implab.gradle.variants.core.VariantsView; |
|
|
14 | 14 | import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer; |
|
|
15 | 15 | import org.junit.jupiter.api.Test; |
|
|
16 | 16 | |
|
|
17 | 17 | class RoleProjectionsViewTest { |
|
|
18 | 18 | @Test |
|
|
19 | 19 | void exposesRoleProjectionsAndTheirCompileUnits() { |
|
|
20 | 20 | var browser = new TestVariant("browser"); |
|
|
21 | 21 | var main = new TestLayer("main"); |
|
|
22 | 22 | var test = new TestLayer("test"); |
|
|
23 | 23 | var production = new TestRole("production"); |
|
|
24 | 24 | var qa = new TestRole("test"); |
|
|
25 | 25 | |
|
|
26 | 26 | var view = view( |
|
|
27 | 27 | Set.of(main, test), |
|
|
28 | 28 | Set.of(production, qa), |
|
|
29 | 29 | Set.of(browser), |
|
|
30 | 30 | Set.of( |
|
|
31 | 31 | new VariantRoleLayer(browser, production, main), |
|
|
32 | 32 | new VariantRoleLayer(browser, qa, main), |
|
|
33 | 33 | new VariantRoleLayer(browser, qa, test))); |
|
|
34 | 34 | |
|
|
35 | 35 | var projections = RoleProjectionsView.of(view); |
|
|
36 |
var productionProjection = projections. |
|
|
|
37 |
var qaProjection = projections. |
|
|
|
36 | var productionProjection = projections.requireProjection(browser, production); | |
|
|
37 | var qaProjection = projections.requireProjection(browser, qa); | |
|
|
38 | 38 | |
|
|
39 | 39 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections()); |
|
|
40 | 40 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser)); |
|
|
41 | 41 | assertEquals(Set.of(qaProjection), projections.getProjectionsForRole(qa)); |
|
|
42 | 42 | assertEquals(Set.of(new CompileUnit(browser, main)), projections.getUnits(productionProjection)); |
|
|
43 | 43 | assertEquals(Set.of(new CompileUnit(browser, main), new CompileUnit(browser, test)), projections.getUnits(qaProjection)); |
|
|
44 | 44 | assertEquals(Set.of(main, test), projections.getLayers(qaProjection)); |
|
|
45 | 45 | assertTrue(projections.contains(browser, production)); |
|
|
46 | 46 | } |
|
|
47 | 47 | |
|
|
48 | 48 | @Test |
|
|
49 | 49 | void rejectsMissingProjectionLookup() { |
|
|
50 | 50 | var browser = new TestVariant("browser"); |
|
|
51 | 51 | var node = new TestVariant("node"); |
|
|
52 | 52 | var main = new TestLayer("main"); |
|
|
53 | 53 | var production = new TestRole("production"); |
|
|
54 | 54 | |
|
|
55 | 55 | var view = view( |
|
|
56 | 56 | Set.of(main), |
|
|
57 | 57 | Set.of(production), |
|
|
58 | 58 | Set.of(browser, node), |
|
|
59 | 59 | Set.of(new VariantRoleLayer(browser, production, main))); |
|
|
60 | 60 | |
|
|
61 | 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 | 64 | assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found")); |
|
|
65 | 65 | } |
|
|
66 | 66 | |
|
|
67 | 67 | private static VariantsView view( |
|
|
68 | 68 | Set<Layer> layers, |
|
|
69 | 69 | Set<Role> roles, |
|
|
70 | 70 | Set<Variant> variants, |
|
|
71 | 71 | Set<VariantRoleLayer> entries) { |
|
|
72 | 72 | try { |
|
|
73 | 73 | Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor( |
|
|
74 | 74 | Set.class, |
|
|
75 | 75 | Set.class, |
|
|
76 | 76 | Set.class, |
|
|
77 | 77 | Set.class); |
|
|
78 | 78 | ctor.setAccessible(true); |
|
|
79 | 79 | return ctor.newInstance(layers, roles, variants, entries); |
|
|
80 | 80 | } catch (Exception e) { |
|
|
81 | 81 | throw new RuntimeException("Unable to create VariantsView fixture", e); |
|
|
82 | 82 | } |
|
|
83 | 83 | } |
|
|
84 | 84 | |
|
|
85 | 85 | private record TestVariant(String value) implements Variant { |
|
|
86 | 86 | @Override |
|
|
87 | 87 | public String getName() { |
|
|
88 | 88 | return value; |
|
|
89 | 89 | } |
|
|
90 | 90 | } |
|
|
91 | 91 | |
|
|
92 | 92 | private record TestLayer(String value) implements Layer { |
|
|
93 | 93 | @Override |
|
|
94 | 94 | public String getName() { |
|
|
95 | 95 | return value; |
|
|
96 | 96 | } |
|
|
97 | 97 | } |
|
|
98 | 98 | |
|
|
99 | 99 | private record TestRole(String value) implements Role { |
|
|
100 | 100 | @Override |
|
|
101 | 101 | public String getName() { |
|
|
102 | 102 | return value; |
|
|
103 | 103 | } |
|
|
104 | 104 | } |
|
|
105 | 105 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
