##// END OF EJS Templates
WIP working on variants api
cin -
r46:f260d19f1118 default
parent child
Show More
@@ -0,0 +1,29
1 package org.implab.gradle.variants;
2
3 import org.gradle.api.Plugin;
4 import org.gradle.api.Project;
5 import org.implab.gradle.common.core.lang.Deferred;
6 import org.implab.gradle.variants.artifacts.VariantArtifactsContext;
7 import org.implab.gradle.variants.core.VariantsExtension;
8
9 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
10
11 @Override
12 public void apply(Project target) {
13 var extensions = target.getExtensions();
14
15 // Apply the main VariantsPlugin to ensure the core variant model is available.
16 target.getPlugins().apply(VariantsPlugin.class);
17 // Access the VariantsExtension to configure variant sources.
18 var variantsExtension = extensions.getByType(VariantsExtension.class);
19
20 var deferred = new Deferred<VariantArtifactsContext>();
21
22 variantsExtension.whenFinalized(variants -> {
23
24 });
25
26
27 }
28
29 }
@@ -0,0 +1,22
1 package org.implab.gradle.variants.artifacts;
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4
5 /**
6 * Resolves stateful slot assemblies from cheap slot identities.
7 *
8 * <p>The returned assembly is a materialized build-model handle. It may expose lazy Gradle providers, but
9 * it is no longer an identity object suitable for replayable discovery.
10 */
11 @NonNullByDefault
12 public interface ArtifactAssemblies {
13 /**
14 * Resolves the assembly for the given slot.
15 *
16 * <p>This call materializes the build-facing body of the slot from its identity.
17 *
18 * @param slot slot identity inside a variant outgoing contract
19 * @return assembly handle for the slot
20 */
21 ArtifactAssembly resolveSlot(ArtifactSlot slot);
22 }
@@ -0,0 +1,32
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Task;
4 import org.gradle.api.file.FileSystemLocation;
5 import org.gradle.api.provider.Provider;
6 import org.gradle.api.tasks.TaskProvider;
7
8 /**
9 * Materialized body of an {@link ArtifactSlot}.
10 *
11 * <p>An assembly is a stateful build object obtained on demand from
12 * {@link ArtifactAssemblies#resolveSlot(ArtifactSlot)}. It describes how the slot artifact is produced and
13 * where that single published artifact will appear.
14 */
15 public interface ArtifactAssembly {
16
17 /**
18 * Returns the published artifact produced for the slot.
19 *
20 * <p>A slot is expected to produce exactly one artifact represented by one file or one directory.
21 *
22 * @return provider of the produced artifact location
23 */
24 Provider<? extends FileSystemLocation> getArtifact();
25
26 /**
27 * Returns the task that assembles the slot artifact.
28 *
29 * @return provider of the assembly task
30 */
31 TaskProvider<? extends Task> getAssemblyTask();
32 }
@@ -0,0 +1,60
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Action;
4 import groovy.lang.Closure;
5 import org.implab.gradle.common.core.lang.Closures;
6
7 /**
8 * DSL model describing how a slot artifact is assembled.
9 *
10 * <p>Selection rules declared here may refer to internal build topology such as roles, layers or units.
11 * Those selectors influence slot assembly only and do not become part of published artifact identity.
12 *
13 * <p>Regardless of the number of declared inputs, a slot is expected to materialize to a single published
14 * artifact.
15 */
16 public interface ArtifactAssemblySpec {
17 /**
18 * Contributes direct input material to the slot assembly.
19 *
20 * <p>The resulting slot still represents one published artifact.
21 *
22 * @param artifact direct input notation understood by the implementation
23 */
24 void from(Object artifact);
25
26 /**
27 * Selects outputs from the whole variant scope.
28 *
29 * @param action output selection rule
30 */
31 void fromVariant(Action<? super OutputSelectionSpec> action);
32
33 default void fromVariant(Closure<?> closure) {
34 fromVariant(Closures.action(closure));
35 }
36
37 /**
38 * Selects outputs from a role inside the current variant.
39 *
40 * @param roleName role name used only for assembly-time selection
41 * @param action output selection rule
42 */
43 void fromRole(String roleName, Action<? super OutputSelectionSpec> action);
44
45 default void fromRole(String roleName, Closure<?> closure) {
46 fromRole(roleName, Closures.action(closure));
47 }
48
49 /**
50 * Selects outputs from a layer inside the current variant.
51 *
52 * @param layerName layer name used only for assembly-time selection
53 * @param action output selection rule
54 */
55 void fromLayer(String layerName, Action<? super OutputSelectionSpec> action);
56
57 default void fromLayer(String layerName, Closure<?> closure) {
58 fromLayer(layerName, Closures.action(closure));
59 }
60 }
@@ -0,0 +1,14
1 package org.implab.gradle.variants.artifacts;
2
3 import org.implab.gradle.variants.core.Variant;
4
5 /**
6 * Identity of a published artifact slot inside a variant outgoing contract.
7 *
8 * <p>This is a cheap immutable identity object suitable for discovery and selection. Heavy build state is
9 * obtained separately through {@link ArtifactAssemblies}.
10 *
11 * @param variant variant owning the outgoing contract
12 * @param slot slot identity inside the variant
13 */
14 public record ArtifactSlot(Variant variant, Slot slot) {}
@@ -0,0 +1,48
1 package org.implab.gradle.variants.artifacts;
2
3 import java.util.Optional;
4 import java.util.Set;
5
6 import org.implab.gradle.variants.core.Variant;
7
8 /**
9 * Finalized view of artifact slot identities.
10 *
11 * <p>This view exposes only cheap slot identities. Assemblies and publication state are resolved
12 * separately.
13 */
14 public interface ArtifactSlotsView {
15 /**
16 * Returns all declared slot identities.
17 *
18 * @return all slots known to the finalized model
19 */
20 Set<ArtifactSlot> getSlots();
21
22 /**
23 * Returns all slots declared for the given variant.
24 *
25 * @param variant variant identity
26 * @return slots declared for the variant
27 */
28 Set<ArtifactSlot> getSlotsForVariant(Variant variant);
29
30 /**
31 * Finds a slot by typed identities.
32 *
33 * @param variant variant identity
34 * @param slot slot identity inside the variant
35 * @return matching slot when present
36 */
37 Optional<ArtifactSlot> findSlot(Variant variant, Slot slot);
38
39 /**
40 * Requires a slot by typed identities.
41 *
42 * @param variant variant identity
43 * @param slot slot identity inside the variant
44 * @return matching slot
45 */
46 ArtifactSlot requireSlot(Variant variant, Slot slot);
47
48 }
@@ -0,0 +1,59
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Action;
4 import org.gradle.api.Task;
5 import org.gradle.api.attributes.AttributeContainer;
6 import groovy.lang.Closure;
7 import org.implab.gradle.common.core.lang.Closures;
8
9 /**
10 * Materialized outgoing publication state of a single slot.
11 *
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 OutgoingVariantSpec}, which
14 * is limited to the root outgoing configuration of the variant.
15 */
16 public interface OutgoingArtifactSlotSpec {
17 /**
18 * Returns the published slot identity.
19 *
20 * @return slot identity
21 */
22 ArtifactSlot getArtifactSlot();
23
24 /**
25 * Returns the assembly backing the published slot.
26 *
27 * @return slot assembly
28 */
29 ArtifactAssembly getAssembly();
30
31 /**
32 * Returns whether this slot is the primary outgoing artifact set of the variant.
33 *
34 * @return {@code true} for the primary slot
35 */
36 boolean isPrimary();
37
38 /**
39 * Configures the task producing the slot artifact.
40 *
41 * @param action task configuration action
42 */
43 void assemblyTask(Action<? super Task> action);
44
45 default void assemblyTask(Closure<?> closure) {
46 assemblyTask(Closures.action(closure));
47 }
48
49 /**
50 * Configures attributes of this slot publication.
51 *
52 * @param action artifact attribute configuration action
53 */
54 void artifactAttributes(Action<? super AttributeContainer> action);
55
56 default void artifactAttributes(Closure<?> closure) {
57 artifactAttributes(Closures.action(closure));
58 }
59 }
@@ -0,0 +1,43
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Action;
4 import org.gradle.api.artifacts.Configuration;
5 import org.implab.gradle.common.core.lang.Closures;
6 import org.implab.gradle.variants.core.Variant;
7
8 import groovy.lang.Closure;
9
10 /**
11 * Materialized root outgoing configuration of a variant.
12 *
13 * <p>This is a variant-level publication hook. Slot-specific publication state is exposed separately via
14 * {@link OutgoingArtifactSlotSpec}.
15 */
16 public interface OutgoingVariantSpec {
17 /**
18 * Returns the variant whose outgoing configuration is represented here.
19 *
20 * @return variant identity
21 */
22 Variant getVariant();
23
24 /**
25 * Returns the root consumable outgoing configuration of the variant.
26 *
27 * @return outgoing configuration
28 */
29 Configuration getConfiguration();
30
31 /**
32 * Applies a configuration action to the root outgoing configuration.
33 *
34 * @param action configuration action
35 */
36 default void configuration(Action<? super Configuration> action) {
37 action.execute(getConfiguration());
38 }
39
40 default void configuration(Closure<?> closure) {
41 configuration(Closures.action(closure));
42 }
43 }
@@ -0,0 +1,24
1 package org.implab.gradle.variants.artifacts;
2
3 /**
4 * DSL model for selecting named outputs from a chosen source scope.
5 *
6 * <p>The selected outputs are inputs to slot assembly. They do not create additional outgoing slots or
7 * affect slot identity.
8 */
9 public interface OutputSelectionSpec {
10 /**
11 * Selects one named output.
12 *
13 * @param name output name
14 */
15 void output(String name);
16
17 /**
18 * Selects several named outputs.
19 *
20 * @param name first output name
21 * @param extra additional output names
22 */
23 void output(String name, String... extra);
24 }
@@ -0,0 +1,12
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Named;
4
5 /**
6 * Named identity of a slot inside a variant outgoing contract.
7 *
8 * <p>A slot does not identify a published artifact on its own. Combine it with a variant to obtain an
9 * {@link ArtifactSlot}.
10 */
11 public interface Slot extends Named {
12 }
@@ -0,0 +1,28
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.NamedDomainObjectContainer;
4 import org.gradle.api.NamedDomainObjectProvider;
5 import org.gradle.api.artifacts.Configuration;
6 import org.implab.gradle.variants.core.Variant;
7
8 /** ΠžΠΏΠΈΡΡ‹Π²Π°Π΅Ρ‚ конфигурация Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π° исходящСй ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ */
9 public interface VariantArtifactsConfiguration {
10 /**
11 * Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ для ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ строится Outgoing конфигурация
12 */
13 Variant getVariant();
14
15 /**
16 * ΠŸΡ€ΠΎΠ²Π°ΠΉΠ΄Π΅Ρ€ зарСгистрированной ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ
17 */
18 NamedDomainObjectProvider<Configuration> getOutgoingConfiguration();
19
20 /**
21 * Π‘Π»ΠΎΡ‚Ρ‹ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ, данная коллСкция Тивая, ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для
22 * получСния ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΠΈ ΠΎΠ± ΠΎΠ±ΡŠΡΠ²Π»Π΅Π½Π½Ρ‹Ρ… слотах, Π½ΠΎ эти слоты Π½Π΅
23 * обязаны Π±Ρ‹Ρ‚ΡŒ сконфигурированы, Ρ‚.Π΅. это Ρ‚ΠΎΠ»ΡŒΠΊΠΎ Identity.
24 *
25 * @see {@link ArtifactSlot}
26 */
27 NamedDomainObjectContainer<Slot> getSlots();
28 }
@@ -0,0 +1,27
1 package org.implab.gradle.variants.artifacts;
2
3 import java.util.Optional;
4
5 import org.gradle.api.Action;
6 import org.implab.gradle.variants.core.Variant;
7 import org.implab.gradle.variants.core.VariantsView;
8
9 /**
10 * ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ Ρ€Π°Π±ΠΎΡ‚Ρ‹ с Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°ΠΌΠΈ ΠΏΡƒΠ±Π»ΠΈΠΊΠ°Ρ†ΠΈΠΈ, становится доступным послС
11 * Ρ„ΠΈΠ½Π°Π»ΠΈΠ·Π°Ρ†ΠΈΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΎΠ². ЀактичСски являСтся ΠΆΠΈΠ²ΠΎΠΉ модСлью
12 */
13 public interface VariantArtifactsContext {
14
15 /**
16 * ЗафиксированноС прСдставлСниС ΠΎ Π²Π°Ρ€ΠΈΠ°Π½Ρ‚Π°Ρ… Π½Π° основС ΠΊΠΎΡ‚ΠΎΡ€ΠΎΠ³ΠΎ Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Ρ‹ ΠΌΠΎΠ³ΡƒΡ‚
17 * ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒ Π°Ρ€Ρ‚Π΅Ρ„Π°ΠΊΡ‚Ρ‹ ΠΈ исходящиС ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€Π°Ρ†ΠΈΠΈ
18 */
19 VariantsView getVariants();
20
21 void all(Action<? super VariantArtifactsConfiguration> action);
22
23 Optional<VariantArtifactsConfiguration> findArtifacts(Variant variant);
24
25 VariantArtifactsConfiguration requireArtifacts(Variant variant);
26
27 }
@@ -0,0 +1,59
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Action;
4 import org.implab.gradle.common.core.lang.Closures;
5
6 import groovy.lang.Closure;
7
8 /**
9 * Project-level DSL entry point for declaring outgoing artifacts per variant.
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.
13 */
14 public interface VariantArtifactsExtension {
15 /**
16 * Configures artifact slots of the named variant.
17 *
18 * @param variantName variant name
19 * @param action variant artifact declaration
20 */
21 void variant(String variantName, Action<? super VariantArtifactsSpec> action);
22
23 default void variant(String variantName, Closure<?> closure) {
24 variant(variantName, Closures.action(closure));
25 }
26
27 /**
28 * Registers a callback invoked with the finalized artifact model.
29 *
30 * @param action finalized-model callback
31 */
32 void whenFinalized(Action<? super VariantArtifactsContext> action);
33
34 default void whenFinalized(Closure<?> closure) {
35 whenFinalized(Closures.action(closure));
36 }
37
38 /**
39 * Registers a callback invoked for each materialized root outgoing configuration.
40 *
41 * @param action variant-level outgoing configuration callback
42 */
43 void whenOutgoingVariant(Action<? super OutgoingVariantSpec> action);
44
45 default void whenOutgoingVariant(Closure<?> closure) {
46 whenOutgoingVariant(Closures.action(closure));
47 }
48
49 /**
50 * Registers a callback invoked for each materialized outgoing slot publication.
51 *
52 * @param action slot-level outgoing publication callback
53 */
54 void whenOutgoingSlot(Action<? super OutgoingArtifactSlotSpec> action);
55
56 default void whenOutgoingSlot(Closure<?> closure) {
57 whenOutgoingSlot(Closures.action(closure));
58 }
59 }
@@ -0,0 +1,40
1 package org.implab.gradle.variants.artifacts;
2
3 import org.gradle.api.Action;
4 import groovy.lang.Closure;
5 import org.implab.gradle.common.core.lang.Closures;
6
7 /**
8 * DSL model for declaring slots of a single variant.
9 *
10 * <p>The surrounding variant defines the external outgoing contract. Slots declared here define artifact
11 * sets within that contract. If a variant exposes more than one slot, one of them is expected to be the
12 * primary slot.
13 */
14 public interface VariantArtifactsSpec {
15 /**
16 * Declares a non-primary slot of the current variant.
17 *
18 * @param name slot name
19 * @param action slot declaration
20 * @return slot identity
21 */
22 Slot slot(String name, Action<? super ArtifactAssemblySpec> action);
23
24 default Slot slot(String name, Closure<?> closure) {
25 return slot(name, Closures.action(closure));
26 }
27
28 /**
29 * Declares the primary slot of the current variant.
30 *
31 * @param name slot name
32 * @param action slot declaration
33 * @return slot identity
34 */
35 Slot primarySlot(String name, Action<? super ArtifactAssemblySpec> action);
36
37 default Slot primarySlot(String name, Closure<?> closure) {
38 return primarySlot(name, Closures.action(closure));
39 }
40 }
@@ -0,0 +1,37
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.VariantArtifactsConfiguration;
7 import org.implab.gradle.variants.artifacts.VariantArtifactsContext;
8 import org.implab.gradle.variants.core.Variant;
9 import org.implab.gradle.variants.core.VariantsView;
10
11 public class DefaultVariantArtifactsContext implements VariantArtifactsContext {
12
13 @Override
14 public VariantsView getVariants() {
15 // TODO Auto-generated method stub
16 throw new UnsupportedOperationException("Unimplemented method 'getVariants'");
17 }
18
19 @Override
20 public void all(Action<? super VariantArtifactsConfiguration> action) {
21 // TODO Auto-generated method stub
22 throw new UnsupportedOperationException("Unimplemented method 'all'");
23 }
24
25 @Override
26 public Optional<VariantArtifactsConfiguration> findArtifacts(Variant variant) {
27 // TODO Auto-generated method stub
28 throw new UnsupportedOperationException("Unimplemented method 'findArtifacts'");
29 }
30
31 @Override
32 public VariantArtifactsConfiguration requireArtifacts(Variant variant) {
33 // TODO Auto-generated method stub
34 throw new UnsupportedOperationException("Unimplemented method 'requireArtifacts'");
35 }
36
37 }
@@ -0,0 +1,11
1 package org.implab.gradle.variants.artifacts.internal;
2
3 import org.gradle.api.Action;
4 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
5 import org.implab.gradle.variants.core.Variant;
6
7 public class VariantArtifactsRegistry {
8 public void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action) {
9
10 }
11 }
@@ -0,0 +1,41
1 /**
2 * Variant-scoped outgoing artifacts.
3 *
4 * <p>This package models the external artifact contract of a project in terms of variant-local slots.
5 * A variant represents one outgoing contract, while a slot represents one artifact set inside that
6 * contract.
7 *
8 * <p>The model intentionally separates cheap identity objects from stateful build objects:
9 *
10 * <ul>
11 * <li>{@link org.implab.gradle.variants.artifacts.ArtifactSlot} identifies a published slot inside a
12 * variant;</li>
13 * <li>{@link org.implab.gradle.variants.artifacts.ArtifactAssembly} is the lazily materialized body of
14 * that slot.</li>
15 * </ul>
16 *
17 * <p>Each slot is expected to materialize to exactly one published artifact: either one file or one
18 * directory. Internal build topology such as roles may participate in slot assembly rules, but does not
19 * belong to external artifact identity.
20 *
21 * <p>Typical usage:
22 *
23 * <pre>{@code
24 * variantArtifacts {
25 * variant("browser") {
26 * primarySlot("runtime") {
27 * fromRole("main") { output("js") }
28 * }
29 * slot("sources") {
30 * fromLayer("main") { output("sources") }
31 * }
32 * }
33 * }
34 * }</pre>
35 *
36 * <p>After finalization, slot identities can be observed through
37 * {@link org.implab.gradle.variants.artifacts.VariantArtifactsContext#getSlots()}, while slot bodies are
38 * obtained on demand through
39 * {@link org.implab.gradle.variants.artifacts.VariantArtifactsContext#getAssemblies()}.
40 */
41 package org.implab.gradle.variants.artifacts;
@@ -1,52 +1,54
1 # AGENTS.md
1 # AGENTS.md
2
2
3 ## ΠŸΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹Π΅ договорСнности
3 ## ΠŸΡ€ΠΎΠ΅ΠΊΡ‚Π½Ρ‹Π΅ договорСнности
4
4
5 ### ΠŸΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ΅ API Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ
5 ### ΠŸΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠ΅ API Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊ
6
6
7 - ΠŸΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»Π΅Π½ `non-null` ΠΏΠΎΠ΄Ρ…ΠΎΠ΄.
7 - ΠŸΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»Π΅Π½ `non-null` ΠΏΠΎΠ΄Ρ…ΠΎΠ΄.
8 - Π’Π°ΠΌ, Π³Π΄Π΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΆΠΈΠ²Π΅Ρ‚ Π² Gradle Provider API, возвращаСтся `Provider<T>` (Π½Π΅ `null`).
8 - Π’Π°ΠΌ, Π³Π΄Π΅ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΆΠΈΠ²Π΅Ρ‚ Π² Gradle Provider API, возвращаСтся `Provider<T>` (Π½Π΅ `null`).
9 - Π’Π°ΠΌ, Π³Π΄Π΅ lookup синхронный, возвращаСтся `Optional<T>` (Π½Π΅ `null`).
9 - Π’Π°ΠΌ, Π³Π΄Π΅ lookup синхронный, возвращаСтся `Optional<T>` (Π½Π΅ `null`).
10 - `find*` рассматриваСтся ΠΊΠ°ΠΊ синоним legacy `get*` (поиск Π±Π΅Π· `fail-fast`).
10 - `find*` рассматриваСтся ΠΊΠ°ΠΊ синоним legacy `get*` (поиск Π±Π΅Π· `fail-fast`).
11 - `require*` это `find*` + `fail-fast` с понятной ошибкой Π² мСстС Π²Ρ‹Π·ΠΎΠ²Π°.
11 - `require*` это `find*` + `fail-fast` с понятной ошибкой Π² мСстС Π²Ρ‹Π·ΠΎΠ²Π°.
12 - Для Π½ΠΎΠ²ΠΎΠ³ΠΎ API ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ Ρ„ΠΎΡ€ΠΌΡ‹ `find/require`; Π½ΠΎΠ²Ρ‹Π΅ `get*` ΠΏΠΎ возмоТности Π½Π΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ.
12 - Для Π½ΠΎΠ²ΠΎΠ³ΠΎ API ΠΏΡ€Π΅Π΄ΠΏΠΎΡ‡Ρ‚ΠΈΡ‚Π΅Π»ΡŒΠ½Ρ‹ Ρ„ΠΎΡ€ΠΌΡ‹ `find/require`; Π½ΠΎΠ²Ρ‹Π΅ `get*` ΠΏΠΎ возмоТности Π½Π΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ.
13 - Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡΡ‹ ΠΈ классы, ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‰ΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ DSL Π΄ΠΎΠ»ΠΆΠ½Ρ‹ ΠΈΠΌΠ΅Ρ‚ΡŒ суффикс `Spec` Ρƒ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΎΠΏΠΈΡΡ‹Π²Π°ΡŽΡ‰ΠΈΡ… ΡƒΡ€ΠΎΠ²Π΅Π½ΡŒ сСрвисов ΠΈ состояниС сцСнария сборки Ρ‚Π°ΠΊΠΎΠ³ΠΎ суффикса Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ.
14 - МодСль Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ Π½Π° ΡƒΡ€ΠΎΠ²Π½Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π΄ΠΎΠ»ΠΆΠ½Π° ΠΈΠΌΠ΅Ρ‚ΡŒ суффикс `Extension`.
13
15
14 ### ДокумСнтация
16 ### ДокумСнтация
15
17
16 - Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ Π½Π° английском языкС
18 - Π΄ΠΎΠΊΡƒΠΌΠ΅Π½Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±Ρ‹Ρ‚ΡŒ Π½Π° английском языкС
17 - ΠΊ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠΌΡƒ API
19 - ΠΊ ΠΏΡƒΠ±Π»ΠΈΡ‡Π½ΠΎΠΌΡƒ API
18 - описаниС Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΎΡ‚Ρ€Π°ΠΆΠ°Ρ‚ΡŒ Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Π³Π΄Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΈ ΠΊΠ°ΠΊΠΎΠ΅ влияниС ΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π½Π° ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅ части ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹
20 - описаниС Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΎΡ‚Ρ€Π°ΠΆΠ°Ρ‚ΡŒ Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅, Π³Π΄Π΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΈ ΠΊΠ°ΠΊΠΎΠ΅ влияниС ΠΎΠΊΠ°Π·Ρ‹Π²Π°Π΅Ρ‚ Π½Π° ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅ части ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΡ‹
19 - Π΄Π°Π²Π°Ρ‚ΡŒ нСбольшоС описаниС ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΊΡ€Π°Ρ‚ΠΊΠΈΠ΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹
21 - Π΄Π°Π²Π°Ρ‚ΡŒ нСбольшоС описаниС ΠΊΠΎΠ½Ρ†Π΅ΠΏΡ†ΠΈΠΈ, Π° Ρ‚Π°ΠΊΠΆΠ΅ ΠΊΡ€Π°Ρ‚ΠΊΠΈΠ΅ ΠΏΡ€ΠΈΠΌΠ΅Ρ€Ρ‹
20 - ΠΊ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠΌΡƒ API достаточно Π΄Π°Π²Π°Ρ‚ΡŒ ΠΊΡ€Π°Ρ‚ΠΊΡƒΡŽ справку ΠΎ Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΈ ΠΈ использовании
22 - ΠΊ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΎΠΌΡƒ API достаточно Π΄Π°Π²Π°Ρ‚ΡŒ ΠΊΡ€Π°Ρ‚ΠΊΡƒΡŽ справку ΠΎ Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈΠΈ ΠΈ использовании
21 - Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ² Π² ΠΊΠΎΠ΄Π΅ ΡΠΎΠΏΡ€ΠΎΠ²ΠΎΠΆΠ΄Π°Ρ‚ΡŒ коммСнтариями с пояснСниями, Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΏΠΎΡΡΠ½ΡΡ‚ΡŒ Π½Π΅ трСбуСтся.
23 - Ρ€Π΅Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡŽ Π°Π»Π³ΠΎΡ€ΠΈΡ‚ΠΌΠΎΠ² Π² ΠΊΠΎΠ΄Π΅ ΡΠΎΠΏΡ€ΠΎΠ²ΠΎΠΆΠ΄Π°Ρ‚ΡŒ коммСнтариями с пояснСниями, Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»ΡŒΠ½Ρ‹Π΅ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΏΠΎΡΡΠ½ΡΡ‚ΡŒ Π½Π΅ трСбуСтся.
22 - докумСнтация Π΄ΠΎΠ»ΠΆΠ½Π° Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ согласно трСбованиям ΠΏΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Ρ‚ΠΈΠΏΠ° javadoc, jsdoc ΠΈ Ρ‚.ΠΏ., Π² зависимости ΠΎΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Ρ… Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ языках ΠΈ инструмСнтах.
24 - докумСнтация Π΄ΠΎΠ»ΠΆΠ½Π° Ρ„ΠΎΡ€ΠΌΠΈΡ€ΠΎΠ²Π°Ρ‚ΡŒΡΡ согласно трСбованиям ΠΏΠΎ Ρ„ΠΎΡ€ΠΌΠ°Ρ‚ΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡŽ Ρ‚ΠΈΠΏΠ° javadoc, jsdoc ΠΈ Ρ‚.ΠΏ., Π² зависимости ΠΎΡ‚ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌΡ‹Ρ… Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π΅ языках ΠΈ инструмСнтах.
23
25
24 ## Identity-first modeling
26 ## Identity-first modeling
25
27
26 Prefer an **identity-first** split between:
28 Prefer an **identity-first** split between:
27
29
28 - **identity objects** used for discovery and selection
30 - **identity objects** used for discovery and selection
29 - **stateful/materialized objects** obtained through separate API calls
31 - **stateful/materialized objects** obtained through separate API calls
30
32
31 ### Rules
33 ### Rules
32
34
33 - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**.
35 - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**.
34 - Identity objects must be:
36 - Identity objects must be:
35 - cheap to create
37 - cheap to create
36 - effectively immutable
38 - effectively immutable
37 - limited to identity and cheap selection metadata
39 - limited to identity and cheap selection metadata
38 - Heavy, computed, provider-based, or runtime-bound state must be resolved separately.
40 - Heavy, computed, provider-based, or runtime-bound state must be resolved separately.
39 - Aggregate content should be accessed through dedicated lookup/materialization APIs.
41 - Aggregate content should be accessed through dedicated lookup/materialization APIs.
40 - Eager observation of identity is acceptable.
42 - Eager observation of identity is acceptable.
41 - Eager observation of computed state is not.
43 - Eager observation of computed state is not.
42
44
43 ### Do not
45 ### Do not
44
46
45 - Do not store heavy computed state inside identity objects.
47 - Do not store heavy computed state inside identity objects.
46 - Do not store runtime references to foreign domains inside identity objects.
48 - Do not store runtime references to foreign domains inside identity objects.
47 - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient.
49 - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient.
48
50
49 ### Rule of thumb
51 ### Rule of thumb
50
52
51 **Identity objects contain selection metadata.
53 **Identity objects contain selection metadata.
52 Aggregate content is obtained separately, on demand.**
54 Aggregate content is obtained separately, on demand.**
@@ -1,78 +1,78
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import java.util.HashMap;
3 import java.util.HashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Objects;
5 import java.util.Objects;
6 import java.util.Optional;
6 import java.util.Optional;
7 import java.util.Set;
7 import java.util.Set;
8 import java.util.stream.Collectors;
8 import java.util.stream.Collectors;
9
9
10 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.eclipse.jdt.annotation.NonNullByDefault;
11 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
12 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Role;
13 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.Variant;
14 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
16
16
17 @NonNullByDefault
17 @NonNullByDefault
18 public final class CompileUnitsView {
18 public final class CompileUnitsView {
19
19
20 private final VariantsView variants;
20 private final VariantsView variants;
21 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
21 private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>();
22
22
23 private CompileUnitsView(VariantsView variants) {
23 private CompileUnitsView(VariantsView variants) {
24 this.variants = variants;
24 this.variants = variants;
25 }
25 }
26
26
27 public Set<CompileUnit> getUnits() {
27 public Set<CompileUnit> getUnits() {
28 return variants.getEntries().stream()
28 return variants.getEntries().stream()
29 .map(CompileUnit::of)
29 .map(CompileUnit::of)
30 .collect(Collectors.toUnmodifiableSet());
30 .collect(Collectors.toUnmodifiableSet());
31 }
31 }
32
32
33 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
33 public Set<CompileUnit> getUnitsForVariant(Variant variant) {
34 Objects.requireNonNull(variant, "Variant can't be null");
34 Objects.requireNonNull(variant, "Variant can't be null");
35
35
36 return unitsByVariant.computeIfAbsent(variant, key -> variants
36 return unitsByVariant.computeIfAbsent(variant, key -> variants
37 .getEntriesForVariant(variant).stream()
37 .getEntriesForVariant(variant).stream()
38 .map(CompileUnit::of)
38 .map(CompileUnit::of)
39 .collect(Collectors.toUnmodifiableSet()));
39 .collect(Collectors.toUnmodifiableSet()));
40 }
40 }
41
41
42 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
42 public Optional<CompileUnit> findUnit(Variant variant, Layer layer) {
43 Objects.requireNonNull(variant, "Variant can't be null");
43 Objects.requireNonNull(variant, "Variant can't be null");
44 Objects.requireNonNull(layer, "Layer can't be null");
44 Objects.requireNonNull(layer, "Layer can't be null");
45
45
46 return getUnitsForVariant(variant).stream()
46 return getUnitsForVariant(variant).stream()
47 .filter(u -> u.layer().equals(layer))
47 .filter(u -> u.layer().equals(layer))
48 .findAny();
48 .findAny();
49 }
49 }
50
50
51 public boolean contains(Variant variant, Layer layer) {
51 public boolean contains(Variant variant, Layer layer) {
52 return findUnit(variant, layer).isPresent();
52 return findUnit(variant, layer).isPresent();
53 }
53 }
54
54
55 /**
55 /**
56 * In which logical roles this compile unit participates.
56 * In which logical roles this compile unit participates.
57 */
57 */
58
58
59 public Set<Role> getRoles(CompileUnit unit) {
59 public Set<Role> getRoles(CompileUnit unit) {
60 Objects.requireNonNull(unit, "Compile unit can't be null");
60 Objects.requireNonNull(unit, "Compile unit can't be null");
61 return variants.getEntriesForVariant(unit.variant()).stream()
61 return variants.getEntriesForVariant(unit.variant()).stream()
62 .filter(entry -> entry.layer().equals(unit.layer()))
62 .filter(entry -> entry.layer().equals(unit.layer()))
63 .map(VariantRoleLayer::role)
63 .map(VariantRoleLayer::role)
64 .collect(Collectors.toUnmodifiableSet());
64 .collect(Collectors.toUnmodifiableSet());
65 }
65 }
66
66
67 public CompileUnit getUnit(Variant variant, Layer layer) {
67 public CompileUnit requireUnit(Variant variant, Layer layer) {
68 return findUnit(variant, layer)
68 return findUnit(variant, layer)
69 .orElseThrow(() -> new IllegalArgumentException(
69 .orElseThrow(() -> new IllegalArgumentException(
70 "Compile unit for variant '" + variant.getName()
70 "Compile unit for variant '" + variant.getName()
71 + "' and layer '" + layer.getName() + "' not found"));
71 + "' and layer '" + layer.getName() + "' not found"));
72 }
72 }
73
73
74 public static CompileUnitsView of(VariantsView variantsView) {
74 public static CompileUnitsView of(VariantsView variantsView) {
75 Objects.requireNonNull(variantsView, "variantsView can't be null");
75 Objects.requireNonNull(variantsView, "variantsView can't be null");
76 return new CompileUnitsView(variantsView);
76 return new CompileUnitsView(variantsView);
77 }
77 }
78 } No newline at end of file
78 }
@@ -1,431 +1,431
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import static org.junit.jupiter.api.Assertions.assertTrue;
3 import static org.junit.jupiter.api.Assertions.assertTrue;
4
4
5 import org.gradle.testkit.runner.BuildResult;
5 import org.gradle.testkit.runner.BuildResult;
6 import org.junit.jupiter.api.Test;
6 import org.junit.jupiter.api.Test;
7
7
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
9
9
10 @Test
10 @Test
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
12 writeSettings("variant-sources-derived-views");
12 writeSettings("variant-sources-derived-views");
13 writeBuildFile("""
13 writeBuildFile("""
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
15
15
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
17 variantsExt.layers.create('main')
17 variantsExt.layers.create('main')
18 variantsExt.layers.create('test')
18 variantsExt.layers.create('test')
19 variantsExt.roles.create('production')
19 variantsExt.roles.create('production')
20 variantsExt.roles.create('test')
20 variantsExt.roles.create('test')
21
21
22 variantsExt.variant('browser') {
22 variantsExt.variant('browser') {
23 role('production') { layers('main') }
23 role('production') { layers('main') }
24 role('test') { layers('main', 'test') }
24 role('test') { layers('main', 'test') }
25 }
25 }
26
26
27 def lines = []
27 def lines = []
28
28
29 variantSources.whenFinalized { ctx ->
29 variantSources.whenFinalized { ctx ->
30 lines << "units=" + ctx.compileUnits.units
30 lines << "units=" + ctx.compileUnits.units
31 .collect { "${it.variant().name}:${it.layer().name}" }
31 .collect { "${it.variant().name}:${it.layer().name}" }
32 .sort()
32 .sort()
33 .join(',')
33 .join(',')
34
34
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
38 def projection = ctx.roleProjections.getProjection(browser, production)
38 def projection = ctx.roleProjections.getProjection(browser, production)
39 def unit = ctx.compileUnits.getUnit(browser, mainLayer)
39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
40
40
41 def left = ctx.sourceSets.getSourceSet(unit)
41 def left = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
43
43
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 .collect { it.layer().name }
45 .collect { it.layer().name }
46 .sort()
46 .sort()
47 .join(',')
47 .join(',')
48 lines << "mainSourceSet=" + left.name
48 lines << "mainSourceSet=" + left.name
49 lines << "sameProvider=" + left.is(right)
49 lines << "sameProvider=" + left.is(right)
50 }
50 }
51
51
52 afterEvaluate {
52 afterEvaluate {
53 variantSources.whenFinalized { ctx ->
53 variantSources.whenFinalized { ctx ->
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
55 }
55 }
56 }
56 }
57
57
58 tasks.register('probe') {
58 tasks.register('probe') {
59 doLast {
59 doLast {
60 lines.each { println(it) }
60 lines.each { println(it) }
61 }
61 }
62 }
62 }
63 """);
63 """);
64
64
65 BuildResult result = runner("probe").build();
65 BuildResult result = runner("probe").build();
66
66
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
72 }
72 }
73
73
74 @Test
74 @Test
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
76 writeSettings("variant-sources-precedence");
76 writeSettings("variant-sources-precedence");
77 writeBuildFile("""
77 writeBuildFile("""
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
79
79
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
81 variantsExt.layers.create('main')
81 variantsExt.layers.create('main')
82 variantsExt.layers.create('test')
82 variantsExt.layers.create('test')
83 variantsExt.roles.create('production')
83 variantsExt.roles.create('production')
84 variantsExt.roles.create('test')
84 variantsExt.roles.create('test')
85
85
86 variantsExt.variant('browser') {
86 variantsExt.variant('browser') {
87 role('production') { layers('main') }
87 role('production') { layers('main') }
88 role('test') { layers('main', 'test') }
88 role('test') { layers('main', 'test') }
89 }
89 }
90
90
91 variantsExt.variant('node') {
91 variantsExt.variant('node') {
92 role('production') { layers('main') }
92 role('production') { layers('main') }
93 }
93 }
94
94
95 def events = []
95 def events = []
96
96
97 variantSources {
97 variantSources {
98 variant('browser') {
98 variant('browser') {
99 events << "variant:" + name
99 events << "variant:" + name
100 }
100 }
101 layer('main') {
101 layer('main') {
102 events << "layer:" + name
102 events << "layer:" + name
103 }
103 }
104 unit('browser', 'main') {
104 unit('browser', 'main') {
105 events << "unit:" + name
105 events << "unit:" + name
106 }
106 }
107 }
107 }
108
108
109 afterEvaluate {
109 afterEvaluate {
110 variantSources.whenFinalized { ctx ->
110 variantSources.whenFinalized { ctx ->
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
115
115
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.getUnit(browser, mainLayer)).get()
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.getUnit(browser, testLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.getUnit(node, mainLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
120
120
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
124 }
124 }
125 }
125 }
126 """);
126 """);
127
127
128 BuildResult result = runner("help").build();
128 BuildResult result = runner("help").build();
129
129
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
133 }
133 }
134
134
135 @Test
135 @Test
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
137 writeSettings("variant-sources-late-fail");
137 writeSettings("variant-sources-late-fail");
138 writeBuildFile("""
138 writeBuildFile("""
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
140
140
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
142 variantsExt.layers.create('main')
142 variantsExt.layers.create('main')
143 variantsExt.roles.create('production')
143 variantsExt.roles.create('production')
144 variantsExt.variant('browser') {
144 variantsExt.variant('browser') {
145 role('production') { layers('main') }
145 role('production') { layers('main') }
146 }
146 }
147
147
148 afterEvaluate {
148 afterEvaluate {
149 variantSources.whenFinalized { ctx ->
149 variantSources.whenFinalized { ctx ->
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
152 def unit = ctx.compileUnits.getUnit(browser, mainLayer)
152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
153
153
154 ctx.sourceSets.getSourceSet(unit).get()
154 ctx.sourceSets.getSourceSet(unit).get()
155 variantSources.layer('main') {
155 variantSources.layer('main') {
156 declareOutputs('late')
156 declareOutputs('late')
157 }
157 }
158 }
158 }
159 }
159 }
160 """);
160 """);
161
161
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
163 }
163 }
164
164
165 @Test
165 @Test
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
167 writeSettings("variant-sources-late-allow");
167 writeSettings("variant-sources-late-allow");
168 writeBuildFile("""
168 writeBuildFile("""
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
170
170
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
172 variantsExt.layers.create('main')
172 variantsExt.layers.create('main')
173 variantsExt.roles.create('production')
173 variantsExt.roles.create('production')
174 variantsExt.variant('browser') {
174 variantsExt.variant('browser') {
175 role('production') { layers('main') }
175 role('production') { layers('main') }
176 }
176 }
177
177
178 variantSources {
178 variantSources {
179 lateConfigurationPolicy {
179 lateConfigurationPolicy {
180 allowLateConfiguration()
180 allowLateConfiguration()
181 }
181 }
182 }
182 }
183
183
184 afterEvaluate {
184 afterEvaluate {
185 variantSources.whenFinalized { ctx ->
185 variantSources.whenFinalized { ctx ->
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
188 def unit = ctx.compileUnits.getUnit(browser, mainLayer)
188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
189
189
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
191 variantSources.layer('main') {
191 variantSources.layer('main') {
192 declareOutputs('late')
192 declareOutputs('late')
193 }
193 }
194 sourceSet.output('late')
194 sourceSet.output('late')
195 println('lateAllowed=ok')
195 println('lateAllowed=ok')
196 }
196 }
197 }
197 }
198 """);
198 """);
199
199
200 BuildResult result = runner("help").build();
200 BuildResult result = runner("help").build();
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
202 }
202 }
203
203
204 @Test
204 @Test
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
206 writeSettings("variant-sources-late-warn");
206 writeSettings("variant-sources-late-warn");
207 writeBuildFile("""
207 writeBuildFile("""
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
209
209
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
211 variantsExt.layers.create('main')
211 variantsExt.layers.create('main')
212 variantsExt.roles.create('production')
212 variantsExt.roles.create('production')
213 variantsExt.variant('browser') {
213 variantsExt.variant('browser') {
214 role('production') { layers('main') }
214 role('production') { layers('main') }
215 }
215 }
216
216
217 variantSources {
217 variantSources {
218 lateConfigurationPolicy {
218 lateConfigurationPolicy {
219 warnOnLateConfiguration()
219 warnOnLateConfiguration()
220 }
220 }
221 }
221 }
222
222
223 afterEvaluate {
223 afterEvaluate {
224 variantSources.whenFinalized { ctx ->
224 variantSources.whenFinalized { ctx ->
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
227 def unit = ctx.compileUnits.getUnit(browser, mainLayer)
227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
228
228
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
230 variantSources.layer('main') {
230 variantSources.layer('main') {
231 declareOutputs('late')
231 declareOutputs('late')
232 }
232 }
233 sourceSet.output('late')
233 sourceSet.output('late')
234 println('lateWarn=ok')
234 println('lateWarn=ok')
235 }
235 }
236 }
236 }
237 """);
237 """);
238
238
239 BuildResult result = runner("help").build();
239 BuildResult result = runner("help").build();
240
240
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
243 }
243 }
244
244
245 @Test
245 @Test
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
247 writeSettings("variant-sources-late-policy-fixed");
247 writeSettings("variant-sources-late-policy-fixed");
248 writeBuildFile("""
248 writeBuildFile("""
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
250
250
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
252 variantsExt.layers.create('main')
252 variantsExt.layers.create('main')
253 variantsExt.roles.create('production')
253 variantsExt.roles.create('production')
254 variantsExt.variant('browser') {
254 variantsExt.variant('browser') {
255 role('production') { layers('main') }
255 role('production') { layers('main') }
256 }
256 }
257
257
258 variantSources {
258 variantSources {
259 variant('browser') {
259 variant('browser') {
260 declareOutputs('js')
260 declareOutputs('js')
261 }
261 }
262 lateConfigurationPolicy {
262 lateConfigurationPolicy {
263 allowLateConfiguration()
263 allowLateConfiguration()
264 }
264 }
265 }
265 }
266 """);
266 """);
267
267
268 assertBuildFails("Lazy configuration policy already applied", "help");
268 assertBuildFails("Lazy configuration policy already applied", "help");
269 }
269 }
270
270
271 @Test
271 @Test
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
273 writeSettings("variant-sources-name-collision-fail");
273 writeSettings("variant-sources-name-collision-fail");
274 writeBuildFile("""
274 writeBuildFile("""
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
276
276
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
278 variantsExt.layers.create('variantBar')
278 variantsExt.layers.create('variantBar')
279 variantsExt.layers.create('bar')
279 variantsExt.layers.create('bar')
280 variantsExt.roles.create('production')
280 variantsExt.roles.create('production')
281
281
282 variantsExt.variant('foo') {
282 variantsExt.variant('foo') {
283 role('production') { layers('variantBar') }
283 role('production') { layers('variantBar') }
284 }
284 }
285 variantsExt.variant('fooVariant') {
285 variantsExt.variant('fooVariant') {
286 role('production') { layers('bar') }
286 role('production') { layers('bar') }
287 }
287 }
288 """);
288 """);
289
289
290 assertBuildFails("The same source set names are produced by different compile units", "help");
290 assertBuildFails("The same source set names are produced by different compile units", "help");
291 }
291 }
292
292
293 @Test
293 @Test
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
295 writeSettings("variant-sources-name-collision-resolve");
295 writeSettings("variant-sources-name-collision-resolve");
296 writeBuildFile("""
296 writeBuildFile("""
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
298
298
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
300 variantsExt.layers.create('variantBar')
300 variantsExt.layers.create('variantBar')
301 variantsExt.layers.create('bar')
301 variantsExt.layers.create('bar')
302 variantsExt.roles.create('production')
302 variantsExt.roles.create('production')
303
303
304 variantsExt.variant('foo') {
304 variantsExt.variant('foo') {
305 role('production') { layers('variantBar') }
305 role('production') { layers('variantBar') }
306 }
306 }
307 variantsExt.variant('fooVariant') {
307 variantsExt.variant('fooVariant') {
308 role('production') { layers('bar') }
308 role('production') { layers('bar') }
309 }
309 }
310
310
311 variantSources {
311 variantSources {
312 namingPolicy {
312 namingPolicy {
313 resolveNameCollision()
313 resolveNameCollision()
314 }
314 }
315 }
315 }
316
316
317 afterEvaluate {
317 afterEvaluate {
318 variantSources.whenFinalized { ctx ->
318 variantSources.whenFinalized { ctx ->
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
323
323
324 def later = ctx.compileUnits.getUnit(fooVariant, bar)
324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
325 def earlier = ctx.compileUnits.getUnit(foo, variantBar)
325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
326
326
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
329 }
329 }
330 }
330 }
331 """);
331 """);
332
332
333 BuildResult result = runner("help").build();
333 BuildResult result = runner("help").build();
334
334
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
337 }
337 }
338
338
339 @Test
339 @Test
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
341 writeSettings("variant-sources-missing-variant");
341 writeSettings("variant-sources-missing-variant");
342 writeBuildFile("""
342 writeBuildFile("""
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
344
344
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
346 variantsExt.layers.create('main')
346 variantsExt.layers.create('main')
347 variantsExt.roles.create('production')
347 variantsExt.roles.create('production')
348 variantsExt.variant('browser') {
348 variantsExt.variant('browser') {
349 role('production') { layers('main') }
349 role('production') { layers('main') }
350 }
350 }
351
351
352 variantSources {
352 variantSources {
353 variant('missing') {
353 variant('missing') {
354 declareOutputs('js')
354 declareOutputs('js')
355 }
355 }
356 }
356 }
357 """);
357 """);
358
358
359 assertBuildFails("Variant 'missing' is't declared", "help");
359 assertBuildFails("Variant 'missing' is't declared", "help");
360 }
360 }
361
361
362 @Test
362 @Test
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
364 writeSettings("variant-sources-missing-layer");
364 writeSettings("variant-sources-missing-layer");
365 writeBuildFile("""
365 writeBuildFile("""
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367
367
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 variantsExt.layers.create('main')
369 variantsExt.layers.create('main')
370 variantsExt.roles.create('production')
370 variantsExt.roles.create('production')
371 variantsExt.variant('browser') {
371 variantsExt.variant('browser') {
372 role('production') { layers('main') }
372 role('production') { layers('main') }
373 }
373 }
374
374
375 variantSources {
375 variantSources {
376 layer('missing') {
376 layer('missing') {
377 declareOutputs('js')
377 declareOutputs('js')
378 }
378 }
379 }
379 }
380 """);
380 """);
381
381
382 assertBuildFails("Layer 'missing' isn't declared", "help");
382 assertBuildFails("Layer 'missing' isn't declared", "help");
383 }
383 }
384
384
385 @Test
385 @Test
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
387 writeSettings("variant-sources-missing-unit");
387 writeSettings("variant-sources-missing-unit");
388 writeBuildFile("""
388 writeBuildFile("""
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390
390
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 variantsExt.layers.create('main')
392 variantsExt.layers.create('main')
393 variantsExt.layers.create('test')
393 variantsExt.layers.create('test')
394 variantsExt.roles.create('production')
394 variantsExt.roles.create('production')
395 variantsExt.variant('browser') {
395 variantsExt.variant('browser') {
396 role('production') { layers('main') }
396 role('production') { layers('main') }
397 }
397 }
398
398
399 variantSources {
399 variantSources {
400 unit('browser', 'test') {
400 unit('browser', 'test') {
401 declareOutputs('js')
401 declareOutputs('js')
402 }
402 }
403 }
403 }
404 """);
404 """);
405
405
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
407 }
407 }
408
408
409 @Test
409 @Test
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
411 writeSettings("variant-sources-name-policy-fixed");
411 writeSettings("variant-sources-name-policy-fixed");
412 writeBuildFile("""
412 writeBuildFile("""
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414
414
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 variantsExt.layers.create('main')
416 variantsExt.layers.create('main')
417 variantsExt.roles.create('production')
417 variantsExt.roles.create('production')
418 variantsExt.variant('browser') {
418 variantsExt.variant('browser') {
419 role('production') { layers('main') }
419 role('production') { layers('main') }
420 }
420 }
421
421
422 variantSources.whenFinalized {
422 variantSources.whenFinalized {
423 variantSources.namingPolicy {
423 variantSources.namingPolicy {
424 resolveNameCollision()
424 resolveNameCollision()
425 }
425 }
426 }
426 }
427 """);
427 """);
428
428
429 assertBuildFails("Naming policy already applied", "help");
429 assertBuildFails("Naming policy already applied", "help");
430 }
430 }
431 }
431 }
@@ -1,104 +1,104
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
4 import static org.junit.jupiter.api.Assertions.assertThrows;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
6
7 import java.lang.reflect.Constructor;
7 import java.lang.reflect.Constructor;
8 import java.util.Set;
8 import java.util.Set;
9
9
10 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
11 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantsView;
13 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
14 import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer;
15 import org.junit.jupiter.api.Test;
15 import org.junit.jupiter.api.Test;
16
16
17 class CompileUnitsViewTest {
17 class CompileUnitsViewTest {
18 @Test
18 @Test
19 void deduplicatesCompileUnitsAcrossRolesAndExposesParticipatingRoles() {
19 void deduplicatesCompileUnitsAcrossRolesAndExposesParticipatingRoles() {
20 var browser = new TestVariant("browser");
20 var browser = new TestVariant("browser");
21 var main = new TestLayer("main");
21 var main = new TestLayer("main");
22 var test = new TestLayer("test");
22 var test = new TestLayer("test");
23 var production = new TestRole("production");
23 var production = new TestRole("production");
24 var qa = new TestRole("test");
24 var qa = new TestRole("test");
25
25
26 var view = view(
26 var view = view(
27 Set.of(main, test),
27 Set.of(main, test),
28 Set.of(production, qa),
28 Set.of(production, qa),
29 Set.of(browser),
29 Set.of(browser),
30 Set.of(
30 Set.of(
31 new VariantRoleLayer(browser, production, main),
31 new VariantRoleLayer(browser, production, main),
32 new VariantRoleLayer(browser, qa, main),
32 new VariantRoleLayer(browser, qa, main),
33 new VariantRoleLayer(browser, qa, test)));
33 new VariantRoleLayer(browser, qa, test)));
34
34
35 var units = CompileUnitsView.of(view);
35 var units = CompileUnitsView.of(view);
36 var browserMain = units.getUnit(browser, main);
36 var browserMain = units.requireUnit(browser, main);
37 var browserTest = units.getUnit(browser, test);
37 var browserTest = units.requireUnit(browser, test);
38
38
39 assertEquals(2, units.getUnits().size());
39 assertEquals(2, units.getUnits().size());
40 assertEquals(Set.of(browserMain, browserTest), units.getUnitsForVariant(browser));
40 assertEquals(Set.of(browserMain, browserTest), units.getUnitsForVariant(browser));
41 assertEquals(Set.of(production, qa), units.getRoles(browserMain));
41 assertEquals(Set.of(production, qa), units.getRoles(browserMain));
42 assertEquals(Set.of(qa), units.getRoles(browserTest));
42 assertEquals(Set.of(qa), units.getRoles(browserTest));
43 assertTrue(units.contains(browser, main));
43 assertTrue(units.contains(browser, main));
44 assertTrue(units.contains(browser, test));
44 assertTrue(units.contains(browser, test));
45 }
45 }
46
46
47 @Test
47 @Test
48 void rejectsMissingCompileUnitLookup() {
48 void rejectsMissingCompileUnitLookup() {
49 var browser = new TestVariant("browser");
49 var browser = new TestVariant("browser");
50 var main = new TestLayer("main");
50 var main = new TestLayer("main");
51 var missing = new TestLayer("missing");
51 var missing = new TestLayer("missing");
52 var production = new TestRole("production");
52 var production = new TestRole("production");
53
53
54 var view = view(
54 var view = view(
55 Set.of(main),
55 Set.of(main),
56 Set.of(production),
56 Set.of(production),
57 Set.of(browser),
57 Set.of(browser),
58 Set.of(new VariantRoleLayer(browser, production, main)));
58 Set.of(new VariantRoleLayer(browser, production, main)));
59
59
60 var units = CompileUnitsView.of(view);
60 var units = CompileUnitsView.of(view);
61
61
62 var ex = assertThrows(IllegalArgumentException.class, () -> units.getUnit(browser, missing));
62 var ex = assertThrows(IllegalArgumentException.class, () -> units.requireUnit(browser, missing));
63 assertTrue(ex.getMessage().contains("Compile unit for variant 'browser' and layer 'missing' not found"));
63 assertTrue(ex.getMessage().contains("Compile unit for variant 'browser' and layer 'missing' not found"));
64 }
64 }
65
65
66 private static VariantsView view(
66 private static VariantsView view(
67 Set<Layer> layers,
67 Set<Layer> layers,
68 Set<Role> roles,
68 Set<Role> roles,
69 Set<Variant> variants,
69 Set<Variant> variants,
70 Set<VariantRoleLayer> entries) {
70 Set<VariantRoleLayer> entries) {
71 try {
71 try {
72 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
72 Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor(
73 Set.class,
73 Set.class,
74 Set.class,
74 Set.class,
75 Set.class,
75 Set.class,
76 Set.class);
76 Set.class);
77 ctor.setAccessible(true);
77 ctor.setAccessible(true);
78 return ctor.newInstance(layers, roles, variants, entries);
78 return ctor.newInstance(layers, roles, variants, entries);
79 } catch (Exception e) {
79 } catch (Exception e) {
80 throw new RuntimeException("Unable to create VariantsView fixture", e);
80 throw new RuntimeException("Unable to create VariantsView fixture", e);
81 }
81 }
82 }
82 }
83
83
84 private record TestVariant(String value) implements Variant {
84 private record TestVariant(String value) implements Variant {
85 @Override
85 @Override
86 public String getName() {
86 public String getName() {
87 return value;
87 return value;
88 }
88 }
89 }
89 }
90
90
91 private record TestLayer(String value) implements Layer {
91 private record TestLayer(String value) implements Layer {
92 @Override
92 @Override
93 public String getName() {
93 public String getName() {
94 return value;
94 return value;
95 }
95 }
96 }
96 }
97
97
98 private record TestRole(String value) implements Role {
98 private record TestRole(String value) implements Role {
99 @Override
99 @Override
100 public String getName() {
100 public String getName() {
101 return value;
101 return value;
102 }
102 }
103 }
103 }
104 }
104 }
General Comments 0
You need to be logged in to leave comments. Login now