| @@ -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,5 +1,7 | |||||
| 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; | |
| @@ -8,10 +10,26 import org.gradle.api.provider.Provider; | |||||
| 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 | |
| @@ -55,10 +73,26 public class Strings { | |||||
| 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; | |
| @@ -67,4 +101,49 public class Strings { | |||||
| 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 | } | |||
| @@ -10,6 +10,7 import org.implab.gradle.variants.artifa | |||||
| 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; | |
| @@ -32,6 +33,7 public abstract class VariantArtifactsPl | |||||
| 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>(); | |
| @@ -46,8 +48,9 public abstract class VariantArtifactsPl | |||||
| 46 |
|
48 | |||
| 47 | @Override |
|
49 | @Override | |
| 48 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
50 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { | |
| 49 |
deferred.whenResolved(context -> |
|
51 | deferred.whenResolved(context -> { | |
| 50 | objects.named(Variant.class, variantName), action)); |
|
52 | new DefaultVariantArtifactSpec(); | |
|
|
53 | }); | |||
| 51 | } |
|
54 | } | |
| 52 |
|
55 | |||
| 53 | @Override |
|
56 | @Override | |
| @@ -1,7 +1,6 | |||||
| 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; | |
| @@ -35,9 +34,4 public interface ArtifactAssembly { | |||||
| 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 | } | |
| @@ -9,43 +9,18 import org.eclipse.jdt.annotation.NonNul | |||||
| 9 | import org.gradle.api.Action; |
|
9 | import org.gradle.api.Action; | |
| 10 | import org.gradle.api.InvalidUserDataException; |
|
10 | import org.gradle.api.InvalidUserDataException; | |
| 11 | import org.gradle.api.Task; |
|
11 | import org.gradle.api.Task; | |
| 12 | import org.gradle.api.file.Directory; |
|
|||
| 13 | import org.gradle.api.file.FileCollection; |
|
|||
| 14 | import org.gradle.api.file.FileSystemLocation; |
|
12 | import org.gradle.api.file.FileSystemLocation; | |
| 15 | import org.gradle.api.model.ObjectFactory; |
|
|||
| 16 | import org.gradle.api.provider.Provider; |
|
13 | import org.gradle.api.provider.Provider; | |
| 17 | import org.gradle.api.tasks.Copy; |
|
|||
| 18 | import org.gradle.api.tasks.TaskContainer; |
|
|||
| 19 | import org.gradle.api.tasks.TaskProvider; |
|
14 | import org.gradle.api.tasks.TaskProvider; | |
| 20 | import org.gradle.language.base.plugins.LifecycleBasePlugin; |
|
|||
| 21 | import org.implab.gradle.common.core.lang.Deferred; |
|
15 | import org.implab.gradle.common.core.lang.Deferred; | |
| 22 | import org.implab.gradle.internal.ReplayableQueue; |
|
16 | import org.implab.gradle.internal.ReplayableQueue; | |
| 23 |
|
17 | |||
| 24 | @NonNullByDefault |
|
18 | @NonNullByDefault | |
| 25 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
19 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { | |
| 26 | private final ObjectFactory objects; |
|
|||
| 27 | private final TaskContainer tasks; |
|
|||
| 28 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
20 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); | |
| 29 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
21 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); | |
| 30 |
|
22 | |||
| 31 |
public ArtifactAssemblyRegistry( |
|
23 | public ArtifactAssemblyRegistry() { | |
| 32 | this.objects = objects; |
|
|||
| 33 | this.tasks = tasks; |
|
|||
| 34 | } |
|
|||
| 35 |
|
||||
| 36 | public ArtifactAssembly register( |
|
|||
| 37 | ArtifactSlot slot, |
|
|||
| 38 | String taskName, |
|
|||
| 39 | Provider<Directory> outputDirectory, |
|
|||
| 40 | FileCollection sources) { |
|
|||
| 41 |
|
||||
| 42 | var task = tasks.register(taskName, Copy.class, copy -> { |
|
|||
| 43 | copy.setGroup(LifecycleBasePlugin.BUILD_GROUP); |
|
|||
| 44 | copy.into(outputDirectory); |
|
|||
| 45 | copy.from(sources); |
|
|||
| 46 | }); |
|
|||
| 47 |
|
||||
| 48 | return register(slot, task, t -> outputDirectory); |
|
|||
| 49 | } |
|
24 | } | |
| 50 |
|
25 | |||
| 51 | public <T extends Task> ArtifactAssembly register( |
|
26 | public <T extends Task> ArtifactAssembly register( | |
| @@ -59,11 +34,7 public class ArtifactAssemblyRegistry im | |||||
| 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; | |
| @@ -96,13 +67,10 public class ArtifactAssemblyRegistry im | |||||
| 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 | |
| @@ -115,10 +83,5 public class ArtifactAssemblyRegistry im | |||||
| 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,8 +1,6 | |||||
| 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; | |
| @@ -21,43 +19,40 import org.implab.gradle.variants.artifa | |||||
| 21 | * вызывает метод {@link #process(Consumer)} для обработки результатов. |
|
19 | * вызывает метод {@link #process(Consumer)} для обработки результатов. | |
| 22 | * |
|
20 | * | |
| 23 | */ |
|
21 | */ | |
| 24 |
final class |
|
22 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { | |
| 25 |
private final |
|
23 | private final Consumer<? super SlotContribution> consumer; | |
| 26 | private final ObjectFactory objectFactory; |
|
24 | private final ObjectFactory objectFactory; | |
| 27 |
|
25 | |||
| 28 |
|
|
26 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { | |
|
|
27 | this.consumer = consumer; | |||
| 29 | this.objectFactory = objectFactory; |
|
28 | this.objectFactory = objectFactory; | |
| 30 | } |
|
29 | } | |
| 31 |
|
30 | |||
| 32 | @Override |
|
31 | @Override | |
| 33 | public void from(Object artifact) { |
|
32 | public void from(Object artifact) { | |
| 34 |
con |
|
33 | consumer.accept(new DirectContribution(artifact)); | |
| 35 | } |
|
34 | } | |
| 36 |
|
35 | |||
| 37 | @Override |
|
36 | @Override | |
| 38 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
37 | public void fromVariant(Action<? super OutputSelectionSpec> action) { | |
| 39 |
con |
|
38 | consumer.accept(new VariantOutputsContribution(outputs(action))); | |
| 40 | } |
|
39 | } | |
| 41 |
|
40 | |||
| 42 | @Override |
|
41 | @Override | |
| 43 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
42 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { | |
| 44 |
|
43 | |||
| 45 |
con |
|
44 | consumer.accept(new RoleOutputsContribution( | |
| 46 | objectFactory.named(Role.class, roleName), |
|
45 | objectFactory.named(Role.class, roleName), | |
| 47 | outputs(action))); |
|
46 | outputs(action))); | |
| 48 | } |
|
47 | } | |
| 49 |
|
48 | |||
| 50 | @Override |
|
49 | @Override | |
| 51 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
50 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { | |
| 52 |
con |
|
51 | consumer.accept(new LayerOutputsContribution( | |
| 53 | objectFactory.named(Layer.class, layerName), |
|
52 | objectFactory.named(Layer.class, layerName), | |
| 54 | outputs(action))); |
|
53 | outputs(action))); | |
| 55 | } |
|
54 | } | |
| 56 |
|
55 | |||
| 57 | void process(Consumer<? super SlotContribution> consumer) { |
|
|||
| 58 | contributions.forEach(consumer); |
|
|||
| 59 | } |
|
|||
| 60 |
|
||||
| 61 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
56 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { | |
| 62 | var spec = new OutputsSetSpec(); |
|
57 | var spec = new OutputsSetSpec(); | |
| 63 | action.execute(spec); |
|
58 | action.execute(spec); | |
| @@ -1,22 +1,51 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 |
import java.util. |
|
3 | import java.util.HashSet; | |
| 4 | import java.util.Set; |
|
4 | import java.util.Set; | |
| 5 | import java.util.function.Consumer; |
|
|||
| 6 | import java.util.function.Function; |
|
|||
| 7 |
|
5 | |||
|
|
6 | import org.gradle.api.file.ConfigurableFileCollection; | |||
| 8 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
7 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
| 9 | import org.implab.gradle.variants.core.VariantsView; |
|
|||
| 10 | import org.implab.gradle.variants.sources.CompileUnit; |
|
8 | import org.implab.gradle.variants.sources.CompileUnit; | |
| 11 |
import org.implab.gradle.variants.sources. |
|
9 | import org.implab.gradle.variants.sources.CompileUnitsView; | |
|
|
10 | import org.implab.gradle.variants.sources.RoleProjectionsView; | |||
|
|
11 | import org.implab.gradle.variants.sources.SourceSetMaterializer; | |||
| 12 |
|
12 | |||
|
|
13 | /** | |||
|
|
14 | * Сборщик входящих элементов из разных источников {@link SlotContribution}. | |||
|
|
15 | * Хранит {@link ConfigurableFileCollection} и добавляет в него новые элементы, | |||
|
|
16 | * при этом делается дедупликация по {@link SlotInputKey}. | |||
|
|
17 | * | |||
|
|
18 | */ | |||
| 13 | public class SlotInputsAssembler implements SlotContributionVisitor { |
|
19 | public class SlotInputsAssembler implements SlotContributionVisitor { | |
| 14 |
|
20 | |||
| 15 | VariantsView variantView; |
|
21 | // sources context | |
| 16 | VariantSourcesContext sources; |
|
22 | private final CompileUnitsView compileUnitsView; | |
| 17 | ArtifactSlot artifactSlot; |
|
23 | ||
|
|
24 | private final RoleProjectionsView roleProjectionsView; | |||
|
|
25 | ||||
|
|
26 | private final SourceSetMaterializer sourceSetMaterializer; | |||
|
|
27 | ||||
|
|
28 | // artifact slot for this assembly | |||
|
|
29 | private final ArtifactSlot artifactSlot; | |||
|
|
30 | ||||
|
|
31 | // content for this assembly | |||
|
|
32 | private final ConfigurableFileCollection artifactInputs; | |||
| 18 |
|
33 | |||
| 19 | Set<SlotInputKey> seen; |
|
34 | // seen inputs, used for deduplication | |
|
|
35 | private final Set<SlotInputKey> seen = new HashSet<>(); | |||
|
|
36 | ||||
|
|
37 | public SlotInputsAssembler( | |||
|
|
38 | ArtifactSlot artifactSlot, | |||
|
|
39 | ConfigurableFileCollection artifactInputs, | |||
|
|
40 | CompileUnitsView compileUnitsView, | |||
|
|
41 | RoleProjectionsView roleProjectionsView, | |||
|
|
42 | SourceSetMaterializer sourceSetMaterializer) { | |||
|
|
43 | this.compileUnitsView = compileUnitsView; | |||
|
|
44 | this.roleProjectionsView = roleProjectionsView; | |||
|
|
45 | this.sourceSetMaterializer = sourceSetMaterializer; | |||
|
|
46 | this.artifactSlot = artifactSlot; | |||
|
|
47 | this.artifactInputs = artifactInputs; | |||
|
|
48 | } | |||
| 20 |
|
49 | |||
| 21 | @Override |
|
50 | @Override | |
| 22 | public void visit(DirectContribution contribution) { |
|
51 | public void visit(DirectContribution contribution) { | |
| @@ -27,29 +56,41 public class SlotInputsAssembler impleme | |||||
| 27 |
|
56 | |||
| 28 | @Override |
|
57 | @Override | |
| 29 | public void visit(VariantOutputsContribution contribution) { |
|
58 | public void visit(VariantOutputsContribution contribution) { | |
| 30 |
|
|
59 | var units = compileUnitsView.getUnitsForVariant(artifactSlot.variant()); | |
|
|
60 | contributeCompileUnits(units, contribution.outputs()); | |||
| 31 | } |
|
61 | } | |
| 32 |
|
62 | |||
| 33 | @Override |
|
63 | @Override | |
| 34 | public void visit(RoleOutputsContribution contribution) { |
|
64 | public void visit(RoleOutputsContribution contribution) { | |
| 35 | // TODO Auto-generated method stub |
|
65 | var roleProjection = roleProjectionsView.requireProjection(artifactSlot.variant(), | |
| 36 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); |
|
66 | contribution.role()); | |
|
|
67 | var units = roleProjectionsView.getUnits(roleProjection); | |||
|
|
68 | ||||
|
|
69 | contributeCompileUnits(units, contribution.outputs()); | |||
|
|
70 | ||||
| 37 | } |
|
71 | } | |
| 38 |
|
72 | |||
| 39 | @Override |
|
73 | @Override | |
| 40 | public void visit(LayerOutputsContribution contribution) { |
|
74 | public void visit(LayerOutputsContribution contribution) { | |
| 41 | // TODO Auto-generated method stub |
|
75 | var unit = compileUnitsView.requireUnit(artifactSlot.variant(), contribution.layer()); | |
| 42 | throw new UnsupportedOperationException("Unimplemented method 'visit'"); |
|
76 | contributeCompileUnits(Set.of(unit), contribution.outputs()); | |
| 43 | } |
|
77 | } | |
| 44 |
|
78 | |||
| 45 | private void contribute(SlotInputKey key, Object input) { |
|
79 | private void contributeCompileUnits(Set<CompileUnit> units, Set<String> outputs) { | |
| 46 |
|
80 | units.stream() | ||
|
|
81 | // expand variant compile units, make (compileUnit, outputName) pairs | |||
|
|
82 | .flatMap(unit -> outputs.stream() | |||
|
|
83 | .map(output -> new CompileUnitOutputKey(unit, output))) | |||
|
|
84 | .forEach(key -> contribute( | |||
|
|
85 | key, | |||
|
|
86 | sourceSetMaterializer.getSourceSet(key.unit()) | |||
|
|
87 | .map(s -> s.output(key.outputName())))); | |||
| 47 | } |
|
88 | } | |
| 48 |
|
89 | |||
| 49 | private Function<CompileUnit, Object> resolveOutputs(Collection<? extends String> outputs) { |
|
90 | private void contribute(SlotInputKey key, Object resolvedInput) { | |
| 50 | return unit -> { |
|
91 | if (!seen.add(key)) | |
| 51 |
|
92 | return; | ||
| 52 | }; |
|
93 | artifactInputs.from(resolvedInput); | |
| 53 | } |
|
94 | } | |
| 54 |
|
95 | |||
| 55 | } |
|
96 | } | |
| @@ -64,7 +64,7 public final class RoleProjectionsView { | |||||
| 64 |
|
64 | |||
| 65 | } |
|
65 | } | |
| 66 |
|
66 | |||
| 67 |
public RoleProjection |
|
67 | public RoleProjection requireProjection(Variant variant, Role role) { | |
| 68 | return findProjection(variant, role) |
|
68 | return findProjection(variant, role) | |
| 69 | .orElseThrow(() -> new IllegalArgumentException( |
|
69 | .orElseThrow(() -> new IllegalArgumentException( | |
| 70 | "Role projection for variant '" + variant.getName() |
|
70 | "Role projection for variant '" + variant.getName() | |
| @@ -33,8 +33,8 class RoleProjectionsViewTest { | |||||
| 33 | new VariantRoleLayer(browser, qa, test))); |
|
33 | new VariantRoleLayer(browser, qa, test))); | |
| 34 |
|
34 | |||
| 35 | var projections = RoleProjectionsView.of(view); |
|
35 | var projections = RoleProjectionsView.of(view); | |
| 36 |
var productionProjection = projections. |
|
36 | var productionProjection = projections.requireProjection(browser, production); | |
| 37 |
var qaProjection = projections. |
|
37 | var qaProjection = projections.requireProjection(browser, qa); | |
| 38 |
|
38 | |||
| 39 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections()); |
|
39 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjections()); | |
| 40 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser)); |
|
40 | assertEquals(Set.of(productionProjection, qaProjection), projections.getProjectionsForVariant(browser)); | |
| @@ -60,7 +60,7 class RoleProjectionsViewTest { | |||||
| 60 |
|
60 | |||
| 61 | var projections = RoleProjectionsView.of(view); |
|
61 | var projections = RoleProjectionsView.of(view); | |
| 62 |
|
62 | |||
| 63 |
var ex = assertThrows(IllegalArgumentException.class, () -> projections. |
|
63 | var ex = assertThrows(IllegalArgumentException.class, () -> projections.requireProjection(node, production)); | |
| 64 | assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found")); |
|
64 | assertTrue(ex.getMessage().contains("Role projection for variant 'node' and role 'production' not found")); | |
| 65 | } |
|
65 | } | |
| 66 |
|
66 | |||
General Comments 0
You need to be logged in to leave comments.
Login now
