| @@ -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 | 1 | # AGENTS.md |
|
|
2 | 2 | |
|
|
3 | 3 | ## ΠΡΠΎΠ΅ΠΊΡΠ½ΡΠ΅ Π΄ΠΎΠ³ΠΎΠ²ΠΎΡΠ΅Π½Π½ΠΎΡΡΠΈ |
|
|
4 | 4 | |
|
|
5 | 5 | ### ΠΡΠ±Π»ΠΈΡΠ½ΠΎΠ΅ API Π±ΠΈΠ±Π»ΠΈΠΎΡΠ΅ΠΊ |
|
|
6 | 6 | |
|
|
7 | 7 | - ΠΡΠ΅Π΄ΠΏΠΎΡΡΠΈΡΠ΅Π»Π΅Π½ `non-null` ΠΏΠΎΠ΄Ρ ΠΎΠ΄. |
|
|
8 | 8 | - Π’Π°ΠΌ, Π³Π΄Π΅ Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅ ΠΆΠΈΠ²Π΅Ρ Π² Gradle Provider API, Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΡΡΡ `Provider<T>` (Π½Π΅ `null`). |
|
|
9 | 9 | - Π’Π°ΠΌ, Π³Π΄Π΅ lookup ΡΠΈΠ½Ρ ΡΠΎΠ½Π½ΡΠΉ, Π²ΠΎΠ·Π²ΡΠ°ΡΠ°Π΅ΡΡΡ `Optional<T>` (Π½Π΅ `null`). |
|
|
10 | 10 | - `find*` ΡΠ°ΡΡΠΌΠ°ΡΡΠΈΠ²Π°Π΅ΡΡΡ ΠΊΠ°ΠΊ ΡΠΈΠ½ΠΎΠ½ΠΈΠΌ legacy `get*` (ΠΏΠΎΠΈΡΠΊ Π±Π΅Π· `fail-fast`). |
|
|
11 | 11 | - `require*` ΡΡΠΎ `find*` + `fail-fast` Ρ ΠΏΠΎΠ½ΡΡΠ½ΠΎΠΉ ΠΎΡΠΈΠ±ΠΊΠΎΠΉ Π² ΠΌΠ΅ΡΡΠ΅ Π²ΡΠ·ΠΎΠ²Π°. |
|
|
12 | 12 | - ΠΠ»Ρ Π½ΠΎΠ²ΠΎΠ³ΠΎ API ΠΏΡΠ΅Π΄ΠΏΠΎΡΡΠΈΡΠ΅Π»ΡΠ½Ρ ΡΠΎΡΠΌΡ `find/require`; Π½ΠΎΠ²ΡΠ΅ `get*` ΠΏΠΎ Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡΠΈ Π½Π΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ. |
|
|
13 | - ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΡ ΠΈ ΠΊΠ»Π°ΡΡΡ, ΠΎΠΏΠΈΡΡΠ²Π°ΡΡΠΈΠ΅ ΠΌΠΎΠ΄Π΅Π»ΠΈ DSL Π΄ΠΎΠ»ΠΆΠ½Ρ ΠΈΠΌΠ΅ΡΡ ΡΡΡΡΠΈΠΊΡ `Spec` Ρ ΠΌΠΎΠ΄Π΅Π»Π΅ΠΉ ΠΎΠΏΠΈΡΡΠ²Π°ΡΡΠΈΡ ΡΡΠΎΠ²Π΅Π½Ρ ΡΠ΅ΡΠ²ΠΈΡΠΎΠ² ΠΈ ΡΠΎΡΡΠΎΡΠ½ΠΈΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΡ ΡΠ±ΠΎΡΠΊΠΈ ΡΠ°ΠΊΠΎΠ³ΠΎ ΡΡΡΡΠΈΠΊΡΠ° Π½Π΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ. | |
|
|
14 | - ΠΠΎΠ΄Π΅Π»Ρ ΡΠ°ΡΡΠΈΡΠ΅Π½ΠΈΡ Π½Π° ΡΡΠΎΠ²Π½Π΅ ΠΏΡΠΎΠ΅ΠΊΡΠ° Π΄ΠΎΠ»ΠΆΠ½Π° ΠΈΠΌΠ΅ΡΡ ΡΡΡΡΠΈΠΊΡ `Extension`. | |
|
|
13 | 15 | |
|
|
14 | 16 | ### ΠΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ |
|
|
15 | 17 | |
|
|
16 | 18 | - Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠΈΡΠΎΠ²Π°Π½ΠΈΠ΅ ΠΊΠΎΠ΄Π° Π΄ΠΎΠ»ΠΆΠ½ΠΎ Π±ΡΡΡ Π½Π° Π°Π½Π³Π»ΠΈΠΉΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅ |
|
|
17 | 19 | - ΠΊ ΠΏΡΠ±Π»ΠΈΡΠ½ΠΎΠΌΡ API |
|
|
18 | 20 | - ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ Π΄ΠΎΠ»ΠΆΠ½ΠΎ ΠΎΡΡΠ°ΠΆΠ°ΡΡ Π½Π°Π·Π½Π°ΡΠ΅Π½ΠΈΠ΅, Π³Π΄Π΅ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΈ ΠΊΠ°ΠΊΠΎΠ΅ Π²Π»ΠΈΡΠ½ΠΈΠ΅ ΠΎΠΊΠ°Π·ΡΠ²Π°Π΅Ρ Π½Π° ΠΎΡΡΠ°Π»ΡΠ½ΡΠ΅ ΡΠ°ΡΡΠΈ ΠΏΡΠΎΠ³ΡΠ°ΠΌΠΌΡ |
|
|
19 | 21 | - Π΄Π°Π²Π°ΡΡ Π½Π΅Π±ΠΎΠ»ΡΡΠΎΠ΅ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΠΊΠΎΠ½ΡΠ΅ΠΏΡΠΈΠΈ, Π° ΡΠ°ΠΊΠΆΠ΅ ΠΊΡΠ°ΡΠΊΠΈΠ΅ ΠΏΡΠΈΠΌΠ΅ΡΡ |
|
|
20 | 22 | - ΠΊ ΠΏΡΠΈΠ²Π°ΡΠ½ΠΎΠΌΡ API Π΄ΠΎΡΡΠ°ΡΠΎΡΠ½ΠΎ Π΄Π°Π²Π°ΡΡ ΠΊΡΠ°ΡΠΊΡΡ ΡΠΏΡΠ°Π²ΠΊΡ ΠΎ Π½Π°Π·Π½Π°ΡΠ΅Π½ΠΈΠΈ ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΠΈ |
|
|
21 | 23 | - ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ Π°Π»Π³ΠΎΡΠΈΡΠΌΠΎΠ² Π² ΠΊΠΎΠ΄Π΅ ΡΠΎΠΏΡΠΎΠ²ΠΎΠΆΠ΄Π°ΡΡ ΠΊΠΎΠΌΠΌΠ΅Π½ΡΠ°ΡΠΈΡΠΌΠΈ Ρ ΠΏΠΎΡΡΠ½Π΅Π½ΠΈΡΠΌΠΈ, ΡΡΠΈΠ²ΠΈΠ°Π»ΡΠ½ΡΠ΅ ΠΎΠΏΠ΅ΡΠ°ΡΠΈΠΈ ΠΏΠΎΡΡΠ½ΡΡΡ Π½Π΅ ΡΡΠ΅Π±ΡΠ΅ΡΡΡ. |
|
|
22 | 24 | - Π΄ΠΎΠΊΡΠΌΠ΅Π½ΡΠ°ΡΠΈΡ Π΄ΠΎΠ»ΠΆΠ½Π° ΡΠΎΡΠΌΠΈΡΠΎΠ²Π°ΡΡΡΡ ΡΠΎΠ³Π»Π°ΡΠ½ΠΎ ΡΡΠ΅Π±ΠΎΠ²Π°Π½ΠΈΡΠΌ ΠΏΠΎ ΡΠΎΡΠΌΠ°ΡΠΈΡΠΎΠ²Π°Π½ΠΈΡ ΡΠΈΠΏΠ° javadoc, jsdoc ΠΈ Ρ.ΠΏ., Π² Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡΠΈ ΠΎΡ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΠΌΡΡ Π² ΠΏΡΠΎΠ΅ΠΊΡΠ΅ ΡΠ·ΡΠΊΠ°Ρ ΠΈ ΠΈΠ½ΡΡΡΡΠΌΠ΅Π½ΡΠ°Ρ . |
|
|
23 | 25 | |
|
|
24 | 26 | ## Identity-first modeling |
|
|
25 | 27 | |
|
|
26 | 28 | Prefer an **identity-first** split between: |
|
|
27 | 29 | |
|
|
28 | 30 | - **identity objects** used for discovery and selection |
|
|
29 | 31 | - **stateful/materialized objects** obtained through separate API calls |
|
|
30 | 32 | |
|
|
31 | 33 | ### Rules |
|
|
32 | 34 | |
|
|
33 | 35 | - Objects intended for replayable observation (`all(...)`, similar collection APIs) should be **identity objects**. |
|
|
34 | 36 | - Identity objects must be: |
|
|
35 | 37 | - cheap to create |
|
|
36 | 38 | - effectively immutable |
|
|
37 | 39 | - limited to identity and cheap selection metadata |
|
|
38 | 40 | - Heavy, computed, provider-based, or runtime-bound state must be resolved separately. |
|
|
39 | 41 | - Aggregate content should be accessed through dedicated lookup/materialization APIs. |
|
|
40 | 42 | - Eager observation of identity is acceptable. |
|
|
41 | 43 | - Eager observation of computed state is not. |
|
|
42 | 44 | |
|
|
43 | 45 | ### Do not |
|
|
44 | 46 | |
|
|
45 | 47 | - Do not store heavy computed state inside identity objects. |
|
|
46 | 48 | - Do not store runtime references to foreign domains inside identity objects. |
|
|
47 | 49 | - Do not use custom events when replayable identity registries plus on-demand lookup are sufficient. |
|
|
48 | 50 | |
|
|
49 | 51 | ### Rule of thumb |
|
|
50 | 52 | |
|
|
51 | 53 | **Identity objects contain selection metadata. |
|
|
52 | 54 | Aggregate content is obtained separately, on demand.** |
| @@ -1,78 +1,78 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Objects; |
|
|
6 | 6 | import java.util.Optional; |
|
|
7 | 7 | import java.util.Set; |
|
|
8 | 8 | import java.util.stream.Collectors; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
11 | 11 | import org.implab.gradle.variants.core.Layer; |
|
|
12 | 12 | import org.implab.gradle.variants.core.Role; |
|
|
13 | 13 | import org.implab.gradle.variants.core.Variant; |
|
|
14 | 14 | import org.implab.gradle.variants.core.VariantsView; |
|
|
15 | 15 | import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer; |
|
|
16 | 16 | |
|
|
17 | 17 | @NonNullByDefault |
|
|
18 | 18 | public final class CompileUnitsView { |
|
|
19 | 19 | |
|
|
20 | 20 | private final VariantsView variants; |
|
|
21 | 21 | private final Map<Variant, Set<CompileUnit>> unitsByVariant = new HashMap<>(); |
|
|
22 | 22 | |
|
|
23 | 23 | private CompileUnitsView(VariantsView variants) { |
|
|
24 | 24 | this.variants = variants; |
|
|
25 | 25 | } |
|
|
26 | 26 | |
|
|
27 | 27 | public Set<CompileUnit> getUnits() { |
|
|
28 | 28 | return variants.getEntries().stream() |
|
|
29 | 29 | .map(CompileUnit::of) |
|
|
30 | 30 | .collect(Collectors.toUnmodifiableSet()); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | public Set<CompileUnit> getUnitsForVariant(Variant variant) { |
|
|
34 | 34 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
|
35 | 35 | |
|
|
36 | 36 | return unitsByVariant.computeIfAbsent(variant, key -> variants |
|
|
37 | 37 | .getEntriesForVariant(variant).stream() |
|
|
38 | 38 | .map(CompileUnit::of) |
|
|
39 | 39 | .collect(Collectors.toUnmodifiableSet())); |
|
|
40 | 40 | } |
|
|
41 | 41 | |
|
|
42 | 42 | public Optional<CompileUnit> findUnit(Variant variant, Layer layer) { |
|
|
43 | 43 | Objects.requireNonNull(variant, "Variant can't be null"); |
|
|
44 | 44 | Objects.requireNonNull(layer, "Layer can't be null"); |
|
|
45 | 45 | |
|
|
46 | 46 | return getUnitsForVariant(variant).stream() |
|
|
47 | 47 | .filter(u -> u.layer().equals(layer)) |
|
|
48 | 48 | .findAny(); |
|
|
49 | 49 | } |
|
|
50 | 50 | |
|
|
51 | 51 | public boolean contains(Variant variant, Layer layer) { |
|
|
52 | 52 | return findUnit(variant, layer).isPresent(); |
|
|
53 | 53 | } |
|
|
54 | 54 | |
|
|
55 | 55 | /** |
|
|
56 | 56 | * In which logical roles this compile unit participates. |
|
|
57 | 57 | */ |
|
|
58 | 58 | |
|
|
59 | 59 | public Set<Role> getRoles(CompileUnit unit) { |
|
|
60 | 60 | Objects.requireNonNull(unit, "Compile unit can't be null"); |
|
|
61 | 61 | return variants.getEntriesForVariant(unit.variant()).stream() |
|
|
62 | 62 | .filter(entry -> entry.layer().equals(unit.layer())) |
|
|
63 | 63 | .map(VariantRoleLayer::role) |
|
|
64 | 64 | .collect(Collectors.toUnmodifiableSet()); |
|
|
65 | 65 | } |
|
|
66 | 66 | |
|
|
67 |
public CompileUnit |
|
|
|
67 | public CompileUnit requireUnit(Variant variant, Layer layer) { | |
|
|
68 | 68 | return findUnit(variant, layer) |
|
|
69 | 69 | .orElseThrow(() -> new IllegalArgumentException( |
|
|
70 | 70 | "Compile unit for variant '" + variant.getName() |
|
|
71 | 71 | + "' and layer '" + layer.getName() + "' not found")); |
|
|
72 | 72 | } |
|
|
73 | 73 | |
|
|
74 | 74 | public static CompileUnitsView of(VariantsView variantsView) { |
|
|
75 | 75 | Objects.requireNonNull(variantsView, "variantsView can't be null"); |
|
|
76 | 76 | return new CompileUnitsView(variantsView); |
|
|
77 | 77 | } |
|
|
78 | 78 | } No newline at end of file |
| @@ -1,431 +1,431 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.gradle.testkit.runner.BuildResult; |
|
|
6 | 6 | import org.junit.jupiter.api.Test; |
|
|
7 | 7 | |
|
|
8 | 8 | class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest { |
|
|
9 | 9 | |
|
|
10 | 10 | @Test |
|
|
11 | 11 | void exposesDerivedViewsAndStableSourceSetProvider() throws Exception { |
|
|
12 | 12 | writeSettings("variant-sources-derived-views"); |
|
|
13 | 13 | writeBuildFile(""" |
|
|
14 | 14 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
15 | 15 | |
|
|
16 | 16 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
17 | 17 | variantsExt.layers.create('main') |
|
|
18 | 18 | variantsExt.layers.create('test') |
|
|
19 | 19 | variantsExt.roles.create('production') |
|
|
20 | 20 | variantsExt.roles.create('test') |
|
|
21 | 21 | |
|
|
22 | 22 | variantsExt.variant('browser') { |
|
|
23 | 23 | role('production') { layers('main') } |
|
|
24 | 24 | role('test') { layers('main', 'test') } |
|
|
25 | 25 | } |
|
|
26 | 26 | |
|
|
27 | 27 | def lines = [] |
|
|
28 | 28 | |
|
|
29 | 29 | variantSources.whenFinalized { ctx -> |
|
|
30 | 30 | lines << "units=" + ctx.compileUnits.units |
|
|
31 | 31 | .collect { "${it.variant().name}:${it.layer().name}" } |
|
|
32 | 32 | .sort() |
|
|
33 | 33 | .join(',') |
|
|
34 | 34 | |
|
|
35 | 35 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
36 | 36 | def production = ctx.variants.roles.find { it.name == 'production' } |
|
|
37 | 37 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
38 | 38 | def projection = ctx.roleProjections.getProjection(browser, production) |
|
|
39 |
def unit = ctx.compileUnits. |
|
|
|
39 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) | |
|
|
40 | 40 | |
|
|
41 | 41 | def left = ctx.sourceSets.getSourceSet(unit) |
|
|
42 | 42 | def right = ctx.sourceSets.getSourceSet(unit) |
|
|
43 | 43 | |
|
|
44 | 44 | lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection) |
|
|
45 | 45 | .collect { it.layer().name } |
|
|
46 | 46 | .sort() |
|
|
47 | 47 | .join(',') |
|
|
48 | 48 | lines << "mainSourceSet=" + left.name |
|
|
49 | 49 | lines << "sameProvider=" + left.is(right) |
|
|
50 | 50 | } |
|
|
51 | 51 | |
|
|
52 | 52 | afterEvaluate { |
|
|
53 | 53 | variantSources.whenFinalized { ctx -> |
|
|
54 | 54 | lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',') |
|
|
55 | 55 | } |
|
|
56 | 56 | } |
|
|
57 | 57 | |
|
|
58 | 58 | tasks.register('probe') { |
|
|
59 | 59 | doLast { |
|
|
60 | 60 | lines.each { println(it) } |
|
|
61 | 61 | } |
|
|
62 | 62 | } |
|
|
63 | 63 | """); |
|
|
64 | 64 | |
|
|
65 | 65 | BuildResult result = runner("probe").build(); |
|
|
66 | 66 | |
|
|
67 | 67 | assertTrue(result.getOutput().contains("units=browser:main,browser:test")); |
|
|
68 | 68 | assertTrue(result.getOutput().contains("projectionUnits=main")); |
|
|
69 | 69 | assertTrue(result.getOutput().contains("mainSourceSet=browserMain")); |
|
|
70 | 70 | assertTrue(result.getOutput().contains("sameProvider=true")); |
|
|
71 | 71 | assertTrue(result.getOutput().contains("late:variants=browser")); |
|
|
72 | 72 | } |
|
|
73 | 73 | |
|
|
74 | 74 | @Test |
|
|
75 | 75 | void appliesSelectorPrecedenceForFutureMaterialization() throws Exception { |
|
|
76 | 76 | writeSettings("variant-sources-precedence"); |
|
|
77 | 77 | writeBuildFile(""" |
|
|
78 | 78 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
79 | 79 | |
|
|
80 | 80 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
81 | 81 | variantsExt.layers.create('main') |
|
|
82 | 82 | variantsExt.layers.create('test') |
|
|
83 | 83 | variantsExt.roles.create('production') |
|
|
84 | 84 | variantsExt.roles.create('test') |
|
|
85 | 85 | |
|
|
86 | 86 | variantsExt.variant('browser') { |
|
|
87 | 87 | role('production') { layers('main') } |
|
|
88 | 88 | role('test') { layers('main', 'test') } |
|
|
89 | 89 | } |
|
|
90 | 90 | |
|
|
91 | 91 | variantsExt.variant('node') { |
|
|
92 | 92 | role('production') { layers('main') } |
|
|
93 | 93 | } |
|
|
94 | 94 | |
|
|
95 | 95 | def events = [] |
|
|
96 | 96 | |
|
|
97 | 97 | variantSources { |
|
|
98 | 98 | variant('browser') { |
|
|
99 | 99 | events << "variant:" + name |
|
|
100 | 100 | } |
|
|
101 | 101 | layer('main') { |
|
|
102 | 102 | events << "layer:" + name |
|
|
103 | 103 | } |
|
|
104 | 104 | unit('browser', 'main') { |
|
|
105 | 105 | events << "unit:" + name |
|
|
106 | 106 | } |
|
|
107 | 107 | } |
|
|
108 | 108 | |
|
|
109 | 109 | afterEvaluate { |
|
|
110 | 110 | variantSources.whenFinalized { ctx -> |
|
|
111 | 111 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
112 | 112 | def node = ctx.variants.variants.find { it.name == 'node' } |
|
|
113 | 113 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
114 | 114 | def testLayer = ctx.variants.layers.find { it.name == 'test' } |
|
|
115 | 115 | |
|
|
116 |
def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits. |
|
|
|
117 |
def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits. |
|
|
|
118 |
def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits. |
|
|
|
116 | def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get() | |
|
|
117 | def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get() | |
|
|
118 | def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get() | |
|
|
119 | 119 | def bySourceSet = events.groupBy { it.split(':', 2)[1] } |
|
|
120 | 120 | |
|
|
121 | 121 | println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
122 | 122 | println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
123 | 123 | println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
124 | 124 | } |
|
|
125 | 125 | } |
|
|
126 | 126 | """); |
|
|
127 | 127 | |
|
|
128 | 128 | BuildResult result = runner("help").build(); |
|
|
129 | 129 | |
|
|
130 | 130 | assertTrue(result.getOutput().contains("browserMain=variant,layer,unit")); |
|
|
131 | 131 | assertTrue(result.getOutput().contains("browserTest=variant")); |
|
|
132 | 132 | assertTrue(result.getOutput().contains("nodeMain=layer")); |
|
|
133 | 133 | } |
|
|
134 | 134 | |
|
|
135 | 135 | @Test |
|
|
136 | 136 | void failsLateConfigurationByDefaultAfterMaterialization() throws Exception { |
|
|
137 | 137 | writeSettings("variant-sources-late-fail"); |
|
|
138 | 138 | writeBuildFile(""" |
|
|
139 | 139 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
140 | 140 | |
|
|
141 | 141 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
142 | 142 | variantsExt.layers.create('main') |
|
|
143 | 143 | variantsExt.roles.create('production') |
|
|
144 | 144 | variantsExt.variant('browser') { |
|
|
145 | 145 | role('production') { layers('main') } |
|
|
146 | 146 | } |
|
|
147 | 147 | |
|
|
148 | 148 | afterEvaluate { |
|
|
149 | 149 | variantSources.whenFinalized { ctx -> |
|
|
150 | 150 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
151 | 151 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
152 |
def unit = ctx.compileUnits. |
|
|
|
152 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) | |
|
|
153 | 153 | |
|
|
154 | 154 | ctx.sourceSets.getSourceSet(unit).get() |
|
|
155 | 155 | variantSources.layer('main') { |
|
|
156 | 156 | declareOutputs('late') |
|
|
157 | 157 | } |
|
|
158 | 158 | } |
|
|
159 | 159 | } |
|
|
160 | 160 | """); |
|
|
161 | 161 | |
|
|
162 | 162 | assertBuildFails("Source sets for [layer=main] layer already materialized", "help"); |
|
|
163 | 163 | } |
|
|
164 | 164 | |
|
|
165 | 165 | @Test |
|
|
166 | 166 | void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception { |
|
|
167 | 167 | writeSettings("variant-sources-late-allow"); |
|
|
168 | 168 | writeBuildFile(""" |
|
|
169 | 169 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
170 | 170 | |
|
|
171 | 171 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
172 | 172 | variantsExt.layers.create('main') |
|
|
173 | 173 | variantsExt.roles.create('production') |
|
|
174 | 174 | variantsExt.variant('browser') { |
|
|
175 | 175 | role('production') { layers('main') } |
|
|
176 | 176 | } |
|
|
177 | 177 | |
|
|
178 | 178 | variantSources { |
|
|
179 | 179 | lateConfigurationPolicy { |
|
|
180 | 180 | allowLateConfiguration() |
|
|
181 | 181 | } |
|
|
182 | 182 | } |
|
|
183 | 183 | |
|
|
184 | 184 | afterEvaluate { |
|
|
185 | 185 | variantSources.whenFinalized { ctx -> |
|
|
186 | 186 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
187 | 187 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
188 |
def unit = ctx.compileUnits. |
|
|
|
188 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) | |
|
|
189 | 189 | |
|
|
190 | 190 | def sourceSet = ctx.sourceSets.getSourceSet(unit).get() |
|
|
191 | 191 | variantSources.layer('main') { |
|
|
192 | 192 | declareOutputs('late') |
|
|
193 | 193 | } |
|
|
194 | 194 | sourceSet.output('late') |
|
|
195 | 195 | println('lateAllowed=ok') |
|
|
196 | 196 | } |
|
|
197 | 197 | } |
|
|
198 | 198 | """); |
|
|
199 | 199 | |
|
|
200 | 200 | BuildResult result = runner("help").build(); |
|
|
201 | 201 | assertTrue(result.getOutput().contains("lateAllowed=ok")); |
|
|
202 | 202 | } |
|
|
203 | 203 | |
|
|
204 | 204 | @Test |
|
|
205 | 205 | void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception { |
|
|
206 | 206 | writeSettings("variant-sources-late-warn"); |
|
|
207 | 207 | writeBuildFile(""" |
|
|
208 | 208 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
209 | 209 | |
|
|
210 | 210 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
211 | 211 | variantsExt.layers.create('main') |
|
|
212 | 212 | variantsExt.roles.create('production') |
|
|
213 | 213 | variantsExt.variant('browser') { |
|
|
214 | 214 | role('production') { layers('main') } |
|
|
215 | 215 | } |
|
|
216 | 216 | |
|
|
217 | 217 | variantSources { |
|
|
218 | 218 | lateConfigurationPolicy { |
|
|
219 | 219 | warnOnLateConfiguration() |
|
|
220 | 220 | } |
|
|
221 | 221 | } |
|
|
222 | 222 | |
|
|
223 | 223 | afterEvaluate { |
|
|
224 | 224 | variantSources.whenFinalized { ctx -> |
|
|
225 | 225 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
226 | 226 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
227 |
def unit = ctx.compileUnits. |
|
|
|
227 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) | |
|
|
228 | 228 | |
|
|
229 | 229 | def sourceSet = ctx.sourceSets.getSourceSet(unit).get() |
|
|
230 | 230 | variantSources.layer('main') { |
|
|
231 | 231 | declareOutputs('late') |
|
|
232 | 232 | } |
|
|
233 | 233 | sourceSet.output('late') |
|
|
234 | 234 | println('lateWarn=ok') |
|
|
235 | 235 | } |
|
|
236 | 236 | } |
|
|
237 | 237 | """); |
|
|
238 | 238 | |
|
|
239 | 239 | BuildResult result = runner("help").build(); |
|
|
240 | 240 | |
|
|
241 | 241 | assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized")); |
|
|
242 | 242 | assertTrue(result.getOutput().contains("lateWarn=ok")); |
|
|
243 | 243 | } |
|
|
244 | 244 | |
|
|
245 | 245 | @Test |
|
|
246 | 246 | void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception { |
|
|
247 | 247 | writeSettings("variant-sources-late-policy-fixed"); |
|
|
248 | 248 | writeBuildFile(""" |
|
|
249 | 249 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
250 | 250 | |
|
|
251 | 251 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
252 | 252 | variantsExt.layers.create('main') |
|
|
253 | 253 | variantsExt.roles.create('production') |
|
|
254 | 254 | variantsExt.variant('browser') { |
|
|
255 | 255 | role('production') { layers('main') } |
|
|
256 | 256 | } |
|
|
257 | 257 | |
|
|
258 | 258 | variantSources { |
|
|
259 | 259 | variant('browser') { |
|
|
260 | 260 | declareOutputs('js') |
|
|
261 | 261 | } |
|
|
262 | 262 | lateConfigurationPolicy { |
|
|
263 | 263 | allowLateConfiguration() |
|
|
264 | 264 | } |
|
|
265 | 265 | } |
|
|
266 | 266 | """); |
|
|
267 | 267 | |
|
|
268 | 268 | assertBuildFails("Lazy configuration policy already applied", "help"); |
|
|
269 | 269 | } |
|
|
270 | 270 | |
|
|
271 | 271 | @Test |
|
|
272 | 272 | void failsOnProjectedNameCollisionByDefault() throws Exception { |
|
|
273 | 273 | writeSettings("variant-sources-name-collision-fail"); |
|
|
274 | 274 | writeBuildFile(""" |
|
|
275 | 275 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
276 | 276 | |
|
|
277 | 277 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
278 | 278 | variantsExt.layers.create('variantBar') |
|
|
279 | 279 | variantsExt.layers.create('bar') |
|
|
280 | 280 | variantsExt.roles.create('production') |
|
|
281 | 281 | |
|
|
282 | 282 | variantsExt.variant('foo') { |
|
|
283 | 283 | role('production') { layers('variantBar') } |
|
|
284 | 284 | } |
|
|
285 | 285 | variantsExt.variant('fooVariant') { |
|
|
286 | 286 | role('production') { layers('bar') } |
|
|
287 | 287 | } |
|
|
288 | 288 | """); |
|
|
289 | 289 | |
|
|
290 | 290 | assertBuildFails("The same source set names are produced by different compile units", "help"); |
|
|
291 | 291 | } |
|
|
292 | 292 | |
|
|
293 | 293 | @Test |
|
|
294 | 294 | void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception { |
|
|
295 | 295 | writeSettings("variant-sources-name-collision-resolve"); |
|
|
296 | 296 | writeBuildFile(""" |
|
|
297 | 297 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
298 | 298 | |
|
|
299 | 299 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
300 | 300 | variantsExt.layers.create('variantBar') |
|
|
301 | 301 | variantsExt.layers.create('bar') |
|
|
302 | 302 | variantsExt.roles.create('production') |
|
|
303 | 303 | |
|
|
304 | 304 | variantsExt.variant('foo') { |
|
|
305 | 305 | role('production') { layers('variantBar') } |
|
|
306 | 306 | } |
|
|
307 | 307 | variantsExt.variant('fooVariant') { |
|
|
308 | 308 | role('production') { layers('bar') } |
|
|
309 | 309 | } |
|
|
310 | 310 | |
|
|
311 | 311 | variantSources { |
|
|
312 | 312 | namingPolicy { |
|
|
313 | 313 | resolveNameCollision() |
|
|
314 | 314 | } |
|
|
315 | 315 | } |
|
|
316 | 316 | |
|
|
317 | 317 | afterEvaluate { |
|
|
318 | 318 | variantSources.whenFinalized { ctx -> |
|
|
319 | 319 | def foo = ctx.variants.variants.find { it.name == 'foo' } |
|
|
320 | 320 | def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' } |
|
|
321 | 321 | def variantBar = ctx.variants.layers.find { it.name == 'variantBar' } |
|
|
322 | 322 | def bar = ctx.variants.layers.find { it.name == 'bar' } |
|
|
323 | 323 | |
|
|
324 |
def later = ctx.compileUnits. |
|
|
|
325 |
def earlier = ctx.compileUnits. |
|
|
|
324 | def later = ctx.compileUnits.requireUnit(fooVariant, bar) | |
|
|
325 | def earlier = ctx.compileUnits.requireUnit(foo, variantBar) | |
|
|
326 | 326 | |
|
|
327 | 327 | println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name) |
|
|
328 | 328 | println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name) |
|
|
329 | 329 | } |
|
|
330 | 330 | } |
|
|
331 | 331 | """); |
|
|
332 | 332 | |
|
|
333 | 333 | BuildResult result = runner("help").build(); |
|
|
334 | 334 | |
|
|
335 | 335 | assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2")); |
|
|
336 | 336 | assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar")); |
|
|
337 | 337 | } |
|
|
338 | 338 | |
|
|
339 | 339 | @Test |
|
|
340 | 340 | void failsOnUnknownVariantSelectorTarget() throws Exception { |
|
|
341 | 341 | writeSettings("variant-sources-missing-variant"); |
|
|
342 | 342 | writeBuildFile(""" |
|
|
343 | 343 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
344 | 344 | |
|
|
345 | 345 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
346 | 346 | variantsExt.layers.create('main') |
|
|
347 | 347 | variantsExt.roles.create('production') |
|
|
348 | 348 | variantsExt.variant('browser') { |
|
|
349 | 349 | role('production') { layers('main') } |
|
|
350 | 350 | } |
|
|
351 | 351 | |
|
|
352 | 352 | variantSources { |
|
|
353 | 353 | variant('missing') { |
|
|
354 | 354 | declareOutputs('js') |
|
|
355 | 355 | } |
|
|
356 | 356 | } |
|
|
357 | 357 | """); |
|
|
358 | 358 | |
|
|
359 | 359 | assertBuildFails("Variant 'missing' is't declared", "help"); |
|
|
360 | 360 | } |
|
|
361 | 361 | |
|
|
362 | 362 | @Test |
|
|
363 | 363 | void failsOnUnknownLayerSelectorTarget() throws Exception { |
|
|
364 | 364 | writeSettings("variant-sources-missing-layer"); |
|
|
365 | 365 | writeBuildFile(""" |
|
|
366 | 366 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
367 | 367 | |
|
|
368 | 368 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
369 | 369 | variantsExt.layers.create('main') |
|
|
370 | 370 | variantsExt.roles.create('production') |
|
|
371 | 371 | variantsExt.variant('browser') { |
|
|
372 | 372 | role('production') { layers('main') } |
|
|
373 | 373 | } |
|
|
374 | 374 | |
|
|
375 | 375 | variantSources { |
|
|
376 | 376 | layer('missing') { |
|
|
377 | 377 | declareOutputs('js') |
|
|
378 | 378 | } |
|
|
379 | 379 | } |
|
|
380 | 380 | """); |
|
|
381 | 381 | |
|
|
382 | 382 | assertBuildFails("Layer 'missing' isn't declared", "help"); |
|
|
383 | 383 | } |
|
|
384 | 384 | |
|
|
385 | 385 | @Test |
|
|
386 | 386 | void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception { |
|
|
387 | 387 | writeSettings("variant-sources-missing-unit"); |
|
|
388 | 388 | writeBuildFile(""" |
|
|
389 | 389 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
390 | 390 | |
|
|
391 | 391 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
392 | 392 | variantsExt.layers.create('main') |
|
|
393 | 393 | variantsExt.layers.create('test') |
|
|
394 | 394 | variantsExt.roles.create('production') |
|
|
395 | 395 | variantsExt.variant('browser') { |
|
|
396 | 396 | role('production') { layers('main') } |
|
|
397 | 397 | } |
|
|
398 | 398 | |
|
|
399 | 399 | variantSources { |
|
|
400 | 400 | unit('browser', 'test') { |
|
|
401 | 401 | declareOutputs('js') |
|
|
402 | 402 | } |
|
|
403 | 403 | } |
|
|
404 | 404 | """); |
|
|
405 | 405 | |
|
|
406 | 406 | assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help"); |
|
|
407 | 407 | } |
|
|
408 | 408 | |
|
|
409 | 409 | @Test |
|
|
410 | 410 | void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception { |
|
|
411 | 411 | writeSettings("variant-sources-name-policy-fixed"); |
|
|
412 | 412 | writeBuildFile(""" |
|
|
413 | 413 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
414 | 414 | |
|
|
415 | 415 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
416 | 416 | variantsExt.layers.create('main') |
|
|
417 | 417 | variantsExt.roles.create('production') |
|
|
418 | 418 | variantsExt.variant('browser') { |
|
|
419 | 419 | role('production') { layers('main') } |
|
|
420 | 420 | } |
|
|
421 | 421 | |
|
|
422 | 422 | variantSources.whenFinalized { |
|
|
423 | 423 | variantSources.namingPolicy { |
|
|
424 | 424 | resolveNameCollision() |
|
|
425 | 425 | } |
|
|
426 | 426 | } |
|
|
427 | 427 | """); |
|
|
428 | 428 | |
|
|
429 | 429 | assertBuildFails("Naming policy already applied", "help"); |
|
|
430 | 430 | } |
|
|
431 | 431 | } |
| @@ -1,104 +1,104 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; |
|
|
4 | 4 | import static org.junit.jupiter.api.Assertions.assertThrows; |
|
|
5 | 5 | import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
6 | 6 | |
|
|
7 | 7 | import java.lang.reflect.Constructor; |
|
|
8 | 8 | import java.util.Set; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.implab.gradle.variants.core.Layer; |
|
|
11 | 11 | import org.implab.gradle.variants.core.Role; |
|
|
12 | 12 | import org.implab.gradle.variants.core.Variant; |
|
|
13 | 13 | import org.implab.gradle.variants.core.VariantsView; |
|
|
14 | 14 | import org.implab.gradle.variants.core.VariantsView.VariantRoleLayer; |
|
|
15 | 15 | import org.junit.jupiter.api.Test; |
|
|
16 | 16 | |
|
|
17 | 17 | class CompileUnitsViewTest { |
|
|
18 | 18 | @Test |
|
|
19 | 19 | void deduplicatesCompileUnitsAcrossRolesAndExposesParticipatingRoles() { |
|
|
20 | 20 | var browser = new TestVariant("browser"); |
|
|
21 | 21 | var main = new TestLayer("main"); |
|
|
22 | 22 | var test = new TestLayer("test"); |
|
|
23 | 23 | var production = new TestRole("production"); |
|
|
24 | 24 | var qa = new TestRole("test"); |
|
|
25 | 25 | |
|
|
26 | 26 | var view = view( |
|
|
27 | 27 | Set.of(main, test), |
|
|
28 | 28 | Set.of(production, qa), |
|
|
29 | 29 | Set.of(browser), |
|
|
30 | 30 | Set.of( |
|
|
31 | 31 | new VariantRoleLayer(browser, production, main), |
|
|
32 | 32 | new VariantRoleLayer(browser, qa, main), |
|
|
33 | 33 | new VariantRoleLayer(browser, qa, test))); |
|
|
34 | 34 | |
|
|
35 | 35 | var units = CompileUnitsView.of(view); |
|
|
36 |
var browserMain = units. |
|
|
|
37 |
var browserTest = units. |
|
|
|
36 | var browserMain = units.requireUnit(browser, main); | |
|
|
37 | var browserTest = units.requireUnit(browser, test); | |
|
|
38 | 38 | |
|
|
39 | 39 | assertEquals(2, units.getUnits().size()); |
|
|
40 | 40 | assertEquals(Set.of(browserMain, browserTest), units.getUnitsForVariant(browser)); |
|
|
41 | 41 | assertEquals(Set.of(production, qa), units.getRoles(browserMain)); |
|
|
42 | 42 | assertEquals(Set.of(qa), units.getRoles(browserTest)); |
|
|
43 | 43 | assertTrue(units.contains(browser, main)); |
|
|
44 | 44 | assertTrue(units.contains(browser, test)); |
|
|
45 | 45 | } |
|
|
46 | 46 | |
|
|
47 | 47 | @Test |
|
|
48 | 48 | void rejectsMissingCompileUnitLookup() { |
|
|
49 | 49 | var browser = new TestVariant("browser"); |
|
|
50 | 50 | var main = new TestLayer("main"); |
|
|
51 | 51 | var missing = new TestLayer("missing"); |
|
|
52 | 52 | var production = new TestRole("production"); |
|
|
53 | 53 | |
|
|
54 | 54 | var view = view( |
|
|
55 | 55 | Set.of(main), |
|
|
56 | 56 | Set.of(production), |
|
|
57 | 57 | Set.of(browser), |
|
|
58 | 58 | Set.of(new VariantRoleLayer(browser, production, main))); |
|
|
59 | 59 | |
|
|
60 | 60 | var units = CompileUnitsView.of(view); |
|
|
61 | 61 | |
|
|
62 |
var ex = assertThrows(IllegalArgumentException.class, () -> units. |
|
|
|
62 | var ex = assertThrows(IllegalArgumentException.class, () -> units.requireUnit(browser, missing)); | |
|
|
63 | 63 | assertTrue(ex.getMessage().contains("Compile unit for variant 'browser' and layer 'missing' not found")); |
|
|
64 | 64 | } |
|
|
65 | 65 | |
|
|
66 | 66 | private static VariantsView view( |
|
|
67 | 67 | Set<Layer> layers, |
|
|
68 | 68 | Set<Role> roles, |
|
|
69 | 69 | Set<Variant> variants, |
|
|
70 | 70 | Set<VariantRoleLayer> entries) { |
|
|
71 | 71 | try { |
|
|
72 | 72 | Constructor<VariantsView> ctor = VariantsView.class.getDeclaredConstructor( |
|
|
73 | 73 | Set.class, |
|
|
74 | 74 | Set.class, |
|
|
75 | 75 | Set.class, |
|
|
76 | 76 | Set.class); |
|
|
77 | 77 | ctor.setAccessible(true); |
|
|
78 | 78 | return ctor.newInstance(layers, roles, variants, entries); |
|
|
79 | 79 | } catch (Exception e) { |
|
|
80 | 80 | throw new RuntimeException("Unable to create VariantsView fixture", e); |
|
|
81 | 81 | } |
|
|
82 | 82 | } |
|
|
83 | 83 | |
|
|
84 | 84 | private record TestVariant(String value) implements Variant { |
|
|
85 | 85 | @Override |
|
|
86 | 86 | public String getName() { |
|
|
87 | 87 | return value; |
|
|
88 | 88 | } |
|
|
89 | 89 | } |
|
|
90 | 90 | |
|
|
91 | 91 | private record TestLayer(String value) implements Layer { |
|
|
92 | 92 | @Override |
|
|
93 | 93 | public String getName() { |
|
|
94 | 94 | return value; |
|
|
95 | 95 | } |
|
|
96 | 96 | } |
|
|
97 | 97 | |
|
|
98 | 98 | private record TestRole(String value) implements Role { |
|
|
99 | 99 | @Override |
|
|
100 | 100 | public String getName() { |
|
|
101 | 101 | return value; |
|
|
102 | 102 | } |
|
|
103 | 103 | } |
|
|
104 | 104 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
