| @@ -0,0 +1,218 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.HashMap; | |
|
|
4 | import java.util.HashSet; | |
|
|
5 | import java.util.Map; | |
|
|
6 | import java.util.Set; | |
|
|
7 | ||
|
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
9 | import org.gradle.api.Action; | |
|
|
10 | import org.gradle.api.file.ConfigurableFileCollection; | |
|
|
11 | import org.gradle.api.file.Directory; | |
|
|
12 | import org.gradle.api.file.DirectoryProperty; | |
|
|
13 | import org.gradle.api.file.FileCollection; | |
|
|
14 | import org.gradle.api.model.ObjectFactory; | |
|
|
15 | import org.gradle.api.provider.Provider; | |
|
|
16 | import org.gradle.api.tasks.Sync; | |
|
|
17 | import org.gradle.api.tasks.TaskContainer; | |
|
|
18 | import org.gradle.language.base.plugins.LifecycleBasePlugin; | |
|
|
19 | import org.implab.gradle.common.core.lang.FilePaths; | |
|
|
20 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
|
|
21 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
22 | import org.implab.gradle.variants.sources.CompileUnit; | |
|
|
23 | import org.implab.gradle.variants.sources.CompileUnitsView; | |
|
|
24 | import org.implab.gradle.variants.sources.RoleProjectionsView; | |
|
|
25 | import org.implab.gradle.variants.sources.SourceSetMaterializer; | |
|
|
26 | ||
|
|
27 | /** | |
|
|
28 | * Адаптер между фрагментами артефактов, представленными в виде | |
|
|
29 | * {@link SlotContribution}, | |
|
|
30 | * и ArtifactAssemblyRegistry, который оперирует уже собранными | |
|
|
31 | * {@link ArtifactAssembly}. | |
|
|
32 | * | |
|
|
33 | * Данный класс отвечает за сборку отдельных фрагментов артефактов в единый | |
|
|
34 | * каталог, который затем регистрируется в ArtifactAssemblyRegistry в виде | |
|
|
35 | * {@link ArtifactAssembly}. Сборка конечного артефакта происходит при помощи | |
|
|
36 | * задачи, которая копирует все входные файлы в выходной каталог. Задача | |
|
|
37 | * создается для каждого {@link ArtifactSlot} и использует | |
|
|
38 | * {@link SlotAssembly#inputs()} как источник входных данных. | |
|
|
39 | * | |
|
|
40 | * Для сборки используется паттерн Visitor: каждый фрагмент артефакта | |
|
|
41 | * представлен в виде реализации интерфейса {@link SlotContribution}, который | |
|
|
42 | * имеет метод accept, принимающий Visitor. | |
|
|
43 | * Visitor реализован во внутреннем классе {@link ContributionVisitor}, который | |
|
|
44 | * знает, как обрабатывать каждый тип фрагмента и добавлять его в сборку. | |
|
|
45 | * Для каждого {@link SlotContribution} создается ключ {@link SlotInputKey}, | |
|
|
46 | * который используется для дедупликации: если фрагмент с таким же ключом уже | |
|
|
47 | * был добавлен, то он игнорируется. | |
|
|
48 | */ | |
|
|
49 | @NonNullByDefault | |
|
|
50 | public class ArtifactAssemblyHandler { | |
|
|
51 | private final ObjectFactory objects; | |
|
|
52 | ||
|
|
53 | private final ArtifactAssemblyRegistry assemblyRegistry; | |
|
|
54 | ||
|
|
55 | private final DirectoryProperty assembliesDirectory; | |
|
|
56 | ||
|
|
57 | private final TaskContainer tasks; | |
|
|
58 | ||
|
|
59 | private final CompileUnitsView compileUnitsView; | |
|
|
60 | ||
|
|
61 | private final RoleProjectionsView roleProjectionsView; | |
|
|
62 | ||
|
|
63 | private final SourceSetMaterializer sourceSetMaterializer; | |
|
|
64 | ||
|
|
65 | private final Map<ArtifactSlot, SlotAssembly> slotInputs = new HashMap<>(); | |
|
|
66 | ||
|
|
67 | public ArtifactAssemblyHandler( | |
|
|
68 | ObjectFactory objects, | |
|
|
69 | TaskContainer tasks, | |
|
|
70 | ArtifactAssemblyRegistry assemblyRegistry, | |
|
|
71 | CompileUnitsView compileUnitsView, | |
|
|
72 | RoleProjectionsView roleProjectionsView, | |
|
|
73 | SourceSetMaterializer sourceSetMaterializer) { | |
|
|
74 | this.objects = objects; | |
|
|
75 | this.tasks = tasks; | |
|
|
76 | this.assemblyRegistry = assemblyRegistry; | |
|
|
77 | this.compileUnitsView = compileUnitsView; | |
|
|
78 | this.roleProjectionsView = roleProjectionsView; | |
|
|
79 | this.sourceSetMaterializer = sourceSetMaterializer; | |
|
|
80 | ||
|
|
81 | assembliesDirectory = objects.directoryProperty(); | |
|
|
82 | } | |
|
|
83 | ||
|
|
84 | public DirectoryProperty getAssembliesDirectory() { | |
|
|
85 | return assembliesDirectory; | |
|
|
86 | } | |
|
|
87 | ||
|
|
88 | public void configureAssembly(ArtifactSlot artifactSlot, Action<? super ArtifactAssemblySpec> action) { | |
|
|
89 | var visitor = contributionVisitor(artifactSlot); | |
|
|
90 | var spec = new DefaultArtifactAssemblySpec(objects, c -> c.accept(visitor)); | |
|
|
91 | action.execute(spec); | |
|
|
92 | } | |
|
|
93 | ||
|
|
94 | public SlotContributionVisitor contributionVisitor(ArtifactSlot artifactSlot) { | |
|
|
95 | var assembly = slotInputs.computeIfAbsent(artifactSlot, this::createSlotAssembly); | |
|
|
96 | return new ContributionVisitor(artifactSlot, assembly); | |
|
|
97 | } | |
|
|
98 | ||
|
|
99 | /** | |
|
|
100 | * Создает сборку для указанного слота артефакта, сборка регистрируется в | |
|
|
101 | * ArtifactAssemblyRegistry, если для слота сборка уже была зарегистрирована | |
|
|
102 | * кем-то еще, то возникает ошибка. | |
|
|
103 | */ | |
|
|
104 | private SlotAssembly createSlotAssembly(ArtifactSlot artifactSlot) { | |
|
|
105 | var assembly = new SlotAssembly(); | |
|
|
106 | var fileCollection = assembly.inputs(); | |
|
|
107 | ||
|
|
108 | var outputDirectory = outputDirectory(artifactSlot); | |
|
|
109 | ||
|
|
110 | var task = tasks.register(assembleTaskName(artifactSlot), Sync.class, copy -> { | |
|
|
111 | copy.setGroup(LifecycleBasePlugin.BUILD_GROUP); | |
|
|
112 | copy.into(outputDirectory); | |
|
|
113 | copy.from(fileCollection); | |
|
|
114 | }); | |
|
|
115 | ||
|
|
116 | assemblyRegistry.register(artifactSlot, task, t -> outputDirectory); | |
|
|
117 | ||
|
|
118 | return assembly; | |
|
|
119 | } | |
|
|
120 | ||
|
|
121 | private String assembleTaskName(ArtifactSlot artifactSlot) { | |
|
|
122 | var variantName = artifactSlot.variant().getName(); | |
|
|
123 | var slotName = artifactSlot.slot().getName(); | |
|
|
124 | ||
|
|
125 | return "assembleVariantArtifactSlot" | |
|
|
126 | + "_v" + variantName.length() + "_" + variantName | |
|
|
127 | + "_s" + slotName.length() + "_" + slotName; | |
|
|
128 | } | |
|
|
129 | ||
|
|
130 | private Provider<Directory> outputDirectory(ArtifactSlot artifactSlot) { | |
|
|
131 | return assembliesDirectory.dir( | |
|
|
132 | FilePaths.cat( | |
|
|
133 | artifactSlot.variant().getName(), | |
|
|
134 | artifactSlot.slot().getName())); | |
|
|
135 | } | |
|
|
136 | ||
|
|
137 | /** | |
|
|
138 | * Собирает отдельные фрагменты артефактов в единый источник | |
|
|
139 | * {@link ConfigurableFileCollection}. | |
|
|
140 | * Для фрагментов {@link SlotContribution} используется механизм дедупликации: | |
|
|
141 | * для каждого | |
|
|
142 | * водящего фрагмента создается ключ {@link SlotInputKey} и добавляются | |
|
|
143 | * фрагменты только | |
|
|
144 | * с уникальным ключом, повторы игнорируются. | |
|
|
145 | */ | |
|
|
146 | private class ContributionVisitor implements SlotContributionVisitor { | |
|
|
147 | // artifact slot for this assembly | |
|
|
148 | private final ArtifactSlot artifactSlot; | |
|
|
149 | ||
|
|
150 | // seen inputs, used for deduplication | |
|
|
151 | private final SlotAssembly assembly; | |
|
|
152 | ||
|
|
153 | ContributionVisitor(ArtifactSlot artifactSlot, SlotAssembly assembly) { | |
|
|
154 | this.artifactSlot = artifactSlot; | |
|
|
155 | this.assembly = assembly; | |
|
|
156 | } | |
|
|
157 | ||
|
|
158 | @Override | |
|
|
159 | public void visit(DirectContribution contribution) { | |
|
|
160 | contribute( | |
|
|
161 | SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), | |
|
|
162 | contribution.input()); | |
|
|
163 | } | |
|
|
164 | ||
|
|
165 | @Override | |
|
|
166 | public void visit(VariantOutputsContribution contribution) { | |
|
|
167 | var units = compileUnitsView.getUnitsForVariant(artifactSlot.variant()); | |
|
|
168 | contributeCompileUnits(units, contribution.outputs()); | |
|
|
169 | } | |
|
|
170 | ||
|
|
171 | @Override | |
|
|
172 | public void visit(RoleOutputsContribution contribution) { | |
|
|
173 | var roleProjection = roleProjectionsView.requireProjection(artifactSlot.variant(), | |
|
|
174 | contribution.role()); | |
|
|
175 | var units = roleProjectionsView.getUnits(roleProjection); | |
|
|
176 | ||
|
|
177 | contributeCompileUnits(units, contribution.outputs()); | |
|
|
178 | ||
|
|
179 | } | |
|
|
180 | ||
|
|
181 | @Override | |
|
|
182 | public void visit(LayerOutputsContribution contribution) { | |
|
|
183 | var unit = compileUnitsView.requireUnit(artifactSlot.variant(), contribution.layer()); | |
|
|
184 | contributeCompileUnits(Set.of(unit), contribution.outputs()); | |
|
|
185 | } | |
|
|
186 | ||
|
|
187 | private void contributeCompileUnits(Set<CompileUnit> units, Set<String> outputs) { | |
|
|
188 | units.stream() | |
|
|
189 | // expand variant compile units, make (compileUnit, outputName) pairs | |
|
|
190 | .flatMap(unit -> outputs.stream() | |
|
|
191 | .map(output -> new CompileUnitOutputKey(unit, output))) | |
|
|
192 | .forEach(key -> contribute( | |
|
|
193 | key, | |
|
|
194 | sourceSetMaterializer.getSourceSet(key.unit()) | |
|
|
195 | .map(s -> s.output(key.outputName())))); | |
|
|
196 | } | |
|
|
197 | ||
|
|
198 | private void contribute(SlotInputKey key, Object resolvedInput) { | |
|
|
199 | assembly.addSlotInput(key, resolvedInput); | |
|
|
200 | } | |
|
|
201 | } | |
|
|
202 | ||
|
|
203 | /** Состояние для отдельного слота */ | |
|
|
204 | class SlotAssembly { | |
|
|
205 | private final ConfigurableFileCollection inputs = objects.fileCollection(); | |
|
|
206 | private final Set<SlotInputKey> seen = new HashSet<>(); | |
|
|
207 | ||
|
|
208 | public void addSlotInput(SlotInputKey key, Object input) { | |
|
|
209 | if (!seen.add(key)) | |
|
|
210 | return; | |
|
|
211 | inputs.from(input); | |
|
|
212 | } | |
|
|
213 | ||
|
|
214 | public FileCollection inputs() { | |
|
|
215 | return inputs; | |
|
|
216 | } | |
|
|
217 | } | |
|
|
218 | } | |
| @@ -0,0 +1,94 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import java.util.Optional; | |
|
|
4 | ||
|
|
5 | import org.gradle.api.Action; | |
|
|
6 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
|
|
7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
|
|
8 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
9 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec; | |
|
|
10 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; | |
|
|
11 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |
|
|
12 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; | |
|
|
13 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |
|
|
14 | import org.implab.gradle.variants.core.Variant; | |
|
|
15 | ||
|
|
16 | public class DefaultOutgoingVariantsContext implements OutgoingVariantsContext { | |
|
|
17 | private final ArtifactAssemblies assemblies; | |
|
|
18 | ||
|
|
19 | private final ArtifactAssemblyHandler assemblyHandler; | |
|
|
20 | ||
|
|
21 | private final OutgoingRegistry outgoingVariants; | |
|
|
22 | ||
|
|
23 | private final MaterializationPolicyHandler materializationHandler; | |
|
|
24 | ||
|
|
25 | public DefaultOutgoingVariantsContext( | |
|
|
26 | ArtifactAssemblies assemblies, | |
|
|
27 | OutgoingRegistry outgoingVariants, | |
|
|
28 | ArtifactAssemblyHandler assemblyHandler, | |
|
|
29 | MaterializationPolicyHandler materializationHandler) { | |
|
|
30 | this.assemblies = assemblies; | |
|
|
31 | this.outgoingVariants = outgoingVariants; | |
|
|
32 | this.assemblyHandler = assemblyHandler; | |
|
|
33 | this.materializationHandler = materializationHandler; | |
|
|
34 | } | |
|
|
35 | ||
|
|
36 | @Override | |
|
|
37 | public ArtifactAssemblies getAssemblies() { | |
|
|
38 | return assemblies; | |
|
|
39 | } | |
|
|
40 | ||
|
|
41 | @Override | |
|
|
42 | public void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action) { | |
|
|
43 | var outgoingVariant = outgoingVariants.maybeCreate(variant); | |
|
|
44 | var variantSpec = new VariantSpec(outgoingVariant); | |
|
|
45 | action.execute(variantSpec); | |
|
|
46 | } | |
|
|
47 | ||
|
|
48 | @Override | |
|
|
49 | public void configureEach(Action<? super OutgoingVariant> action) { | |
|
|
50 | outgoingVariants.configureEach(action::execute); | |
|
|
51 | } | |
|
|
52 | ||
|
|
53 | @Override | |
|
|
54 | public Optional<OutgoingVariant> findOutgoing(Variant variant) { | |
|
|
55 | return outgoingVariants.find(variant); | |
|
|
56 | } | |
|
|
57 | ||
|
|
58 | @Override | |
|
|
59 | public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) { | |
|
|
60 | materializationHandler.whenVariantMaterialized(action); | |
|
|
61 | } | |
|
|
62 | ||
|
|
63 | @Override | |
|
|
64 | public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) { | |
|
|
65 | materializationHandler.whenSlotMaterialized(action); | |
|
|
66 | } | |
|
|
67 | ||
|
|
68 | class VariantSpec implements VariantArtifactsSpec { | |
|
|
69 | ||
|
|
70 | private final OutgoingVariant outgoingVariant; | |
|
|
71 | ||
|
|
72 | VariantSpec(OutgoingVariant outgoingVariant) { | |
|
|
73 | this.outgoingVariant = outgoingVariant; | |
|
|
74 | } | |
|
|
75 | ||
|
|
76 | @Override | |
|
|
77 | public void slot(String name, Action<? super ArtifactAssemblySpec> action) { | |
|
|
78 | var slot = outgoingVariant.getSlots().maybeCreate(name); | |
|
|
79 | var artifactSlot = new ArtifactSlot(outgoingVariant.getVariant(), slot); | |
|
|
80 | assemblyHandler.configureAssembly(artifactSlot, action); | |
|
|
81 | } | |
|
|
82 | ||
|
|
83 | @Override | |
|
|
84 | public void primarySlot(String name, Action<? super ArtifactAssemblySpec> action) { | |
|
|
85 | var slot = outgoingVariant.getSlots().maybeCreate(name); | |
|
|
86 | var artifactSlot = new ArtifactSlot(outgoingVariant.getVariant(), slot); | |
|
|
87 | assemblyHandler.configureAssembly(artifactSlot, action); | |
|
|
88 | ||
|
|
89 | outgoingVariant.getPrimarySlot().set(slot); | |
|
|
90 | } | |
|
|
91 | ||
|
|
92 | } | |
|
|
93 | ||
|
|
94 | } | |
| @@ -0,0 +1,118 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | import org.gradle.api.Action; | |
|
|
5 | import org.gradle.api.artifacts.Configuration; | |
|
|
6 | import org.gradle.api.attributes.AttributeContainer; | |
|
|
7 | import org.implab.gradle.internal.ReplayableQueue; | |
|
|
8 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
|
|
9 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; | |
|
|
10 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
11 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec; | |
|
|
12 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; | |
|
|
13 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |
|
|
14 | import org.implab.gradle.variants.core.Variant; | |
|
|
15 | ||
|
|
16 | /** | |
|
|
17 | * Handles outgoing artifact materialization policy. | |
|
|
18 | * | |
|
|
19 | * <p>Materialization is the phase where the plugin interprets variant artifact | |
|
|
20 | * declarations as Gradle outgoing publication state: a consumable configuration, | |
|
|
21 | * its primary artifact set, secondary artifact variants, attributes, and backing | |
|
|
22 | * assembly tasks. | |
|
|
23 | * | |
|
|
24 | * <p>The handler provides extension points for customizing the materialized | |
|
|
25 | * Gradle-facing objects. These hooks intentionally expose Gradle API objects. | |
|
|
26 | * This allows advanced customization, but also means that callers can bypass | |
|
|
27 | * the plugin model. Such customizations are considered caller responsibility. | |
|
|
28 | * | |
|
|
29 | * <p>The internal binding mechanics are not part of the public contract. The | |
|
|
30 | * contract is the materialized outgoing state exposed through the specification | |
|
|
31 | * objects. | |
|
|
32 | */ | |
|
|
33 | @NonNullByDefault | |
|
|
34 | public class MaterializationPolicyHandler implements Action<OutgoingVariant> { | |
|
|
35 | ||
|
|
36 | private final ArtifactAssemblies resolver; | |
|
|
37 | ||
|
|
38 | private final ReplayableQueue<OutgoingConfigurationSpec> variantMaterialization = new ReplayableQueue<>(); | |
|
|
39 | private final ReplayableQueue<OutgoingConfigurationSlotSpec> slotMaterialization = new ReplayableQueue<>(); | |
|
|
40 | ||
|
|
41 | public MaterializationPolicyHandler(ArtifactAssemblies resolver) { | |
|
|
42 | this.resolver = resolver; | |
|
|
43 | } | |
|
|
44 | ||
|
|
45 | @Override | |
|
|
46 | public void execute(OutgoingVariant outgoingVariant) { | |
|
|
47 | var slots = outgoingVariant.getSlots(); | |
|
|
48 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); | |
|
|
49 | var variant = outgoingVariant.getVariant(); | |
|
|
50 | ||
|
|
51 | // связываем конфигурацию | |
|
|
52 | outgoingVariant.configureOutgoing(configuration -> { | |
|
|
53 | var primarySlot = primarySlotProvider.get(); | |
|
|
54 | var outgoing = configuration.getOutgoing(); | |
|
|
55 | ||
|
|
56 | variantMaterialized(variant, configuration); | |
|
|
57 | ||
|
|
58 | slotMaterialized(new ArtifactSlot(variant, primarySlot), true, outgoing.getAttributes()); | |
|
|
59 | ||
|
|
60 | outgoing.getVariants().configureEach(variantConfiguration -> { | |
|
|
61 | var slotName = variantConfiguration.getName(); | |
|
|
62 | var slot = slots.findByName(slotName); | |
|
|
63 | if (slot != null) { | |
|
|
64 | slotMaterialized(new ArtifactSlot(variant, slot), false, variantConfiguration.getAttributes()); | |
|
|
65 | } | |
|
|
66 | }); | |
|
|
67 | }); | |
|
|
68 | }; | |
|
|
69 | ||
|
|
70 | public void whenVariantMaterialized(Action<? super OutgoingConfigurationSpec> action) { | |
|
|
71 | variantMaterialization.forEach(action::execute); | |
|
|
72 | } | |
|
|
73 | ||
|
|
74 | public void whenSlotMaterialized(Action<? super OutgoingConfigurationSlotSpec> action) { | |
|
|
75 | slotMaterialization.forEach(action::execute); | |
|
|
76 | } | |
|
|
77 | ||
|
|
78 | private void variantMaterialized(Variant variant, Configuration configuration) { | |
|
|
79 | variantMaterialization.add(new OutgoingConfigurationSpec() { | |
|
|
80 | ||
|
|
81 | @Override | |
|
|
82 | public Variant getVariant() { | |
|
|
83 | return variant; | |
|
|
84 | } | |
|
|
85 | ||
|
|
86 | @Override | |
|
|
87 | public Configuration getConfiguration() { | |
|
|
88 | return configuration; | |
|
|
89 | } | |
|
|
90 | ||
|
|
91 | }); | |
|
|
92 | } | |
|
|
93 | ||
|
|
94 | private void slotMaterialized(ArtifactSlot slot, boolean primary, AttributeContainer attributes) { | |
|
|
95 | slotMaterialization.add(new OutgoingConfigurationSlotSpec() { | |
|
|
96 | @Override | |
|
|
97 | public boolean isPrimary() { | |
|
|
98 | return primary; | |
|
|
99 | } | |
|
|
100 | ||
|
|
101 | @Override | |
|
|
102 | public ArtifactSlot getArtifactSlot() { | |
|
|
103 | return slot; | |
|
|
104 | } | |
|
|
105 | ||
|
|
106 | @Override | |
|
|
107 | public ArtifactAssembly getAssembly() { | |
|
|
108 | return resolver.require(slot); | |
|
|
109 | } | |
|
|
110 | ||
|
|
111 | @Override | |
|
|
112 | public void artifactAttributes(Action<? super AttributeContainer> action) { | |
|
|
113 | action.execute(attributes); | |
|
|
114 | } | |
|
|
115 | }); | |
|
|
116 | } | |
|
|
117 | ||
|
|
118 | } | |
| @@ -0,0 +1,33 | |||
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
2 | ||
|
|
3 | import org.gradle.api.Action; | |
|
|
4 | import org.gradle.api.InvalidUserDataException; | |
|
|
5 | import org.gradle.api.provider.ProviderFactory; | |
|
|
6 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |
|
|
7 | ||
|
|
8 | public class SingleSlotConvention implements Action<OutgoingVariant> { | |
|
|
9 | ||
|
|
10 | private final ProviderFactory providers; | |
|
|
11 | ||
|
|
12 | public SingleSlotConvention(ProviderFactory providers) { | |
|
|
13 | this.providers = providers; | |
|
|
14 | } | |
|
|
15 | ||
|
|
16 | @Override | |
|
|
17 | public void execute(OutgoingVariant outgoingVariant) { | |
|
|
18 | var slots = outgoingVariant.getSlots(); | |
|
|
19 | ||
|
|
20 | outgoingVariant.getPrimarySlot().convention( | |
|
|
21 | // если есть ровно один слот, то он считается primary | |
|
|
22 | providers.provider(() -> { | |
|
|
23 | if (slots.size() == 0) | |
|
|
24 | throw new InvalidUserDataException("No slots declared for " + outgoingVariant.getVariant()); | |
|
|
25 | if (slots.size() > 1) | |
|
|
26 | throw new InvalidUserDataException("Multiple slots declared for " + outgoingVariant.getVariant() + | |
|
|
27 | ", please specify primary slot explicitly"); | |
|
|
28 | ||
|
|
29 | return slots.stream().findAny().get(); | |
|
|
30 | })); | |
|
|
31 | } | |
|
|
32 | ||
|
|
33 | } | |
| @@ -0,0 +1,9 | |||
|
|
1 | /** | |
|
|
2 | * Internal implementation of the variant artifacts plugin. | |
|
|
3 | * | |
|
|
4 | * <p>Types in this package are not part of the public API. They may change, | |
|
|
5 | * move, or be removed without compatibility guarantees. Build logic should use | |
|
|
6 | * the public contracts from {@link org.implab.gradle.variants.artifacts} | |
|
|
7 | * instead. | |
|
|
8 | */ | |
|
|
9 | package org.implab.gradle.variants.artifacts.internal; | |
| @@ -1,54 +1,56 | |||
|
|
1 | 1 | # AGENTS.md |
|
|
2 | 2 | |
|
|
3 | 3 | ## Проектные договоренности |
|
|
4 | 4 | |
|
|
5 | - для управления исходным кодом используется Mercurial | |
|
|
6 | ||
|
|
5 | 7 | ### Публичное API библиотек |
|
|
6 | 8 | |
|
|
7 | 9 | - Предпочтителен `non-null` подход. |
|
|
8 | 10 | - Там, где значение живет в Gradle Provider API, возвращается `Provider<T>` (не `null`). |
|
|
9 | 11 | - Там, где lookup синхронный, возвращается `Optional<T>` (не `null`). |
|
|
10 | 12 | - `find*` рассматривается как синоним legacy `get*` (поиск без `fail-fast`). |
|
|
11 | 13 | - `require*` это `find*` + `fail-fast` с понятной ошибкой в месте вызова. |
|
|
12 | 14 | - Для нового API предпочтительны формы `find/require`; новые `get*` по возможности не добавлять. |
|
|
13 | 15 | - Интерфейсы и классы, описывающие модели DSL должны иметь суффикс `Spec` у моделей описывающих уровень сервисов и состояние сценария сборки такого суффикса не должно быть. |
|
|
14 | 16 | - Модель расширения на уровне проекта должна иметь суффикс `Extension`. |
|
|
15 | 17 | |
|
|
16 | 18 | ### Документация |
|
|
17 | 19 | |
|
|
18 | 20 | - документирование кода должно быть на английском языке |
|
|
19 | 21 | - к публичному API |
|
|
20 | 22 | - описание должно отражать назначение, где используется и какое влияние оказывает на остальные части программы |
|
|
21 | 23 | - давать небольшое описание концепции, а также краткие примеры |
|
|
22 | 24 | - к приватному API достаточно давать краткую справку о назначении и использовании |
|
|
23 | 25 | - реализацию алгоритмов в коде сопровождать комментариями с пояснениями, тривиальные операции пояснять не требуется. |
|
|
24 | 26 | - документация должна формироваться согласно требованиям по форматированию типа javadoc, jsdoc и т.п., в зависимости от используемых в проекте языках и инструментах. |
|
|
25 | 27 | |
|
|
26 | 28 | ## Identity-first modeling |
|
|
27 | 29 | |
|
|
28 | 30 | Prefer an **identity-first** split between: |
|
|
29 | 31 | |
|
|
30 | 32 | - **identity objects** used for discovery and selection |
|
|
31 | 33 | - **stateful/materialized objects** obtained through separate API calls |
|
|
32 | 34 | |
|
|
33 | 35 | ### Rules |
|
|
34 | 36 | |
|
|
35 | 37 | - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**. |
|
|
36 | 38 | - Identity objects must be: |
|
|
37 | 39 | - cheap to create |
|
|
38 | 40 | - effectively immutable |
|
|
39 | 41 | - limited to identity and cheap selection metadata |
|
|
40 | 42 | - Heavy, computed, provider-based, or runtime-bound state must be resolved separately. |
|
|
41 | 43 | - Aggregate content should be accessed through dedicated lookup/materialization APIs. |
|
|
42 | 44 | - Eager observation of identity is acceptable. |
|
|
43 | 45 | - Eager observation of computed state is not. |
|
|
44 | 46 | |
|
|
45 | 47 | ### Do not |
|
|
46 | 48 | |
|
|
47 | 49 | - Do not store heavy computed state inside identity objects. |
|
|
48 | 50 | - Do not store runtime references to foreign domains inside identity objects. |
|
|
49 | 51 | - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient. |
|
|
50 | 52 | |
|
|
51 | 53 | ### Rule of thumb |
|
|
52 | 54 | |
|
|
53 | 55 | **Identity objects contain selection metadata. |
|
|
54 | 56 | Aggregate content is obtained separately, on demand.** |
| @@ -1,149 +1,147 | |||
|
|
1 | 1 | package org.implab.gradle.common.core.lang; |
|
|
2 | 2 | |
|
|
3 | import java.util.function.Consumer; | |
|
|
4 | import java.util.function.Function; | |
|
|
5 | 3 | import java.util.regex.Pattern; |
|
|
6 | 4 | |
|
|
7 | 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
8 | 6 | import org.gradle.api.provider.Provider; |
|
|
9 | 7 | |
|
|
10 | 8 | @NonNullByDefault |
|
|
11 | 9 | public class Strings { |
|
|
12 | 10 | |
|
|
13 | 11 | private static final boolean[] ALLOWED_FILE_NAME_CHAR = new boolean[128]; |
|
|
14 | 12 | |
|
|
15 | 13 | private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); |
|
|
16 | 14 | |
|
|
17 | 15 | private static final Pattern firstLetter = Pattern.compile("^\\w"); |
|
|
18 | 16 | |
|
|
19 | 17 | private static final Pattern INVALID_NAME_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); |
|
|
20 | 18 | |
|
|
21 | 19 | static { |
|
|
22 | 20 | for (char c = '0'; c <= '9'; c++) |
|
|
23 | 21 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
|
24 | 22 | for (char c = 'A'; c <= 'Z'; c++) |
|
|
25 | 23 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
|
26 | 24 | for (char c = 'a'; c <= 'z'; c++) |
|
|
27 | 25 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
|
28 | 26 | ALLOWED_FILE_NAME_CHAR['.'] = true; |
|
|
29 | 27 | ALLOWED_FILE_NAME_CHAR['_'] = true; |
|
|
30 | 28 | ALLOWED_FILE_NAME_CHAR['-'] = true; |
|
|
31 | 29 | } |
|
|
32 | 30 | |
|
|
33 | 31 | public static String capitalize(String string) { |
|
|
34 | 32 | return string == null ? null |
|
|
35 | 33 | : string.length() == 0 ? string |
|
|
36 | 34 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); |
|
|
37 | 35 | } |
|
|
38 | 36 | |
|
|
39 | 37 | public static String toCamelCase(String name) { |
|
|
40 | 38 | if (name == null || name.isEmpty()) |
|
|
41 | 39 | return name; |
|
|
42 | 40 | StringBuilder out = new StringBuilder(name.length()); |
|
|
43 | 41 | boolean up = false; |
|
|
44 | 42 | boolean first = true; |
|
|
45 | 43 | for (int i = 0; i < name.length(); i++) { |
|
|
46 | 44 | char c = name.charAt(i); |
|
|
47 | 45 | switch (c) { |
|
|
48 | 46 | case '-', '_', ' ', '.' -> up = true; |
|
|
49 | 47 | default -> { |
|
|
50 | 48 | out.append( |
|
|
51 | 49 | first ? Character.toLowerCase(c) |
|
|
52 | 50 | : up ? Character.toUpperCase(c): c); |
|
|
53 | 51 | up = false; |
|
|
54 | 52 | first = false; |
|
|
55 | 53 | } |
|
|
56 | 54 | } |
|
|
57 | 55 | } |
|
|
58 | 56 | return out.toString(); |
|
|
59 | 57 | } |
|
|
60 | 58 | |
|
|
61 | 59 | public static void argumentNotNullOrEmpty(String value, String argumentName) { |
|
|
62 | 60 | if (value == null || value.length() == 0) |
|
|
63 | 61 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); |
|
|
64 | 62 | } |
|
|
65 | 63 | |
|
|
66 | 64 | public static void argumentNotNullOrBlank(String value, String argumentName) { |
|
|
67 | 65 | if (value == null || value.trim().length() == 0) |
|
|
68 | 66 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); |
|
|
69 | 67 | } |
|
|
70 | 68 | |
|
|
71 | 69 | public static String requireNonBlank(String value) { |
|
|
72 | 70 | argumentNotNullOrBlank(value, "value"); |
|
|
73 | 71 | return value; |
|
|
74 | 72 | } |
|
|
75 | 73 | |
|
|
76 | 74 | public static String requireNonEmpty(String value) { |
|
|
77 | 75 | argumentNotNullOrEmpty(value, "value"); |
|
|
78 | 76 | return value; |
|
|
79 | 77 | } |
|
|
80 | 78 | |
|
|
81 | 79 | |
|
|
82 | 80 | public static String sanitizeName(String value) { |
|
|
83 | 81 | return INVALID_NAME_CHAR.matcher(value).replaceAll("_"); |
|
|
84 | 82 | } |
|
|
85 | 83 | |
|
|
86 | 84 | public static String sanitizeFileName(String value) { |
|
|
87 | 85 | int length = value.length(); |
|
|
88 | 86 | for (int i = 0; i < length; i++) { |
|
|
89 | 87 | char c = value.charAt(i); |
|
|
90 | 88 | if (c >= ALLOWED_FILE_NAME_CHAR.length || !ALLOWED_FILE_NAME_CHAR[c]) |
|
|
91 | 89 | return sanitizeFileName(value, i); |
|
|
92 | 90 | } |
|
|
93 | 91 | return value; |
|
|
94 | 92 | } |
|
|
95 | 93 | |
|
|
96 | 94 | public static String asString(Object value) { |
|
|
97 | 95 | if (value == null) |
|
|
98 | 96 | return null; |
|
|
99 | 97 | if (value instanceof Provider<?> provider) |
|
|
100 | 98 | return asString(provider.get()); |
|
|
101 | 99 | else |
|
|
102 | 100 | return value.toString(); |
|
|
103 | 101 | } |
|
|
104 | 102 | |
|
|
105 | 103 | private static String sanitizeFileName(String value, int invalidIndex) { |
|
|
106 | 104 | int length = value.length(); |
|
|
107 | 105 | StringBuilder out = new StringBuilder(length + 16); |
|
|
108 | 106 | out.append(value, 0, invalidIndex); |
|
|
109 | 107 | |
|
|
110 | 108 | for (int i = invalidIndex; i < length; i++) { |
|
|
111 | 109 | char c = value.charAt(i); |
|
|
112 | 110 | if (c < ALLOWED_FILE_NAME_CHAR.length && ALLOWED_FILE_NAME_CHAR[c]) { |
|
|
113 | 111 | out.append(c); |
|
|
114 | 112 | } else if (Character.isHighSurrogate(c) && i + 1 < length && Character.isLowSurrogate(value.charAt(i + 1))) { |
|
|
115 | 113 | appendUrlEncodedUtf8(out, Character.toCodePoint(c, value.charAt(++i))); |
|
|
116 | 114 | } else if (Character.isSurrogate(c)) { |
|
|
117 | 115 | appendUrlEncodedUtf8(out, 0xFFFD); |
|
|
118 | 116 | } else { |
|
|
119 | 117 | appendUrlEncodedUtf8(out, c); |
|
|
120 | 118 | } |
|
|
121 | 119 | } |
|
|
122 | 120 | |
|
|
123 | 121 | return out.toString(); |
|
|
124 | 122 | } |
|
|
125 | 123 | |
|
|
126 | 124 | private static void appendUrlEncodedUtf8(StringBuilder out, int codePoint) { |
|
|
127 | 125 | if (codePoint <= 0x7F) { |
|
|
128 | 126 | appendUrlEncodedByte(out, codePoint); |
|
|
129 | 127 | } else if (codePoint <= 0x7FF) { |
|
|
130 | 128 | appendUrlEncodedByte(out, 0xC0 | (codePoint >>> 6)); |
|
|
131 | 129 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
|
132 | 130 | } else if (codePoint <= 0xFFFF) { |
|
|
133 | 131 | appendUrlEncodedByte(out, 0xE0 | (codePoint >>> 12)); |
|
|
134 | 132 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); |
|
|
135 | 133 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
|
136 | 134 | } else { |
|
|
137 | 135 | appendUrlEncodedByte(out, 0xF0 | (codePoint >>> 18)); |
|
|
138 | 136 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 12) & 0x3F)); |
|
|
139 | 137 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); |
|
|
140 | 138 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
|
141 | 139 | } |
|
|
142 | 140 | } |
|
|
143 | 141 | |
|
|
144 | 142 | private static void appendUrlEncodedByte(StringBuilder out, int value) { |
|
|
145 | 143 | out.append('%'); |
|
|
146 | 144 | out.append(HEX_DIGITS[(value >>> 4) & 0x0F]); |
|
|
147 | 145 | out.append(HEX_DIGITS[value & 0x0F]); |
|
|
148 | 146 | } |
|
|
149 | 147 | } |
| @@ -1,45 +1,38 | |||
|
|
1 | 1 | # design notes |
|
|
2 | 2 | |
|
|
3 | 3 | ## core model |
|
|
4 | 4 | |
|
|
5 | 5 | - OutgoingRegistry (Variant) |
|
|
6 | 6 | исходящая конфигурация |
|
|
7 | 7 | - [provider] configuration |
|
|
8 | 8 | - [container, live] slots |
|
|
9 | 9 | набор вариантов (слотов) |
|
|
10 | 10 | - [property] primarySlot |
|
|
11 | 11 | - AssemblyRegistry (Varaint, Slot) |
|
|
12 | 12 | содержимое слота может быть добавлено после появления слота в OutgoingRegistry |
|
|
13 | 13 | - assembleTask |
|
|
14 | 14 | - inputs |
|
|
15 | 15 | - compile unit output (CompileUnit, String) |
|
|
16 | 16 | - direct object (task, file collection, etc.) |
|
|
17 | 17 | - artifact: directory |
|
|
18 | 18 | - customTask |
|
|
19 | 19 | - artifact: FileSystemLocation |
|
|
20 | - when, all - replayable hooks для получения содержимого сборки | |
|
|
20 | 21 | |
|
|
21 | 22 | ## extension |
|
|
22 | 23 | |
|
|
23 | 24 | - adapter |
|
|
24 | 25 | - whenFinalized |
|
|
25 | 26 | - dsl |
|
|
26 | 27 | - variant(name) |
|
|
27 | 28 | - slot(name) |
|
|
28 | 29 | - from*** |
|
|
29 | 30 | - whenFinalized |
|
|
30 | 31 | - whenOutgoingConfiguration |
|
|
31 | 32 | - whenOutgoingSlot |
|
|
32 | 33 | |
|
|
33 | outgoing = outgoings.maybeCreate(variant) | |
|
|
34 | ||
|
|
35 | slot = outgoings.slots.maybeCreate(slotName) | |
|
|
36 | assembly = assemblies.register(variantSlot, task, mapOutput) | |
|
|
37 | outgoing.configure(configuration -> { | |
|
|
38 | slots.all(slot -> { | |
|
|
39 | assemblies.when(variant, slot) { assembly -> | |
|
|
40 | configuration.variants.create(slot.name) { | |
|
|
41 | artifact(assembly.artifact) | |
|
|
42 | } | |
|
|
43 | } | |
|
|
44 | }); | |
|
|
45 | }); No newline at end of file | |
|
|
34 | - ArtifactAssemblyBridge | |
|
|
35 | связывает содержимое ArtifactAssembly и исходящей конфигурации OutgoingVariant | |
|
|
36 | - VariantArtifactsHandler | |
|
|
37 | управляет состоянием ArtifactAssembly, используется для DSL сборки | |
|
|
38 | - configureVariant конфигурирует VariantArtifactsSpec No newline at end of file | |
| @@ -1,24 +1,27 | |||
|
|
1 | 1 | package org.implab.gradle.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.LinkedList; |
|
|
4 | 4 | import java.util.List; |
|
|
5 | 5 | import java.util.function.Consumer; |
|
|
6 | 6 | |
|
|
7 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
8 | ||
|
|
9 | @NonNullByDefault | |
|
|
7 | 10 | public class ReplayableQueue<T> { |
|
|
8 | 11 | private final List<Consumer<? super T>> consumers = new LinkedList<>(); |
|
|
9 | 12 | private final List<T> values = new LinkedList<>(); |
|
|
10 | 13 | |
|
|
11 | 14 | public void add(T value) { |
|
|
12 | 15 | consumers.forEach(consumer -> consumer.accept(value)); |
|
|
13 | 16 | values.add(value); |
|
|
14 | 17 | } |
|
|
15 | 18 | |
|
|
16 | 19 | List<T> values() { |
|
|
17 | 20 | return List.copyOf(values); |
|
|
18 | 21 | } |
|
|
19 | 22 | |
|
|
20 | 23 | public void forEach(Consumer<? super T> consumer) { |
|
|
21 | 24 | values.forEach(consumer); |
|
|
22 | 25 | consumers.add(consumer); |
|
|
23 | 26 | } |
|
|
24 | 27 | } No newline at end of file |
| @@ -1,78 +1,112 | |||
|
|
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 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; | |
|
|
8 | 7 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
|
9 | 8 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; |
|
|
10 | 9 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; |
|
|
11 | 10 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
|
12 |
import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyB |
|
|
|
13 |
import org.implab.gradle.variants.artifacts.internal. |
|
|
|
11 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder; | |
|
|
12 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler; | |
|
|
13 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry; | |
|
|
14 | import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext; | |
|
|
15 | import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler; | |
|
|
14 | 16 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; |
|
|
17 | import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention; | |
|
|
15 | 18 | import org.implab.gradle.variants.core.Variant; |
|
|
16 | 19 | import org.implab.gradle.variants.core.VariantsExtension; |
|
|
20 | import org.implab.gradle.variants.sources.VariantSourcesExtension; | |
|
|
21 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec; | |
|
|
17 | 22 | |
|
|
18 | 23 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
|
|
19 | 24 | |
|
|
20 | 25 | @Override |
|
|
21 | 26 | public void apply(Project target) { |
|
|
22 | 27 | var extensions = target.getExtensions(); |
|
|
23 | 28 | var objects = target.getObjects(); |
|
|
24 | 29 | var providers = target.getProviders(); |
|
|
30 | var plugins = target.getPlugins(); | |
|
|
25 | 31 | var configurations = target.getConfigurations(); |
|
|
26 | 32 | var tasks = target.getTasks(); |
|
|
33 | var layout = target.getLayout(); | |
|
|
27 | 34 | |
|
|
28 | 35 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
|
29 |
|
|
|
|
36 | plugins.apply(VariantsPlugin.class); | |
|
|
37 | plugins.apply(VariantSourcesPlugin.class); | |
|
|
30 | 38 | // Access the VariantsExtension to configure variant sources. |
|
|
31 | 39 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
|
32 | ||
|
|
33 | var outgoing = new OutgoingRegistry(configurations, objects, providers); | |
|
|
34 | var assemblies = new ArtifactAssemblyRegistry(objects, tasks); | |
|
|
35 | ||
|
|
36 | // wire artifact assemblies to configuration variants | |
|
|
37 | var assembliesBridge = new ArtifactAssemblyBridge(assemblies); | |
|
|
40 | var sourcesExtension = extensions.getByType(VariantSourcesExtension.class); | |
|
|
38 | 41 | |
|
|
39 | 42 | var deferred = new Deferred<OutgoingVariantsContext>(); |
|
|
40 | 43 | |
|
|
41 | deferred.whenResolved(context -> context.all(assembliesBridge)); | |
|
|
44 | variantsExtension.whenFinalized(variants -> { | |
|
|
45 | ||
|
|
46 | var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants()); | |
|
|
47 | var assemblies = new ArtifactAssemblyRegistry(); | |
|
|
48 | ||
|
|
49 | // wire artifact assemblies to configuration variants | |
|
|
50 | var assembliesBridge = new ArtifactAssemblyBinder(assemblies); | |
|
|
51 | var primarySlotConvention = new SingleSlotConvention(providers); | |
|
|
52 | var materializationHandler = new MaterializationPolicyHandler(assemblies); | |
|
|
53 | ||
|
|
54 | // bind slot assemblies to outgoing variants | |
|
|
55 | outgoing.configureEach(assembliesBridge::execute); | |
|
|
56 | // apply primary slot convention when outgoing variant has a single slot | |
|
|
57 | outgoing.configureEach(primarySlotConvention::execute); | |
|
|
58 | // apply materialization policy hooks to outgoing variants | |
|
|
59 | outgoing.configureEach(materializationHandler::execute); | |
|
|
42 | 60 | |
|
|
43 |
|
|
|
|
61 | sourcesExtension.whenFinalized(sources -> { | |
|
|
62 | var assemblyHandler = new ArtifactAssemblyHandler( | |
|
|
63 | objects, | |
|
|
64 | tasks, | |
|
|
65 | assemblies, | |
|
|
66 | sources.getCompileUnits(), | |
|
|
67 | sources.getRoleProjections(), | |
|
|
68 | sources.getSourceSets()); | |
|
|
69 | assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies")); | |
|
|
70 | ||
|
|
71 | deferred.resolve(new DefaultOutgoingVariantsContext( | |
|
|
72 | assemblies, | |
|
|
73 | outgoing, | |
|
|
74 | assemblyHandler, | |
|
|
75 | materializationHandler)); | |
|
|
76 | }); | |
|
|
44 | 77 | |
|
|
45 | 78 | }); |
|
|
46 | 79 | |
|
|
47 | 80 | var variantArtifacts = new VariantArtifactsExtension() { |
|
|
48 | 81 | |
|
|
49 | 82 | @Override |
|
|
50 | 83 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
|
51 | 84 | deferred.whenResolved(context -> { |
|
|
52 | new DefaultVariantArtifactSpec(); | |
|
|
85 | var variant = objects.named(Variant.class, variantName); | |
|
|
86 | context.configureVariant(variant, action); | |
|
|
53 | 87 | }); |
|
|
54 | 88 | } |
|
|
55 | 89 | |
|
|
56 | 90 | @Override |
|
|
57 |
public void when |
|
|
|
58 |
deferred.whenResolved( |
|
|
|
91 | public void whenAvailable(Action<? super OutgoingVariantsContext> action) { | |
|
|
92 | deferred.whenResolved(handler -> action.execute(handler)); | |
|
|
59 | 93 | } |
|
|
60 | 94 | |
|
|
61 | 95 | @Override |
|
|
62 |
public void whenOutgoing |
|
|
|
63 |
deferred.whenResolved(registry -> registry. |
|
|
|
96 | public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) { | |
|
|
97 | deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action)); | |
|
|
64 | 98 | |
|
|
65 | 99 | } |
|
|
66 | 100 | |
|
|
67 | 101 | @Override |
|
|
68 |
public void whenOutgoingSlot(Action<? super Outgoing |
|
|
|
69 |
deferred.whenResolved(registry -> registry. |
|
|
|
102 | public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) { | |
|
|
103 | deferred.whenResolved(registry -> registry.whenOutgoingSlot(action)); | |
|
|
70 | 104 | } |
|
|
71 | 105 | |
|
|
72 | 106 | }; |
|
|
73 | 107 | |
|
|
74 | 108 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); |
|
|
75 | 109 | |
|
|
76 | 110 | } |
|
|
77 | 111 | |
|
|
78 | 112 | } |
| @@ -1,54 +1,56 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Optional; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
6 | 6 | import org.gradle.api.Action; |
|
|
7 | 7 | import org.gradle.api.InvalidUserDataException; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | 10 | * Resolves stateful slot assemblies from cheap slot identities. |
|
|
11 | 11 | * |
|
|
12 | 12 | * <p> |
|
|
13 | 13 | * The returned assembly is a materialized build-model handle. It may expose |
|
|
14 | 14 | * lazy Gradle providers, but |
|
|
15 | 15 | * it is no longer an identity object suitable for replayable discovery. |
|
|
16 | 16 | */ |
|
|
17 | 17 | @NonNullByDefault |
|
|
18 | 18 | public interface ArtifactAssemblies { |
|
|
19 | 19 | /** |
|
|
20 | 20 | * Resolves the assembly for the given slot. |
|
|
21 | 21 | * |
|
|
22 | 22 | * <p> |
|
|
23 | 23 | * This call materializes the build-facing body of the slot from its identity. |
|
|
24 | 24 | * |
|
|
25 | 25 | * @param slot slot identity inside a variant outgoing contract |
|
|
26 | 26 | * @return assembly handle for the slot |
|
|
27 | 27 | */ |
|
|
28 | 28 | default ArtifactAssembly require(ArtifactSlot slot) { |
|
|
29 | 29 | return find(slot) |
|
|
30 | 30 | .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered")); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | /** |
|
|
34 | * Регистрирует обработчик на конкретный слот. Если слот еще не зарегистрирован, | |
|
|
35 | * то обработчик будет добавлен в очередь и будет вызван при регистрации слота. | |
|
|
36 | * Порядок и точный момент вызова обработчиков не определен. | |
|
|
34 | * Registers a configuration action for the assembly of the given slot. | |
|
|
37 | 35 | * |
|
|
38 | * @param slot Слот на который нужно зарегистрировать обработчик | |
|
|
39 | * @param action Обработчик | |
|
|
36 | * <p>If the assembly is already registered, the action is invoked immediately. | |
|
|
37 | * Otherwise, it is queued and invoked when slot materialization registers the | |
|
|
38 | * assembly handle. | |
|
|
39 | * | |
|
|
40 | * @param slot slot identity inside a variant outgoing contract | |
|
|
41 | * @param action assembly configuration action | |
|
|
40 | 42 | */ |
|
|
41 | 43 | void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action); |
|
|
42 | 44 | |
|
|
43 | 45 | /** |
|
|
44 | * Регистрирует глобальный обработчик, который будет вызван для всех слотов. | |
|
|
45 | * Обработчик будет вызван как для уже зарегистрированных слотов так и для тех, | |
|
|
46 | * которые будут зарегистрированы в будущем. | |
|
|
46 | * Adds global configuration action for all materialized assemblies. If some assemblies are | |
|
|
47 | * already registered, the action will be invoked for them immediately. For assemblies that | |
|
|
48 | * are materialized later, the action will be invoked when they are registered. | |
|
|
47 | 49 | * |
|
|
48 | * @param action Обработчик | |
|
|
50 | * @param action assembly configuration action. | |
|
|
49 | 51 | */ |
|
|
50 |
void |
|
|
|
52 | void configureEach(Action<? super ArtifactAssembly> action); | |
|
|
51 | 53 | |
|
|
52 | 54 | /** Ищет зарегистрированный слот */ |
|
|
53 | 55 | Optional<ArtifactAssembly> find(ArtifactSlot slot); |
|
|
54 | 56 | } |
| @@ -1,59 +1,61 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.Task; |
|
|
5 | 5 | import org.gradle.api.attributes.AttributeContainer; |
|
|
6 | 6 | import groovy.lang.Closure; |
|
|
7 | 7 | import org.implab.gradle.common.core.lang.Closures; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | 10 | * Materialized outgoing publication state of a single slot. |
|
|
11 | 11 | * |
|
|
12 | 12 | * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific |
|
|
13 | 13 | * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which |
|
|
14 | 14 | * is limited to the root outgoing configuration of the variant. |
|
|
15 | 15 | */ |
|
|
16 | 16 | public interface OutgoingConfigurationSlotSpec { |
|
|
17 | 17 | /** |
|
|
18 | 18 | * Returns the published slot identity. |
|
|
19 | 19 | * |
|
|
20 | 20 | * @return slot identity |
|
|
21 | 21 | */ |
|
|
22 | 22 | ArtifactSlot getArtifactSlot(); |
|
|
23 | 23 | |
|
|
24 | 24 | /** |
|
|
25 | 25 | * Returns the assembly backing the published slot. |
|
|
26 | 26 | * |
|
|
27 | 27 | * @return slot assembly |
|
|
28 | 28 | */ |
|
|
29 | 29 | ArtifactAssembly getAssembly(); |
|
|
30 | 30 | |
|
|
31 | 31 | /** |
|
|
32 | 32 | * Returns whether this slot is the primary outgoing artifact set of the variant. |
|
|
33 | 33 | * |
|
|
34 | 34 | * @return {@code true} for the primary slot |
|
|
35 | 35 | */ |
|
|
36 | 36 | boolean isPrimary(); |
|
|
37 | 37 | |
|
|
38 | 38 | /** |
|
|
39 | 39 | * Configures the task producing the slot artifact. |
|
|
40 | 40 | * |
|
|
41 | 41 | * @param action task configuration action |
|
|
42 | 42 | */ |
|
|
43 |
void assemblyTask(Action<? super Task> action) |
|
|
|
43 | default void assemblyTask(Action<? super Task> action) { | |
|
|
44 | getAssembly().getAssemblyTask().configure(action); | |
|
|
45 | } | |
|
|
44 | 46 | |
|
|
45 | 47 | default void assemblyTask(Closure<?> closure) { |
|
|
46 | 48 | assemblyTask(Closures.action(closure)); |
|
|
47 | 49 | } |
|
|
48 | 50 | |
|
|
49 | 51 | /** |
|
|
50 | 52 | * Configures attributes of this slot publication. |
|
|
51 | 53 | * |
|
|
52 | 54 | * @param action artifact attribute configuration action |
|
|
53 | 55 | */ |
|
|
54 | 56 | void artifactAttributes(Action<? super AttributeContainer> action); |
|
|
55 | 57 | |
|
|
56 | 58 | default void artifactAttributes(Closure<?> closure) { |
|
|
57 | 59 | artifactAttributes(Closures.action(closure)); |
|
|
58 | 60 | } |
|
|
59 | 61 | } |
| @@ -1,50 +1,50 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.NamedDomainObjectContainer; |
|
|
5 | 5 | import org.gradle.api.NamedDomainObjectProvider; |
|
|
6 | 6 | import org.gradle.api.artifacts.Configuration; |
|
|
7 | 7 | import org.gradle.api.provider.Property; |
|
|
8 | 8 | import org.implab.gradle.variants.core.Variant; |
|
|
9 | 9 | |
|
|
10 | 10 | /** |
|
|
11 | 11 | * Описывает исходящую конфигурацию варианта |
|
|
12 | 12 | * |
|
|
13 | 13 | * Задает связь между моделью вариантов и моделью конфигураций gradle через |
|
|
14 | 14 | * свойство {@link #getConfiguration()}. Также задает отдельную ось слотов |
|
|
15 | 15 | * публикации, но не задает правил связывания этих слотов с самой конфигурацией |
|
|
16 | 16 | * и их содержимым. Самый простой вариант это {@link ArtifactAssemblies}. |
|
|
17 | 17 | */ |
|
|
18 | 18 | public interface OutgoingVariant { |
|
|
19 | 19 | /** |
|
|
20 | 20 | * Исходный вариант для которого строится Outgoing конфигурация |
|
|
21 | 21 | */ |
|
|
22 | 22 | Variant getVariant(); |
|
|
23 | 23 | |
|
|
24 | 24 | /** |
|
|
25 | 25 | * Провайдер зарегистрированной конфигурации |
|
|
26 | 26 | */ |
|
|
27 | NamedDomainObjectProvider<Configuration> getConfiguration(); | |
|
|
27 | NamedDomainObjectProvider<? extends Configuration> getConfiguration(); | |
|
|
28 | 28 | |
|
|
29 | default void configure(Action<? super Configuration> action) { | |
|
|
29 | default void configureOutgoing(Action<? super Configuration> action) { | |
|
|
30 | 30 | getConfiguration().configure(action); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | /** |
|
|
34 | 34 | * Слоты конфигурации, данная коллекция живая, используется для |
|
|
35 | 35 | * получения информации об объявленных слотах, но эти слоты не |
|
|
36 | 36 | * обязаны быть сконфигурированы, т.е. это только Identity. |
|
|
37 | 37 | * |
|
|
38 | 38 | * @see {@link ArtifactSlot} |
|
|
39 | 39 | */ |
|
|
40 | 40 | NamedDomainObjectContainer<Slot> getSlots(); |
|
|
41 | 41 | |
|
|
42 | 42 | /** |
|
|
43 | 43 | * Основной набор артефактов (primary variant) для исходящей конфигурации |
|
|
44 | 44 | * |
|
|
45 | 45 | * <p> |
|
|
46 | 46 | * Если в свойстве {@link #getSlots()} есть только один слой, то по конвенции он |
|
|
47 | 47 | * считается также основным. |
|
|
48 | 48 | */ |
|
|
49 | 49 | Property<Slot> getPrimarySlot(); |
|
|
50 | 50 | } |
| @@ -1,38 +1,36 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Optional; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.gradle.api.Action; |
|
|
6 | 6 | import org.gradle.api.InvalidUserDataException; |
|
|
7 | 7 | import org.implab.gradle.variants.core.Variant; |
|
|
8 | import org.implab.gradle.variants.core.VariantsView; | |
|
|
9 | 8 | |
|
|
10 | 9 | /** |
|
|
11 | 10 | * Контекст работы с вариантами публикации, становится доступным после |
|
|
12 | 11 | * финализации модели вариантов. Фактически является живой моделью |
|
|
13 | 12 | */ |
|
|
14 | 13 | public interface OutgoingVariantsContext { |
|
|
15 | 14 | |
|
|
16 | /** | |
|
|
17 | * Зафиксированное представление о вариантах на основе которого адаптеры могут | |
|
|
18 | * конфигурировать артефакты и исходящие конфигурации | |
|
|
19 | */ | |
|
|
20 | VariantsView getVariants(); | |
|
|
15 | ArtifactAssemblies getAssemblies(); | |
|
|
21 | 16 | |
|
|
22 | ArtifactAssemblies getAssemblies(); | |
|
|
17 | void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action); | |
|
|
23 | 18 | |
|
|
24 | 19 | /** |
|
|
25 | 20 | * Replayable hook для всех объявленных конфигураций |
|
|
26 | 21 | */ |
|
|
27 |
void |
|
|
|
22 | void configureEach(Action<? super OutgoingVariant> action); | |
|
|
28 | 23 | |
|
|
29 | 24 | Optional<OutgoingVariant> findOutgoing(Variant variant); |
|
|
30 | 25 | |
|
|
31 | 26 | default OutgoingVariant requireOutgoing(Variant variant) { |
|
|
32 | 27 | return findOutgoing(variant) |
|
|
33 | 28 | .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered")); |
|
|
34 | 29 | } |
|
|
35 | 30 | |
|
|
36 | ArtifactAssemblyRules slotRules(ArtifactSlot slot); | |
|
|
31 | void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action); | |
|
|
32 | ||
|
|
33 | void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action); | |
|
|
34 | ||
|
|
37 | 35 | |
|
|
38 | 36 | } |
| @@ -1,59 +1,64 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.implab.gradle.common.core.lang.Closures; |
|
|
5 | 5 | |
|
|
6 | 6 | import groovy.lang.Closure; |
|
|
7 | 7 | |
|
|
8 | 8 | /** |
|
|
9 | 9 | * Project-level DSL entry point for declaring outgoing artifacts per variant. |
|
|
10 | 10 | * |
|
|
11 | * <p>A variant represents one external outgoing contract. Slots declared inside a variant represent | |
|
|
12 | * artifact sets within that contract. One slot is expected to materialize to one published artifact. | |
|
|
11 | * <p> | |
|
|
12 | * A variant represents one external outgoing contract. Slots declared inside a | |
|
|
13 | * variant represent | |
|
|
14 | * artifact sets within that contract. One slot is expected to materialize to | |
|
|
15 | * one published artifact. | |
|
|
13 | 16 | */ |
|
|
14 | 17 | public interface VariantArtifactsExtension { |
|
|
15 | 18 | /** |
|
|
16 | 19 | * Configures artifact slots of the named variant. |
|
|
17 | 20 | * |
|
|
18 | 21 | * @param variantName variant name |
|
|
19 | 22 | * @param action variant artifact declaration |
|
|
20 | 23 | */ |
|
|
21 | 24 | void variant(String variantName, Action<? super VariantArtifactsSpec> action); |
|
|
22 | 25 | |
|
|
23 | 26 | default void variant(String variantName, Closure<?> closure) { |
|
|
24 | 27 | variant(variantName, Closures.action(closure)); |
|
|
25 | 28 | } |
|
|
26 | 29 | |
|
|
27 | 30 | /** |
|
|
28 |
* Registers a callback invoked w |
|
|
|
31 | * Registers a callback invoked when the outgoing variants context becomes | |
|
|
32 | * available. | |
|
|
29 | 33 | * |
|
|
30 |
* @param action |
|
|
|
34 | * @param action outgoing variants context callback | |
|
|
31 | 35 | */ |
|
|
32 |
void when |
|
|
|
36 | void whenAvailable(Action<? super OutgoingVariantsContext> action); | |
|
|
33 | 37 | |
|
|
34 |
default void when |
|
|
|
35 |
when |
|
|
|
38 | default void whenAvailable(Closure<?> closure) { | |
|
|
39 | whenAvailable(Closures.action(closure)); | |
|
|
36 | 40 | } |
|
|
37 | 41 | |
|
|
38 | 42 | /** |
|
|
39 |
* Registers a callback invoked for each materialized root outgoing |
|
|
|
43 | * Registers a callback invoked for each materialized root outgoing | |
|
|
44 | * configuration. | |
|
|
40 | 45 | * |
|
|
41 | 46 | * @param action variant-level outgoing configuration callback |
|
|
42 | 47 | */ |
|
|
43 |
void whenOutgoing |
|
|
|
48 | void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action); | |
|
|
44 | 49 | |
|
|
45 |
default void whenOutgoing |
|
|
|
46 |
whenOutgoing |
|
|
|
50 | default void whenOutgoingConfiguration(Closure<?> closure) { | |
|
|
51 | whenOutgoingConfiguration(Closures.action(closure)); | |
|
|
47 | 52 | } |
|
|
48 | 53 | |
|
|
49 | 54 | /** |
|
|
50 | 55 | * Registers a callback invoked for each materialized outgoing slot publication. |
|
|
51 | 56 | * |
|
|
52 | 57 | * @param action slot-level outgoing publication callback |
|
|
53 | 58 | */ |
|
|
54 | 59 | void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action); |
|
|
55 | 60 | |
|
|
56 | 61 | default void whenOutgoingSlot(Closure<?> closure) { |
|
|
57 | 62 | whenOutgoingSlot(Closures.action(closure)); |
|
|
58 | 63 | } |
|
|
59 | 64 | } |
| @@ -1,54 +1,56 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
4 | 4 | import org.gradle.api.Action; |
|
|
5 | 5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; |
|
|
6 | 6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
7 | 7 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | 10 | * Связывает описание исходящих конфигураций gradle и сборку содержимого слотов |
|
|
11 | 11 | * из {@link ArtifactAssemblies} |
|
|
12 | 12 | */ |
|
|
13 | 13 | @NonNullByDefault |
|
|
14 |
public class ArtifactAssemblyB |
|
|
|
14 | public class ArtifactAssemblyBinder implements Action<OutgoingVariant> { | |
|
|
15 | 15 | |
|
|
16 | 16 | private final ArtifactAssemblies resolver; |
|
|
17 | 17 | |
|
|
18 |
public ArtifactAssemblyB |
|
|
|
18 | public ArtifactAssemblyBinder(ArtifactAssemblies resolver) { | |
|
|
19 | 19 | this.resolver = resolver; |
|
|
20 | 20 | } |
|
|
21 | 21 | |
|
|
22 | 22 | @Override |
|
|
23 | 23 | public void execute(OutgoingVariant outgoingVariant) { |
|
|
24 | 24 | var slots = outgoingVariant.getSlots(); |
|
|
25 | 25 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); |
|
|
26 | 26 | var variant = outgoingVariant.getVariant(); |
|
|
27 | 27 | |
|
|
28 | 28 | // связываем конфигурацию |
|
|
29 | outgoingVariant.configure(configuration -> { | |
|
|
29 | outgoingVariant.configureOutgoing(configuration -> { | |
|
|
30 | 30 | var primarySlot = primarySlotProvider.get(); |
|
|
31 | 31 | var outgoing = configuration.getOutgoing(); |
|
|
32 | 32 | |
|
|
33 | 33 | // связываем основной вариант конфигурации |
|
|
34 | 34 | resolver.when( |
|
|
35 | 35 | new ArtifactSlot(variant, primarySlot), |
|
|
36 | 36 | assembly -> outgoing.artifact(assembly.getArtifact())); |
|
|
37 | 37 | |
|
|
38 | 38 | // для всех объявленных слотов |
|
|
39 | 39 | slots.all(slot -> { |
|
|
40 | 40 | // кроме основного |
|
|
41 | 41 | if (slot.equals(primarySlot)) |
|
|
42 | 42 | return; |
|
|
43 | 43 | |
|
|
44 | 44 | // связываем артефакты |
|
|
45 | 45 | resolver.when( |
|
|
46 | 46 | new ArtifactSlot(variant, slot), |
|
|
47 | 47 | assembly -> outgoing.getVariants() |
|
|
48 | 48 | .register(slot.getName()) |
|
|
49 | 49 | .configure(cv -> cv.artifact(assembly.getArtifact()))); |
|
|
50 | 50 | }); |
|
|
51 | 51 | }); |
|
|
52 | 52 | } |
|
|
53 | 53 | |
|
|
54 | ||
|
|
55 | ||
|
|
54 | 56 | } |
| @@ -1,87 +1,90 | |||
|
|
1 | package org.implab.gradle.variants.artifacts; | |
|
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
|
|
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 | 12 | import org.gradle.api.file.FileSystemLocation; |
|
|
13 | 13 | import org.gradle.api.provider.Provider; |
|
|
14 | 14 | import org.gradle.api.tasks.TaskProvider; |
|
|
15 | 15 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
16 | 16 | import org.implab.gradle.internal.ReplayableQueue; |
|
|
17 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
|
|
18 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; | |
|
|
19 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
|
|
17 | 20 | |
|
|
18 | 21 | @NonNullByDefault |
|
|
19 | 22 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
|
20 | 23 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
|
21 | 24 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
|
22 | 25 | |
|
|
23 | 26 | public ArtifactAssemblyRegistry() { |
|
|
24 | 27 | } |
|
|
25 | 28 | |
|
|
26 | 29 | public <T extends Task> ArtifactAssembly register( |
|
|
27 | 30 | ArtifactSlot slot, |
|
|
28 | 31 | TaskProvider<T> task, |
|
|
29 | 32 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { |
|
|
30 | 33 | |
|
|
31 | 34 | var deferred = getDeferred(slot); |
|
|
32 | 35 | if (deferred.resolved()) { |
|
|
33 | 36 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); |
|
|
34 | 37 | } |
|
|
35 | 38 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); |
|
|
36 | 39 | |
|
|
37 | 40 | var assembly = new Assembly(outputArtifact, task); |
|
|
38 | 41 | deferred.resolve(assembly); |
|
|
39 | 42 | assemblies.add(assembly); |
|
|
40 | 43 | return assembly; |
|
|
41 | 44 | } |
|
|
42 | 45 | |
|
|
43 | 46 | @Override |
|
|
44 | 47 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { |
|
|
45 | 48 | // to prevent creation of map entries on lookup use the map directly |
|
|
46 | 49 | var deferred = assembliesBySlots.get(slot); |
|
|
47 | 50 | return deferred != null && deferred.resolved() |
|
|
48 | 51 | ? Optional.of(deferred.value()) |
|
|
49 | 52 | : Optional.empty(); |
|
|
50 | 53 | } |
|
|
51 | 54 | |
|
|
52 | 55 | @Override |
|
|
53 | 56 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { |
|
|
54 | 57 | getDeferred(slot).whenResolved(action::execute); |
|
|
55 | 58 | } |
|
|
56 | 59 | |
|
|
57 | 60 | @Override |
|
|
58 |
public void |
|
|
|
61 | public void configureEach(Action<? super ArtifactAssembly> action) { | |
|
|
59 | 62 | assemblies.forEach(action::execute); |
|
|
60 | 63 | } |
|
|
61 | 64 | |
|
|
62 | 65 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { |
|
|
63 | 66 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); |
|
|
64 | 67 | } |
|
|
65 | 68 | |
|
|
66 | 69 | static class Assembly implements ArtifactAssembly { |
|
|
67 | 70 | |
|
|
68 | 71 | private final Provider<? extends FileSystemLocation> artifact; |
|
|
69 | 72 | private final TaskProvider<? extends Task> task; |
|
|
70 | 73 | |
|
|
71 | 74 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { |
|
|
72 | 75 | this.artifact = artifact; |
|
|
73 | 76 | this.task = task; |
|
|
74 | 77 | } |
|
|
75 | 78 | |
|
|
76 | 79 | @Override |
|
|
77 | 80 | public Provider<? extends FileSystemLocation> getArtifact() { |
|
|
78 | 81 | return artifact; |
|
|
79 | 82 | } |
|
|
80 | 83 | |
|
|
81 | 84 | @Override |
|
|
82 | 85 | public TaskProvider<? extends Task> getAssemblyTask() { |
|
|
83 | 86 | return task; |
|
|
84 | 87 | } |
|
|
85 | 88 | |
|
|
86 | 89 | } |
|
|
87 | 90 | } |
| @@ -1,77 +1,79 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashSet; |
|
|
4 | 4 | import java.util.Set; |
|
|
5 | 5 | import java.util.function.Consumer; |
|
|
6 | 6 | import java.util.stream.Stream; |
|
|
7 | 7 | |
|
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
8 | 9 | import org.gradle.api.Action; |
|
|
9 | 10 | import org.gradle.api.model.ObjectFactory; |
|
|
10 | 11 | import org.implab.gradle.common.core.lang.Strings; |
|
|
11 | 12 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
|
12 | 13 | import org.implab.gradle.variants.core.Layer; |
|
|
13 | 14 | import org.implab.gradle.variants.core.Role; |
|
|
14 | 15 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; |
|
|
15 | 16 | |
|
|
16 | 17 | /** |
|
|
17 | 18 | * Реализация DSL модели, строит набор {@link SlotContribution}. При построении набора |
|
|
18 | 19 | * правила и корректность не проверяются. По окончании использования клиент |
|
|
19 | 20 | * вызывает метод {@link #process(Consumer)} для обработки результатов. |
|
|
20 | 21 | * |
|
|
21 | 22 | */ |
|
|
23 | @NonNullByDefault | |
|
|
22 | 24 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { |
|
|
23 | 25 | private final Consumer<? super SlotContribution> consumer; |
|
|
24 | 26 | private final ObjectFactory objectFactory; |
|
|
25 | 27 | |
|
|
26 | 28 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { |
|
|
27 | 29 | this.consumer = consumer; |
|
|
28 | 30 | this.objectFactory = objectFactory; |
|
|
29 | 31 | } |
|
|
30 | 32 | |
|
|
31 | 33 | @Override |
|
|
32 | 34 | public void from(Object artifact) { |
|
|
33 | 35 | consumer.accept(new DirectContribution(artifact)); |
|
|
34 | 36 | } |
|
|
35 | 37 | |
|
|
36 | 38 | @Override |
|
|
37 | 39 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
|
38 | 40 | consumer.accept(new VariantOutputsContribution(outputs(action))); |
|
|
39 | 41 | } |
|
|
40 | 42 | |
|
|
41 | 43 | @Override |
|
|
42 | 44 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
|
43 | 45 | |
|
|
44 | 46 | consumer.accept(new RoleOutputsContribution( |
|
|
45 | 47 | objectFactory.named(Role.class, roleName), |
|
|
46 | 48 | outputs(action))); |
|
|
47 | 49 | } |
|
|
48 | 50 | |
|
|
49 | 51 | @Override |
|
|
50 | 52 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
|
51 | 53 | consumer.accept(new LayerOutputsContribution( |
|
|
52 | 54 | objectFactory.named(Layer.class, layerName), |
|
|
53 | 55 | outputs(action))); |
|
|
54 | 56 | } |
|
|
55 | 57 | |
|
|
56 | 58 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
|
57 | 59 | var spec = new OutputsSetSpec(); |
|
|
58 | 60 | action.execute(spec); |
|
|
59 | 61 | return spec.outputs(); |
|
|
60 | 62 | } |
|
|
61 | 63 | |
|
|
62 | 64 | private static class OutputsSetSpec implements OutputSelectionSpec { |
|
|
63 | 65 | private final Set<String> outputs = new HashSet<>(); |
|
|
64 | 66 | |
|
|
65 | 67 | @Override |
|
|
66 | 68 | public void output(String name, String... extra) { |
|
|
67 | 69 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
|
68 | 70 | .map(Strings::requireNonBlank) |
|
|
69 | 71 | .forEach(outputs::add); |
|
|
70 | 72 | } |
|
|
71 | 73 | |
|
|
72 | 74 | Set<String> outputs() { |
|
|
73 | 75 | return Set.copyOf(outputs); |
|
|
74 | 76 | } |
|
|
75 | 77 | |
|
|
76 | 78 | } |
|
|
77 | 79 | } |
| @@ -1,56 +1,121 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.LinkedHashMap; |
|
|
4 | import java.util.LinkedList; | |
|
|
5 | import java.util.List; | |
|
|
6 | 4 | import java.util.Map; |
|
|
7 | 5 | import java.util.Optional; |
|
|
6 | import java.util.Set; | |
|
|
8 | 7 | import java.util.function.Consumer; |
|
|
9 | 8 | |
|
|
9 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
10 | import org.gradle.api.InvalidUserDataException; | |
|
|
11 | import org.gradle.api.NamedDomainObjectContainer; | |
|
|
12 | import org.gradle.api.NamedDomainObjectProvider; | |
|
|
13 | import org.gradle.api.artifacts.Configuration; | |
|
|
10 | 14 | import org.gradle.api.artifacts.ConfigurationContainer; |
|
|
11 | 15 | import org.gradle.api.model.ObjectFactory; |
|
|
12 |
import org.gradle.api.provider.Pro |
|
|
|
16 | import org.gradle.api.provider.Property; | |
|
|
17 | import org.implab.gradle.internal.ReplayableQueue; | |
|
|
13 | 18 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
19 | import org.implab.gradle.variants.artifacts.Slot; | |
|
|
14 | 20 | import org.implab.gradle.variants.core.Variant; |
|
|
15 | 21 | |
|
|
22 | /** | |
|
|
23 | * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами | |
|
|
24 | * сборки. Связь устанавливается 1:1. | |
|
|
25 | */ | |
|
|
26 | @NonNullByDefault | |
|
|
16 | 27 | public class OutgoingRegistry { |
|
|
17 |
private final Map<Variant, |
|
|
|
18 | private final List<Consumer<? super DefaultOutgoingConfiguration>> hooks = new LinkedList<>(); | |
|
|
19 | ||
|
|
28 | private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>(); | |
|
|
29 | private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>(); | |
|
|
20 | 30 | private final ConfigurationContainer configurations; |
|
|
21 | 31 | private final ObjectFactory objects; |
|
|
22 | private final ProviderFactory providers; | |
|
|
32 | private final Set<Variant> declaredVariants; | |
|
|
23 | 33 | |
|
|
24 | public OutgoingRegistry(ConfigurationContainer configurations, ObjectFactory objects, ProviderFactory providers) { | |
|
|
34 | public OutgoingRegistry( | |
|
|
35 | ConfigurationContainer configurations, | |
|
|
36 | ObjectFactory objects, | |
|
|
37 | Set<Variant> declaredVariants) { | |
|
|
25 | 38 | this.configurations = configurations; |
|
|
26 | 39 | this.objects = objects; |
|
|
27 | this.providers = providers; | |
|
|
40 | this.declaredVariants = declaredVariants; | |
|
|
28 | 41 | } |
|
|
29 | 42 | |
|
|
30 |
public Optional<OutgoingVariant> find |
|
|
|
43 | public Optional<OutgoingVariant> find(Variant variant) { | |
|
|
31 | 44 | return Optional.ofNullable(outgoingByVariant.get(variant)); |
|
|
32 | 45 | } |
|
|
33 | 46 | |
|
|
34 | 47 | public OutgoingVariant maybeCreate(Variant variant) { |
|
|
35 | var outgoing = outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration); | |
|
|
36 | hooks.forEach(hook -> hook.accept(outgoing)); | |
|
|
48 | return find(variant).orElseGet(() -> create(variant)); | |
|
|
49 | } | |
|
|
50 | ||
|
|
51 | public OutgoingVariant create(Variant variant) { | |
|
|
52 | if (!declaredVariants.contains(variant)) | |
|
|
53 | throw new InvalidUserDataException("Variant " + variant + " isn't declared"); | |
|
|
54 | if (outgoingByVariant.containsKey(variant)) | |
|
|
55 | throw new InvalidUserDataException("Outgoing variant " + variant + " already exists"); | |
|
|
56 | ||
|
|
57 | var configuration = configurations.consumable(outgoingConfigurationName(variant)); | |
|
|
58 | var outgoing = new Outgoing(variant, configuration); | |
|
|
59 | ||
|
|
60 | outgoingByVariant.put(variant, outgoing); | |
|
|
61 | ||
|
|
62 | outgoingVariants.add(outgoing); | |
|
|
63 | ||
|
|
37 | 64 | return outgoing; |
|
|
38 | 65 | } |
|
|
39 | 66 | |
|
|
40 | public void all(Consumer<? super OutgoingVariant> action) { | |
|
|
41 | outgoingByVariant.values().forEach(action); | |
|
|
42 | hooks.add(action); | |
|
|
43 | } | |
|
|
44 | ||
|
|
45 | private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) { | |
|
|
46 | var configuration = configurations.register(outgoingConfigurationName(variant)); | |
|
|
47 | ||
|
|
48 | return new DefaultOutgoingConfiguration(variant, configuration, objects, providers); | |
|
|
67 | /** | |
|
|
68 | * Replayable hook which is applied when an outgoing variant is defined | |
|
|
69 | * | |
|
|
70 | * @param action | |
|
|
71 | */ | |
|
|
72 | public void configureEach(Consumer<? super OutgoingVariant> action) { | |
|
|
73 | outgoingVariants.forEach(action); | |
|
|
49 | 74 | } |
|
|
50 | 75 | |
|
|
51 | 76 | private String outgoingConfigurationName(Variant variant) { |
|
|
52 | 77 | return variant.getName() + "Elements"; |
|
|
53 | 78 | } |
|
|
54 | 79 | |
|
|
80 | private class Outgoing implements OutgoingVariant { | |
|
|
81 | ||
|
|
82 | private final Variant variant; | |
|
|
83 | ||
|
|
84 | private final NamedDomainObjectProvider<? extends Configuration> configurationProvider; | |
|
|
85 | ||
|
|
86 | private final NamedDomainObjectContainer<Slot> slots; | |
|
|
87 | ||
|
|
88 | private final Property<Slot> primarySlot; | |
|
|
89 | ||
|
|
90 | public Outgoing( | |
|
|
91 | Variant variant, | |
|
|
92 | NamedDomainObjectProvider<? extends Configuration> configurationProvider) { | |
|
|
93 | this.variant = variant; | |
|
|
94 | this.configurationProvider = configurationProvider; | |
|
|
95 | this.slots = objects.domainObjectContainer(Slot.class); | |
|
|
96 | this.primarySlot = objects.property(Slot.class); | |
|
|
97 | primarySlot.finalizeValueOnRead(); | |
|
|
98 | } | |
|
|
99 | ||
|
|
100 | @Override | |
|
|
101 | public Property<Slot> getPrimarySlot() { | |
|
|
102 | return primarySlot; | |
|
|
103 | } | |
|
|
104 | ||
|
|
105 | @Override | |
|
|
106 | public Variant getVariant() { | |
|
|
107 | return variant; | |
|
|
108 | } | |
|
|
109 | ||
|
|
110 | @Override | |
|
|
111 | public NamedDomainObjectProvider<? extends Configuration> getConfiguration() { | |
|
|
112 | return configurationProvider; | |
|
|
113 | } | |
|
|
114 | ||
|
|
115 | @Override | |
|
|
116 | public NamedDomainObjectContainer<Slot> getSlots() { | |
|
|
117 | return slots; | |
|
|
118 | } | |
|
|
119 | } | |
|
|
55 | 120 | |
|
|
56 | 121 | } |
| @@ -1,9 +1,9 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
4 | 4 | |
|
|
5 | 5 | @NonNullByDefault |
|
|
6 |
|
|
|
|
6 | interface SlotContribution { | |
|
|
7 | 7 | |
|
|
8 | 8 | void accept(SlotContributionVisitor visitor); |
|
|
9 | 9 | } |
| @@ -1,17 +1,15 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | import java.util.function.Consumer; | |
|
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
|
|
4 | 4 | |
|
|
5 | public interface SlotContributionVisitor { | |
|
|
5 | @NonNullByDefault | |
|
|
6 | interface SlotContributionVisitor { | |
|
|
6 | 7 | void visit(DirectContribution contribution); |
|
|
7 | 8 | |
|
|
8 | 9 | void visit(VariantOutputsContribution contribution); |
|
|
9 | 10 | |
|
|
10 | 11 | void visit(RoleOutputsContribution contribution); |
|
|
11 | 12 | |
|
|
12 | 13 | void visit(LayerOutputsContribution contribution); |
|
|
13 | 14 | |
|
|
14 | default Consumer<SlotContribution> consumer() { | |
|
|
15 | return slot -> slot.accept(this); | |
|
|
16 | 15 | } |
|
|
17 | } | |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
|
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
