diff --git a/design_notes.md b/design_notes.md --- a/design_notes.md +++ b/design_notes.md @@ -29,3 +29,17 @@ - whenFinalized - whenOutgoingConfiguration - whenOutgoingSlot + +outgoing = outgoings.maybeCreate(variant) + +slot = outgoings.slots.maybeCreate(slotName) +assembly = assemblies.register(variantSlot, task, mapOutput) +outgoing.configure(configuration -> { + slots.all(slot -> { + assemblies.when(variant, slot) { assembly -> + configuration.variants.create(slot.name) { + artifact(assembly.artifact) + } + } + }); +}); \ No newline at end of file diff --git a/variants/src/main/java/org/implab/gradle/variants/VariantArtifactsPlugin.java b/variants/src/main/java/org/implab/gradle/variants/VariantArtifactsPlugin.java --- a/variants/src/main/java/org/implab/gradle/variants/VariantArtifactsPlugin.java +++ b/variants/src/main/java/org/implab/gradle/variants/VariantArtifactsPlugin.java @@ -5,11 +5,11 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.implab.gradle.common.core.lang.Deferred; import org.implab.gradle.variants.artifacts.ArtifactAssemblyRegistry; -import org.implab.gradle.variants.artifacts.OutgoingArtifactSlotSpec; import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; -import org.implab.gradle.variants.artifacts.VariantArtifactsContext; +import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; +import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBridge; import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; import org.implab.gradle.variants.core.Variant; import org.implab.gradle.variants.core.VariantsExtension; @@ -32,10 +32,14 @@ public abstract class VariantArtifactsPl var outgoing = new OutgoingRegistry(configurations, objects, providers); var assemblies = new ArtifactAssemblyRegistry(objects, tasks); - var deferred = new Deferred(); + var assembliesBridge = new ArtifactAssemblyBridge(assemblies); + + var deferred = new Deferred(); + + deferred.whenResolved(context -> context.all(assembliesBridge)); variantsExtension.whenFinalized(variants -> { - deferred.resolve(new VariantArtifactsRegistry(variants)); + }); var variantArtifacts = new VariantArtifactsExtension() { @@ -47,7 +51,7 @@ public abstract class VariantArtifactsPl } @Override - public void whenFinalized(Action action) { + public void whenFinalized(Action action) { deferred.whenResolved(registry -> action.execute(registry.variantsContext())); } diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblies.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblies.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblies.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblies.java @@ -3,6 +3,7 @@ package org.implab.gradle.variants.artif import java.util.Optional; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.gradle.api.Action; import org.gradle.api.InvalidUserDataException; /** @@ -29,5 +30,25 @@ public interface ArtifactAssemblies { .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered")); } + /** + * Регистрирует обработчик на конкретный слот. Если слот еще не зарегистрирован, + * то обработчик будет добавлен в очередь и будет вызван при регистрации слота. + * Порядок и точный момент вызова обработчиков не определен. + * + * @param slot Слот на который нужно зарегистрировать обработчик + * @param action Обработчик + */ + void when(ArtifactSlot slot, Action action); + + /** + * Регистрирует глобальный обработчик, который будет вызван для всех слотов. + * Обработчик будет вызван как для уже зарегистрированных слотов так и для тех, + * которые будут зарегистрированы в будущем. + * + * @param action Обработчик + */ + void all(Action action); + + /** Ищет зарегистрированный слот */ Optional find(ArtifactSlot slot); } diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblyRegistry.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblyRegistry.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblyRegistry.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/ArtifactAssemblyRegistry.java @@ -6,6 +6,7 @@ import java.util.Optional; import java.util.function.Function; import org.eclipse.jdt.annotation.NonNullByDefault; +import org.gradle.api.Action; import org.gradle.api.InvalidUserDataException; import org.gradle.api.Task; import org.gradle.api.file.Directory; @@ -17,12 +18,15 @@ import org.gradle.api.tasks.Copy; import org.gradle.api.tasks.TaskContainer; import org.gradle.api.tasks.TaskProvider; import org.gradle.language.base.plugins.LifecycleBasePlugin; +import org.implab.gradle.common.core.lang.Deferred; +import org.implab.gradle.internal.ReplayableQueue; @NonNullByDefault public class ArtifactAssemblyRegistry implements ArtifactAssemblies { private final ObjectFactory objects; private final TaskContainer tasks; - private final Map assemblies = new LinkedHashMap<>(); + private final Map> assembliesBySlots = new LinkedHashMap<>(); + private final ReplayableQueue assemblies = new ReplayableQueue<>(); public ArtifactAssemblyRegistry(ObjectFactory objects, TaskContainer tasks) { this.objects = objects; @@ -47,24 +51,45 @@ public class ArtifactAssemblyRegistry im public ArtifactAssembly register( ArtifactSlot slot, TaskProvider task, - Function> mapOutputDirectory) { - if (assemblies.containsKey(slot)) { + Function> mapOutputArtifact) { + + var deferred = getDeferred(slot); + if (deferred.resolved()) { throw new InvalidUserDataException("Artifact assembly '" + slot + "' is already registered"); } - var outputArtifact = task.flatMap(t -> mapOutputDirectory.apply(t)); + var outputArtifact = task.flatMap(mapOutputArtifact::apply); var output = objects.fileCollection() .from(outputArtifact) .builtBy(task); var assembly = new Assembly(outputArtifact, task, output); - assemblies.put(slot, assembly); + deferred.resolve(assembly); + assemblies.add(assembly); return assembly; } @Override public Optional find(ArtifactSlot slot) { - return Optional.ofNullable(assemblies.get(slot)); + // to prevent creation of map entries on lookup use the map directly + var deferred = assembliesBySlots.get(slot); + return deferred != null && deferred.resolved() + ? Optional.of(deferred.value()) + : Optional.empty(); + } + + @Override + public void when(ArtifactSlot slot, Action action) { + getDeferred(slot).whenResolved(action::execute); + } + + @Override + public void all(Action action) { + assemblies.forEach(action::execute); + } + + private Deferred getDeferred(ArtifactSlot slot) { + return assembliesBySlots.computeIfAbsent(slot, k -> new Deferred<>()); } static class Assembly implements ArtifactAssembly { diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingArtifactSlotSpec.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfigurationSlotSpec.java rename from variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingArtifactSlotSpec.java rename to variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfigurationSlotSpec.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingArtifactSlotSpec.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfigurationSlotSpec.java @@ -13,7 +13,7 @@ import org.implab.gradle.common.core.lan * publication tweaks should be applied here rather than through {@link OutgoingConfigurationSpec}, which * is limited to the root outgoing configuration of the variant. */ -public interface OutgoingArtifactSlotSpec { +public interface OutgoingConfigurationSlotSpec { /** * Returns the published slot identity. * diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfiguration.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariant.java rename from variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfiguration.java rename to variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariant.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingConfiguration.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariant.java @@ -1,13 +1,21 @@ package org.implab.gradle.variants.artifacts; +import org.gradle.api.Action; import org.gradle.api.NamedDomainObjectContainer; import org.gradle.api.NamedDomainObjectProvider; import org.gradle.api.artifacts.Configuration; import org.gradle.api.provider.Property; import org.implab.gradle.variants.core.Variant; -/** Описывает исходящую конфигурацию варианта */ -public interface OutgoingConfiguration { +/** + * Описывает исходящую конфигурацию варианта + * + * Задает связь между моделью вариантов и моделью конфигураций gradle через + * свойство {@link #getConfiguration()}. Также задает отдельную ось слотов + * публикации, но не задает правил связывания этих слотов с самой конфигурацией + * и их содержимым. Самый простой вариант это {@link ArtifactAssemblies}. + */ +public interface OutgoingVariant { /** * Исходный вариант для которого строится Outgoing конфигурация */ @@ -16,7 +24,11 @@ public interface OutgoingConfiguration { /** * Провайдер зарегистрированной конфигурации */ - NamedDomainObjectProvider getOutgoingConfiguration(); + NamedDomainObjectProvider getConfiguration(); + + default void configure(Action action) { + getConfiguration().configure(action); + } /** * Слоты конфигурации, данная коллекция живая, используется для diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsContext.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariantsContext.java rename from variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsContext.java rename to variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariantsContext.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsContext.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/OutgoingVariantsContext.java @@ -11,7 +11,7 @@ import org.implab.gradle.variants.core.V * Контекст работы с вариантами публикации, становится доступным после * финализации модели вариантов. Фактически является живой моделью */ -public interface VariantArtifactsContext { +public interface OutgoingVariantsContext { /** * Зафиксированное представление о вариантах на основе которого адаптеры могут @@ -24,11 +24,11 @@ public interface VariantArtifactsContext /** * Replayable hook для всех объявленных конфигураций */ - void all(Action action); + void all(Action action); - Optional findOutgoing(Variant variant); + Optional findOutgoing(Variant variant); - default OutgoingConfiguration requireOutgoing(Variant variant) { + default OutgoingVariant requireOutgoing(Variant variant) { return findOutgoing(variant) .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered")); } diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsExtension.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsExtension.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsExtension.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/VariantArtifactsExtension.java @@ -29,7 +29,7 @@ public interface VariantArtifactsExtensi * * @param action finalized-model callback */ - void whenFinalized(Action action); + void whenFinalized(Action action); default void whenFinalized(Closure closure) { whenFinalized(Closures.action(closure)); @@ -51,7 +51,7 @@ public interface VariantArtifactsExtensi * * @param action slot-level outgoing publication callback */ - void whenOutgoingSlot(Action action); + void whenOutgoingSlot(Action action); default void whenOutgoingSlot(Closure closure) { whenOutgoingSlot(Closures.action(closure)); diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/ArtifactAssemblyBridge.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/ArtifactAssemblyBridge.java new file mode 100644 --- /dev/null +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/ArtifactAssemblyBridge.java @@ -0,0 +1,54 @@ +package org.implab.gradle.variants.artifacts.internal; + +import org.eclipse.jdt.annotation.NonNullByDefault; +import org.gradle.api.Action; +import org.implab.gradle.variants.artifacts.ArtifactAssemblies; +import org.implab.gradle.variants.artifacts.ArtifactSlot; +import org.implab.gradle.variants.artifacts.OutgoingVariant; + +/** + * Связывает описание исходящих конфигураций gradle и сборку содержимого слотов + * из {@link ArtifactAssemblies} + */ +@NonNullByDefault +public class ArtifactAssemblyBridge implements Action { + + private final ArtifactAssemblies resolver; + + public ArtifactAssemblyBridge(ArtifactAssemblies resolver) { + this.resolver = resolver; + } + + @Override + public void execute(OutgoingVariant outgoingVariant) { + var slots = outgoingVariant.getSlots(); + var primarySlotProvider = outgoingVariant.getPrimarySlot(); + var variant = outgoingVariant.getVariant(); + + // связываем конфигурацию + outgoingVariant.configure(configuration -> { + var primarySlot = primarySlotProvider.get(); + var outgoing = configuration.getOutgoing(); + + // связываем основной вариант конфигурации + resolver.when( + new ArtifactSlot(variant, primarySlot), + assembly -> outgoing.artifact(assembly.getArtifact())); + + // для всех объявленных слотов + slots.all(slot -> { + // кроме основного + if (slot.equals(primarySlot)) + return; + + // связываем артефакты + resolver.when( + new ArtifactSlot(variant, slot), + assembly -> outgoing.getVariants() + .register(slot.getName()) + .configure(cv -> cv.artifact(assembly.getArtifact()))); + }); + }); + } + +} diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/DefaultOutgoingConfiguration.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/DefaultOutgoingConfiguration.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/DefaultOutgoingConfiguration.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/DefaultOutgoingConfiguration.java @@ -7,11 +7,11 @@ import org.gradle.api.model.ObjectFactor import org.gradle.api.provider.Property; import org.gradle.api.provider.ProviderFactory; import org.gradle.api.provider.Provider; -import org.implab.gradle.variants.artifacts.OutgoingConfiguration; +import org.implab.gradle.variants.artifacts.OutgoingVariant; import org.implab.gradle.variants.artifacts.Slot; import org.implab.gradle.variants.core.Variant; -class DefaultOutgoingConfiguration implements OutgoingConfiguration { +class DefaultOutgoingConfiguration implements OutgoingVariant { private final Variant variant; @@ -46,7 +46,7 @@ class DefaultOutgoingConfiguration imple } @Override - public NamedDomainObjectProvider getOutgoingConfiguration() { + public NamedDomainObjectProvider getConfiguration() { return configurationProvider; } diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/OutgoingRegistry.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/OutgoingRegistry.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/OutgoingRegistry.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/OutgoingRegistry.java @@ -1,17 +1,21 @@ package org.implab.gradle.variants.artifacts.internal; import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.function.Consumer; import org.gradle.api.artifacts.ConfigurationContainer; import org.gradle.api.model.ObjectFactory; import org.gradle.api.provider.ProviderFactory; -import org.implab.gradle.variants.artifacts.OutgoingConfiguration; +import org.implab.gradle.variants.artifacts.OutgoingVariant; import org.implab.gradle.variants.core.Variant; public class OutgoingRegistry { private final Map outgoingByVariant = new LinkedHashMap<>(); + private final List> hooks = new LinkedList<>(); private final ConfigurationContainer configurations; private final ObjectFactory objects; @@ -23,12 +27,19 @@ public class OutgoingRegistry { this.providers = providers; } - public Optional findOutgoing(Variant variant) { + public Optional findOutgoing(Variant variant) { return Optional.ofNullable(outgoingByVariant.get(variant)); } - public OutgoingConfiguration maybeCreate(Variant variant) { - return outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration); + public OutgoingVariant maybeCreate(Variant variant) { + var outgoing = outgoingByVariant.computeIfAbsent(variant, this::newOutgoingConfiguration); + hooks.forEach(hook -> hook.accept(outgoing)); + return outgoing; + } + + public void all(Consumer action) { + outgoingByVariant.values().forEach(action); + hooks.add(action); } private DefaultOutgoingConfiguration newOutgoingConfiguration(Variant variant) { @@ -37,7 +48,7 @@ public class OutgoingRegistry { return new DefaultOutgoingConfiguration(variant, configuration, objects, providers); } - String outgoingConfigurationName(Variant variant) { + private String outgoingConfigurationName(Variant variant) { return variant.getName() + "Elements"; } diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/SlotInputsAssembler.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/SlotInputsAssembler.java new file mode 100644 --- /dev/null +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/internal/SlotInputsAssembler.java @@ -0,0 +1,55 @@ +package org.implab.gradle.variants.artifacts.internal; + +import java.util.Collection; +import java.util.Set; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.implab.gradle.variants.artifacts.ArtifactSlot; +import org.implab.gradle.variants.core.VariantsView; +import org.implab.gradle.variants.sources.CompileUnit; +import org.implab.gradle.variants.sources.VariantSourcesContext; + +public class SlotInputsAssembler implements SlotContributionVisitor { + + VariantsView variantView; + VariantSourcesContext sources; + ArtifactSlot artifactSlot; + + Set seen; + + @Override + public void visit(DirectContribution contribution) { + contribute( + SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), + contribution.input()); + } + + @Override + public void visit(VariantOutputsContribution contribution) { + sources.getCompileUnits().getUnitsForVariant(artifactSlot.variant()).stream() + } + + @Override + public void visit(RoleOutputsContribution contribution) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'visit'"); + } + + @Override + public void visit(LayerOutputsContribution contribution) { + // TODO Auto-generated method stub + throw new UnsupportedOperationException("Unimplemented method 'visit'"); + } + + private void contribute(SlotInputKey key, Object input) { + + } + + private Function resolveOutputs(Collection outputs) { + return unit -> { + + }; + } + +} diff --git a/variants/src/main/java/org/implab/gradle/variants/artifacts/package-info.java b/variants/src/main/java/org/implab/gradle/variants/artifacts/package-info.java --- a/variants/src/main/java/org/implab/gradle/variants/artifacts/package-info.java +++ b/variants/src/main/java/org/implab/gradle/variants/artifacts/package-info.java @@ -34,8 +34,8 @@ * } * *

After finalization, slot identities can be observed through - * {@link org.implab.gradle.variants.artifacts.VariantArtifactsContext#getSlots()}, while slot bodies are + * {@link org.implab.gradle.variants.artifacts.OutgoingVariantsContext#getSlots()}, while slot bodies are * obtained on demand through - * {@link org.implab.gradle.variants.artifacts.VariantArtifactsContext#getAssemblies()}. + * {@link org.implab.gradle.variants.artifacts.OutgoingVariantsContext#getAssemblies()}. */ package org.implab.gradle.variants.artifacts;