##// END OF EJS Templates
Rework variant artifacts materialization model...
cin -
r51:9db7822cd26c default
parent child
Show More
@@ -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;
@@ -2,6 +2,8
2 2
3 3 ## Проектные договоренности
4 4
5 - для управления исходным кодом используется Mercurial
6
5 7 ### Публичное API библиотек
6 8
7 9 - Предпочтителен `non-null` подход.
@@ -1,7 +1,5
1 1 package org.implab.gradle.common.core.lang;
2 2
3 import java.util.function.Consumer;
4 import java.util.function.Function;
5 3 import java.util.regex.Pattern;
6 4
7 5 import org.eclipse.jdt.annotation.NonNullByDefault;
@@ -17,6 +17,7
17 17 - artifact: directory
18 18 - customTask
19 19 - artifact: FileSystemLocation
20 - when, all - replayable hooks для получения содержимого сборки
20 21
21 22 ## extension
22 23
@@ -30,16 +31,8
30 31 - whenOutgoingConfiguration
31 32 - whenOutgoingSlot
32 33
33 outgoing = outgoings.maybeCreate(variant)
34
35 slot = outgoings.slots.maybeCreate(slotName)
36 assembly = assemblies.register(variantSlot, task, mapOutput)
37 outgoing.configure(configuration -> {
38 slots.all(slot -> {
39 assemblies.when(variant, slot) { assembly ->
40 configuration.variants.create(slot.name) {
41 artifact(assembly.artifact)
42 }
43 }
44 });
45 }); No newline at end of file
34 - ArtifactAssemblyBridge
35 связывает содержимое ArtifactAssembly и исходящей конфигурации OutgoingVariant
36 - VariantArtifactsHandler
37 управляет состоянием ArtifactAssembly, используется для DSL сборки
38 - configureVariant конфигурирует VariantArtifactsSpec No newline at end of file
@@ -4,6 +4,9 import java.util.LinkedList;
4 4 import java.util.List;
5 5 import java.util.function.Consumer;
6 6
7 import org.eclipse.jdt.annotation.NonNullByDefault;
8
9 @NonNullByDefault
7 10 public class ReplayableQueue<T> {
8 11 private final List<Consumer<? super T>> consumers = new LinkedList<>();
9 12 private final List<T> values = new LinkedList<>();
@@ -4,16 +4,21 import org.gradle.api.Action;
4 4 import org.gradle.api.Plugin;
5 5 import org.gradle.api.Project;
6 6 import org.implab.gradle.common.core.lang.Deferred;
7 import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry;
8 7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
9 8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
10 9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
11 10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBridge;
13 import org.implab.gradle.variants.artifacts.internal.DefaultVariantArtifactSpec;
11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
14 16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
15 18 import org.implab.gradle.variants.core.Variant;
16 19 import org.implab.gradle.variants.core.VariantsExtension;
20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
17 22
18 23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
19 24
@@ -22,25 +27,53 public abstract class VariantArtifactsPl
22 27 var extensions = target.getExtensions();
23 28 var objects = target.getObjects();
24 29 var providers = target.getProviders();
30 var plugins = target.getPlugins();
25 31 var configurations = target.getConfigurations();
26 32 var tasks = target.getTasks();
33 var layout = target.getLayout();
27 34
28 35 // Apply the main VariantsPlugin to ensure the core variant model is available.
29 target.getPlugins().apply(VariantsPlugin.class);
36 plugins.apply(VariantsPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
30 38 // Access the VariantsExtension to configure variant sources.
31 39 var variantsExtension = extensions.getByType(VariantsExtension.class);
32
33 var outgoing = new OutgoingRegistry(configurations, objects, providers);
34 var assemblies = new ArtifactAssemblyRegistry(objects, tasks);
35
36 // wire artifact assemblies to configuration variants
37 var assembliesBridge = new ArtifactAssemblyBridge(assemblies);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
38 41
39 42 var deferred = new Deferred<OutgoingVariantsContext>();
40 43
41 deferred.whenResolved(context -> context.all(assembliesBridge));
44 variantsExtension.whenFinalized(variants -> {
45
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 var assemblies = new ArtifactAssemblyRegistry();
48
49 // wire artifact assemblies to configuration variants
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 var primarySlotConvention = new SingleSlotConvention(providers);
52 var materializationHandler = new MaterializationPolicyHandler(assemblies);
53
54 // bind slot assemblies to outgoing variants
55 outgoing.configureEach(assembliesBridge::execute);
56 // apply primary slot convention when outgoing variant has a single slot
57 outgoing.configureEach(primarySlotConvention::execute);
58 // apply materialization policy hooks to outgoing variants
59 outgoing.configureEach(materializationHandler::execute);
42 60
43 variantsExtension.whenFinalized(variants -> {
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
@@ -49,24 +82,25 public abstract class VariantArtifactsPl
49 82 @Override
50 83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
51 84 deferred.whenResolved(context -> {
52 new DefaultVariantArtifactSpec();
85 var variant = objects.named(Variant.class, variantName);
86 context.configureVariant(variant, action);
53 87 });
54 88 }
55 89
56 90 @Override
57 public void whenFinalized(Action<? super OutgoingVariantsContext> action) {
58 deferred.whenResolved(registry -> action.execute(registry.variantsContext()));
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 deferred.whenResolved(handler -> action.execute(handler));
59 93 }
60 94
61 95 @Override
62 public void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action) {
63 deferred.whenResolved(registry -> registry.configureOutgoing(action));
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
64 98
65 99 }
66 100
67 101 @Override
68 public void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action) {
69 deferred.whenResolved(registry -> registry.configureOutgoingSlot(action));
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
70 104 }
71 105
72 106 };
@@ -31,23 +31,25 public interface ArtifactAssemblies {
31 31 }
32 32
33 33 /**
34 * Регистрирует обработчик на конкретный слот. Если слот еще не зарегистрирован,
35 * то обработчик будет добавлен в очередь и будет вызван при регистрации слота.
36 * Порядок и точный момент вызова обработчиков не определен.
34 * Registers a configuration action for the assembly of the given slot.
37 35 *
38 * @param slot Слот на который нужно зарегистрировать обработчик
39 * @param action Обработчик
36 * <p>If the assembly is already registered, the action is invoked immediately.
37 * Otherwise, it is queued and invoked when slot materialization registers the
38 * assembly handle.
39 *
40 * @param slot slot identity inside a variant outgoing contract
41 * @param action assembly configuration action
40 42 */
41 43 void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action);
42 44
43 45 /**
44 * Регистрирует глобальный обработчик, который будет вызван для всех слотов.
45 * Обработчик будет вызван как для уже зарегистрированных слотов так и для тех,
46 * которые будут зарегистрированы в будущем.
46 * Adds global configuration action for all materialized assemblies. If some assemblies are
47 * already registered, the action will be invoked for them immediately. For assemblies that
48 * are materialized later, the action will be invoked when they are registered.
47 49 *
48 * @param action Обработчик
50 * @param action assembly configuration action.
49 51 */
50 void all(Action<? super ArtifactAssembly> action);
52 void configureEach(Action<? super ArtifactAssembly> action);
51 53
52 54 /** Ищет зарегистрированный слот */
53 55 Optional<ArtifactAssembly> find(ArtifactSlot slot);
@@ -40,7 +40,9 public interface OutgoingConfigurationSl
40 40 *
41 41 * @param action task configuration action
42 42 */
43 void assemblyTask(Action<? super Task> action);
43 default void assemblyTask(Action<? super Task> action) {
44 getAssembly().getAssemblyTask().configure(action);
45 }
44 46
45 47 default void assemblyTask(Closure<?> closure) {
46 48 assemblyTask(Closures.action(closure));
@@ -24,9 +24,9 public interface OutgoingVariant {
24 24 /**
25 25 * Провайдер зарегистрированной конфигурации
26 26 */
27 NamedDomainObjectProvider<Configuration> getConfiguration();
27 NamedDomainObjectProvider<? extends Configuration> getConfiguration();
28 28
29 default void configure(Action<? super Configuration> action) {
29 default void configureOutgoing(Action<? super Configuration> action) {
30 30 getConfiguration().configure(action);
31 31 }
32 32
@@ -5,7 +5,6 import java.util.Optional;
5 5 import org.gradle.api.Action;
6 6 import org.gradle.api.InvalidUserDataException;
7 7 import org.implab.gradle.variants.core.Variant;
8 import org.implab.gradle.variants.core.VariantsView;
9 8
10 9 /**
11 10 * Контекст работы с вариантами публикации, становится доступным после
@@ -13,18 +12,14 import org.implab.gradle.variants.core.V
13 12 */
14 13 public interface OutgoingVariantsContext {
15 14
16 /**
17 * Зафиксированное представление о вариантах на основе которого адаптеры могут
18 * конфигурировать артефакты и исходящие конфигурации
19 */
20 VariantsView getVariants();
15 ArtifactAssemblies getAssemblies();
21 16
22 ArtifactAssemblies getAssemblies();
17 void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action);
23 18
24 19 /**
25 20 * Replayable hook для всех объявленных конфигураций
26 21 */
27 void all(Action<? super OutgoingVariant> action);
22 void configureEach(Action<? super OutgoingVariant> action);
28 23
29 24 Optional<OutgoingVariant> findOutgoing(Variant variant);
30 25
@@ -33,6 +28,9 public interface OutgoingVariantsContext
33 28 .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered"));
34 29 }
35 30
36 ArtifactAssemblyRules slotRules(ArtifactSlot slot);
31 void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action);
32
33 void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action);
34
37 35
38 36 }
@@ -8,8 +8,11 import groovy.lang.Closure;
8 8 /**
9 9 * Project-level DSL entry point for declaring outgoing artifacts per variant.
10 10 *
11 * <p>A variant represents one external outgoing contract. Slots declared inside a variant represent
12 * artifact sets within that contract. One slot is expected to materialize to one published artifact.
11 * <p>
12 * A variant represents one external outgoing contract. Slots declared inside a
13 * variant represent
14 * artifact sets within that contract. One slot is expected to materialize to
15 * one published artifact.
13 16 */
14 17 public interface VariantArtifactsExtension {
15 18 /**
@@ -25,25 +28,27 public interface VariantArtifactsExtensi
25 28 }
26 29
27 30 /**
28 * Registers a callback invoked with the finalized artifact model.
31 * Registers a callback invoked when the outgoing variants context becomes
32 * available.
29 33 *
30 * @param action finalized-model callback
34 * @param action outgoing variants context callback
31 35 */
32 void whenFinalized(Action<? super OutgoingVariantsContext> action);
36 void whenAvailable(Action<? super OutgoingVariantsContext> action);
33 37
34 default void whenFinalized(Closure<?> closure) {
35 whenFinalized(Closures.action(closure));
38 default void whenAvailable(Closure<?> closure) {
39 whenAvailable(Closures.action(closure));
36 40 }
37 41
38 42 /**
39 * Registers a callback invoked for each materialized root outgoing configuration.
43 * Registers a callback invoked for each materialized root outgoing
44 * configuration.
40 45 *
41 46 * @param action variant-level outgoing configuration callback
42 47 */
43 void whenOutgoingVariant(Action<? super OutgoingConfigurationSpec> action);
48 void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action);
44 49
45 default void whenOutgoingVariant(Closure<?> closure) {
46 whenOutgoingVariant(Closures.action(closure));
50 default void whenOutgoingConfiguration(Closure<?> closure) {
51 whenOutgoingConfiguration(Closures.action(closure));
47 52 }
48 53
49 54 /**
@@ -11,11 +11,11 import org.implab.gradle.variants.artifa
11 11 * из {@link ArtifactAssemblies}
12 12 */
13 13 @NonNullByDefault
14 public class ArtifactAssemblyBridge implements Action<OutgoingVariant> {
14 public class ArtifactAssemblyBinder implements Action<OutgoingVariant> {
15 15
16 16 private final ArtifactAssemblies resolver;
17 17
18 public ArtifactAssemblyBridge(ArtifactAssemblies resolver) {
18 public ArtifactAssemblyBinder(ArtifactAssemblies resolver) {
19 19 this.resolver = resolver;
20 20 }
21 21
@@ -26,7 +26,7 public class ArtifactAssemblyBridge impl
26 26 var variant = outgoingVariant.getVariant();
27 27
28 28 // связываем конфигурацию
29 outgoingVariant.configure(configuration -> {
29 outgoingVariant.configureOutgoing(configuration -> {
30 30 var primarySlot = primarySlotProvider.get();
31 31 var outgoing = configuration.getOutgoing();
32 32
@@ -51,4 +51,6 public class ArtifactAssemblyBridge impl
51 51 });
52 52 }
53 53
54
55
54 56 }
@@ -1,4 +1,4
1 package org.implab.gradle.variants.artifacts;
1 package org.implab.gradle.variants.artifacts.internal;
2 2
3 3 import java.util.LinkedHashMap;
4 4 import java.util.Map;
@@ -14,6 +14,9 import org.gradle.api.provider.Provider;
14 14 import org.gradle.api.tasks.TaskProvider;
15 15 import org.implab.gradle.common.core.lang.Deferred;
16 16 import org.implab.gradle.internal.ReplayableQueue;
17 import org.implab.gradle.variants.artifacts.ArtifactAssemblies;
18 import org.implab.gradle.variants.artifacts.ArtifactAssembly;
19 import org.implab.gradle.variants.artifacts.ArtifactSlot;
17 20
18 21 @NonNullByDefault
19 22 public class ArtifactAssemblyRegistry implements ArtifactAssemblies {
@@ -55,7 +58,7 public class ArtifactAssemblyRegistry im
55 58 }
56 59
57 60 @Override
58 public void all(Action<? super ArtifactAssembly> action) {
61 public void configureEach(Action<? super ArtifactAssembly> action) {
59 62 assemblies.forEach(action::execute);
60 63 }
61 64
@@ -5,6 +5,7 import java.util.Set;
5 5 import java.util.function.Consumer;
6 6 import java.util.stream.Stream;
7 7
8 import org.eclipse.jdt.annotation.NonNullByDefault;
8 9 import org.gradle.api.Action;
9 10 import org.gradle.api.model.ObjectFactory;
10 11 import org.implab.gradle.common.core.lang.Strings;
@@ -19,6 +20,7 import org.implab.gradle.variants.artifa
19 20 * вызывает метод {@link #process(Consumer)} для обработки результатов.
20 21 *
21 22 */
23 @NonNullByDefault
22 24 final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec {
23 25 private final Consumer<? super SlotContribution> consumer;
24 26 private final ObjectFactory objectFactory;
@@ -1,56 +1,121
1 1 package org.implab.gradle.variants.artifacts.internal;
2 2
3 3 import java.util.LinkedHashMap;
4 import java.util.LinkedList;
5 import java.util.List;
6 4 import java.util.Map;
7 5 import java.util.Optional;
6 import java.util.Set;
8 7 import java.util.function.Consumer;
9 8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.gradle.api.InvalidUserDataException;
11 import org.gradle.api.NamedDomainObjectContainer;
12 import org.gradle.api.NamedDomainObjectProvider;
13 import org.gradle.api.artifacts.Configuration;
10 14 import org.gradle.api.artifacts.ConfigurationContainer;
11 15 import org.gradle.api.model.ObjectFactory;
12 import org.gradle.api.provider.ProviderFactory;
16 import org.gradle.api.provider.Property;
17 import org.implab.gradle.internal.ReplayableQueue;
13 18 import org.implab.gradle.variants.artifacts.OutgoingVariant;
19 import org.implab.gradle.variants.artifacts.Slot;
14 20 import org.implab.gradle.variants.core.Variant;
15 21
22 /**
23 * Реестр исходящих вариантов. Связывает исходящие конфигурации с вариантами
24 * сборки. Связь устанавливается 1:1.
25 */
26 @NonNullByDefault
16 27 public class OutgoingRegistry {
17 private final Map<Variant, DefaultOutgoingConfiguration> outgoingByVariant = new LinkedHashMap<>();
18 private final List<Consumer<? super DefaultOutgoingConfiguration>> hooks = new LinkedList<>();
19
28 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
29 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
20 30 private final ConfigurationContainer configurations;
21 31 private final ObjectFactory objects;
22 private final ProviderFactory providers;
32 private final Set<Variant> declaredVariants;
23 33
24 public OutgoingRegistry(ConfigurationContainer configurations, ObjectFactory objects, ProviderFactory providers) {
34 public OutgoingRegistry(
35 ConfigurationContainer configurations,
36 ObjectFactory objects,
37 Set<Variant> declaredVariants) {
25 38 this.configurations = configurations;
26 39 this.objects = objects;
27 this.providers = providers;
40 this.declaredVariants = declaredVariants;
28 41 }
29 42
30 public Optional<OutgoingVariant> findOutgoing(Variant variant) {
43 public Optional<OutgoingVariant> find(Variant variant) {
31 44 return Optional.ofNullable(outgoingByVariant.get(variant));
32 45 }
33 46
34 47 public OutgoingVariant maybeCreate(Variant variant) {
35 var outgoing = outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration);
36 hooks.forEach(hook -> hook.accept(outgoing));
48 return find(variant).orElseGet(() -> create(variant));
49 }
50
51 public OutgoingVariant create(Variant variant) {
52 if (!declaredVariants.contains(variant))
53 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
54 if (outgoingByVariant.containsKey(variant))
55 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
56
57 var configuration = configurations.consumable(outgoingConfigurationName(variant));
58 var outgoing = new Outgoing(variant, configuration);
59
60 outgoingByVariant.put(variant, outgoing);
61
62 outgoingVariants.add(outgoing);
63
37 64 return outgoing;
38 65 }
39 66
40 public void all(Consumer<? super OutgoingVariant> action) {
41 outgoingByVariant.values().forEach(action);
42 hooks.add(action);
43 }
44
45 private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) {
46 var configuration = configurations.register(outgoingConfigurationName(variant));
47
48 return new DefaultOutgoingConfiguration(variant, configuration, objects, providers);
67 /**
68 * Replayable hook which is applied when an outgoing variant is defined
69 *
70 * @param action
71 */
72 public void configureEach(Consumer<? super OutgoingVariant> action) {
73 outgoingVariants.forEach(action);
49 74 }
50 75
51 76 private String outgoingConfigurationName(Variant variant) {
52 77 return variant.getName() + "Elements";
53 78 }
54 79
80 private class Outgoing implements OutgoingVariant {
81
82 private final Variant variant;
83
84 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
85
86 private final NamedDomainObjectContainer<Slot> slots;
87
88 private final Property<Slot> primarySlot;
89
90 public Outgoing(
91 Variant variant,
92 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
93 this.variant = variant;
94 this.configurationProvider = configurationProvider;
95 this.slots = objects.domainObjectContainer(Slot.class);
96 this.primarySlot = objects.property(Slot.class);
97 primarySlot.finalizeValueOnRead();
98 }
99
100 @Override
101 public Property<Slot> getPrimarySlot() {
102 return primarySlot;
103 }
104
105 @Override
106 public Variant getVariant() {
107 return variant;
108 }
109
110 @Override
111 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
112 return configurationProvider;
113 }
114
115 @Override
116 public NamedDomainObjectContainer<Slot> getSlots() {
117 return slots;
118 }
119 }
55 120
56 121 }
@@ -3,7 +3,7 package org.implab.gradle.variants.artif
3 3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 4
5 5 @NonNullByDefault
6 public interface SlotContribution {
6 interface SlotContribution {
7 7
8 8 void accept(SlotContributionVisitor visitor);
9 9 }
@@ -1,8 +1,9
1 1 package org.implab.gradle.variants.artifacts.internal;
2 2
3 import java.util.function.Consumer;
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 4
5 public interface SlotContributionVisitor {
5 @NonNullByDefault
6 interface SlotContributionVisitor {
6 7 void visit(DirectContribution contribution);
7 8
8 9 void visit(VariantOutputsContribution contribution);
@@ -11,7 +12,4 public interface SlotContributionVisitor
11 12
12 13 void visit(LayerOutputsContribution contribution);
13 14
14 default Consumer<SlotContribution> consumer() {
15 return slot -> slot.accept(this);
16 15 }
17 }
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now