##// END OF EJS Templates
WIP variant artifacts DSL, FilePaths traits
cin -
r50:ca3982e55d9e default
parent child
Show More
@@ -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 -> registry.configureVariant(
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(ObjectFactory objects, TaskContainer tasks) {
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 BoundArtifactAssemblySpec implements ArtifactAssemblySpec {
22 final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec {
25 private final List<SlotContribution> contributions = new LinkedList<>();
23 private final Consumer<? super SlotContribution> consumer;
26 private final ObjectFactory objectFactory;
24 private final ObjectFactory objectFactory;
27
25
28 BoundArtifactAssemblySpec(ObjectFactory objectFactory) {
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 contributions.add(new DirectContribution(artifact));
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 contributions.add(new VariantOutputsContribution(outputs(action)));
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 contributions.add(new RoleOutputsContribution(
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 contributions.add(new LayerOutputsContribution(
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.Collection;
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.VariantSourcesContext;
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 sources.getCompileUnits().getUnitsForVariant(artifactSlot.variant()).stream()
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 getProjection(Variant variant, Role role) {
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.getProjection(browser, production);
36 var productionProjection = projections.requireProjection(browser, production);
37 var qaProjection = projections.getProjection(browser, qa);
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.getProjection(node, production));
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