| @@ -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 | # AGENTS.md |
|
1 | # AGENTS.md | |
| 2 |
|
2 | |||
| 3 | ## Проектные договоренности |
|
3 | ## Проектные договоренности | |
| 4 |
|
4 | |||
|
|
5 | - для управления исходным кодом используется Mercurial | |||
|
|
6 | ||||
| 5 | ### Публичное API библиотек |
|
7 | ### Публичное API библиотек | |
| 6 |
|
8 | |||
| 7 | - Предпочтителен `non-null` подход. |
|
9 | - Предпочтителен `non-null` подход. | |
| 8 | - Там, где значение живет в Gradle Provider API, возвращается `Provider<T>` (не `null`). |
|
10 | - Там, где значение живет в Gradle Provider API, возвращается `Provider<T>` (не `null`). | |
| 9 | - Там, где lookup синхронный, возвращается `Optional<T>` (не `null`). |
|
11 | - Там, где lookup синхронный, возвращается `Optional<T>` (не `null`). | |
| 10 | - `find*` рассматривается как синоним legacy `get*` (поиск без `fail-fast`). |
|
12 | - `find*` рассматривается как синоним legacy `get*` (поиск без `fail-fast`). | |
| 11 | - `require*` это `find*` + `fail-fast` с понятной ошибкой в месте вызова. |
|
13 | - `require*` это `find*` + `fail-fast` с понятной ошибкой в месте вызова. | |
| 12 | - Для нового API предпочтительны формы `find/require`; новые `get*` по возможности не добавлять. |
|
14 | - Для нового API предпочтительны формы `find/require`; новые `get*` по возможности не добавлять. | |
| 13 | - Интерфейсы и классы, описывающие модели DSL должны иметь суффикс `Spec` у моделей описывающих уровень сервисов и состояние сценария сборки такого суффикса не должно быть. |
|
15 | - Интерфейсы и классы, описывающие модели DSL должны иметь суффикс `Spec` у моделей описывающих уровень сервисов и состояние сценария сборки такого суффикса не должно быть. | |
| 14 | - Модель расширения на уровне проекта должна иметь суффикс `Extension`. |
|
16 | - Модель расширения на уровне проекта должна иметь суффикс `Extension`. | |
| 15 |
|
17 | |||
| 16 | ### Документация |
|
18 | ### Документация | |
| 17 |
|
19 | |||
| 18 | - документирование кода должно быть на английском языке |
|
20 | - документирование кода должно быть на английском языке | |
| 19 | - к публичному API |
|
21 | - к публичному API | |
| 20 | - описание должно отражать назначение, где используется и какое влияние оказывает на остальные части программы |
|
22 | - описание должно отражать назначение, где используется и какое влияние оказывает на остальные части программы | |
| 21 | - давать небольшое описание концепции, а также краткие примеры |
|
23 | - давать небольшое описание концепции, а также краткие примеры | |
| 22 | - к приватному API достаточно давать краткую справку о назначении и использовании |
|
24 | - к приватному API достаточно давать краткую справку о назначении и использовании | |
| 23 | - реализацию алгоритмов в коде сопровождать комментариями с пояснениями, тривиальные операции пояснять не требуется. |
|
25 | - реализацию алгоритмов в коде сопровождать комментариями с пояснениями, тривиальные операции пояснять не требуется. | |
| 24 | - документация должна формироваться согласно требованиям по форматированию типа javadoc, jsdoc и т.п., в зависимости от используемых в проекте языках и инструментах. |
|
26 | - документация должна формироваться согласно требованиям по форматированию типа javadoc, jsdoc и т.п., в зависимости от используемых в проекте языках и инструментах. | |
| 25 |
|
27 | |||
| 26 | ## Identity-first modeling |
|
28 | ## Identity-first modeling | |
| 27 |
|
29 | |||
| 28 | Prefer an **identity-first** split between: |
|
30 | Prefer an **identity-first** split between: | |
| 29 |
|
31 | |||
| 30 | - **identity objects** used for discovery and selection |
|
32 | - **identity objects** used for discovery and selection | |
| 31 | - **stateful/materialized objects** obtained through separate API calls |
|
33 | - **stateful/materialized objects** obtained through separate API calls | |
| 32 |
|
34 | |||
| 33 | ### Rules |
|
35 | ### Rules | |
| 34 |
|
36 | |||
| 35 | - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**. |
|
37 | - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**. | |
| 36 | - Identity objects must be: |
|
38 | - Identity objects must be: | |
| 37 | - cheap to create |
|
39 | - cheap to create | |
| 38 | - effectively immutable |
|
40 | - effectively immutable | |
| 39 | - limited to identity and cheap selection metadata |
|
41 | - limited to identity and cheap selection metadata | |
| 40 | - Heavy, computed, provider-based, or runtime-bound state must be resolved separately. |
|
42 | - Heavy, computed, provider-based, or runtime-bound state must be resolved separately. | |
| 41 | - Aggregate content should be accessed through dedicated lookup/materialization APIs. |
|
43 | - Aggregate content should be accessed through dedicated lookup/materialization APIs. | |
| 42 | - Eager observation of identity is acceptable. |
|
44 | - Eager observation of identity is acceptable. | |
| 43 | - Eager observation of computed state is not. |
|
45 | - Eager observation of computed state is not. | |
| 44 |
|
46 | |||
| 45 | ### Do not |
|
47 | ### Do not | |
| 46 |
|
48 | |||
| 47 | - Do not store heavy computed state inside identity objects. |
|
49 | - Do not store heavy computed state inside identity objects. | |
| 48 | - Do not store runtime references to foreign domains inside identity objects. |
|
50 | - Do not store runtime references to foreign domains inside identity objects. | |
| 49 | - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient. |
|
51 | - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient. | |
| 50 |
|
52 | |||
| 51 | ### Rule of thumb |
|
53 | ### Rule of thumb | |
| 52 |
|
54 | |||
| 53 | **Identity objects contain selection metadata. |
|
55 | **Identity objects contain selection metadata. | |
| 54 | Aggregate content is obtained separately, on demand.** |
|
56 | Aggregate content is obtained separately, on demand.** | |
| @@ -1,149 +1,147 | |||||
| 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; |
|
|||
| 5 | import java.util.regex.Pattern; |
|
3 | import java.util.regex.Pattern; | |
| 6 |
|
4 | |||
| 7 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 8 | import org.gradle.api.provider.Provider; |
|
6 | import org.gradle.api.provider.Provider; | |
| 9 |
|
7 | |||
| 10 | @NonNullByDefault |
|
8 | @NonNullByDefault | |
| 11 | public class Strings { |
|
9 | public class Strings { | |
| 12 |
|
10 | |||
| 13 | private static final boolean[] ALLOWED_FILE_NAME_CHAR = new boolean[128]; |
|
11 | private static final boolean[] ALLOWED_FILE_NAME_CHAR = new boolean[128]; | |
| 14 |
|
12 | |||
| 15 | private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); |
|
13 | private static final char[] HEX_DIGITS = "0123456789ABCDEF".toCharArray(); | |
| 16 |
|
14 | |||
| 17 | private static final Pattern firstLetter = Pattern.compile("^\\w"); |
|
15 | private static final Pattern firstLetter = Pattern.compile("^\\w"); | |
| 18 |
|
16 | |||
| 19 | private static final Pattern INVALID_NAME_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); |
|
17 | private static final Pattern INVALID_NAME_CHAR = Pattern.compile("[^A-Za-z0-9_.-]"); | |
| 20 |
|
18 | |||
| 21 | static { |
|
19 | static { | |
| 22 | for (char c = '0'; c <= '9'; c++) |
|
20 | for (char c = '0'; c <= '9'; c++) | |
| 23 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
21 | ALLOWED_FILE_NAME_CHAR[c] = true; | |
| 24 | for (char c = 'A'; c <= 'Z'; c++) |
|
22 | for (char c = 'A'; c <= 'Z'; c++) | |
| 25 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
23 | ALLOWED_FILE_NAME_CHAR[c] = true; | |
| 26 | for (char c = 'a'; c <= 'z'; c++) |
|
24 | for (char c = 'a'; c <= 'z'; c++) | |
| 27 | ALLOWED_FILE_NAME_CHAR[c] = true; |
|
25 | ALLOWED_FILE_NAME_CHAR[c] = true; | |
| 28 | ALLOWED_FILE_NAME_CHAR['.'] = true; |
|
26 | ALLOWED_FILE_NAME_CHAR['.'] = true; | |
| 29 | ALLOWED_FILE_NAME_CHAR['_'] = true; |
|
27 | ALLOWED_FILE_NAME_CHAR['_'] = true; | |
| 30 | ALLOWED_FILE_NAME_CHAR['-'] = true; |
|
28 | ALLOWED_FILE_NAME_CHAR['-'] = true; | |
| 31 | } |
|
29 | } | |
| 32 |
|
30 | |||
| 33 | public static String capitalize(String string) { |
|
31 | public static String capitalize(String string) { | |
| 34 | return string == null ? null |
|
32 | return string == null ? null | |
| 35 | : string.length() == 0 ? string |
|
33 | : string.length() == 0 ? string | |
| 36 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); |
|
34 | : firstLetter.matcher(string).replaceFirst(m -> m.group().toUpperCase()); | |
| 37 | } |
|
35 | } | |
| 38 |
|
36 | |||
| 39 | public static String toCamelCase(String name) { |
|
37 | public static String toCamelCase(String name) { | |
| 40 | if (name == null || name.isEmpty()) |
|
38 | if (name == null || name.isEmpty()) | |
| 41 | return name; |
|
39 | return name; | |
| 42 | StringBuilder out = new StringBuilder(name.length()); |
|
40 | StringBuilder out = new StringBuilder(name.length()); | |
| 43 | boolean up = false; |
|
41 | boolean up = false; | |
| 44 | boolean first = true; |
|
42 | boolean first = true; | |
| 45 | for (int i = 0; i < name.length(); i++) { |
|
43 | for (int i = 0; i < name.length(); i++) { | |
| 46 | char c = name.charAt(i); |
|
44 | char c = name.charAt(i); | |
| 47 | switch (c) { |
|
45 | switch (c) { | |
| 48 | case '-', '_', ' ', '.' -> up = true; |
|
46 | case '-', '_', ' ', '.' -> up = true; | |
| 49 | default -> { |
|
47 | default -> { | |
| 50 | out.append( |
|
48 | out.append( | |
| 51 | first ? Character.toLowerCase(c) |
|
49 | first ? Character.toLowerCase(c) | |
| 52 | : up ? Character.toUpperCase(c): c); |
|
50 | : up ? Character.toUpperCase(c): c); | |
| 53 | up = false; |
|
51 | up = false; | |
| 54 | first = false; |
|
52 | first = false; | |
| 55 | } |
|
53 | } | |
| 56 | } |
|
54 | } | |
| 57 | } |
|
55 | } | |
| 58 | return out.toString(); |
|
56 | return out.toString(); | |
| 59 | } |
|
57 | } | |
| 60 |
|
58 | |||
| 61 | public static void argumentNotNullOrEmpty(String value, String argumentName) { |
|
59 | public static void argumentNotNullOrEmpty(String value, String argumentName) { | |
| 62 | if (value == null || value.length() == 0) |
|
60 | if (value == null || value.length() == 0) | |
| 63 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); |
|
61 | throw new IllegalArgumentException(String.format("Argument %s can't be null or empty", argumentName)); | |
| 64 | } |
|
62 | } | |
| 65 |
|
63 | |||
| 66 | public static void argumentNotNullOrBlank(String value, String argumentName) { |
|
64 | public static void argumentNotNullOrBlank(String value, String argumentName) { | |
| 67 | if (value == null || value.trim().length() == 0) |
|
65 | if (value == null || value.trim().length() == 0) | |
| 68 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); |
|
66 | throw new IllegalArgumentException(String.format("Argument %s can't be null or blank", argumentName)); | |
| 69 | } |
|
67 | } | |
| 70 |
|
68 | |||
| 71 | public static String requireNonBlank(String value) { |
|
69 | public static String requireNonBlank(String value) { | |
| 72 | argumentNotNullOrBlank(value, "value"); |
|
70 | argumentNotNullOrBlank(value, "value"); | |
| 73 | return value; |
|
71 | return value; | |
| 74 | } |
|
72 | } | |
| 75 |
|
73 | |||
| 76 | public static String requireNonEmpty(String value) { |
|
74 | public static String requireNonEmpty(String value) { | |
| 77 | argumentNotNullOrEmpty(value, "value"); |
|
75 | argumentNotNullOrEmpty(value, "value"); | |
| 78 | return value; |
|
76 | return value; | |
| 79 | } |
|
77 | } | |
| 80 |
|
78 | |||
| 81 |
|
79 | |||
| 82 | public static String sanitizeName(String value) { |
|
80 | public static String sanitizeName(String value) { | |
| 83 | return INVALID_NAME_CHAR.matcher(value).replaceAll("_"); |
|
81 | return INVALID_NAME_CHAR.matcher(value).replaceAll("_"); | |
| 84 | } |
|
82 | } | |
| 85 |
|
83 | |||
| 86 | public static String sanitizeFileName(String value) { |
|
84 | public static String sanitizeFileName(String value) { | |
| 87 | int length = value.length(); |
|
85 | int length = value.length(); | |
| 88 | for (int i = 0; i < length; i++) { |
|
86 | for (int i = 0; i < length; i++) { | |
| 89 | char c = value.charAt(i); |
|
87 | char c = value.charAt(i); | |
| 90 | if (c >= ALLOWED_FILE_NAME_CHAR.length || !ALLOWED_FILE_NAME_CHAR[c]) |
|
88 | if (c >= ALLOWED_FILE_NAME_CHAR.length || !ALLOWED_FILE_NAME_CHAR[c]) | |
| 91 | return sanitizeFileName(value, i); |
|
89 | return sanitizeFileName(value, i); | |
| 92 | } |
|
90 | } | |
| 93 | return value; |
|
91 | return value; | |
| 94 | } |
|
92 | } | |
| 95 |
|
93 | |||
| 96 | public static String asString(Object value) { |
|
94 | public static String asString(Object value) { | |
| 97 | if (value == null) |
|
95 | if (value == null) | |
| 98 | return null; |
|
96 | return null; | |
| 99 | if (value instanceof Provider<?> provider) |
|
97 | if (value instanceof Provider<?> provider) | |
| 100 | return asString(provider.get()); |
|
98 | return asString(provider.get()); | |
| 101 | else |
|
99 | else | |
| 102 | return value.toString(); |
|
100 | return value.toString(); | |
| 103 | } |
|
101 | } | |
| 104 |
|
102 | |||
| 105 | private static String sanitizeFileName(String value, int invalidIndex) { |
|
103 | private static String sanitizeFileName(String value, int invalidIndex) { | |
| 106 | int length = value.length(); |
|
104 | int length = value.length(); | |
| 107 | StringBuilder out = new StringBuilder(length + 16); |
|
105 | StringBuilder out = new StringBuilder(length + 16); | |
| 108 | out.append(value, 0, invalidIndex); |
|
106 | out.append(value, 0, invalidIndex); | |
| 109 |
|
107 | |||
| 110 | for (int i = invalidIndex; i < length; i++) { |
|
108 | for (int i = invalidIndex; i < length; i++) { | |
| 111 | char c = value.charAt(i); |
|
109 | char c = value.charAt(i); | |
| 112 | if (c < ALLOWED_FILE_NAME_CHAR.length && ALLOWED_FILE_NAME_CHAR[c]) { |
|
110 | if (c < ALLOWED_FILE_NAME_CHAR.length && ALLOWED_FILE_NAME_CHAR[c]) { | |
| 113 | out.append(c); |
|
111 | out.append(c); | |
| 114 | } else if (Character.isHighSurrogate(c) && i + 1 < length && Character.isLowSurrogate(value.charAt(i + 1))) { |
|
112 | } else if (Character.isHighSurrogate(c) && i + 1 < length && Character.isLowSurrogate(value.charAt(i + 1))) { | |
| 115 | appendUrlEncodedUtf8(out, Character.toCodePoint(c, value.charAt(++i))); |
|
113 | appendUrlEncodedUtf8(out, Character.toCodePoint(c, value.charAt(++i))); | |
| 116 | } else if (Character.isSurrogate(c)) { |
|
114 | } else if (Character.isSurrogate(c)) { | |
| 117 | appendUrlEncodedUtf8(out, 0xFFFD); |
|
115 | appendUrlEncodedUtf8(out, 0xFFFD); | |
| 118 | } else { |
|
116 | } else { | |
| 119 | appendUrlEncodedUtf8(out, c); |
|
117 | appendUrlEncodedUtf8(out, c); | |
| 120 | } |
|
118 | } | |
| 121 | } |
|
119 | } | |
| 122 |
|
120 | |||
| 123 | return out.toString(); |
|
121 | return out.toString(); | |
| 124 | } |
|
122 | } | |
| 125 |
|
123 | |||
| 126 | private static void appendUrlEncodedUtf8(StringBuilder out, int codePoint) { |
|
124 | private static void appendUrlEncodedUtf8(StringBuilder out, int codePoint) { | |
| 127 | if (codePoint <= 0x7F) { |
|
125 | if (codePoint <= 0x7F) { | |
| 128 | appendUrlEncodedByte(out, codePoint); |
|
126 | appendUrlEncodedByte(out, codePoint); | |
| 129 | } else if (codePoint <= 0x7FF) { |
|
127 | } else if (codePoint <= 0x7FF) { | |
| 130 | appendUrlEncodedByte(out, 0xC0 | (codePoint >>> 6)); |
|
128 | appendUrlEncodedByte(out, 0xC0 | (codePoint >>> 6)); | |
| 131 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
129 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |
| 132 | } else if (codePoint <= 0xFFFF) { |
|
130 | } else if (codePoint <= 0xFFFF) { | |
| 133 | appendUrlEncodedByte(out, 0xE0 | (codePoint >>> 12)); |
|
131 | appendUrlEncodedByte(out, 0xE0 | (codePoint >>> 12)); | |
| 134 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); |
|
132 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); | |
| 135 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
133 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |
| 136 | } else { |
|
134 | } else { | |
| 137 | appendUrlEncodedByte(out, 0xF0 | (codePoint >>> 18)); |
|
135 | appendUrlEncodedByte(out, 0xF0 | (codePoint >>> 18)); | |
| 138 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 12) & 0x3F)); |
|
136 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 12) & 0x3F)); | |
| 139 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); |
|
137 | appendUrlEncodedByte(out, 0x80 | ((codePoint >>> 6) & 0x3F)); | |
| 140 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); |
|
138 | appendUrlEncodedByte(out, 0x80 | (codePoint & 0x3F)); | |
| 141 | } |
|
139 | } | |
| 142 | } |
|
140 | } | |
| 143 |
|
141 | |||
| 144 | private static void appendUrlEncodedByte(StringBuilder out, int value) { |
|
142 | private static void appendUrlEncodedByte(StringBuilder out, int value) { | |
| 145 | out.append('%'); |
|
143 | out.append('%'); | |
| 146 | out.append(HEX_DIGITS[(value >>> 4) & 0x0F]); |
|
144 | out.append(HEX_DIGITS[(value >>> 4) & 0x0F]); | |
| 147 | out.append(HEX_DIGITS[value & 0x0F]); |
|
145 | out.append(HEX_DIGITS[value & 0x0F]); | |
| 148 | } |
|
146 | } | |
| 149 | } |
|
147 | } | |
| @@ -1,45 +1,38 | |||||
| 1 | # design notes |
|
1 | # design notes | |
| 2 |
|
2 | |||
| 3 | ## core model |
|
3 | ## core model | |
| 4 |
|
4 | |||
| 5 | - OutgoingRegistry (Variant) |
|
5 | - OutgoingRegistry (Variant) | |
| 6 | исходящая конфигурация |
|
6 | исходящая конфигурация | |
| 7 | - [provider] configuration |
|
7 | - [provider] configuration | |
| 8 | - [container, live] slots |
|
8 | - [container, live] slots | |
| 9 | набор вариантов (слотов) |
|
9 | набор вариантов (слотов) | |
| 10 | - [property] primarySlot |
|
10 | - [property] primarySlot | |
| 11 | - AssemblyRegistry (Varaint, Slot) |
|
11 | - AssemblyRegistry (Varaint, Slot) | |
| 12 | содержимое слота может быть добавлено после появления слота в OutgoingRegistry |
|
12 | содержимое слота может быть добавлено после появления слота в OutgoingRegistry | |
| 13 | - assembleTask |
|
13 | - assembleTask | |
| 14 | - inputs |
|
14 | - inputs | |
| 15 | - compile unit output (CompileUnit, String) |
|
15 | - compile unit output (CompileUnit, String) | |
| 16 | - direct object (task, file collection, etc.) |
|
16 | - direct object (task, file collection, etc.) | |
| 17 | - artifact: directory |
|
17 | - artifact: directory | |
| 18 | - customTask |
|
18 | - customTask | |
| 19 | - artifact: FileSystemLocation |
|
19 | - artifact: FileSystemLocation | |
|
|
20 | - when, all - replayable hooks для получения содержимого сборки | |||
| 20 |
|
21 | |||
| 21 | ## extension |
|
22 | ## extension | |
| 22 |
|
23 | |||
| 23 | - adapter |
|
24 | - adapter | |
| 24 | - whenFinalized |
|
25 | - whenFinalized | |
| 25 | - dsl |
|
26 | - dsl | |
| 26 | - variant(name) |
|
27 | - variant(name) | |
| 27 | - slot(name) |
|
28 | - slot(name) | |
| 28 | - from*** |
|
29 | - from*** | |
| 29 | - whenFinalized |
|
30 | - whenFinalized | |
| 30 | - whenOutgoingConfiguration |
|
31 | - whenOutgoingConfiguration | |
| 31 | - whenOutgoingSlot |
|
32 | - whenOutgoingSlot | |
| 32 |
|
33 | |||
| 33 | outgoing = outgoings.maybeCreate(variant) |
|
34 | - ArtifactAssemblyBridge | |
| 34 |
|
35 | связывает содержимое ArtifactAssembly и исходящей конфигурации OutgoingVariant | ||
| 35 | slot = outgoings.slots.maybeCreate(slotName) |
|
36 | - VariantArtifactsHandler | |
| 36 | assembly = assemblies.register(variantSlot, task, mapOutput) |
|
37 | управляет состоянием ArtifactAssembly, используется для DSL сборки | |
| 37 | outgoing.configure(configuration -> { |
|
38 | - configureVariant конфигурирует VariantArtifactsSpec No newline at end of file | |
| 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 |
|
|||
| @@ -1,24 +1,27 | |||||
| 1 | package org.implab.gradle.internal; |
|
1 | package org.implab.gradle.internal; | |
| 2 |
|
2 | |||
| 3 | import java.util.LinkedList; |
|
3 | import java.util.LinkedList; | |
| 4 | import java.util.List; |
|
4 | import java.util.List; | |
| 5 | import java.util.function.Consumer; |
|
5 | import java.util.function.Consumer; | |
| 6 |
|
6 | |||
|
|
7 | import org.eclipse.jdt.annotation.NonNullByDefault; | |||
|
|
8 | ||||
|
|
9 | @NonNullByDefault | |||
| 7 | public class ReplayableQueue<T> { |
|
10 | public class ReplayableQueue<T> { | |
| 8 | private final List<Consumer<? super T>> consumers = new LinkedList<>(); |
|
11 | private final List<Consumer<? super T>> consumers = new LinkedList<>(); | |
| 9 | private final List<T> values = new LinkedList<>(); |
|
12 | private final List<T> values = new LinkedList<>(); | |
| 10 |
|
13 | |||
| 11 | public void add(T value) { |
|
14 | public void add(T value) { | |
| 12 | consumers.forEach(consumer -> consumer.accept(value)); |
|
15 | consumers.forEach(consumer -> consumer.accept(value)); | |
| 13 | values.add(value); |
|
16 | values.add(value); | |
| 14 | } |
|
17 | } | |
| 15 |
|
18 | |||
| 16 | List<T> values() { |
|
19 | List<T> values() { | |
| 17 | return List.copyOf(values); |
|
20 | return List.copyOf(values); | |
| 18 | } |
|
21 | } | |
| 19 |
|
22 | |||
| 20 | public void forEach(Consumer<? super T> consumer) { |
|
23 | public void forEach(Consumer<? super T> consumer) { | |
| 21 | values.forEach(consumer); |
|
24 | values.forEach(consumer); | |
| 22 | consumers.add(consumer); |
|
25 | consumers.add(consumer); | |
| 23 | } |
|
26 | } | |
| 24 | } No newline at end of file |
|
27 | } | |
| @@ -1,78 +1,112 | |||||
| 1 | package org.implab.gradle.variants; |
|
1 | package org.implab.gradle.variants; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Action; |
|
3 | import org.gradle.api.Action; | |
| 4 | import org.gradle.api.Plugin; |
|
4 | import org.gradle.api.Plugin; | |
| 5 | import org.gradle.api.Project; |
|
5 | import org.gradle.api.Project; | |
| 6 | import org.implab.gradle.common.core.lang.Deferred; |
|
6 | import org.implab.gradle.common.core.lang.Deferred; | |
| 7 | import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; |
|
|||
| 8 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
7 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; | |
| 9 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; |
|
8 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; | |
| 10 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; |
|
9 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; | |
| 11 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
10 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; | |
| 12 |
import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyB |
|
11 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder; | |
| 13 |
import org.implab.gradle.variants.artifacts.internal. |
|
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 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; |
|
16 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; | |
|
|
17 | import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention; | |||
| 15 | import org.implab.gradle.variants.core.Variant; |
|
18 | import org.implab.gradle.variants.core.Variant; | |
| 16 | import org.implab.gradle.variants.core.VariantsExtension; |
|
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 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
|
23 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { | |
| 19 |
|
24 | |||
| 20 | @Override |
|
25 | @Override | |
| 21 | public void apply(Project target) { |
|
26 | public void apply(Project target) { | |
| 22 | var extensions = target.getExtensions(); |
|
27 | var extensions = target.getExtensions(); | |
| 23 | var objects = target.getObjects(); |
|
28 | var objects = target.getObjects(); | |
| 24 | var providers = target.getProviders(); |
|
29 | var providers = target.getProviders(); | |
|
|
30 | var plugins = target.getPlugins(); | |||
| 25 | var configurations = target.getConfigurations(); |
|
31 | var configurations = target.getConfigurations(); | |
| 26 | var tasks = target.getTasks(); |
|
32 | var tasks = target.getTasks(); | |
|
|
33 | var layout = target.getLayout(); | |||
| 27 |
|
34 | |||
| 28 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
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 | // Access the VariantsExtension to configure variant sources. |
|
38 | // Access the VariantsExtension to configure variant sources. | |
| 31 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
39 | var variantsExtension = extensions.getByType(VariantsExtension.class); | |
| 32 |
|
40 | var sourcesExtension = extensions.getByType(VariantSourcesExtension.class); | ||
| 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); |
|
|||
| 38 |
|
41 | |||
| 39 | var deferred = new Deferred<OutgoingVariantsContext>(); |
|
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 | var variantArtifacts = new VariantArtifactsExtension() { |
|
80 | var variantArtifacts = new VariantArtifactsExtension() { | |
| 48 |
|
81 | |||
| 49 | @Override |
|
82 | @Override | |
| 50 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
83 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { | |
| 51 | deferred.whenResolved(context -> { |
|
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 | @Override |
|
90 | @Override | |
| 57 |
public void when |
|
91 | public void whenAvailable(Action<? super OutgoingVariantsContext> action) { | |
| 58 |
deferred.whenResolved( |
|
92 | deferred.whenResolved(handler -> action.execute(handler)); | |
| 59 | } |
|
93 | } | |
| 60 |
|
94 | |||
| 61 | @Override |
|
95 | @Override | |
| 62 |
public void whenOutgoing |
|
96 | public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) { | |
| 63 |
deferred.whenResolved(registry -> registry. |
|
97 | deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action)); | |
| 64 |
|
98 | |||
| 65 | } |
|
99 | } | |
| 66 |
|
100 | |||
| 67 | @Override |
|
101 | @Override | |
| 68 |
public void whenOutgoingSlot(Action<? super Outgoing |
|
102 | public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) { | |
| 69 |
deferred.whenResolved(registry -> registry. |
|
103 | deferred.whenResolved(registry -> registry.whenOutgoingSlot(action)); | |
| 70 | } |
|
104 | } | |
| 71 |
|
105 | |||
| 72 | }; |
|
106 | }; | |
| 73 |
|
107 | |||
| 74 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); |
|
108 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); | |
| 75 |
|
109 | |||
| 76 | } |
|
110 | } | |
| 77 |
|
111 | |||
| 78 | } |
|
112 | } | |
| @@ -1,54 +1,56 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import java.util.Optional; |
|
3 | import java.util.Optional; | |
| 4 |
|
4 | |||
| 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
5 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 6 | import org.gradle.api.Action; |
|
6 | import org.gradle.api.Action; | |
| 7 | import org.gradle.api.InvalidUserDataException; |
|
7 | import org.gradle.api.InvalidUserDataException; | |
| 8 |
|
8 | |||
| 9 | /** |
|
9 | /** | |
| 10 | * Resolves stateful slot assemblies from cheap slot identities. |
|
10 | * Resolves stateful slot assemblies from cheap slot identities. | |
| 11 | * |
|
11 | * | |
| 12 | * <p> |
|
12 | * <p> | |
| 13 | * The returned assembly is a materialized build-model handle. It may expose |
|
13 | * The returned assembly is a materialized build-model handle. It may expose | |
| 14 | * lazy Gradle providers, but |
|
14 | * lazy Gradle providers, but | |
| 15 | * it is no longer an identity object suitable for replayable discovery. |
|
15 | * it is no longer an identity object suitable for replayable discovery. | |
| 16 | */ |
|
16 | */ | |
| 17 | @NonNullByDefault |
|
17 | @NonNullByDefault | |
| 18 | public interface ArtifactAssemblies { |
|
18 | public interface ArtifactAssemblies { | |
| 19 | /** |
|
19 | /** | |
| 20 | * Resolves the assembly for the given slot. |
|
20 | * Resolves the assembly for the given slot. | |
| 21 | * |
|
21 | * | |
| 22 | * <p> |
|
22 | * <p> | |
| 23 | * This call materializes the build-facing body of the slot from its identity. |
|
23 | * This call materializes the build-facing body of the slot from its identity. | |
| 24 | * |
|
24 | * | |
| 25 | * @param slot slot identity inside a variant outgoing contract |
|
25 | * @param slot slot identity inside a variant outgoing contract | |
| 26 | * @return assembly handle for the slot |
|
26 | * @return assembly handle for the slot | |
| 27 | */ |
|
27 | */ | |
| 28 | default ArtifactAssembly require(ArtifactSlot slot) { |
|
28 | default ArtifactAssembly require(ArtifactSlot slot) { | |
| 29 | return find(slot) |
|
29 | return find(slot) | |
| 30 | .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered")); |
|
30 | .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered")); | |
| 31 | } |
|
31 | } | |
| 32 |
|
32 | |||
| 33 | /** |
|
33 | /** | |
| 34 | * Регистрирует обработчик на конкретный слот. Если слот еще не зарегистрирован, |
|
34 | * Registers a configuration action for the assembly of the given slot. | |
| 35 | * то обработчик будет добавлен в очередь и будет вызван при регистрации слота. |
|
|||
| 36 | * Порядок и точный момент вызова обработчиков не определен. |
|
|||
| 37 | * |
|
35 | * | |
| 38 | * @param slot Слот на который нужно зарегистрировать обработчик |
|
36 | * <p>If the assembly is already registered, the action is invoked immediately. | |
| 39 | * @param action Обработчик |
|
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 | void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action); |
|
43 | void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action); | |
| 42 |
|
44 | |||
| 43 | /** |
|
45 | /** | |
| 44 | * Регистрирует глобальный обработчик, который будет вызван для всех слотов. |
|
46 | * Adds global configuration action for all materialized assemblies. If some assemblies are | |
| 45 | * Обработчик будет вызван как для уже зарегистрированных слотов так и для тех, |
|
47 | * already registered, the action will be invoked for them immediately. For assemblies that | |
| 46 | * которые будут зарегистрированы в будущем. |
|
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 | Optional<ArtifactAssembly> find(ArtifactSlot slot); |
|
55 | Optional<ArtifactAssembly> find(ArtifactSlot slot); | |
| 54 | } |
|
56 | } | |
| @@ -1,59 +1,61 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Action; |
|
3 | import org.gradle.api.Action; | |
| 4 | import org.gradle.api.Task; |
|
4 | import org.gradle.api.Task; | |
| 5 | import org.gradle.api.attributes.AttributeContainer; |
|
5 | import org.gradle.api.attributes.AttributeContainer; | |
| 6 | import groovy.lang.Closure; |
|
6 | import groovy.lang.Closure; | |
| 7 | import org.implab.gradle.common.core.lang.Closures; |
|
7 | import org.implab.gradle.common.core.lang.Closures; | |
| 8 |
|
8 | |||
| 9 | /** |
|
9 | /** | |
| 10 | * Materialized outgoing publication state of a single slot. |
|
10 | * Materialized outgoing publication state of a single slot. | |
| 11 | * |
|
11 | * | |
| 12 | * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific |
|
12 | * <p>This type is a DSL facade to represent already created publication-facing state. Slot-specific | |
| 13 | * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which |
|
13 | * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which | |
| 14 | * is limited to the root outgoing configuration of the variant. |
|
14 | * is limited to the root outgoing configuration of the variant. | |
| 15 | */ |
|
15 | */ | |
| 16 | public interface OutgoingConfigurationSlotSpec { |
|
16 | public interface OutgoingConfigurationSlotSpec { | |
| 17 | /** |
|
17 | /** | |
| 18 | * Returns the published slot identity. |
|
18 | * Returns the published slot identity. | |
| 19 | * |
|
19 | * | |
| 20 | * @return slot identity |
|
20 | * @return slot identity | |
| 21 | */ |
|
21 | */ | |
| 22 | ArtifactSlot getArtifactSlot(); |
|
22 | ArtifactSlot getArtifactSlot(); | |
| 23 |
|
23 | |||
| 24 | /** |
|
24 | /** | |
| 25 | * Returns the assembly backing the published slot. |
|
25 | * Returns the assembly backing the published slot. | |
| 26 | * |
|
26 | * | |
| 27 | * @return slot assembly |
|
27 | * @return slot assembly | |
| 28 | */ |
|
28 | */ | |
| 29 | ArtifactAssembly getAssembly(); |
|
29 | ArtifactAssembly getAssembly(); | |
| 30 |
|
30 | |||
| 31 | /** |
|
31 | /** | |
| 32 | * Returns whether this slot is the primary outgoing artifact set of the variant. |
|
32 | * Returns whether this slot is the primary outgoing artifact set of the variant. | |
| 33 | * |
|
33 | * | |
| 34 | * @return {@code true} for the primary slot |
|
34 | * @return {@code true} for the primary slot | |
| 35 | */ |
|
35 | */ | |
| 36 | boolean isPrimary(); |
|
36 | boolean isPrimary(); | |
| 37 |
|
37 | |||
| 38 | /** |
|
38 | /** | |
| 39 | * Configures the task producing the slot artifact. |
|
39 | * Configures the task producing the slot artifact. | |
| 40 | * |
|
40 | * | |
| 41 | * @param action task configuration action |
|
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 | default void assemblyTask(Closure<?> closure) { |
|
47 | default void assemblyTask(Closure<?> closure) { | |
| 46 | assemblyTask(Closures.action(closure)); |
|
48 | assemblyTask(Closures.action(closure)); | |
| 47 | } |
|
49 | } | |
| 48 |
|
50 | |||
| 49 | /** |
|
51 | /** | |
| 50 | * Configures attributes of this slot publication. |
|
52 | * Configures attributes of this slot publication. | |
| 51 | * |
|
53 | * | |
| 52 | * @param action artifact attribute configuration action |
|
54 | * @param action artifact attribute configuration action | |
| 53 | */ |
|
55 | */ | |
| 54 | void artifactAttributes(Action<? super AttributeContainer> action); |
|
56 | void artifactAttributes(Action<? super AttributeContainer> action); | |
| 55 |
|
57 | |||
| 56 | default void artifactAttributes(Closure<?> closure) { |
|
58 | default void artifactAttributes(Closure<?> closure) { | |
| 57 | artifactAttributes(Closures.action(closure)); |
|
59 | artifactAttributes(Closures.action(closure)); | |
| 58 | } |
|
60 | } | |
| 59 | } |
|
61 | } | |
| @@ -1,50 +1,50 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Action; |
|
3 | import org.gradle.api.Action; | |
| 4 | import org.gradle.api.NamedDomainObjectContainer; |
|
4 | import org.gradle.api.NamedDomainObjectContainer; | |
| 5 | import org.gradle.api.NamedDomainObjectProvider; |
|
5 | import org.gradle.api.NamedDomainObjectProvider; | |
| 6 | import org.gradle.api.artifacts.Configuration; |
|
6 | import org.gradle.api.artifacts.Configuration; | |
| 7 | import org.gradle.api.provider.Property; |
|
7 | import org.gradle.api.provider.Property; | |
| 8 | import org.implab.gradle.variants.core.Variant; |
|
8 | import org.implab.gradle.variants.core.Variant; | |
| 9 |
|
9 | |||
| 10 | /** |
|
10 | /** | |
| 11 | * Описывает исходящую конфигурацию варианта |
|
11 | * Описывает исходящую конфигурацию варианта | |
| 12 | * |
|
12 | * | |
| 13 | * Задает связь между моделью вариантов и моделью конфигураций gradle через |
|
13 | * Задает связь между моделью вариантов и моделью конфигураций gradle через | |
| 14 | * свойство {@link #getConfiguration()}. Также задает отдельную ось слотов |
|
14 | * свойство {@link #getConfiguration()}. Также задает отдельную ось слотов | |
| 15 | * публикации, но не задает правил связывания этих слотов с самой конфигурацией |
|
15 | * публикации, но не задает правил связывания этих слотов с самой конфигурацией | |
| 16 | * и их содержимым. Самый простой вариант это {@link ArtifactAssemblies}. |
|
16 | * и их содержимым. Самый простой вариант это {@link ArtifactAssemblies}. | |
| 17 | */ |
|
17 | */ | |
| 18 | public interface OutgoingVariant { |
|
18 | public interface OutgoingVariant { | |
| 19 | /** |
|
19 | /** | |
| 20 | * Исходный вариант для которого строится Outgoing конфигурация |
|
20 | * Исходный вариант для которого строится Outgoing конфигурация | |
| 21 | */ |
|
21 | */ | |
| 22 | Variant getVariant(); |
|
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 | getConfiguration().configure(action); |
|
30 | getConfiguration().configure(action); | |
| 31 | } |
|
31 | } | |
| 32 |
|
32 | |||
| 33 | /** |
|
33 | /** | |
| 34 | * Слоты конфигурации, данная коллекция живая, используется для |
|
34 | * Слоты конфигурации, данная коллекция живая, используется для | |
| 35 | * получения информации об объявленных слотах, но эти слоты не |
|
35 | * получения информации об объявленных слотах, но эти слоты не | |
| 36 | * обязаны быть сконфигурированы, т.е. это только Identity. |
|
36 | * обязаны быть сконфигурированы, т.е. это только Identity. | |
| 37 | * |
|
37 | * | |
| 38 | * @see {@link ArtifactSlot} |
|
38 | * @see {@link ArtifactSlot} | |
| 39 | */ |
|
39 | */ | |
| 40 | NamedDomainObjectContainer<Slot> getSlots(); |
|
40 | NamedDomainObjectContainer<Slot> getSlots(); | |
| 41 |
|
41 | |||
| 42 | /** |
|
42 | /** | |
| 43 | * Основной набор артефактов (primary variant) для исходящей конфигурации |
|
43 | * Основной набор артефактов (primary variant) для исходящей конфигурации | |
| 44 | * |
|
44 | * | |
| 45 | * <p> |
|
45 | * <p> | |
| 46 | * Если в свойстве {@link #getSlots()} есть только один слой, то по конвенции он |
|
46 | * Если в свойстве {@link #getSlots()} есть только один слой, то по конвенции он | |
| 47 | * считается также основным. |
|
47 | * считается также основным. | |
| 48 | */ |
|
48 | */ | |
| 49 | Property<Slot> getPrimarySlot(); |
|
49 | Property<Slot> getPrimarySlot(); | |
| 50 | } |
|
50 | } | |
| @@ -1,38 +1,36 | |||||
| 1 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import java.util.Optional; |
|
3 | import java.util.Optional; | |
| 4 |
|
4 | |||
| 5 | import org.gradle.api.Action; |
|
5 | import org.gradle.api.Action; | |
| 6 | import org.gradle.api.InvalidUserDataException; |
|
6 | import org.gradle.api.InvalidUserDataException; | |
| 7 | import org.implab.gradle.variants.core.Variant; |
|
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 | public interface OutgoingVariantsContext { |
|
13 | public interface OutgoingVariantsContext { | |
| 15 |
|
14 | |||
| 16 | /** |
|
15 | ArtifactAssemblies getAssemblies(); | |
| 17 | * Зафиксированное представление о вариантах на основе которого адаптеры могут |
|
|||
| 18 | * конфигурировать артефакты и исходящие конфигурации |
|
|||
| 19 | */ |
|
|||
| 20 | VariantsView getVariants(); |
|
|||
| 21 |
|
16 | |||
| 22 | ArtifactAssemblies getAssemblies(); |
|
17 | void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action); | |
| 23 |
|
18 | |||
| 24 | /** |
|
19 | /** | |
| 25 | * Replayable hook для всех объявленных конфигураций |
|
20 | * Replayable hook для всех объявленных конфигураций | |
| 26 | */ |
|
21 | */ | |
| 27 |
void |
|
22 | void configureEach(Action<? super OutgoingVariant> action); | |
| 28 |
|
23 | |||
| 29 | Optional<OutgoingVariant> findOutgoing(Variant variant); |
|
24 | Optional<OutgoingVariant> findOutgoing(Variant variant); | |
| 30 |
|
25 | |||
| 31 | default OutgoingVariant requireOutgoing(Variant variant) { |
|
26 | default OutgoingVariant requireOutgoing(Variant variant) { | |
| 32 | return findOutgoing(variant) |
|
27 | return findOutgoing(variant) | |
| 33 | .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered")); |
|
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 | package org.implab.gradle.variants.artifacts; |
|
1 | package org.implab.gradle.variants.artifacts; | |
| 2 |
|
2 | |||
| 3 | import org.gradle.api.Action; |
|
3 | import org.gradle.api.Action; | |
| 4 | import org.implab.gradle.common.core.lang.Closures; |
|
4 | import org.implab.gradle.common.core.lang.Closures; | |
| 5 |
|
5 | |||
| 6 | import groovy.lang.Closure; |
|
6 | import groovy.lang.Closure; | |
| 7 |
|
7 | |||
| 8 | /** |
|
8 | /** | |
| 9 | * Project-level DSL entry point for declaring outgoing artifacts per variant. |
|
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 |
|
11 | * <p> | |
| 12 | * artifact sets within that contract. One slot is expected to materialize to one published artifact. |
|
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 | public interface VariantArtifactsExtension { |
|
17 | public interface VariantArtifactsExtension { | |
| 15 | /** |
|
18 | /** | |
| 16 | * Configures artifact slots of the named variant. |
|
19 | * Configures artifact slots of the named variant. | |
| 17 | * |
|
20 | * | |
| 18 | * @param variantName variant name |
|
21 | * @param variantName variant name | |
| 19 | * @param action variant artifact declaration |
|
22 | * @param action variant artifact declaration | |
| 20 | */ |
|
23 | */ | |
| 21 | void variant(String variantName, Action<? super VariantArtifactsSpec> action); |
|
24 | void variant(String variantName, Action<? super VariantArtifactsSpec> action); | |
| 22 |
|
25 | |||
| 23 | default void variant(String variantName, Closure<?> closure) { |
|
26 | default void variant(String variantName, Closure<?> closure) { | |
| 24 | variant(variantName, Closures.action(closure)); |
|
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 |
|
38 | default void whenAvailable(Closure<?> closure) { | |
| 35 |
when |
|
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 | * @param action variant-level outgoing configuration callback |
|
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 |
|
50 | default void whenOutgoingConfiguration(Closure<?> closure) { | |
| 46 |
whenOutgoing |
|
51 | whenOutgoingConfiguration(Closures.action(closure)); | |
| 47 | } |
|
52 | } | |
| 48 |
|
53 | |||
| 49 | /** |
|
54 | /** | |
| 50 | * Registers a callback invoked for each materialized outgoing slot publication. |
|
55 | * Registers a callback invoked for each materialized outgoing slot publication. | |
| 51 | * |
|
56 | * | |
| 52 | * @param action slot-level outgoing publication callback |
|
57 | * @param action slot-level outgoing publication callback | |
| 53 | */ |
|
58 | */ | |
| 54 | void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action); |
|
59 | void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action); | |
| 55 |
|
60 | |||
| 56 | default void whenOutgoingSlot(Closure<?> closure) { |
|
61 | default void whenOutgoingSlot(Closure<?> closure) { | |
| 57 | whenOutgoingSlot(Closures.action(closure)); |
|
62 | whenOutgoingSlot(Closures.action(closure)); | |
| 58 | } |
|
63 | } | |
| 59 | } |
|
64 | } | |
| @@ -1,54 +1,56 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 4 | import org.gradle.api.Action; |
|
4 | import org.gradle.api.Action; | |
| 5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; |
|
5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; | |
| 6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; | |
| 7 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
7 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |
| 8 |
|
8 | |||
| 9 | /** |
|
9 | /** | |
| 10 | * Связывает описание исходящих конфигураций gradle и сборку содержимого слотов |
|
10 | * Связывает описание исходящих конфигураций gradle и сборку содержимого слотов | |
| 11 | * из {@link ArtifactAssemblies} |
|
11 | * из {@link ArtifactAssemblies} | |
| 12 | */ |
|
12 | */ | |
| 13 | @NonNullByDefault |
|
13 | @NonNullByDefault | |
| 14 |
public class ArtifactAssemblyB |
|
14 | public class ArtifactAssemblyBinder implements Action<OutgoingVariant> { | |
| 15 |
|
15 | |||
| 16 | private final ArtifactAssemblies resolver; |
|
16 | private final ArtifactAssemblies resolver; | |
| 17 |
|
17 | |||
| 18 |
public ArtifactAssemblyB |
|
18 | public ArtifactAssemblyBinder(ArtifactAssemblies resolver) { | |
| 19 | this.resolver = resolver; |
|
19 | this.resolver = resolver; | |
| 20 | } |
|
20 | } | |
| 21 |
|
21 | |||
| 22 | @Override |
|
22 | @Override | |
| 23 | public void execute(OutgoingVariant outgoingVariant) { |
|
23 | public void execute(OutgoingVariant outgoingVariant) { | |
| 24 | var slots = outgoingVariant.getSlots(); |
|
24 | var slots = outgoingVariant.getSlots(); | |
| 25 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); |
|
25 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); | |
| 26 | var variant = outgoingVariant.getVariant(); |
|
26 | var variant = outgoingVariant.getVariant(); | |
| 27 |
|
27 | |||
| 28 | // связываем конфигурацию |
|
28 | // связываем конфигурацию | |
| 29 | outgoingVariant.configure(configuration -> { |
|
29 | outgoingVariant.configureOutgoing(configuration -> { | |
| 30 | var primarySlot = primarySlotProvider.get(); |
|
30 | var primarySlot = primarySlotProvider.get(); | |
| 31 | var outgoing = configuration.getOutgoing(); |
|
31 | var outgoing = configuration.getOutgoing(); | |
| 32 |
|
32 | |||
| 33 | // связываем основной вариант конфигурации |
|
33 | // связываем основной вариант конфигурации | |
| 34 | resolver.when( |
|
34 | resolver.when( | |
| 35 | new ArtifactSlot(variant, primarySlot), |
|
35 | new ArtifactSlot(variant, primarySlot), | |
| 36 | assembly -> outgoing.artifact(assembly.getArtifact())); |
|
36 | assembly -> outgoing.artifact(assembly.getArtifact())); | |
| 37 |
|
37 | |||
| 38 | // для всех объявленных слотов |
|
38 | // для всех объявленных слотов | |
| 39 | slots.all(slot -> { |
|
39 | slots.all(slot -> { | |
| 40 | // кроме основного |
|
40 | // кроме основного | |
| 41 | if (slot.equals(primarySlot)) |
|
41 | if (slot.equals(primarySlot)) | |
| 42 | return; |
|
42 | return; | |
| 43 |
|
43 | |||
| 44 | // связываем артефакты |
|
44 | // связываем артефакты | |
| 45 | resolver.when( |
|
45 | resolver.when( | |
| 46 | new ArtifactSlot(variant, slot), |
|
46 | new ArtifactSlot(variant, slot), | |
| 47 | assembly -> outgoing.getVariants() |
|
47 | assembly -> outgoing.getVariants() | |
| 48 | .register(slot.getName()) |
|
48 | .register(slot.getName()) | |
| 49 | .configure(cv -> cv.artifact(assembly.getArtifact()))); |
|
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 | import java.util.LinkedHashMap; |
|
3 | import java.util.LinkedHashMap; | |
| 4 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 5 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
| 6 | import java.util.function.Function; |
|
6 | import java.util.function.Function; | |
| 7 |
|
7 | |||
| 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 9 | import org.gradle.api.Action; |
|
9 | import org.gradle.api.Action; | |
| 10 | import org.gradle.api.InvalidUserDataException; |
|
10 | import org.gradle.api.InvalidUserDataException; | |
| 11 | import org.gradle.api.Task; |
|
11 | import org.gradle.api.Task; | |
| 12 | import org.gradle.api.file.FileSystemLocation; |
|
12 | import org.gradle.api.file.FileSystemLocation; | |
| 13 | import org.gradle.api.provider.Provider; |
|
13 | import org.gradle.api.provider.Provider; | |
| 14 | import org.gradle.api.tasks.TaskProvider; |
|
14 | import org.gradle.api.tasks.TaskProvider; | |
| 15 | import org.implab.gradle.common.core.lang.Deferred; |
|
15 | import org.implab.gradle.common.core.lang.Deferred; | |
| 16 | import org.implab.gradle.internal.ReplayableQueue; |
|
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 | @NonNullByDefault |
|
21 | @NonNullByDefault | |
| 19 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { |
|
22 | public class ArtifactAssemblyRegistry implements ArtifactAssemblies { | |
| 20 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); |
|
23 | private final Map<ArtifactSlot, Deferred<ArtifactAssembly>> assembliesBySlots = new LinkedHashMap<>(); | |
| 21 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); |
|
24 | private final ReplayableQueue<ArtifactAssembly> assemblies = new ReplayableQueue<>(); | |
| 22 |
|
25 | |||
| 23 | public ArtifactAssemblyRegistry() { |
|
26 | public ArtifactAssemblyRegistry() { | |
| 24 | } |
|
27 | } | |
| 25 |
|
28 | |||
| 26 | public <T extends Task> ArtifactAssembly register( |
|
29 | public <T extends Task> ArtifactAssembly register( | |
| 27 | ArtifactSlot slot, |
|
30 | ArtifactSlot slot, | |
| 28 | TaskProvider<T> task, |
|
31 | TaskProvider<T> task, | |
| 29 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { |
|
32 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> mapOutputArtifact) { | |
| 30 |
|
33 | |||
| 31 | var deferred = getDeferred(slot); |
|
34 | var deferred = getDeferred(slot); | |
| 32 | if (deferred.resolved()) { |
|
35 | if (deferred.resolved()) { | |
| 33 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); |
|
36 | throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); | |
| 34 | } |
|
37 | } | |
| 35 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); |
|
38 | var outputArtifact = task.flatMap(mapOutputArtifact::apply); | |
| 36 |
|
39 | |||
| 37 | var assembly = new Assembly(outputArtifact, task); |
|
40 | var assembly = new Assembly(outputArtifact, task); | |
| 38 | deferred.resolve(assembly); |
|
41 | deferred.resolve(assembly); | |
| 39 | assemblies.add(assembly); |
|
42 | assemblies.add(assembly); | |
| 40 | return assembly; |
|
43 | return assembly; | |
| 41 | } |
|
44 | } | |
| 42 |
|
45 | |||
| 43 | @Override |
|
46 | @Override | |
| 44 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { |
|
47 | public Optional<ArtifactAssembly> find(ArtifactSlot slot) { | |
| 45 | // to prevent creation of map entries on lookup use the map directly |
|
48 | // to prevent creation of map entries on lookup use the map directly | |
| 46 | var deferred = assembliesBySlots.get(slot); |
|
49 | var deferred = assembliesBySlots.get(slot); | |
| 47 | return deferred != null && deferred.resolved() |
|
50 | return deferred != null && deferred.resolved() | |
| 48 | ? Optional.of(deferred.value()) |
|
51 | ? Optional.of(deferred.value()) | |
| 49 | : Optional.empty(); |
|
52 | : Optional.empty(); | |
| 50 | } |
|
53 | } | |
| 51 |
|
54 | |||
| 52 | @Override |
|
55 | @Override | |
| 53 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { |
|
56 | public void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action) { | |
| 54 | getDeferred(slot).whenResolved(action::execute); |
|
57 | getDeferred(slot).whenResolved(action::execute); | |
| 55 | } |
|
58 | } | |
| 56 |
|
59 | |||
| 57 | @Override |
|
60 | @Override | |
| 58 |
public void |
|
61 | public void configureEach(Action<? super ArtifactAssembly> action) { | |
| 59 | assemblies.forEach(action::execute); |
|
62 | assemblies.forEach(action::execute); | |
| 60 | } |
|
63 | } | |
| 61 |
|
64 | |||
| 62 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { |
|
65 | private Deferred<ArtifactAssembly> getDeferred(ArtifactSlot slot) { | |
| 63 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); |
|
66 | return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); | |
| 64 | } |
|
67 | } | |
| 65 |
|
68 | |||
| 66 | static class Assembly implements ArtifactAssembly { |
|
69 | static class Assembly implements ArtifactAssembly { | |
| 67 |
|
70 | |||
| 68 | private final Provider<? extends FileSystemLocation> artifact; |
|
71 | private final Provider<? extends FileSystemLocation> artifact; | |
| 69 | private final TaskProvider<? extends Task> task; |
|
72 | private final TaskProvider<? extends Task> task; | |
| 70 |
|
73 | |||
| 71 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { |
|
74 | Assembly(Provider<? extends FileSystemLocation> artifact, TaskProvider<? extends Task> task) { | |
| 72 | this.artifact = artifact; |
|
75 | this.artifact = artifact; | |
| 73 | this.task = task; |
|
76 | this.task = task; | |
| 74 | } |
|
77 | } | |
| 75 |
|
78 | |||
| 76 | @Override |
|
79 | @Override | |
| 77 | public Provider<? extends FileSystemLocation> getArtifact() { |
|
80 | public Provider<? extends FileSystemLocation> getArtifact() { | |
| 78 | return artifact; |
|
81 | return artifact; | |
| 79 | } |
|
82 | } | |
| 80 |
|
83 | |||
| 81 | @Override |
|
84 | @Override | |
| 82 | public TaskProvider<? extends Task> getAssemblyTask() { |
|
85 | public TaskProvider<? extends Task> getAssemblyTask() { | |
| 83 | return task; |
|
86 | return task; | |
| 84 | } |
|
87 | } | |
| 85 |
|
88 | |||
| 86 | } |
|
89 | } | |
| 87 | } |
|
90 | } | |
| @@ -1,77 +1,79 | |||||
| 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.Set; |
|
4 | import java.util.Set; | |
| 5 | import java.util.function.Consumer; |
|
5 | import java.util.function.Consumer; | |
| 6 | import java.util.stream.Stream; |
|
6 | import java.util.stream.Stream; | |
| 7 |
|
7 | |||
|
|
8 | import org.eclipse.jdt.annotation.NonNullByDefault; | |||
| 8 | import org.gradle.api.Action; |
|
9 | import org.gradle.api.Action; | |
| 9 | import org.gradle.api.model.ObjectFactory; |
|
10 | import org.gradle.api.model.ObjectFactory; | |
| 10 | import org.implab.gradle.common.core.lang.Strings; |
|
11 | import org.implab.gradle.common.core.lang.Strings; | |
| 11 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
12 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; | |
| 12 | import org.implab.gradle.variants.core.Layer; |
|
13 | import org.implab.gradle.variants.core.Layer; | |
| 13 | import org.implab.gradle.variants.core.Role; |
|
14 | import org.implab.gradle.variants.core.Role; | |
| 14 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; |
|
15 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; | |
| 15 |
|
16 | |||
| 16 | /** |
|
17 | /** | |
| 17 | * Реализация DSL модели, строит набор {@link SlotContribution}. При построении набора |
|
18 | * Реализация DSL модели, строит набор {@link SlotContribution}. При построении набора | |
| 18 | * правила и корректность не проверяются. По окончании использования клиент |
|
19 | * правила и корректность не проверяются. По окончании использования клиент | |
| 19 | * вызывает метод {@link #process(Consumer)} для обработки результатов. |
|
20 | * вызывает метод {@link #process(Consumer)} для обработки результатов. | |
| 20 | * |
|
21 | * | |
| 21 | */ |
|
22 | */ | |
|
|
23 | @NonNullByDefault | |||
| 22 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { |
|
24 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { | |
| 23 | private final Consumer<? super SlotContribution> consumer; |
|
25 | private final Consumer<? super SlotContribution> consumer; | |
| 24 | private final ObjectFactory objectFactory; |
|
26 | private final ObjectFactory objectFactory; | |
| 25 |
|
27 | |||
| 26 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { |
|
28 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { | |
| 27 | this.consumer = consumer; |
|
29 | this.consumer = consumer; | |
| 28 | this.objectFactory = objectFactory; |
|
30 | this.objectFactory = objectFactory; | |
| 29 | } |
|
31 | } | |
| 30 |
|
32 | |||
| 31 | @Override |
|
33 | @Override | |
| 32 | public void from(Object artifact) { |
|
34 | public void from(Object artifact) { | |
| 33 | consumer.accept(new DirectContribution(artifact)); |
|
35 | consumer.accept(new DirectContribution(artifact)); | |
| 34 | } |
|
36 | } | |
| 35 |
|
37 | |||
| 36 | @Override |
|
38 | @Override | |
| 37 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
39 | public void fromVariant(Action<? super OutputSelectionSpec> action) { | |
| 38 | consumer.accept(new VariantOutputsContribution(outputs(action))); |
|
40 | consumer.accept(new VariantOutputsContribution(outputs(action))); | |
| 39 | } |
|
41 | } | |
| 40 |
|
42 | |||
| 41 | @Override |
|
43 | @Override | |
| 42 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
44 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { | |
| 43 |
|
45 | |||
| 44 | consumer.accept(new RoleOutputsContribution( |
|
46 | consumer.accept(new RoleOutputsContribution( | |
| 45 | objectFactory.named(Role.class, roleName), |
|
47 | objectFactory.named(Role.class, roleName), | |
| 46 | outputs(action))); |
|
48 | outputs(action))); | |
| 47 | } |
|
49 | } | |
| 48 |
|
50 | |||
| 49 | @Override |
|
51 | @Override | |
| 50 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
52 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { | |
| 51 | consumer.accept(new LayerOutputsContribution( |
|
53 | consumer.accept(new LayerOutputsContribution( | |
| 52 | objectFactory.named(Layer.class, layerName), |
|
54 | objectFactory.named(Layer.class, layerName), | |
| 53 | outputs(action))); |
|
55 | outputs(action))); | |
| 54 | } |
|
56 | } | |
| 55 |
|
57 | |||
| 56 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
58 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { | |
| 57 | var spec = new OutputsSetSpec(); |
|
59 | var spec = new OutputsSetSpec(); | |
| 58 | action.execute(spec); |
|
60 | action.execute(spec); | |
| 59 | return spec.outputs(); |
|
61 | return spec.outputs(); | |
| 60 | } |
|
62 | } | |
| 61 |
|
63 | |||
| 62 | private static class OutputsSetSpec implements OutputSelectionSpec { |
|
64 | private static class OutputsSetSpec implements OutputSelectionSpec { | |
| 63 | private final Set<String> outputs = new HashSet<>(); |
|
65 | private final Set<String> outputs = new HashSet<>(); | |
| 64 |
|
66 | |||
| 65 | @Override |
|
67 | @Override | |
| 66 | public void output(String name, String... extra) { |
|
68 | public void output(String name, String... extra) { | |
| 67 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
69 | Stream.concat(Stream.of(name), Stream.of(extra)) | |
| 68 | .map(Strings::requireNonBlank) |
|
70 | .map(Strings::requireNonBlank) | |
| 69 | .forEach(outputs::add); |
|
71 | .forEach(outputs::add); | |
| 70 | } |
|
72 | } | |
| 71 |
|
73 | |||
| 72 | Set<String> outputs() { |
|
74 | Set<String> outputs() { | |
| 73 | return Set.copyOf(outputs); |
|
75 | return Set.copyOf(outputs); | |
| 74 | } |
|
76 | } | |
| 75 |
|
77 | |||
| 76 | } |
|
78 | } | |
| 77 | } |
|
79 | } | |
| @@ -1,56 +1,121 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 | import java.util.LinkedHashMap; |
|
3 | import java.util.LinkedHashMap; | |
| 4 | import java.util.LinkedList; |
|
|||
| 5 | import java.util.List; |
|
|||
| 6 | import java.util.Map; |
|
4 | import java.util.Map; | |
| 7 | import java.util.Optional; |
|
5 | import java.util.Optional; | |
|
|
6 | import java.util.Set; | |||
| 8 | import java.util.function.Consumer; |
|
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 | import org.gradle.api.artifacts.ConfigurationContainer; |
|
14 | import org.gradle.api.artifacts.ConfigurationContainer; | |
| 11 | import org.gradle.api.model.ObjectFactory; |
|
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 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
18 | import org.implab.gradle.variants.artifacts.OutgoingVariant; | |
|
|
19 | import org.implab.gradle.variants.artifacts.Slot; | |||
| 14 | import org.implab.gradle.variants.core.Variant; |
|
20 | import org.implab.gradle.variants.core.Variant; | |
| 15 |
|
21 | |||
|
|
22 | /** | |||
|
|
23 | * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами | |||
|
|
24 | * сборки. Связь устанавливается 1:1. | |||
|
|
25 | */ | |||
|
|
26 | @NonNullByDefault | |||
| 16 | public class OutgoingRegistry { |
|
27 | public class OutgoingRegistry { | |
| 17 |
private final Map<Variant, |
|
28 | private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>(); | |
| 18 | private final List<Consumer<? super DefaultOutgoingConfiguration>> hooks = new LinkedList<>(); |
|
29 | private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>(); | |
| 19 |
|
||||
| 20 | private final ConfigurationContainer configurations; |
|
30 | private final ConfigurationContainer configurations; | |
| 21 | private final ObjectFactory objects; |
|
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 | this.configurations = configurations; |
|
38 | this.configurations = configurations; | |
| 26 | this.objects = objects; |
|
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 | return Optional.ofNullable(outgoingByVariant.get(variant)); |
|
44 | return Optional.ofNullable(outgoingByVariant.get(variant)); | |
| 32 | } |
|
45 | } | |
| 33 |
|
46 | |||
| 34 | public OutgoingVariant maybeCreate(Variant variant) { |
|
47 | public OutgoingVariant maybeCreate(Variant variant) { | |
| 35 | var outgoing = outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration); |
|
48 | return find(variant).orElseGet(() -> create(variant)); | |
| 36 | hooks.forEach(hook -> hook.accept(outgoing)); |
|
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 | return outgoing; |
|
64 | return outgoing; | |
| 38 | } |
|
65 | } | |
| 39 |
|
66 | |||
| 40 | public void all(Consumer<? super OutgoingVariant> action) { |
|
67 | /** | |
| 41 | outgoingByVariant.values().forEach(action); |
|
68 | * Replayable hook which is applied when an outgoing variant is defined | |
| 42 | hooks.add(action); |
|
69 | * | |
| 43 | } |
|
70 | * @param action | |
| 44 |
|
71 | */ | ||
| 45 | private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) { |
|
72 | public void configureEach(Consumer<? super OutgoingVariant> action) { | |
| 46 | var configuration = configurations.register(outgoingConfigurationName(variant)); |
|
73 | outgoingVariants.forEach(action); | |
| 47 |
|
||||
| 48 | return new DefaultOutgoingConfiguration(variant, configuration, objects, providers); |
|
|||
| 49 | } |
|
74 | } | |
| 50 |
|
75 | |||
| 51 | private String outgoingConfigurationName(Variant variant) { |
|
76 | private String outgoingConfigurationName(Variant variant) { | |
| 52 | return variant.getName() + "Elements"; |
|
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 | package org.implab.gradle.variants.artifacts.internal; |
|
1 | package org.implab.gradle.variants.artifacts.internal; | |
| 2 |
|
2 | |||
| 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
3 | import org.eclipse.jdt.annotation.NonNullByDefault; | |
| 4 |
|
4 | |||
| 5 | @NonNullByDefault |
|
5 | @NonNullByDefault | |
| 6 |
|
|
6 | interface SlotContribution { | |
| 7 |
|
7 | |||
| 8 | void accept(SlotContributionVisitor visitor); |
|
8 | void accept(SlotContributionVisitor visitor); | |
| 9 | } |
|
9 | } | |
| @@ -1,17 +1,15 | |||||
| 1 | package org.implab.gradle.variants.artifacts.internal; |
|
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 | void visit(DirectContribution contribution); |
|
7 | void visit(DirectContribution contribution); | |
| 7 |
|
8 | |||
| 8 | void visit(VariantOutputsContribution contribution); |
|
9 | void visit(VariantOutputsContribution contribution); | |
| 9 |
|
10 | |||
| 10 | void visit(RoleOutputsContribution contribution); |
|
11 | void visit(RoleOutputsContribution contribution); | |
| 11 |
|
12 | |||
| 12 | void visit(LayerOutputsContribution contribution); |
|
13 | void visit(LayerOutputsContribution contribution); | |
| 13 |
|
14 | |||
| 14 | default Consumer<SlotContribution> consumer() { |
|
|||
| 15 | return slot -> slot.accept(this); |
|
|||
| 16 | } |
|
|||
| 17 | } |
|
15 | } | |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
| 1 | NO CONTENT: file was removed |
|
NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
