| @@ -1,112 +1,112 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.Plugin; |
|
|
5 | 5 | import org.gradle.api.Project; |
|
|
6 | 6 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
7 | 7 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
|
8 | 8 | import org.implab.gradle.variants.artifacts.OutgoingVariantsContext; |
|
|
9 | 9 | import org.implab.gradle.variants.artifacts.VariantArtifactsExtension; |
|
|
10 | 10 | import org.implab.gradle.variants.artifacts.VariantArtifactsSpec; |
|
|
11 | 11 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder; |
|
|
12 | 12 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler; |
|
|
13 | 13 | import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry; |
|
|
14 | 14 | import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext; |
|
|
15 | 15 | import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler; |
|
|
16 | 16 | import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry; |
|
|
17 | 17 | import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention; |
|
|
18 | 18 | import org.implab.gradle.variants.core.Variant; |
|
|
19 | 19 | import org.implab.gradle.variants.core.VariantsExtension; |
|
|
20 | 20 | import org.implab.gradle.variants.sources.VariantSourcesExtension; |
|
|
21 | 21 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec; |
|
|
22 | 22 | |
|
|
23 | 23 | public abstract class VariantArtifactsPlugin implements Plugin<Project> { |
|
|
24 | 24 | |
|
|
25 | 25 | @Override |
|
|
26 | 26 | public void apply(Project target) { |
|
|
27 | 27 | var extensions = target.getExtensions(); |
|
|
28 | 28 | var objects = target.getObjects(); |
|
|
29 | 29 | var providers = target.getProviders(); |
|
|
30 | 30 | var plugins = target.getPlugins(); |
|
|
31 | 31 | var configurations = target.getConfigurations(); |
|
|
32 | 32 | var tasks = target.getTasks(); |
|
|
33 | 33 | var layout = target.getLayout(); |
|
|
34 | 34 | |
|
|
35 | 35 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
|
36 | 36 | plugins.apply(VariantsPlugin.class); |
|
|
37 | 37 | plugins.apply(VariantSourcesPlugin.class); |
|
|
38 | 38 | // Access the VariantsExtension to configure variant sources. |
|
|
39 | 39 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
|
40 | 40 | var sourcesExtension = extensions.getByType(VariantSourcesExtension.class); |
|
|
41 | 41 | |
|
|
42 | 42 | var deferred = new Deferred<OutgoingVariantsContext>(); |
|
|
43 | 43 | |
|
|
44 | 44 | variantsExtension.whenFinalized(variants -> { |
|
|
45 | 45 | |
|
|
46 | 46 | var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants()); |
|
|
47 | 47 | var assemblies = new ArtifactAssemblyRegistry(); |
|
|
48 | 48 | |
|
|
49 | 49 | // wire artifact assemblies to configuration variants |
|
|
50 | 50 | var assembliesBridge = new ArtifactAssemblyBinder(assemblies); |
|
|
51 | 51 | var primarySlotConvention = new SingleSlotConvention(providers); |
|
|
52 | var materializationHandler = new MaterializationPolicyHandler(); | |
|
|
52 | var materializationHandler = new MaterializationPolicyHandler(objects); | |
|
|
53 | 53 | |
|
|
54 | 54 | // bind slot assemblies to outgoing variants |
|
|
55 | 55 | outgoing.configureEach(assembliesBridge::execute); |
|
|
56 | 56 | // apply primary slot convention when outgoing variant has a single slot |
|
|
57 | 57 | outgoing.configureEach(primarySlotConvention::execute); |
|
|
58 | 58 | // apply materialization policy hooks to outgoing variants |
|
|
59 | 59 | outgoing.configureEach(materializationHandler::execute); |
|
|
60 | 60 | |
|
|
61 | 61 | sourcesExtension.whenFinalized(sources -> { |
|
|
62 | 62 | var assemblyHandler = new ArtifactAssemblyHandler( |
|
|
63 | 63 | objects, |
|
|
64 | 64 | tasks, |
|
|
65 | 65 | assemblies, |
|
|
66 | 66 | sources.getCompileUnits(), |
|
|
67 | 67 | sources.getRoleProjections(), |
|
|
68 | 68 | sources.getSourceSets()); |
|
|
69 | 69 | assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies")); |
|
|
70 | 70 | |
|
|
71 | 71 | deferred.resolve(new DefaultOutgoingVariantsContext( |
|
|
72 | 72 | assemblies, |
|
|
73 | 73 | outgoing, |
|
|
74 | 74 | assemblyHandler, |
|
|
75 | 75 | materializationHandler)); |
|
|
76 | 76 | }); |
|
|
77 | 77 | |
|
|
78 | 78 | }); |
|
|
79 | 79 | |
|
|
80 | 80 | var variantArtifacts = new VariantArtifactsExtension() { |
|
|
81 | 81 | |
|
|
82 | 82 | @Override |
|
|
83 | 83 | public void variant(String variantName, Action<? super VariantArtifactsSpec> action) { |
|
|
84 | 84 | deferred.whenResolved(context -> { |
|
|
85 | 85 | var variant = objects.named(Variant.class, variantName); |
|
|
86 | 86 | context.configureVariant(variant, action); |
|
|
87 | 87 | }); |
|
|
88 | 88 | } |
|
|
89 | 89 | |
|
|
90 | 90 | @Override |
|
|
91 | 91 | public void whenAvailable(Action<? super OutgoingVariantsContext> action) { |
|
|
92 | 92 | deferred.whenResolved(handler -> action.execute(handler)); |
|
|
93 | 93 | } |
|
|
94 | 94 | |
|
|
95 | 95 | @Override |
|
|
96 | 96 | public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) { |
|
|
97 | 97 | deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action)); |
|
|
98 | 98 | |
|
|
99 | 99 | } |
|
|
100 | 100 | |
|
|
101 | 101 | @Override |
|
|
102 | 102 | public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) { |
|
|
103 | 103 | deferred.whenResolved(registry -> registry.whenOutgoingSlot(action)); |
|
|
104 | 104 | } |
|
|
105 | 105 | |
|
|
106 | 106 | }; |
|
|
107 | 107 | |
|
|
108 | 108 | extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts); |
|
|
109 | 109 | |
|
|
110 | 110 | } |
|
|
111 | 111 | |
|
|
112 | 112 | } |
| @@ -1,56 +1,61 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Optional; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
6 | 6 | import org.gradle.api.Action; |
|
|
7 | 7 | import org.gradle.api.InvalidUserDataException; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | 10 | * Resolves stateful slot assemblies from cheap slot identities. |
|
|
11 | 11 | * |
|
|
12 | 12 | * <p> |
|
|
13 | 13 | * The returned assembly is a materialized build-model handle. It may expose |
|
|
14 | 14 | * lazy Gradle providers, but |
|
|
15 | 15 | * it is no longer an identity object suitable for replayable discovery. |
|
|
16 | 16 | */ |
|
|
17 | 17 | @NonNullByDefault |
|
|
18 | 18 | public interface ArtifactAssemblies { |
|
|
19 | 19 | /** |
|
|
20 | 20 | * Resolves the assembly for the given slot. |
|
|
21 | 21 | * |
|
|
22 | 22 | * <p> |
|
|
23 | 23 | * This call materializes the build-facing body of the slot from its identity. |
|
|
24 | 24 | * |
|
|
25 | 25 | * @param slot slot identity inside a variant outgoing contract |
|
|
26 | 26 | * @return assembly handle for the slot |
|
|
27 | 27 | */ |
|
|
28 | 28 | default ArtifactAssembly require(ArtifactSlot slot) { |
|
|
29 | 29 | return find(slot) |
|
|
30 | 30 | .orElseThrow(() -> new InvalidUserDataException("Artifact assembly '" + slot + "' isn't registered")); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | /** |
|
|
34 | 34 | * Registers a configuration action for the assembly of the given slot. |
|
|
35 | 35 | * |
|
|
36 | 36 | * <p>If the assembly is already registered, the action is invoked immediately. |
|
|
37 | 37 | * Otherwise, it is queued and invoked when slot materialization registers the |
|
|
38 | 38 | * assembly handle. |
|
|
39 | 39 | * |
|
|
40 | 40 | * @param slot slot identity inside a variant outgoing contract |
|
|
41 | 41 | * @param action assembly configuration action |
|
|
42 | 42 | */ |
|
|
43 | 43 | void when(ArtifactSlot slot, Action<? super ArtifactAssembly> action); |
|
|
44 | 44 | |
|
|
45 | 45 | /** |
|
|
46 | 46 | * Adds global configuration action for all materialized assemblies. If some assemblies are |
|
|
47 | 47 | * already registered, the action will be invoked for them immediately. For assemblies that |
|
|
48 | 48 | * are materialized later, the action will be invoked when they are registered. |
|
|
49 | 49 | * |
|
|
50 | 50 | * @param action assembly configuration action. |
|
|
51 | 51 | */ |
|
|
52 | 52 | void configureEach(Action<? super ArtifactAssembly> action); |
|
|
53 | 53 | |
|
|
54 | /** ΠΡΠ΅Ρ Π·Π°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΡΠΉ ΡΠ»ΠΎΡ */ | |
|
|
54 | /** | |
|
|
55 | * Finds a registered assembly for the given slot identity. | |
|
|
56 | * | |
|
|
57 | * @param slot slot identity inside a variant outgoing contract | |
|
|
58 | * @return registered assembly, if the slot body has already been materialized | |
|
|
59 | */ | |
|
|
55 | 60 | Optional<ArtifactAssembly> find(ArtifactSlot slot); |
|
|
56 | 61 | } |
| @@ -1,43 +1,43 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.artifacts.Configuration; |
|
|
5 | 5 | import org.implab.gradle.common.core.lang.Closures; |
|
|
6 | 6 | import org.implab.gradle.variants.core.Variant; |
|
|
7 | 7 | |
|
|
8 | 8 | import groovy.lang.Closure; |
|
|
9 | 9 | |
|
|
10 | 10 | /** |
|
|
11 | 11 | * Materialized root outgoing configuration of a variant. |
|
|
12 | 12 | * |
|
|
13 | 13 | * <p>This is a variant-level publication hook. Slot-specific publication state is exposed separately via |
|
|
14 |
* {@link Outgoing |
|
|
|
14 | * {@link OutgoingConfigurationSlotSpec}. | |
|
|
15 | 15 | */ |
|
|
16 | 16 | public interface OutgoingConfigurationSpec { |
|
|
17 | 17 | /** |
|
|
18 | 18 | * Returns the variant whose outgoing configuration is represented here. |
|
|
19 | 19 | * |
|
|
20 | 20 | * @return variant identity |
|
|
21 | 21 | */ |
|
|
22 | 22 | Variant getVariant(); |
|
|
23 | 23 | |
|
|
24 | 24 | /** |
|
|
25 | 25 | * Returns the root consumable outgoing configuration of the variant. |
|
|
26 | 26 | * |
|
|
27 | 27 | * @return outgoing configuration |
|
|
28 | 28 | */ |
|
|
29 | 29 | Configuration getConfiguration(); |
|
|
30 | 30 | |
|
|
31 | 31 | /** |
|
|
32 | 32 | * Applies a configuration action to the root outgoing configuration. |
|
|
33 | 33 | * |
|
|
34 | 34 | * @param action configuration action |
|
|
35 | 35 | */ |
|
|
36 | 36 | default void configuration(Action<? super Configuration> action) { |
|
|
37 | 37 | action.execute(getConfiguration()); |
|
|
38 | 38 | } |
|
|
39 | 39 | |
|
|
40 | 40 | default void configuration(Closure<?> closure) { |
|
|
41 | 41 | configuration(Closures.action(closure)); |
|
|
42 | 42 | } |
|
|
43 | 43 | } |
| @@ -1,50 +1,63 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.NamedDomainObjectContainer; |
|
|
5 | 5 | import org.gradle.api.NamedDomainObjectProvider; |
|
|
6 | 6 | import org.gradle.api.artifacts.Configuration; |
|
|
7 | 7 | import org.gradle.api.provider.Property; |
|
|
8 | 8 | import org.implab.gradle.variants.core.Variant; |
|
|
9 | 9 | |
|
|
10 | 10 | /** |
|
|
11 | * ΠΠΏΠΈΡΡΠ²Π°Π΅Ρ ΠΈΡΡ ΠΎΠ΄ΡΡΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ Π²Π°ΡΠΈΠ°Π½ΡΠ° | |
|
|
11 | * Plugin model object for one variant-level outgoing contract. | |
|
|
12 | 12 | * |
|
|
13 | * ΠΠ°Π΄Π°Π΅Ρ ΡΠ²ΡΠ·Ρ ΠΌΠ΅ΠΆΠ΄Ρ ΠΌΠΎΠ΄Π΅Π»ΡΡ Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ² ΠΈ ΠΌΠΎΠ΄Π΅Π»ΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΉ gradle ΡΠ΅ΡΠ΅Π· | |
|
|
14 | * ΡΠ²ΠΎΠΉΡΡΠ²ΠΎ {@link #getConfiguration()}. Π’Π°ΠΊΠΆΠ΅ Π·Π°Π΄Π°Π΅Ρ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΠΎΡΡ ΡΠ»ΠΎΡΠΎΠ² | |
|
|
15 | * ΠΏΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΠΈ, Π½ΠΎ Π½Π΅ Π·Π°Π΄Π°Π΅Ρ ΠΏΡΠ°Π²ΠΈΠ» ΡΠ²ΡΠ·ΡΠ²Π°Π½ΠΈΡ ΡΡΠΈΡ ΡΠ»ΠΎΡΠΎΠ² Ρ ΡΠ°ΠΌΠΎΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠ΅ΠΉ | |
|
|
16 | * ΠΈ ΠΈΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΡΠΌ. Π‘Π°ΠΌΡΠΉ ΠΏΡΠΎΡΡΠΎΠΉ Π²Π°ΡΠΈΠ°Π½Ρ ΡΡΠΎ {@link ArtifactAssemblies}. | |
|
|
13 | * <p>An outgoing variant connects a core {@link Variant} identity with the lazy | |
|
|
14 | * Gradle consumable configuration registered for that variant. It also exposes a | |
|
|
15 | * live container of slot identities. Slot identities are only declarations and do | |
|
|
16 | * not imply that a Gradle outgoing artifact variant or an {@link ArtifactAssembly} | |
|
|
17 | * has been materialized. | |
|
|
17 | 18 | */ |
|
|
18 | 19 | public interface OutgoingVariant { |
|
|
19 | 20 | /** |
|
|
20 | * ΠΡΡ ΠΎΠ΄Π½ΡΠΉ Π²Π°ΡΠΈΠ°Π½Ρ Π΄Π»Ρ ΠΊΠΎΡΠΎΡΠΎΠ³ΠΎ ΡΡΡΠΎΠΈΡΡΡ Outgoing ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ | |
|
|
21 | * Returns the variant that owns this outgoing contract. | |
|
|
22 | * | |
|
|
23 | * @return variant identity | |
|
|
21 | 24 | */ |
|
|
22 | 25 | Variant getVariant(); |
|
|
23 | 26 | |
|
|
24 | 27 | /** |
|
|
25 | * ΠΡΠΎΠ²Π°ΠΉΠ΄Π΅Ρ Π·Π°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°Π½Π½ΠΎΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ | |
|
|
28 | * Returns the provider of the registered consumable outgoing configuration. | |
|
|
29 | * | |
|
|
30 | * @return outgoing configuration provider | |
|
|
26 | 31 | */ |
|
|
27 | 32 | NamedDomainObjectProvider<? extends Configuration> getConfiguration(); |
|
|
28 | 33 | |
|
|
34 | /** | |
|
|
35 | * Configures the registered outgoing configuration. | |
|
|
36 | * | |
|
|
37 | * @param action configuration action | |
|
|
38 | */ | |
|
|
29 | 39 | default void configureOutgoing(Action<? super Configuration> action) { |
|
|
30 | 40 | getConfiguration().configure(action); |
|
|
31 | 41 | } |
|
|
32 | 42 | |
|
|
33 | 43 | /** |
|
|
34 | * Π‘Π»ΠΎΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ, Π΄Π°Π½Π½Π°Ρ ΠΊΠΎΠ»Π»Π΅ΠΊΡΠΈΡ ΠΆΠΈΠ²Π°Ρ, ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π»Ρ | |
|
|
35 | * ΠΏΠΎΠ»ΡΡΠ΅Π½ΠΈΡ ΠΈΠ½ΡΠΎΡΠΌΠ°ΡΠΈΠΈ ΠΎΠ± ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΡ ΡΠ»ΠΎΡΠ°Ρ , Π½ΠΎ ΡΡΠΈ ΡΠ»ΠΎΡΡ Π½Π΅ | |
|
|
36 | * ΠΎΠ±ΡΠ·Π°Π½Ρ Π±ΡΡΡ ΡΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠΈΡΠΎΠ²Π°Π½Ρ, Ρ.Π΅. ΡΡΠΎ ΡΠΎΠ»ΡΠΊΠΎ Identity. | |
|
|
44 | * Returns the live slot identity container. | |
|
|
45 | * | |
|
|
46 | * <p>This collection is intended for discovery and selection. Slot presence does | |
|
|
47 | * not guarantee that the slot has a configured assembly body or a materialized | |
|
|
48 | * Gradle outgoing artifact variant. | |
|
|
37 | 49 | * |
|
|
38 | 50 | * @see {@link ArtifactSlot} |
|
|
39 | 51 | */ |
|
|
40 | 52 | NamedDomainObjectContainer<Slot> getSlots(); |
|
|
41 | 53 | |
|
|
42 | 54 | /** |
|
|
43 | * ΠΡΠ½ΠΎΠ²Π½ΠΎΠΉ Π½Π°Π±ΠΎΡ Π°ΡΡΠ΅ΡΠ°ΠΊΡΠΎΠ² (primary variant) Π΄Π»Ρ ΠΈΡΡ ΠΎΠ΄ΡΡΠ΅ΠΉ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ | |
|
|
55 | * Returns the primary slot property. | |
|
|
44 | 56 | * |
|
|
45 | * <p> | |
|
|
46 | * ΠΡΠ»ΠΈ Π² ΡΠ²ΠΎΠΉΡΡΠ²Π΅ {@link #getSlots()} Π΅ΡΡΡ ΡΠΎΠ»ΡΠΊΠΎ ΠΎΠ΄ΠΈΠ½ ΡΠ»ΠΎΠΉ, ΡΠΎ ΠΏΠΎ ΠΊΠΎΠ½Π²Π΅Π½ΡΠΈΠΈ ΠΎΠ½ | |
|
|
47 | * ΡΡΠΈΡΠ°Π΅ΡΡΡ ΡΠ°ΠΊΠΆΠ΅ ΠΎΡΠ½ΠΎΠ²Π½ΡΠΌ. | |
|
|
57 | * <p>If exactly one slot is declared, the single-slot convention uses that slot as | |
|
|
58 | * the primary one. Reading this property finalizes the selected primary slot. | |
|
|
59 | * | |
|
|
60 | * @return primary slot property | |
|
|
48 | 61 | */ |
|
|
49 | 62 | Property<Slot> getPrimarySlot(); |
|
|
50 | 63 | } |
| @@ -1,36 +1,87 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Optional; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.gradle.api.Action; |
|
|
6 | 6 | import org.gradle.api.InvalidUserDataException; |
|
|
7 | 7 | import org.implab.gradle.variants.core.Variant; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | * ΠΠΎΠ½ΡΠ΅ΠΊΡΡ ΡΠ°Π±ΠΎΡΡ Ρ Π²Π°ΡΠΈΠ°Π½ΡΠ°ΠΌΠΈ ΠΏΡΠ±Π»ΠΈΠΊΠ°ΡΠΈΠΈ, ΡΡΠ°Π½ΠΎΠ²ΠΈΡΡΡ Π΄ΠΎΡΡΡΠΏΠ½ΡΠΌ ΠΏΠΎΡΠ»Π΅ | |
|
|
11 | * ΡΠΈΠ½Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΠΌΠΎΠ΄Π΅Π»ΠΈ Π²Π°ΡΠΈΠ°Π½ΡΠΎΠ². Π€Π°ΠΊΡΠΈΡΠ΅ΡΠΊΠΈ ΡΠ²Π»ΡΠ΅ΡΡΡ ΠΆΠΈΠ²ΠΎΠΉ ΠΌΠΎΠ΄Π΅Π»ΡΡ | |
|
|
10 | * Live context for declaring and observing variant outgoing publications. | |
|
|
11 | * | |
|
|
12 | * <p>The context becomes available after the core variant model has been finalized. | |
|
|
13 | * It owns variant-level outgoing declarations, assembly lookup, and hooks for | |
|
|
14 | * materialized Gradle-facing publication state. | |
|
|
12 | 15 | */ |
|
|
13 | 16 | public interface OutgoingVariantsContext { |
|
|
14 | 17 | |
|
|
18 | /** | |
|
|
19 | * Returns the assembly lookup service. | |
|
|
20 | * | |
|
|
21 | * <p>Assemblies are stateful slot bodies resolved from cheap {@link ArtifactSlot} | |
|
|
22 | * identities. | |
|
|
23 | * | |
|
|
24 | * @return assembly lookup service | |
|
|
25 | */ | |
|
|
15 | 26 | ArtifactAssemblies getAssemblies(); |
|
|
16 | 27 | |
|
|
28 | /** | |
|
|
29 | * Configures artifact slots of the given variant. | |
|
|
30 | * | |
|
|
31 | * @param variant variant identity | |
|
|
32 | * @param action variant artifact declaration | |
|
|
33 | */ | |
|
|
17 | 34 | void configureVariant(Variant variant, Action<? super VariantArtifactsSpec> action); |
|
|
18 | 35 | |
|
|
19 | 36 | /** |
|
|
20 | * Replayable hook Π΄Π»Ρ Π²ΡΠ΅Ρ ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΉ | |
|
|
37 | * Registers a replayable action for declared outgoing variants. | |
|
|
38 | * | |
|
|
39 | * <p>The action receives the plugin model object, not necessarily a materialized | |
|
|
40 | * Gradle publication. Use {@link #whenOutgoingConfiguration(Action)} and | |
|
|
41 | * {@link #whenOutgoingSlot(Action)} for materialized Gradle-facing state. | |
|
|
42 | * | |
|
|
43 | * @param action outgoing variant action | |
|
|
21 | 44 | */ |
|
|
22 | 45 | void configureEach(Action<? super OutgoingVariant> action); |
|
|
23 | 46 | |
|
|
47 | /** | |
|
|
48 | * Finds the outgoing model for the given variant. | |
|
|
49 | * | |
|
|
50 | * @param variant variant identity | |
|
|
51 | * @return outgoing model, if declared | |
|
|
52 | */ | |
|
|
24 | 53 | Optional<OutgoingVariant> findOutgoing(Variant variant); |
|
|
25 | 54 | |
|
|
55 | /** | |
|
|
56 | * Requires the outgoing model for the given variant. | |
|
|
57 | * | |
|
|
58 | * @param variant variant identity | |
|
|
59 | * @return outgoing model | |
|
|
60 | * @throws InvalidUserDataException if the outgoing variant is not declared | |
|
|
61 | */ | |
|
|
26 | 62 | default OutgoingVariant requireOutgoing(Variant variant) { |
|
|
27 | 63 | return findOutgoing(variant) |
|
|
28 | 64 | .orElseThrow(() -> new InvalidUserDataException("Outgoing variant '" + variant + "' isn't registered")); |
|
|
29 | 65 | } |
|
|
30 | 66 | |
|
|
67 | /** | |
|
|
68 | * Registers a replayable action for materialized variant-level outgoing | |
|
|
69 | * configurations. | |
|
|
70 | * | |
|
|
71 | * @param action materialized outgoing configuration action | |
|
|
72 | */ | |
|
|
31 | 73 | void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action); |
|
|
32 | 74 | |
|
|
75 | /** | |
|
|
76 | * Registers a replayable action for materialized slot publications. | |
|
|
77 | * | |
|
|
78 | * <p>Slot publication hooks follow the Gradle outgoing publication model. A | |
|
|
79 | * declared slot identity by itself does not guarantee that a slot publication has | |
|
|
80 | * been materialized. | |
|
|
81 | * | |
|
|
82 | * @param action materialized slot publication action | |
|
|
83 | */ | |
|
|
33 | 84 | void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action); |
|
|
34 | 85 | |
|
|
35 | 86 | |
|
|
36 | 87 | } |
| @@ -1,40 +1,38 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import groovy.lang.Closure; |
|
|
5 | 5 | import org.implab.gradle.common.core.lang.Closures; |
|
|
6 | 6 | |
|
|
7 | 7 | /** |
|
|
8 | 8 | * DSL model for declaring slots of a single variant. |
|
|
9 | 9 | * |
|
|
10 | 10 | * <p>The surrounding variant defines the external outgoing contract. Slots declared here define artifact |
|
|
11 | 11 | * sets within that contract. If a variant exposes more than one slot, one of them is expected to be the |
|
|
12 | 12 | * primary slot. |
|
|
13 | 13 | */ |
|
|
14 | 14 | public interface VariantArtifactsSpec { |
|
|
15 | 15 | /** |
|
|
16 | 16 | * Declares a non-primary slot of the current variant. |
|
|
17 | 17 | * |
|
|
18 | 18 | * @param name slot name |
|
|
19 | 19 | * @param action slot declaration |
|
|
20 | * @return slot identity | |
|
|
21 | 20 | */ |
|
|
22 | 21 | void slot(String name, Action<? super ArtifactAssemblySpec> action); |
|
|
23 | 22 | |
|
|
24 | 23 | default void slot(String name, Closure<?> closure) { |
|
|
25 | 24 | slot(name, Closures.action(closure)); |
|
|
26 | 25 | } |
|
|
27 | 26 | |
|
|
28 | 27 | /** |
|
|
29 | 28 | * Declares the primary slot of the current variant. |
|
|
30 | 29 | * |
|
|
31 | 30 | * @param name slot name |
|
|
32 | 31 | * @param action slot declaration |
|
|
33 | * @return slot identity | |
|
|
34 | 32 | */ |
|
|
35 | 33 | void primarySlot(String name, Action<? super ArtifactAssemblySpec> action); |
|
|
36 | 34 | |
|
|
37 | 35 | default void primarySlot(String name, Closure<?> closure) { |
|
|
38 | 36 | primarySlot(name, Closures.action(closure)); |
|
|
39 | 37 | } |
|
|
40 | 38 | } |
| @@ -1,56 +1,56 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
4 | 4 | import org.gradle.api.Action; |
|
|
5 | 5 | import org.implab.gradle.variants.artifacts.ArtifactAssemblies; |
|
|
6 | 6 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
7 | 7 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
8 | 8 | |
|
|
9 | 9 | /** |
|
|
10 | * Π‘Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΎΠΏΠΈΡΠ°Π½ΠΈΠ΅ ΠΈΡΡ ΠΎΠ΄ΡΡΠΈΡ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΉ gradle ΠΈ ΡΠ±ΠΎΡΠΊΡ ΡΠΎΠ΄Π΅ΡΠΆΠΈΠΌΠΎΠ³ΠΎ ΡΠ»ΠΎΡΠΎΠ² | |
|
|
11 | * ΠΈΠ· {@link ArtifactAssemblies} | |
|
|
10 | * Binds materialized slot assemblies to Gradle outgoing publications. | |
|
|
12 | 11 | */ |
|
|
13 | 12 | @NonNullByDefault |
|
|
14 | 13 | public class ArtifactAssemblyBinder implements Action<OutgoingVariant> { |
|
|
15 | 14 | |
|
|
16 | 15 | private final ArtifactAssemblies resolver; |
|
|
17 | 16 | |
|
|
18 | 17 | public ArtifactAssemblyBinder(ArtifactAssemblies resolver) { |
|
|
19 | 18 | this.resolver = resolver; |
|
|
20 | 19 | } |
|
|
21 | 20 | |
|
|
22 | 21 | @Override |
|
|
23 | 22 | public void execute(OutgoingVariant outgoingVariant) { |
|
|
24 | 23 | var slots = outgoingVariant.getSlots(); |
|
|
25 | 24 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); |
|
|
26 | 25 | var variant = outgoingVariant.getVariant(); |
|
|
27 | 26 | |
|
|
28 | // ΡΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ | |
|
|
27 | // Bind publication state when the owning configuration is materialized. | |
|
|
29 | 28 | outgoingVariant.configureOutgoing(configuration -> { |
|
|
30 | 29 | var primarySlot = primarySlotProvider.get(); |
|
|
31 | 30 | var outgoing = configuration.getOutgoing(); |
|
|
32 | 31 | |
|
|
33 | // ΡΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠΉ Π²Π°ΡΠΈΠ°Π½Ρ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΠΈ | |
|
|
32 | // Bind the primary artifact set to the root outgoing configuration. | |
|
|
34 | 33 | resolver.when( |
|
|
35 | 34 | new ArtifactSlot(variant, primarySlot), |
|
|
36 | 35 | assembly -> outgoing.artifact(assembly.getArtifact())); |
|
|
37 | 36 | |
|
|
38 | // Π΄Π»Ρ Π²ΡΠ΅Ρ ΠΎΠ±ΡΡΠ²Π»Π΅Π½Π½ΡΡ ΡΠ»ΠΎΡΠΎΠ² | |
|
|
37 | // Bind non-primary slots to Gradle secondary artifact variants. | |
|
|
39 | 38 | slots.all(slot -> { |
|
|
40 | // ΠΊΡΠΎΠΌΠ΅ ΠΎΡΠ½ΠΎΠ²Π½ΠΎΠ³ΠΎ | |
|
|
41 | 39 | if (slot.equals(primarySlot)) |
|
|
42 | 40 | return; |
|
|
43 | 41 | |
|
|
44 | // ΡΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ Π°ΡΡΠ΅ΡΠ°ΠΊΡΡ | |
|
|
45 | 42 | resolver.when( |
|
|
46 | 43 | new ArtifactSlot(variant, slot), |
|
|
44 | // Gradle artifact variants must be created while the owning | |
|
|
45 | // configuration is being materialized. Lazy registration may | |
|
|
46 | // otherwise be realized only after dependency resolution starts. | |
|
|
47 | 47 | assembly -> outgoing.getVariants() |
|
|
48 |
. |
|
|
|
49 |
|
|
|
|
48 | .create(slot.getName()) | |
|
|
49 | .artifact(assembly.getArtifact())); | |
|
|
50 | 50 | }); |
|
|
51 | 51 | }); |
|
|
52 | 52 | } |
|
|
53 | 53 | |
|
|
54 | 54 | |
|
|
55 | 55 | |
|
|
56 | 56 | } |
| @@ -1,218 +1,202 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashMap; |
|
|
4 | 4 | import java.util.HashSet; |
|
|
5 | 5 | import java.util.Map; |
|
|
6 | 6 | import java.util.Set; |
|
|
7 | 7 | |
|
|
8 | 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
9 | 9 | import org.gradle.api.Action; |
|
|
10 | 10 | import org.gradle.api.file.ConfigurableFileCollection; |
|
|
11 | 11 | import org.gradle.api.file.Directory; |
|
|
12 | 12 | import org.gradle.api.file.DirectoryProperty; |
|
|
13 | 13 | import org.gradle.api.file.FileCollection; |
|
|
14 | 14 | import org.gradle.api.model.ObjectFactory; |
|
|
15 | 15 | import org.gradle.api.provider.Provider; |
|
|
16 | 16 | import org.gradle.api.tasks.Sync; |
|
|
17 | 17 | import org.gradle.api.tasks.TaskContainer; |
|
|
18 | 18 | import org.gradle.language.base.plugins.LifecycleBasePlugin; |
|
|
19 | 19 | import org.implab.gradle.common.core.lang.FilePaths; |
|
|
20 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; | |
|
|
20 | 21 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
|
21 | 22 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
22 | 23 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
23 | 24 | import org.implab.gradle.variants.sources.CompileUnitsView; |
|
|
24 | 25 | import org.implab.gradle.variants.sources.RoleProjectionsView; |
|
|
25 | 26 | import org.implab.gradle.variants.sources.SourceSetMaterializer; |
|
|
26 | 27 | |
|
|
27 | 28 | /** |
|
|
28 | * ΠΠ΄Π°ΠΏΡΠ΅Ρ ΠΌΠ΅ΠΆΠ΄Ρ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ°ΠΌΠΈ Π°ΡΡΠ΅ΡΠ°ΠΊΡΠΎΠ², ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½Π½ΡΠΌΠΈ Π² Π²ΠΈΠ΄Π΅ | |
|
|
29 | * {@link SlotContribution}, | |
|
|
30 | * ΠΈ ArtifactAssemblyRegistry, ΠΊΠΎΡΠΎΡΡΠΉ ΠΎΠΏΠ΅ΡΠΈΡΡΠ΅Ρ ΡΠΆΠ΅ ΡΠΎΠ±ΡΠ°Π½Π½ΡΠΌΠΈ | |
|
|
31 | * {@link ArtifactAssembly}. | |
|
|
29 | * Adapts slot contribution declarations to materialized {@link ArtifactAssembly} | |
|
|
30 | * handles. | |
|
|
32 | 31 | * |
|
|
33 | * ΠΠ°Π½Π½ΡΠΉ ΠΊΠ»Π°ΡΡ ΠΎΡΠ²Π΅ΡΠ°Π΅Ρ Π·Π° ΡΠ±ΠΎΡΠΊΡ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΡ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠΎΠ² Π°ΡΡΠ΅ΡΠ°ΠΊΡΠΎΠ² Π² Π΅Π΄ΠΈΠ½ΡΠΉ | |
|
|
34 | * ΠΊΠ°ΡΠ°Π»ΠΎΠ³, ΠΊΠΎΡΠΎΡΡΠΉ Π·Π°ΡΠ΅ΠΌ ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ Π² ArtifactAssemblyRegistry Π² Π²ΠΈΠ΄Π΅ | |
|
|
35 | * {@link ArtifactAssembly}. Π‘Π±ΠΎΡΠΊΠ° ΠΊΠΎΠ½Π΅ΡΠ½ΠΎΠ³ΠΎ Π°ΡΡΠ΅ΡΠ°ΠΊΡΠ° ΠΏΡΠΎΠΈΡΡ ΠΎΠ΄ΠΈΡ ΠΏΡΠΈ ΠΏΠΎΠΌΠΎΡΠΈ | |
|
|
36 | * Π·Π°Π΄Π°ΡΠΈ, ΠΊΠΎΡΠΎΡΠ°Ρ ΠΊΠΎΠΏΠΈΡΡΠ΅Ρ Π²ΡΠ΅ Π²Ρ ΠΎΠ΄Π½ΡΠ΅ ΡΠ°ΠΉΠ»Ρ Π² Π²ΡΡ ΠΎΠ΄Π½ΠΎΠΉ ΠΊΠ°ΡΠ°Π»ΠΎΠ³. ΠΠ°Π΄Π°ΡΠ° | |
|
|
37 | * ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ {@link ArtifactSlot} ΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅Ρ | |
|
|
38 | * {@link SlotAssembly#inputs()} ΠΊΠ°ΠΊ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ Π²Ρ ΠΎΠ΄Π½ΡΡ Π΄Π°Π½Π½ΡΡ . | |
|
|
32 | * <p>The handler creates one {@link Sync} task per {@link ArtifactSlot}. The task | |
|
|
33 | * copies all collected slot inputs into a single output directory. That output | |
|
|
34 | * directory is then registered in {@link ArtifactAssemblyRegistry} as the | |
|
|
35 | * published artifact for the slot. | |
|
|
39 | 36 | * |
|
|
40 | * ΠΠ»Ρ ΡΠ±ΠΎΡΠΊΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΏΠ°ΡΡΠ΅ΡΠ½ Visitor: ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΡΠ°Π³ΠΌΠ΅Π½Ρ Π°ΡΡΠ΅ΡΠ°ΠΊΡΠ° | |
|
|
41 | * ΠΏΡΠ΅Π΄ΡΡΠ°Π²Π»Π΅Π½ Π² Π²ΠΈΠ΄Π΅ ΡΠ΅Π°Π»ΠΈΠ·Π°ΡΠΈΠΈ ΠΈΠ½ΡΠ΅ΡΡΠ΅ΠΉΡΠ° {@link SlotContribution}, ΠΊΠΎΡΠΎΡΡΠΉ | |
|
|
42 | * ΠΈΠΌΠ΅Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄ accept, ΠΏΡΠΈΠ½ΠΈΠΌΠ°ΡΡΠΈΠΉ Visitor. | |
|
|
43 | * Visitor ΡΠ΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ Π²ΠΎ Π²Π½ΡΡΡΠ΅Π½Π½Π΅ΠΌ ΠΊΠ»Π°ΡΡΠ΅ {@link ContributionVisitor}, ΠΊΠΎΡΠΎΡΡΠΉ | |
|
|
44 | * Π·Π½Π°Π΅Ρ, ΠΊΠ°ΠΊ ΠΎΠ±ΡΠ°Π±Π°ΡΡΠ²Π°ΡΡ ΠΊΠ°ΠΆΠ΄ΡΠΉ ΡΠΈΠΏ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ° ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡ Π΅Π³ΠΎ Π² ΡΠ±ΠΎΡΠΊΡ. | |
|
|
45 | * ΠΠ»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ {@link SlotContribution} ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ ΠΊΠ»ΡΡ {@link SlotInputKey}, | |
|
|
46 | * ΠΊΠΎΡΠΎΡΡΠΉ ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ Π΄Π»Ρ Π΄Π΅Π΄ΡΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈ: Π΅ΡΠ»ΠΈ ΡΡΠ°Π³ΠΌΠ΅Π½Ρ Ρ ΡΠ°ΠΊΠΈΠΌ ΠΆΠ΅ ΠΊΠ»ΡΡΠΎΠΌ ΡΠΆΠ΅ | |
|
|
47 | * Π±ΡΠ» Π΄ΠΎΠ±Π°Π²Π»Π΅Π½, ΡΠΎ ΠΎΠ½ ΠΈΠ³Π½ΠΎΡΠΈΡΡΠ΅ΡΡΡ. | |
|
|
37 | * <p>Input collection uses {@link SlotContributionVisitor}. Each contribution is | |
|
|
38 | * converted to a {@link SlotInputKey}; duplicate keys are ignored so that repeated | |
|
|
39 | * topology-based selections do not add the same input twice. | |
|
|
48 | 40 | */ |
|
|
49 | 41 | @NonNullByDefault |
|
|
50 | 42 | public class ArtifactAssemblyHandler { |
|
|
51 | 43 | private final ObjectFactory objects; |
|
|
52 | 44 | |
|
|
53 | 45 | private final ArtifactAssemblyRegistry assemblyRegistry; |
|
|
54 | 46 | |
|
|
55 | 47 | private final DirectoryProperty assembliesDirectory; |
|
|
56 | 48 | |
|
|
57 | 49 | private final TaskContainer tasks; |
|
|
58 | 50 | |
|
|
59 | 51 | private final CompileUnitsView compileUnitsView; |
|
|
60 | 52 | |
|
|
61 | 53 | private final RoleProjectionsView roleProjectionsView; |
|
|
62 | 54 | |
|
|
63 | 55 | private final SourceSetMaterializer sourceSetMaterializer; |
|
|
64 | 56 | |
|
|
65 | 57 | private final Map<ArtifactSlot, SlotAssembly> slotInputs = new HashMap<>(); |
|
|
66 | 58 | |
|
|
67 | 59 | public ArtifactAssemblyHandler( |
|
|
68 | 60 | ObjectFactory objects, |
|
|
69 | 61 | TaskContainer tasks, |
|
|
70 | 62 | ArtifactAssemblyRegistry assemblyRegistry, |
|
|
71 | 63 | CompileUnitsView compileUnitsView, |
|
|
72 | 64 | RoleProjectionsView roleProjectionsView, |
|
|
73 | 65 | SourceSetMaterializer sourceSetMaterializer) { |
|
|
74 | 66 | this.objects = objects; |
|
|
75 | 67 | this.tasks = tasks; |
|
|
76 | 68 | this.assemblyRegistry = assemblyRegistry; |
|
|
77 | 69 | this.compileUnitsView = compileUnitsView; |
|
|
78 | 70 | this.roleProjectionsView = roleProjectionsView; |
|
|
79 | 71 | this.sourceSetMaterializer = sourceSetMaterializer; |
|
|
80 | 72 | |
|
|
81 | 73 | assembliesDirectory = objects.directoryProperty(); |
|
|
82 | 74 | } |
|
|
83 | 75 | |
|
|
84 | 76 | public DirectoryProperty getAssembliesDirectory() { |
|
|
85 | 77 | return assembliesDirectory; |
|
|
86 | 78 | } |
|
|
87 | 79 | |
|
|
88 | 80 | public void configureAssembly(ArtifactSlot artifactSlot, Action<? super ArtifactAssemblySpec> action) { |
|
|
89 | 81 | var visitor = contributionVisitor(artifactSlot); |
|
|
90 | 82 | var spec = new DefaultArtifactAssemblySpec(objects, c -> c.accept(visitor)); |
|
|
91 | 83 | action.execute(spec); |
|
|
92 | 84 | } |
|
|
93 | 85 | |
|
|
94 | 86 | public SlotContributionVisitor contributionVisitor(ArtifactSlot artifactSlot) { |
|
|
95 | 87 | var assembly = slotInputs.computeIfAbsent(artifactSlot, this::createSlotAssembly); |
|
|
96 | 88 | return new ContributionVisitor(artifactSlot, assembly); |
|
|
97 | 89 | } |
|
|
98 | 90 | |
|
|
99 | 91 | /** |
|
|
100 | * Π‘ΠΎΠ·Π΄Π°Π΅Ρ ΡΠ±ΠΎΡΠΊΡ Π΄Π»Ρ ΡΠΊΠ°Π·Π°Π½Π½ΠΎΠ³ΠΎ ΡΠ»ΠΎΡΠ° Π°ΡΡΠ΅ΡΠ°ΠΊΡΠ°, ΡΠ±ΠΎΡΠΊΠ° ΡΠ΅Π³ΠΈΡΡΡΠΈΡΡΠ΅ΡΡΡ Π² | |
|
|
101 | * ArtifactAssemblyRegistry, Π΅ΡΠ»ΠΈ Π΄Π»Ρ ΡΠ»ΠΎΡΠ° ΡΠ±ΠΎΡΠΊΠ° ΡΠΆΠ΅ Π±ΡΠ»Π° Π·Π°ΡΠ΅Π³ΠΈΡΡΡΠΈΡΠΎΠ²Π°Π½Π° | |
|
|
102 | * ΠΊΠ΅ΠΌ-ΡΠΎ Π΅ΡΠ΅, ΡΠΎ Π²ΠΎΠ·Π½ΠΈΠΊΠ°Π΅Ρ ΠΎΡΠΈΠ±ΠΊΠ°. | |
|
|
92 | * Creates the assembly task for the given slot and registers its output artifact. | |
|
|
103 | 93 | */ |
|
|
104 | 94 | private SlotAssembly createSlotAssembly(ArtifactSlot artifactSlot) { |
|
|
105 | 95 | var assembly = new SlotAssembly(); |
|
|
106 | 96 | var fileCollection = assembly.inputs(); |
|
|
107 | 97 | |
|
|
108 | 98 | var outputDirectory = outputDirectory(artifactSlot); |
|
|
109 | 99 | |
|
|
110 | 100 | var task = tasks.register(assembleTaskName(artifactSlot), Sync.class, copy -> { |
|
|
111 | 101 | copy.setGroup(LifecycleBasePlugin.BUILD_GROUP); |
|
|
112 | 102 | copy.into(outputDirectory); |
|
|
113 | 103 | copy.from(fileCollection); |
|
|
114 | 104 | }); |
|
|
115 | 105 | |
|
|
116 | 106 | assemblyRegistry.register(artifactSlot, task, t -> outputDirectory); |
|
|
117 | 107 | |
|
|
118 | 108 | return assembly; |
|
|
119 | 109 | } |
|
|
120 | 110 | |
|
|
121 | 111 | private String assembleTaskName(ArtifactSlot artifactSlot) { |
|
|
122 | 112 | var variantName = artifactSlot.variant().getName(); |
|
|
123 | 113 | var slotName = artifactSlot.slot().getName(); |
|
|
124 | 114 | |
|
|
125 | 115 | return "assembleVariantArtifactSlot" |
|
|
126 | 116 | + "_v" + variantName.length() + "_" + variantName |
|
|
127 | 117 | + "_s" + slotName.length() + "_" + slotName; |
|
|
128 | 118 | } |
|
|
129 | 119 | |
|
|
130 | 120 | private Provider<Directory> outputDirectory(ArtifactSlot artifactSlot) { |
|
|
131 | 121 | return assembliesDirectory.dir( |
|
|
132 | 122 | FilePaths.cat( |
|
|
133 | 123 | artifactSlot.variant().getName(), |
|
|
134 | 124 | artifactSlot.slot().getName())); |
|
|
135 | 125 | } |
|
|
136 | 126 | |
|
|
137 | 127 | /** |
|
|
138 | * Π‘ΠΎΠ±ΠΈΡΠ°Π΅Ρ ΠΎΡΠ΄Π΅Π»ΡΠ½ΡΠ΅ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΡ Π°ΡΡΠ΅ΡΠ°ΠΊΡΠΎΠ² Π² Π΅Π΄ΠΈΠ½ΡΠΉ ΠΈΡΡΠΎΡΠ½ΠΈΠΊ | |
|
|
139 | * {@link ConfigurableFileCollection}. | |
|
|
140 | * ΠΠ»Ρ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠΎΠ² {@link SlotContribution} ΠΈΡΠΏΠΎΠ»ΡΠ·ΡΠ΅ΡΡΡ ΠΌΠ΅Ρ Π°Π½ΠΈΠ·ΠΌ Π΄Π΅Π΄ΡΠΏΠ»ΠΈΠΊΠ°ΡΠΈΠΈ: | |
|
|
141 | * Π΄Π»Ρ ΠΊΠ°ΠΆΠ΄ΠΎΠ³ΠΎ | |
|
|
142 | * Π²ΠΎΠ΄ΡΡΠ΅Π³ΠΎ ΡΡΠ°Π³ΠΌΠ΅Π½ΡΠ° ΡΠΎΠ·Π΄Π°Π΅ΡΡΡ ΠΊΠ»ΡΡ {@link SlotInputKey} ΠΈ Π΄ΠΎΠ±Π°Π²Π»ΡΡΡΡΡ | |
|
|
143 | * ΡΡΠ°Π³ΠΌΠ΅Π½ΡΡ ΡΠΎΠ»ΡΠΊΠΎ | |
|
|
144 | * Ρ ΡΠ½ΠΈΠΊΠ°Π»ΡΠ½ΡΠΌ ΠΊΠ»ΡΡΠΎΠΌ, ΠΏΠΎΠ²ΡΠΎΡΡ ΠΈΠ³Π½ΠΎΡΠΈΡΡΡΡΡΡ. | |
|
|
128 | * Collects slot contributions into one {@link ConfigurableFileCollection}. | |
|
|
145 | 129 | */ |
|
|
146 | 130 | private class ContributionVisitor implements SlotContributionVisitor { |
|
|
147 | 131 | // artifact slot for this assembly |
|
|
148 | 132 | private final ArtifactSlot artifactSlot; |
|
|
149 | 133 | |
|
|
150 | 134 | // seen inputs, used for deduplication |
|
|
151 | 135 | private final SlotAssembly assembly; |
|
|
152 | 136 | |
|
|
153 | 137 | ContributionVisitor(ArtifactSlot artifactSlot, SlotAssembly assembly) { |
|
|
154 | 138 | this.artifactSlot = artifactSlot; |
|
|
155 | 139 | this.assembly = assembly; |
|
|
156 | 140 | } |
|
|
157 | 141 | |
|
|
158 | 142 | @Override |
|
|
159 | 143 | public void visit(DirectContribution contribution) { |
|
|
160 | 144 | contribute( |
|
|
161 | 145 | SlotInputKey.newUniqueKey("Direct input for " + artifactSlot), |
|
|
162 | 146 | contribution.input()); |
|
|
163 | 147 | } |
|
|
164 | 148 | |
|
|
165 | 149 | @Override |
|
|
166 | 150 | public void visit(VariantOutputsContribution contribution) { |
|
|
167 | 151 | var units = compileUnitsView.getUnitsForVariant(artifactSlot.variant()); |
|
|
168 | 152 | contributeCompileUnits(units, contribution.outputs()); |
|
|
169 | 153 | } |
|
|
170 | 154 | |
|
|
171 | 155 | @Override |
|
|
172 | 156 | public void visit(RoleOutputsContribution contribution) { |
|
|
173 | 157 | var roleProjection = roleProjectionsView.requireProjection(artifactSlot.variant(), |
|
|
174 | 158 | contribution.role()); |
|
|
175 | 159 | var units = roleProjectionsView.getUnits(roleProjection); |
|
|
176 | 160 | |
|
|
177 | 161 | contributeCompileUnits(units, contribution.outputs()); |
|
|
178 | 162 | |
|
|
179 | 163 | } |
|
|
180 | 164 | |
|
|
181 | 165 | @Override |
|
|
182 | 166 | public void visit(LayerOutputsContribution contribution) { |
|
|
183 | 167 | var unit = compileUnitsView.requireUnit(artifactSlot.variant(), contribution.layer()); |
|
|
184 | 168 | contributeCompileUnits(Set.of(unit), contribution.outputs()); |
|
|
185 | 169 | } |
|
|
186 | 170 | |
|
|
187 | 171 | private void contributeCompileUnits(Set<CompileUnit> units, Set<String> outputs) { |
|
|
188 | 172 | units.stream() |
|
|
189 | 173 | // expand variant compile units, make (compileUnit, outputName) pairs |
|
|
190 | 174 | .flatMap(unit -> outputs.stream() |
|
|
191 | 175 | .map(output -> new CompileUnitOutputKey(unit, output))) |
|
|
192 | 176 | .forEach(key -> contribute( |
|
|
193 | 177 | key, |
|
|
194 | 178 | sourceSetMaterializer.getSourceSet(key.unit()) |
|
|
195 | 179 | .map(s -> s.output(key.outputName())))); |
|
|
196 | 180 | } |
|
|
197 | 181 | |
|
|
198 | 182 | private void contribute(SlotInputKey key, Object resolvedInput) { |
|
|
199 | 183 | assembly.addSlotInput(key, resolvedInput); |
|
|
200 | 184 | } |
|
|
201 | 185 | } |
|
|
202 | 186 | |
|
|
203 | /** Π‘ΠΎΡΡΠΎΡΠ½ΠΈΠ΅ Π΄Π»Ρ ΠΎΡΠ΄Π΅Π»ΡΠ½ΠΎΠ³ΠΎ ΡΠ»ΠΎΡΠ° */ | |
|
|
187 | /** Mutable input state for one slot assembly. */ | |
|
|
204 | 188 | class SlotAssembly { |
|
|
205 | 189 | private final ConfigurableFileCollection inputs = objects.fileCollection(); |
|
|
206 | 190 | private final Set<SlotInputKey> seen = new HashSet<>(); |
|
|
207 | 191 | |
|
|
208 | 192 | public void addSlotInput(SlotInputKey key, Object input) { |
|
|
209 | 193 | if (!seen.add(key)) |
|
|
210 | 194 | return; |
|
|
211 | 195 | inputs.from(input); |
|
|
212 | 196 | } |
|
|
213 | 197 | |
|
|
214 | 198 | public FileCollection inputs() { |
|
|
215 | 199 | return inputs; |
|
|
216 | 200 | } |
|
|
217 | 201 | } |
|
|
218 | 202 | } |
| @@ -1,79 +1,81 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashSet; |
|
|
4 | 4 | import java.util.Set; |
|
|
5 | 5 | import java.util.function.Consumer; |
|
|
6 | 6 | import java.util.stream.Stream; |
|
|
7 | 7 | |
|
|
8 | 8 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
9 | 9 | import org.gradle.api.Action; |
|
|
10 | 10 | import org.gradle.api.model.ObjectFactory; |
|
|
11 | 11 | import org.implab.gradle.common.core.lang.Strings; |
|
|
12 | 12 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
|
13 | 13 | import org.implab.gradle.variants.core.Layer; |
|
|
14 | 14 | import org.implab.gradle.variants.core.Role; |
|
|
15 | 15 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; |
|
|
16 | 16 | |
|
|
17 | 17 | /** |
|
|
18 | * Π Π΅Π°Π»ΠΈΠ·Π°ΡΠΈΡ DSL ΠΌΠΎΠ΄Π΅Π»ΠΈ, ΡΡΡΠΎΠΈΡ Π½Π°Π±ΠΎΡ {@link SlotContribution}. ΠΡΠΈ ΠΏΠΎΡΡΡΠΎΠ΅Π½ΠΈΠΈ Π½Π°Π±ΠΎΡΠ° | |
|
|
19 | * ΠΏΡΠ°Π²ΠΈΠ»Π° ΠΈ ΠΊΠΎΡΡΠ΅ΠΊΡΠ½ΠΎΡΡΡ Π½Π΅ ΠΏΡΠΎΠ²Π΅ΡΡΡΡΡΡ. ΠΠΎ ΠΎΠΊΠΎΠ½ΡΠ°Π½ΠΈΠΈ ΠΈΡΠΏΠΎΠ»ΡΠ·ΠΎΠ²Π°Π½ΠΈΡ ΠΊΠ»ΠΈΠ΅Π½Ρ | |
|
|
20 | * Π²ΡΠ·ΡΠ²Π°Π΅Ρ ΠΌΠ΅ΡΠΎΠ΄ {@link #process(Consumer)} Π΄Π»Ρ ΠΎΠ±ΡΠ°Π±ΠΎΡΠΊΠΈ ΡΠ΅Π·ΡΠ»ΡΡΠ°ΡΠΎΠ². | |
|
|
18 | * Default DSL facade for collecting {@link SlotContribution} declarations. | |
|
|
21 | 19 | * |
|
|
20 | * <p>The spec does not validate topology references immediately. It translates DSL | |
|
|
21 | * calls to contribution objects and passes them to the supplied consumer; semantic | |
|
|
22 | * validation happens later when the assembly handler resolves contributions | |
|
|
23 | * against the finalized source model. | |
|
|
22 | 24 | */ |
|
|
23 | 25 | @NonNullByDefault |
|
|
24 | 26 | final class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { |
|
|
25 | 27 | private final Consumer<? super SlotContribution> consumer; |
|
|
26 | 28 | private final ObjectFactory objectFactory; |
|
|
27 | 29 | |
|
|
28 | 30 | DefaultArtifactAssemblySpec(ObjectFactory objectFactory, Consumer<? super SlotContribution> consumer) { |
|
|
29 | 31 | this.consumer = consumer; |
|
|
30 | 32 | this.objectFactory = objectFactory; |
|
|
31 | 33 | } |
|
|
32 | 34 | |
|
|
33 | 35 | @Override |
|
|
34 | 36 | public void from(Object artifact) { |
|
|
35 | 37 | consumer.accept(new DirectContribution(artifact)); |
|
|
36 | 38 | } |
|
|
37 | 39 | |
|
|
38 | 40 | @Override |
|
|
39 | 41 | public void fromVariant(Action<? super OutputSelectionSpec> action) { |
|
|
40 | 42 | consumer.accept(new VariantOutputsContribution(outputs(action))); |
|
|
41 | 43 | } |
|
|
42 | 44 | |
|
|
43 | 45 | @Override |
|
|
44 | 46 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { |
|
|
45 | 47 | |
|
|
46 | 48 | consumer.accept(new RoleOutputsContribution( |
|
|
47 | 49 | objectFactory.named(Role.class, roleName), |
|
|
48 | 50 | outputs(action))); |
|
|
49 | 51 | } |
|
|
50 | 52 | |
|
|
51 | 53 | @Override |
|
|
52 | 54 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { |
|
|
53 | 55 | consumer.accept(new LayerOutputsContribution( |
|
|
54 | 56 | objectFactory.named(Layer.class, layerName), |
|
|
55 | 57 | outputs(action))); |
|
|
56 | 58 | } |
|
|
57 | 59 | |
|
|
58 | 60 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { |
|
|
59 | 61 | var spec = new OutputsSetSpec(); |
|
|
60 | 62 | action.execute(spec); |
|
|
61 | 63 | return spec.outputs(); |
|
|
62 | 64 | } |
|
|
63 | 65 | |
|
|
64 | 66 | private static class OutputsSetSpec implements OutputSelectionSpec { |
|
|
65 | 67 | private final Set<String> outputs = new HashSet<>(); |
|
|
66 | 68 | |
|
|
67 | 69 | @Override |
|
|
68 | 70 | public void output(String name, String... extra) { |
|
|
69 | 71 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
|
70 | 72 | .map(Strings::requireNonBlank) |
|
|
71 | 73 | .forEach(outputs::add); |
|
|
72 | 74 | } |
|
|
73 | 75 | |
|
|
74 | 76 | Set<String> outputs() { |
|
|
75 | 77 | return Set.copyOf(outputs); |
|
|
76 | 78 | } |
|
|
77 | 79 | |
|
|
78 | 80 | } |
|
|
79 | 81 | } |
| @@ -1,107 +1,112 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
4 | 4 | import org.gradle.api.Action; |
|
|
5 | 5 | import org.gradle.api.artifacts.Configuration; |
|
|
6 | 6 | import org.gradle.api.attributes.AttributeContainer; |
|
|
7 | import org.implab.gradle.internal.ReplayableQueue; | |
|
|
7 | import org.gradle.api.model.ObjectFactory; | |
|
|
8 | import org.implab.gradle.common.core.lang.ReplayableQueue; | |
|
|
8 | 9 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
9 | 10 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec; |
|
|
10 | 11 | import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec; |
|
|
11 | 12 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
13 | import org.implab.gradle.variants.artifacts.Slot; | |
|
|
12 | 14 | import org.implab.gradle.variants.core.Variant; |
|
|
13 | 15 | |
|
|
14 | 16 | /** |
|
|
15 | 17 | * Handles outgoing artifact materialization policy. |
|
|
16 | 18 | * |
|
|
17 | 19 | * <p>Materialization is the phase where the plugin interprets variant artifact |
|
|
18 | 20 | * declarations as Gradle outgoing publication state: a consumable configuration, |
|
|
19 | 21 | * its primary artifact set, secondary artifact variants, and attributes. |
|
|
20 | 22 | * |
|
|
21 | 23 | * <p>The handler provides extension points for customizing the materialized |
|
|
22 | 24 | * Gradle-facing objects. These hooks intentionally expose Gradle API objects. |
|
|
23 | 25 | * This allows advanced customization, but also means that callers can bypass |
|
|
24 | 26 | * the plugin model. Such customizations are considered caller responsibility. |
|
|
25 | 27 | * |
|
|
26 | 28 | * <p>The internal binding mechanics are not part of the public contract. The |
|
|
27 | 29 | * contract is the materialized outgoing state exposed through the specification |
|
|
28 | 30 | * objects. |
|
|
29 | 31 | */ |
|
|
30 | 32 | @NonNullByDefault |
|
|
31 | 33 | public class MaterializationPolicyHandler implements Action<OutgoingVariant> { |
|
|
32 | 34 | |
|
|
33 | 35 | private final ReplayableQueue<OutgoingConfigurationSpec> variantMaterialization = new ReplayableQueue<>(); |
|
|
34 | 36 | private final ReplayableQueue<OutgoingConfigurationSlotSpec> slotMaterialization = new ReplayableQueue<>(); |
|
|
37 | private final ObjectFactory objects; | |
|
|
35 | 38 | |
|
|
36 | public MaterializationPolicyHandler() { | |
|
|
39 | public MaterializationPolicyHandler(ObjectFactory objects) { | |
|
|
40 | this.objects = objects; | |
|
|
37 | 41 | } |
|
|
38 | 42 | |
|
|
39 | 43 | @Override |
|
|
40 | 44 | public void execute(OutgoingVariant outgoingVariant) { |
|
|
41 | var slots = outgoingVariant.getSlots(); | |
|
|
42 | 45 | var primarySlotProvider = outgoingVariant.getPrimarySlot(); |
|
|
43 | 46 | var variant = outgoingVariant.getVariant(); |
|
|
44 | 47 | |
|
|
45 | // ΡΠ²ΡΠ·ΡΠ²Π°Π΅ΠΌ ΠΊΠΎΠ½ΡΠΈΠ³ΡΡΠ°ΡΠΈΡ | |
|
|
48 | // Materialization hooks are attached to the owning outgoing configuration. | |
|
|
46 | 49 | outgoingVariant.configureOutgoing(configuration -> { |
|
|
47 | 50 | var primarySlot = primarySlotProvider.get(); |
|
|
48 | 51 | var outgoing = configuration.getOutgoing(); |
|
|
49 | 52 | |
|
|
50 | 53 | variantMaterialized(variant, configuration); |
|
|
51 | 54 | |
|
|
52 | 55 | slotMaterialized(new ArtifactSlot(variant, primarySlot), true, outgoing.getAttributes()); |
|
|
53 | 56 | |
|
|
54 | slots.forEach(slot -> { | |
|
|
57 | // Slot publication hooks follow materialized Gradle artifact variants, | |
|
|
58 | // not the live slot identity view. | |
|
|
59 | outgoing.getVariants().configureEach(variantConfiguration -> { | |
|
|
60 | var slot = objects.named(Slot.class, variantConfiguration.getName()); | |
|
|
55 | 61 | if (slot.equals(primarySlot)) |
|
|
56 | 62 | return; |
|
|
57 | 63 | |
|
|
58 | var variantConfiguration = outgoing.getVariants().getByName(slot.getName()); | |
|
|
59 | 64 | slotMaterialized(new ArtifactSlot(variant, slot), false, variantConfiguration.getAttributes()); |
|
|
60 | 65 | }); |
|
|
61 | 66 | }); |
|
|
62 | 67 | }; |
|
|
63 | 68 | |
|
|
64 | 69 | public void whenVariantMaterialized(Action<? super OutgoingConfigurationSpec> action) { |
|
|
65 | 70 | variantMaterialization.forEach(action::execute); |
|
|
66 | 71 | } |
|
|
67 | 72 | |
|
|
68 | 73 | public void whenSlotMaterialized(Action<? super OutgoingConfigurationSlotSpec> action) { |
|
|
69 | 74 | slotMaterialization.forEach(action::execute); |
|
|
70 | 75 | } |
|
|
71 | 76 | |
|
|
72 | 77 | private void variantMaterialized(Variant variant, Configuration configuration) { |
|
|
73 | 78 | variantMaterialization.add(new OutgoingConfigurationSpec() { |
|
|
74 | 79 | |
|
|
75 | 80 | @Override |
|
|
76 | 81 | public Variant getVariant() { |
|
|
77 | 82 | return variant; |
|
|
78 | 83 | } |
|
|
79 | 84 | |
|
|
80 | 85 | @Override |
|
|
81 | 86 | public Configuration getConfiguration() { |
|
|
82 | 87 | return configuration; |
|
|
83 | 88 | } |
|
|
84 | 89 | |
|
|
85 | 90 | }); |
|
|
86 | 91 | } |
|
|
87 | 92 | |
|
|
88 | 93 | private void slotMaterialized(ArtifactSlot slot, boolean primary, AttributeContainer attributes) { |
|
|
89 | 94 | slotMaterialization.add(new OutgoingConfigurationSlotSpec() { |
|
|
90 | 95 | @Override |
|
|
91 | 96 | public boolean isPrimary() { |
|
|
92 | 97 | return primary; |
|
|
93 | 98 | } |
|
|
94 | 99 | |
|
|
95 | 100 | @Override |
|
|
96 | 101 | public ArtifactSlot getArtifactSlot() { |
|
|
97 | 102 | return slot; |
|
|
98 | 103 | } |
|
|
99 | 104 | |
|
|
100 | 105 | @Override |
|
|
101 | 106 | public void artifactAttributes(Action<? super AttributeContainer> action) { |
|
|
102 | 107 | action.execute(attributes); |
|
|
103 | 108 | } |
|
|
104 | 109 | }); |
|
|
105 | 110 | } |
|
|
106 | 111 | |
|
|
107 | 112 | } |
| @@ -1,33 +1,33 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.gradle.api.InvalidUserDataException; |
|
|
5 | 5 | import org.gradle.api.provider.ProviderFactory; |
|
|
6 | 6 | import org.implab.gradle.variants.artifacts.OutgoingVariant; |
|
|
7 | 7 | |
|
|
8 | 8 | public class SingleSlotConvention implements Action<OutgoingVariant> { |
|
|
9 | 9 | |
|
|
10 | 10 | private final ProviderFactory providers; |
|
|
11 | 11 | |
|
|
12 | 12 | public SingleSlotConvention(ProviderFactory providers) { |
|
|
13 | 13 | this.providers = providers; |
|
|
14 | 14 | } |
|
|
15 | 15 | |
|
|
16 | 16 | @Override |
|
|
17 | 17 | public void execute(OutgoingVariant outgoingVariant) { |
|
|
18 | 18 | var slots = outgoingVariant.getSlots(); |
|
|
19 | 19 | |
|
|
20 | 20 | outgoingVariant.getPrimarySlot().convention( |
|
|
21 | // Π΅ΡΠ»ΠΈ Π΅ΡΡΡ ΡΠΎΠ²Π½ΠΎ ΠΎΠ΄ΠΈΠ½ ΡΠ»ΠΎΡ, ΡΠΎ ΠΎΠ½ ΡΡΠΈΡΠ°Π΅ΡΡΡ primary | |
|
|
21 | // If exactly one slot is declared, it becomes the primary slot. | |
|
|
22 | 22 | providers.provider(() -> { |
|
|
23 | 23 | if (slots.size() == 0) |
|
|
24 | 24 | throw new InvalidUserDataException("No slots declared for " + outgoingVariant.getVariant()); |
|
|
25 | 25 | if (slots.size() > 1) |
|
|
26 | 26 | throw new InvalidUserDataException("Multiple slots declared for " + outgoingVariant.getVariant() + |
|
|
27 | 27 | ", please specify primary slot explicitly"); |
|
|
28 | 28 | |
|
|
29 | 29 | return slots.stream().findAny().get(); |
|
|
30 | 30 | })); |
|
|
31 | 31 | } |
|
|
32 | 32 | |
|
|
33 | 33 | } |
| @@ -1,41 +1,40 | |||
|
|
1 | 1 | /** |
|
|
2 | 2 | * Variant-scoped outgoing artifacts. |
|
|
3 | 3 | * |
|
|
4 | 4 | * <p>This package models the external artifact contract of a project in terms of variant-local slots. |
|
|
5 | 5 | * A variant represents one outgoing contract, while a slot represents one artifact set inside that |
|
|
6 | 6 | * contract. |
|
|
7 | 7 | * |
|
|
8 | 8 | * <p>The model intentionally separates cheap identity objects from stateful build objects: |
|
|
9 | 9 | * |
|
|
10 | 10 | * <ul> |
|
|
11 | 11 | * <li>{@link org.implab.gradle.variants.artifacts.ArtifactSlot} identifies a published slot inside a |
|
|
12 | 12 | * variant;</li> |
|
|
13 | 13 | * <li>{@link org.implab.gradle.variants.artifacts.ArtifactAssembly} is the lazily materialized body of |
|
|
14 | 14 | * that slot.</li> |
|
|
15 | 15 | * </ul> |
|
|
16 | 16 | * |
|
|
17 | 17 | * <p>Each slot is expected to materialize to exactly one published artifact: either one file or one |
|
|
18 | 18 | * directory. Internal build topology such as roles may participate in slot assembly rules, but does not |
|
|
19 | 19 | * belong to external artifact identity. |
|
|
20 | 20 | * |
|
|
21 | 21 | * <p>Typical usage: |
|
|
22 | 22 | * |
|
|
23 | 23 | * <pre>{@code |
|
|
24 | 24 | * variantArtifacts { |
|
|
25 | 25 | * variant("browser") { |
|
|
26 | 26 | * primarySlot("runtime") { |
|
|
27 | 27 | * fromRole("main") { output("js") } |
|
|
28 | 28 | * } |
|
|
29 | 29 | * slot("sources") { |
|
|
30 | 30 | * fromLayer("main") { output("sources") } |
|
|
31 | 31 | * } |
|
|
32 | 32 | * } |
|
|
33 | 33 | * } |
|
|
34 | 34 | * }</pre> |
|
|
35 | 35 | * |
|
|
36 | * <p>After finalization, slot identities can be observed through | |
|
|
37 | * {@link org.implab.gradle.variants.artifacts.OutgoingVariantsContext#getSlots()}, while slot bodies are | |
|
|
38 | * obtained on demand through | |
|
|
36 | * <p>After finalization, {@link org.implab.gradle.variants.artifacts.OutgoingVariant} exposes declared slot | |
|
|
37 | * identities through {@code getSlots()}, while slot bodies are obtained on demand through | |
|
|
39 | 38 | * {@link org.implab.gradle.variants.artifacts.OutgoingVariantsContext#getAssemblies()}. |
|
|
40 | 39 | */ |
|
|
41 | 40 | package org.implab.gradle.variants.artifacts; |
| @@ -1,563 +1,691 | |||
|
|
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.gradle.testkit.runner.TaskOutcome; |
|
|
7 | 7 | import org.junit.jupiter.api.Test; |
|
|
8 | 8 | |
|
|
9 | 9 | class VariantArtifactsPluginFunctionalTest extends AbstractFunctionalTest { |
|
|
10 | 10 | |
|
|
11 | 11 | @Test |
|
|
12 | 12 | void gradleReferenceLazyOutgoingConfigurationAllowsSecondaryArtifactSelection() throws Exception { |
|
|
13 | 13 | writeFile("settings.gradle", """ |
|
|
14 | 14 | rootProject.name = 'gradle-reference-outgoing-resolution' |
|
|
15 | 15 | include 'producer', 'consumer' |
|
|
16 | 16 | """); |
|
|
17 | 17 | writeFile("producer/inputs/typesPackage", "types\n"); |
|
|
18 | 18 | writeFile("producer/inputs/js", "js\n"); |
|
|
19 | 19 | writeBuildFile(""" |
|
|
20 | 20 | import org.gradle.api.attributes.Attribute |
|
|
21 | 21 | |
|
|
22 | 22 | def variantAttr = Attribute.of('test.variant', String) |
|
|
23 | 23 | def slotAttr = Attribute.of('test.slot', String) |
|
|
24 | 24 | |
|
|
25 | 25 | project(':producer') { |
|
|
26 | 26 | def browserElements = configurations.consumable('browserElements') |
|
|
27 | 27 | |
|
|
28 | 28 | println('reference: registered browserElements provider') |
|
|
29 | 29 | |
|
|
30 | 30 | browserElements.configure { configuration -> |
|
|
31 | 31 | println('reference: configuring browserElements') |
|
|
32 | 32 | |
|
|
33 | 33 | configuration.attributes.attribute(variantAttr, 'browser') |
|
|
34 | 34 | configuration.outgoing.attributes.attribute(slotAttr, 'typesPackage') |
|
|
35 | 35 | configuration.outgoing.artifact(layout.projectDirectory.file('inputs/typesPackage')) |
|
|
36 | 36 | |
|
|
37 | 37 | configuration.outgoing.variants.create('js') { secondary -> |
|
|
38 | 38 | println('reference: creating js outgoing variant') |
|
|
39 | 39 | |
|
|
40 | 40 | secondary.attributes.attribute(slotAttr, 'js') |
|
|
41 | 41 | secondary.artifact(layout.projectDirectory.file('inputs/js')) |
|
|
42 | 42 | } |
|
|
43 | 43 | } |
|
|
44 | 44 | } |
|
|
45 | 45 | |
|
|
46 | 46 | project(':consumer') { |
|
|
47 | 47 | configurations { |
|
|
48 | 48 | compileView { |
|
|
49 | 49 | canBeResolved = true |
|
|
50 | 50 | canBeConsumed = false |
|
|
51 | 51 | canBeDeclared = true |
|
|
52 | 52 | attributes { |
|
|
53 | 53 | attribute(variantAttr, 'browser') |
|
|
54 | 54 | attribute(slotAttr, 'typesPackage') |
|
|
55 | 55 | } |
|
|
56 | 56 | } |
|
|
57 | 57 | } |
|
|
58 | 58 | |
|
|
59 | 59 | dependencies { |
|
|
60 | 60 | compileView project(':producer') |
|
|
61 | 61 | } |
|
|
62 | 62 | |
|
|
63 | 63 | tasks.register('probe') { |
|
|
64 | 64 | doLast { |
|
|
65 | 65 | println('reference: resolving primary files') |
|
|
66 | 66 | |
|
|
67 | 67 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') |
|
|
68 | 68 | |
|
|
69 | 69 | println('reference: resolving secondary files') |
|
|
70 | 70 | |
|
|
71 | 71 | def jsFiles = configurations.compileView.incoming.artifactView { |
|
|
72 | 72 | attributes { |
|
|
73 | 73 | attribute(slotAttr, 'js') |
|
|
74 | 74 | } |
|
|
75 | 75 | }.files.files.collect { it.name }.sort().join(',') |
|
|
76 | 76 | |
|
|
77 | 77 | println('compileFiles=' + compileFiles) |
|
|
78 | 78 | println('jsFiles=' + jsFiles) |
|
|
79 | 79 | } |
|
|
80 | 80 | } |
|
|
81 | 81 | } |
|
|
82 | 82 | """); |
|
|
83 | 83 | |
|
|
84 | 84 | BuildResult result = runner(":consumer:probe").build(); |
|
|
85 | 85 | var output = result.getOutput(); |
|
|
86 | 86 | var registered = output.indexOf("reference: registered browserElements provider"); |
|
|
87 | 87 | var resolvingPrimary = output.indexOf("reference: resolving primary files"); |
|
|
88 | 88 | var configuring = output.indexOf("reference: configuring browserElements"); |
|
|
89 | 89 | var creatingSecondary = output.indexOf("reference: creating js outgoing variant"); |
|
|
90 | 90 | var resolvingSecondary = output.indexOf("reference: resolving secondary files"); |
|
|
91 | 91 | |
|
|
92 | 92 | assertTrue(registered >= 0); |
|
|
93 | 93 | assertTrue(resolvingPrimary >= 0); |
|
|
94 | 94 | assertTrue(configuring >= 0); |
|
|
95 | 95 | assertTrue(creatingSecondary >= 0); |
|
|
96 | 96 | assertTrue(resolvingSecondary >= 0); |
|
|
97 | 97 | assertTrue(registered < resolvingPrimary); |
|
|
98 | 98 | assertTrue(resolvingPrimary < configuring); |
|
|
99 | 99 | assertTrue(configuring < creatingSecondary); |
|
|
100 | 100 | assertTrue(creatingSecondary < resolvingSecondary); |
|
|
101 | 101 | assertTrue(output.contains("compileFiles=typesPackage")); |
|
|
102 | 102 | assertTrue(output.contains("jsFiles=js")); |
|
|
103 | 103 | } |
|
|
104 | 104 | |
|
|
105 | 105 | @Test |
|
|
106 | void gradleReferenceRegisteredSecondaryArtifactVariantIsNotRealizedBeforeResolution() throws Exception { | |
|
|
107 | // Gradle issue: https://github.com/gradle/gradle/issues/27441 | |
|
|
108 | // Registered outgoing artifact variants are not realized before dependency resolution. | |
|
|
109 | writeFile("settings.gradle", """ | |
|
|
110 | rootProject.name = 'gradle-reference-registered-secondary-variant' | |
|
|
111 | include 'producer', 'consumer' | |
|
|
112 | """); | |
|
|
113 | writeFile("producer/inputs/typesPackage", "types\n"); | |
|
|
114 | writeFile("producer/inputs/js", "js\n"); | |
|
|
115 | writeFile("build.gradle", """ | |
|
|
116 | import org.gradle.api.attributes.Attribute | |
|
|
117 | ||
|
|
118 | def variantAttr = Attribute.of('test.variant', String) | |
|
|
119 | def slotAttr = Attribute.of('test.slot', String) | |
|
|
120 | ||
|
|
121 | project(':producer') { | |
|
|
122 | def browserElements = configurations.consumable('browserElements') | |
|
|
123 | ||
|
|
124 | browserElements.configure { configuration -> | |
|
|
125 | configuration.attributes.attribute(variantAttr, 'browser') | |
|
|
126 | configuration.outgoing.attributes.attribute(slotAttr, 'typesPackage') | |
|
|
127 | configuration.outgoing.artifact(layout.projectDirectory.file('inputs/typesPackage')) | |
|
|
128 | ||
|
|
129 | configuration.outgoing.variants.register('js') { secondary -> | |
|
|
130 | secondary.attributes.attribute(slotAttr, 'js') | |
|
|
131 | secondary.artifact(layout.projectDirectory.file('inputs/js')) | |
|
|
132 | } | |
|
|
133 | } | |
|
|
134 | } | |
|
|
135 | ||
|
|
136 | project(':consumer') { | |
|
|
137 | configurations { | |
|
|
138 | compileView { | |
|
|
139 | canBeResolved = true | |
|
|
140 | canBeConsumed = false | |
|
|
141 | canBeDeclared = true | |
|
|
142 | attributes { | |
|
|
143 | attribute(variantAttr, 'browser') | |
|
|
144 | attribute(slotAttr, 'typesPackage') | |
|
|
145 | } | |
|
|
146 | } | |
|
|
147 | } | |
|
|
148 | ||
|
|
149 | dependencies { | |
|
|
150 | compileView project(':producer') | |
|
|
151 | } | |
|
|
152 | ||
|
|
153 | tasks.register('probe') { | |
|
|
154 | doLast { | |
|
|
155 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') | |
|
|
156 | def jsFiles = configurations.compileView.incoming.artifactView { | |
|
|
157 | attributes { | |
|
|
158 | attribute(slotAttr, 'js') | |
|
|
159 | } | |
|
|
160 | }.files.files.collect { it.name }.sort().join(',') | |
|
|
161 | ||
|
|
162 | println('compileFiles=' + compileFiles) | |
|
|
163 | println('jsFiles=' + jsFiles) | |
|
|
164 | } | |
|
|
165 | } | |
|
|
166 | } | |
|
|
167 | """); | |
|
|
168 | ||
|
|
169 | assertBuildFails("Cannot create variant 'js' after dependency configuration ':producer:browserElements' has been resolved", | |
|
|
170 | ":consumer:probe"); | |
|
|
171 | } | |
|
|
172 | ||
|
|
173 | @Test | |
|
|
106 | 174 | void materializesPrimaryAndSecondarySlotsAndInvokesOutgoingHooks() throws Exception { |
|
|
107 | 175 | writeSettings("variant-artifacts-slots"); |
|
|
108 | 176 | writeFile("inputs/base.js", "console.log('base')\n"); |
|
|
109 | 177 | writeFile("inputs/amd.js", "console.log('amd')\n"); |
|
|
110 | 178 | writeFile("inputs/mainJs.txt", "mainJs marker\n"); |
|
|
111 | 179 | writeFile("inputs/amdJs.txt", "amdJs marker\n"); |
|
|
112 | 180 | writeBuildFile(""" |
|
|
113 | 181 | import org.gradle.api.attributes.Attribute |
|
|
114 | 182 | |
|
|
115 | 183 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
116 | 184 | |
|
|
117 | 185 | def variantAttr = Attribute.of('test.variant', String) |
|
|
118 | 186 | def slotAttr = Attribute.of('test.slot', String) |
|
|
119 | 187 | |
|
|
120 | 188 | variants.layers.create('mainBase') |
|
|
121 | 189 | variants.layers.create('mainAmd') |
|
|
122 | 190 | variants.roles.create('main') |
|
|
123 | 191 | variants.roles.create('test') |
|
|
124 | 192 | variants.variant('browser') { |
|
|
125 | 193 | role('main') { |
|
|
126 | 194 | layers('mainBase', 'mainAmd') |
|
|
127 | 195 | } |
|
|
128 | 196 | } |
|
|
129 | 197 | |
|
|
130 | 198 | variantSources { |
|
|
131 | 199 | layer('mainBase') { |
|
|
132 | 200 | declareOutputs('js') |
|
|
133 | 201 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) |
|
|
134 | 202 | } |
|
|
135 | 203 | layer('mainAmd') { |
|
|
136 | 204 | declareOutputs('js') |
|
|
137 | 205 | registerOutput('js', layout.projectDirectory.file('inputs/amd.js')) |
|
|
138 | 206 | } |
|
|
139 | 207 | } |
|
|
140 | 208 | |
|
|
141 | 209 | variantArtifacts { |
|
|
142 | 210 | variant('browser') { |
|
|
143 | 211 | primarySlot('mainJs') { |
|
|
144 | 212 | fromRole('main') { |
|
|
145 | 213 | output('js') |
|
|
146 | 214 | } |
|
|
147 | 215 | from(layout.projectDirectory.file('inputs/mainJs.txt')) |
|
|
148 | 216 | } |
|
|
149 | 217 | slot('amdJs') { |
|
|
150 | 218 | fromLayer('mainAmd') { |
|
|
151 | 219 | output('js') |
|
|
152 | 220 | } |
|
|
153 | 221 | from(layout.projectDirectory.file('inputs/amdJs.txt')) |
|
|
154 | 222 | } |
|
|
155 | 223 | } |
|
|
156 | 224 | |
|
|
157 | 225 | whenOutgoingConfiguration { publication -> |
|
|
158 | 226 | publication.configuration { |
|
|
159 | 227 | attributes.attribute(variantAttr, publication.variant.name) |
|
|
160 | 228 | } |
|
|
161 | 229 | } |
|
|
162 | 230 | |
|
|
163 | 231 | whenOutgoingSlot { publication -> |
|
|
164 | 232 | def slotName = publication.artifactSlot.slot.name |
|
|
165 | 233 | publication.artifactAttributes { |
|
|
166 | 234 | attribute(slotAttr, slotName) |
|
|
167 | 235 | } |
|
|
168 | 236 | } |
|
|
169 | 237 | } |
|
|
170 | 238 | |
|
|
171 | 239 | tasks.register('probe') { |
|
|
172 | 240 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_mainJs' |
|
|
173 | 241 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s5_amdJs' |
|
|
174 | 242 | |
|
|
175 | 243 | doLast { |
|
|
176 | 244 | def mainDir = layout.buildDirectory.dir('variant-assemblies/browser/mainJs').get().asFile |
|
|
177 | 245 | def amdDir = layout.buildDirectory.dir('variant-assemblies/browser/amdJs').get().asFile |
|
|
178 | 246 | |
|
|
179 | 247 | assert new File(mainDir, 'base.js').exists() |
|
|
180 | 248 | assert new File(mainDir, 'amd.js').exists() |
|
|
181 | 249 | assert new File(mainDir, 'mainJs.txt').exists() |
|
|
182 | 250 | |
|
|
183 | 251 | assert !new File(amdDir, 'base.js').exists() |
|
|
184 | 252 | assert new File(amdDir, 'amd.js').exists() |
|
|
185 | 253 | assert new File(amdDir, 'amdJs.txt').exists() |
|
|
186 | 254 | |
|
|
187 | 255 | def elements = configurations.getByName('browserElements') |
|
|
188 | 256 | def amdVariant = elements.outgoing.variants.getByName('amdJs') |
|
|
189 | 257 | |
|
|
190 | 258 | println('variantAttr=' + elements.attributes.getAttribute(variantAttr)) |
|
|
191 | 259 | println('primarySlotAttr=' + elements.outgoing.attributes.getAttribute(slotAttr)) |
|
|
192 | 260 | println('amdSlotAttr=' + amdVariant.attributes.getAttribute(slotAttr)) |
|
|
193 | 261 | println('configurations=' + configurations.matching { it.name == 'browserElements' }.collect { it.name }.join(',')) |
|
|
194 | 262 | println('secondaryVariants=' + elements.outgoing.variants.collect { it.name }.sort().join(',')) |
|
|
195 | 263 | } |
|
|
196 | 264 | } |
|
|
197 | 265 | """); |
|
|
198 | 266 | |
|
|
199 | 267 | BuildResult result = runner("probe").build(); |
|
|
200 | 268 | |
|
|
201 | 269 | assertTrue(result.getOutput().contains("variantAttr=browser")); |
|
|
202 | 270 | assertTrue(result.getOutput().contains("primarySlotAttr=mainJs")); |
|
|
203 | 271 | assertTrue(result.getOutput().contains("amdSlotAttr=amdJs")); |
|
|
204 | 272 | assertTrue(result.getOutput().contains("configurations=browserElements")); |
|
|
205 | 273 | assertTrue(result.getOutput().contains("secondaryVariants=amdJs")); |
|
|
206 | 274 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
207 | 275 | } |
|
|
208 | 276 | |
|
|
209 | 277 | @Test |
|
|
278 | void outgoingSlotHookFollowsMaterializedGradleArtifactVariants() throws Exception { | |
|
|
279 | writeSettings("variant-artifacts-materialized-gradle-variant"); | |
|
|
280 | writeFile("inputs/typesPackage", "types\n"); | |
|
|
281 | writeFile("inputs/js", "js\n"); | |
|
|
282 | writeBuildFile(""" | |
|
|
283 | import org.gradle.api.attributes.Attribute | |
|
|
284 | ||
|
|
285 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin | |
|
|
286 | ||
|
|
287 | def slotAttr = Attribute.of('test.slot', String) | |
|
|
288 | ||
|
|
289 | variants.layers.create('main') | |
|
|
290 | variants.roles.create('main') | |
|
|
291 | variants.variant('browser') { | |
|
|
292 | role('main') { | |
|
|
293 | layers('main') | |
|
|
294 | } | |
|
|
295 | } | |
|
|
296 | ||
|
|
297 | variantArtifacts { | |
|
|
298 | variant('browser') { | |
|
|
299 | primarySlot('typesPackage') { | |
|
|
300 | from(layout.projectDirectory.file('inputs/typesPackage')) | |
|
|
301 | } | |
|
|
302 | } | |
|
|
303 | ||
|
|
304 | whenOutgoingConfiguration { publication -> | |
|
|
305 | publication.configuration { | |
|
|
306 | outgoing.variants.create('js') { secondary -> | |
|
|
307 | secondary.artifact(layout.projectDirectory.file('inputs/js')) | |
|
|
308 | } | |
|
|
309 | } | |
|
|
310 | } | |
|
|
311 | ||
|
|
312 | whenOutgoingSlot { publication -> | |
|
|
313 | publication.artifactAttributes { | |
|
|
314 | attribute(slotAttr, publication.artifactSlot.slot.name) | |
|
|
315 | } | |
|
|
316 | } | |
|
|
317 | } | |
|
|
318 | ||
|
|
319 | tasks.register('probe') { | |
|
|
320 | doLast { | |
|
|
321 | def elements = configurations.getByName('browserElements') | |
|
|
322 | def jsVariant = elements.outgoing.variants.getByName('js') | |
|
|
323 | ||
|
|
324 | println('primarySlotAttr=' + elements.outgoing.attributes.getAttribute(slotAttr)) | |
|
|
325 | println('jsSlotAttr=' + jsVariant.attributes.getAttribute(slotAttr)) | |
|
|
326 | } | |
|
|
327 | } | |
|
|
328 | """); | |
|
|
329 | ||
|
|
330 | BuildResult result = runner("probe").build(); | |
|
|
331 | ||
|
|
332 | assertTrue(result.getOutput().contains("primarySlotAttr=typesPackage")); | |
|
|
333 | assertTrue(result.getOutput().contains("jsSlotAttr=js")); | |
|
|
334 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); | |
|
|
335 | } | |
|
|
336 | ||
|
|
337 | @Test | |
|
|
210 | 338 | void allowsSingleSlotVariantWithoutExplicitPrimarySlot() throws Exception { |
|
|
211 | 339 | writeSettings("variant-artifacts-single-slot"); |
|
|
212 | 340 | writeBuildFile(""" |
|
|
213 | 341 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
214 | 342 | |
|
|
215 | 343 | variants.layers.create('main') |
|
|
216 | 344 | variants.roles.create('main') |
|
|
217 | 345 | variants.variant('browser') { |
|
|
218 | 346 | role('main') { |
|
|
219 | 347 | layers('main') |
|
|
220 | 348 | } |
|
|
221 | 349 | } |
|
|
222 | 350 | |
|
|
223 | 351 | variantSources.layer('main') { |
|
|
224 | 352 | declareOutputs('types') |
|
|
225 | 353 | } |
|
|
226 | 354 | |
|
|
227 | 355 | variantArtifacts { |
|
|
228 | 356 | variant('browser') { |
|
|
229 | 357 | slot('typesPackage') { |
|
|
230 | 358 | fromVariant { |
|
|
231 | 359 | output('types') |
|
|
232 | 360 | } |
|
|
233 | 361 | } |
|
|
234 | 362 | } |
|
|
235 | 363 | } |
|
|
236 | 364 | |
|
|
237 | 365 | tasks.register('probe') { |
|
|
238 | 366 | doLast { |
|
|
239 | 367 | variantArtifacts.whenAvailable { ctx -> |
|
|
240 | 368 | def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser') |
|
|
241 | 369 | println('primary=' + ctx.findOutgoing(browser).get().primarySlot.get().name) |
|
|
242 | 370 | } |
|
|
243 | 371 | } |
|
|
244 | 372 | } |
|
|
245 | 373 | """); |
|
|
246 | 374 | |
|
|
247 | 375 | BuildResult result = runner("probe").build(); |
|
|
248 | 376 | |
|
|
249 | 377 | assertTrue(result.getOutput().contains("primary=typesPackage")); |
|
|
250 | 378 | } |
|
|
251 | 379 | |
|
|
252 | 380 | @Test |
|
|
253 | 381 | void materializesDirectSlotInputsWithoutVariantSourceBindings() throws Exception { |
|
|
254 | 382 | writeSettings("variant-artifacts-direct-input"); |
|
|
255 | 383 | writeFile("inputs/bundle.js", "console.log('bundle')\n"); |
|
|
256 | 384 | writeBuildFile(""" |
|
|
257 | 385 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
258 | 386 | |
|
|
259 | 387 | variants.layers.create('main') |
|
|
260 | 388 | variants.roles.create('main') |
|
|
261 | 389 | variants.variant('browser') { |
|
|
262 | 390 | role('main') { |
|
|
263 | 391 | layers('main') |
|
|
264 | 392 | } |
|
|
265 | 393 | } |
|
|
266 | 394 | |
|
|
267 | 395 | variantArtifacts { |
|
|
268 | 396 | variant('browser') { |
|
|
269 | 397 | primarySlot('bundle') { |
|
|
270 | 398 | from(layout.projectDirectory.file('inputs/bundle.js')) |
|
|
271 | 399 | } |
|
|
272 | 400 | } |
|
|
273 | 401 | } |
|
|
274 | 402 | |
|
|
275 | 403 | tasks.register('probe') { |
|
|
276 | 404 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle' |
|
|
277 | 405 | |
|
|
278 | 406 | doLast { |
|
|
279 | 407 | def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile |
|
|
280 | 408 | assert new File(bundleDir, 'bundle.js').exists() |
|
|
281 | 409 | } |
|
|
282 | 410 | } |
|
|
283 | 411 | """); |
|
|
284 | 412 | |
|
|
285 | 413 | BuildResult result = runner("probe").build(); |
|
|
286 | 414 | |
|
|
287 | 415 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
288 | 416 | } |
|
|
289 | 417 | |
|
|
290 | 418 | @Test |
|
|
291 | 419 | void combinesDirectAndTopologyAwareSlotInputs() throws Exception { |
|
|
292 | 420 | writeSettings("variant-artifacts-combined-inputs"); |
|
|
293 | 421 | writeFile("inputs/base.js", "console.log('base')\n"); |
|
|
294 | 422 | writeFile("inputs/marker.txt", "marker\n"); |
|
|
295 | 423 | writeBuildFile(""" |
|
|
296 | 424 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
297 | 425 | |
|
|
298 | 426 | variants.layers.create('main') |
|
|
299 | 427 | variants.roles.create('main') |
|
|
300 | 428 | variants.variant('browser') { |
|
|
301 | 429 | role('main') { |
|
|
302 | 430 | layers('main') |
|
|
303 | 431 | } |
|
|
304 | 432 | } |
|
|
305 | 433 | |
|
|
306 | 434 | variantSources.layer('main') { |
|
|
307 | 435 | declareOutputs('js') |
|
|
308 | 436 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) |
|
|
309 | 437 | } |
|
|
310 | 438 | |
|
|
311 | 439 | variantArtifacts { |
|
|
312 | 440 | variant('browser') { |
|
|
313 | 441 | primarySlot('bundle') { |
|
|
314 | 442 | fromVariant { |
|
|
315 | 443 | output('js') |
|
|
316 | 444 | } |
|
|
317 | 445 | from(layout.projectDirectory.file('inputs/marker.txt')) |
|
|
318 | 446 | } |
|
|
319 | 447 | } |
|
|
320 | 448 | } |
|
|
321 | 449 | |
|
|
322 | 450 | tasks.register('probe') { |
|
|
323 | 451 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle' |
|
|
324 | 452 | |
|
|
325 | 453 | doLast { |
|
|
326 | 454 | def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile |
|
|
327 | 455 | assert new File(bundleDir, 'base.js').exists() |
|
|
328 | 456 | assert new File(bundleDir, 'marker.txt').exists() |
|
|
329 | 457 | } |
|
|
330 | 458 | } |
|
|
331 | 459 | """); |
|
|
332 | 460 | |
|
|
333 | 461 | BuildResult result = runner("probe").build(); |
|
|
334 | 462 | |
|
|
335 | 463 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
336 | 464 | } |
|
|
337 | 465 | |
|
|
338 | 466 | @Test |
|
|
339 | 467 | void failsOnUnknownVariantReference() throws Exception { |
|
|
340 | 468 | writeSettings("variant-artifacts-missing-variant"); |
|
|
341 | 469 | writeBuildFile(""" |
|
|
342 | 470 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
343 | 471 | |
|
|
344 | 472 | variants.layers.create('main') |
|
|
345 | 473 | |
|
|
346 | 474 | variantArtifacts { |
|
|
347 | 475 | variant('browser') { |
|
|
348 | 476 | slot('mainJs') { |
|
|
349 | 477 | fromVariant { |
|
|
350 | 478 | output('js') |
|
|
351 | 479 | } |
|
|
352 | 480 | } |
|
|
353 | 481 | } |
|
|
354 | 482 | } |
|
|
355 | 483 | """); |
|
|
356 | 484 | |
|
|
357 | 485 | assertBuildFails("isn't declared", "help"); |
|
|
358 | 486 | } |
|
|
359 | 487 | |
|
|
360 | 488 | @Test |
|
|
361 | 489 | void failsOnUnknownRoleReference() throws Exception { |
|
|
362 | 490 | writeSettings("variant-artifacts-missing-role"); |
|
|
363 | 491 | writeBuildFile(""" |
|
|
364 | 492 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
365 | 493 | |
|
|
366 | 494 | variants.layers.create('main') |
|
|
367 | 495 | variants.roles.create('main') |
|
|
368 | 496 | variants.variant('browser') { |
|
|
369 | 497 | role('main') { |
|
|
370 | 498 | layers('main') |
|
|
371 | 499 | } |
|
|
372 | 500 | } |
|
|
373 | 501 | |
|
|
374 | 502 | variantArtifacts { |
|
|
375 | 503 | variant('browser') { |
|
|
376 | 504 | slot('mainJs') { |
|
|
377 | 505 | fromRole('test') { |
|
|
378 | 506 | output('js') |
|
|
379 | 507 | } |
|
|
380 | 508 | } |
|
|
381 | 509 | } |
|
|
382 | 510 | } |
|
|
383 | 511 | """); |
|
|
384 | 512 | |
|
|
385 | 513 | assertBuildFails("Role projection for variant 'browser' and role 'test' not found", "help"); |
|
|
386 | 514 | } |
|
|
387 | 515 | |
|
|
388 | 516 | @Test |
|
|
389 | 517 | void failsWhenPrimarySlotIsMissingForMultipleSlots() throws Exception { |
|
|
390 | 518 | writeSettings("variant-artifacts-missing-primary"); |
|
|
391 | 519 | writeBuildFile(""" |
|
|
392 | 520 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
393 | 521 | |
|
|
394 | 522 | variants.layers.create('main') |
|
|
395 | 523 | variants.roles.create('main') |
|
|
396 | 524 | variants.variant('browser') { |
|
|
397 | 525 | role('main') { |
|
|
398 | 526 | layers('main') |
|
|
399 | 527 | } |
|
|
400 | 528 | } |
|
|
401 | 529 | |
|
|
402 | 530 | variantSources.layer('main') { |
|
|
403 | 531 | declareOutputs('types', 'js') |
|
|
404 | 532 | } |
|
|
405 | 533 | |
|
|
406 | 534 | variantArtifacts { |
|
|
407 | 535 | variant('browser') { |
|
|
408 | 536 | slot('typesPackage') { |
|
|
409 | 537 | fromVariant { |
|
|
410 | 538 | output('types') |
|
|
411 | 539 | } |
|
|
412 | 540 | } |
|
|
413 | 541 | slot('js') { |
|
|
414 | 542 | fromVariant { |
|
|
415 | 543 | output('js') |
|
|
416 | 544 | } |
|
|
417 | 545 | } |
|
|
418 | 546 | } |
|
|
419 | 547 | } |
|
|
420 | 548 | |
|
|
421 | 549 | tasks.register('probe') { |
|
|
422 | 550 | doLast { |
|
|
423 | 551 | variantArtifacts.whenAvailable { ctx -> |
|
|
424 | 552 | def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser') |
|
|
425 | 553 | ctx.findOutgoing(browser).get().primarySlot.get() |
|
|
426 | 554 | } |
|
|
427 | 555 | } |
|
|
428 | 556 | } |
|
|
429 | 557 | """); |
|
|
430 | 558 | |
|
|
431 | 559 | assertBuildFails("Multiple slots declared for browser, please specify primary slot explicitly", "probe"); |
|
|
432 | 560 | } |
|
|
433 | 561 | |
|
|
434 | 562 | @Test |
|
|
435 | 563 | void failsOnLayerReferenceOutsideVariantTopology() throws Exception { |
|
|
436 | 564 | writeSettings("variant-artifacts-layer-outside-topology"); |
|
|
437 | 565 | writeBuildFile(""" |
|
|
438 | 566 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
439 | 567 | |
|
|
440 | 568 | variants.layers.create('mainBase') |
|
|
441 | 569 | variants.layers.create('extra') |
|
|
442 | 570 | variants.roles.create('main') |
|
|
443 | 571 | variants.variant('browser') { |
|
|
444 | 572 | role('main') { |
|
|
445 | 573 | layers('mainBase') |
|
|
446 | 574 | } |
|
|
447 | 575 | } |
|
|
448 | 576 | |
|
|
449 | 577 | variantArtifacts { |
|
|
450 | 578 | variant('browser') { |
|
|
451 | 579 | slot('extraJs') { |
|
|
452 | 580 | fromLayer('extra') { |
|
|
453 | 581 | output('js') |
|
|
454 | 582 | } |
|
|
455 | 583 | } |
|
|
456 | 584 | } |
|
|
457 | 585 | } |
|
|
458 | 586 | """); |
|
|
459 | 587 | |
|
|
460 | 588 | assertBuildFails("Compile unit for variant 'browser' and layer 'extra' not found", "help"); |
|
|
461 | 589 | } |
|
|
462 | 590 | |
|
|
463 | 591 | @Test |
|
|
464 | 592 | void preservesPrimaryResolutionAndAllowsSecondaryArtifactSelection() throws Exception { |
|
|
465 | 593 | writeFile("settings.gradle", """ |
|
|
466 | 594 | rootProject.name = 'variant-artifacts-resolution' |
|
|
467 | 595 | include 'producer', 'consumer' |
|
|
468 | 596 | """); |
|
|
469 | 597 | writeFile("producer/inputs/types.d.ts", "export type Foo = string\n"); |
|
|
470 | 598 | writeFile("producer/inputs/index.js", "export const foo = 'bar'\n"); |
|
|
471 | 599 | writeBuildFile(""" |
|
|
472 | 600 | import org.gradle.api.attributes.Attribute |
|
|
473 | 601 | |
|
|
474 | 602 | def variantAttr = Attribute.of('test.variant', String) |
|
|
475 | 603 | def slotAttr = Attribute.of('test.slot', String) |
|
|
476 | 604 | |
|
|
477 | 605 | subprojects { |
|
|
478 | 606 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
479 | 607 | } |
|
|
480 | 608 | |
|
|
481 | 609 | project(':producer') { |
|
|
482 | 610 | variants.layers.create('main') |
|
|
483 | 611 | variants.roles.create('main') |
|
|
484 | 612 | variants.variant('browser') { |
|
|
485 | 613 | role('main') { |
|
|
486 | 614 | layers('main') |
|
|
487 | 615 | } |
|
|
488 | 616 | } |
|
|
489 | 617 | |
|
|
490 | 618 | variantSources.layer('main') { |
|
|
491 | 619 | declareOutputs('types', 'js') |
|
|
492 | 620 | registerOutput('types', layout.projectDirectory.file('inputs/types.d.ts')) |
|
|
493 | 621 | registerOutput('js', layout.projectDirectory.file('inputs/index.js')) |
|
|
494 | 622 | } |
|
|
495 | 623 | |
|
|
496 | 624 | variantArtifacts { |
|
|
497 | 625 | variant('browser') { |
|
|
498 | 626 | primarySlot('typesPackage') { |
|
|
499 | 627 | fromVariant { |
|
|
500 | 628 | output('types') |
|
|
501 | 629 | } |
|
|
502 | 630 | } |
|
|
503 | 631 | slot('js') { |
|
|
504 | 632 | fromVariant { |
|
|
505 | 633 | output('js') |
|
|
506 | 634 | } |
|
|
507 | 635 | } |
|
|
508 | 636 | } |
|
|
509 | 637 | |
|
|
510 | 638 | whenOutgoingConfiguration { publication -> |
|
|
511 | 639 | publication.configuration { |
|
|
512 | 640 | attributes.attribute(variantAttr, publication.variant.name) |
|
|
513 | 641 | } |
|
|
514 | 642 | } |
|
|
515 | 643 | |
|
|
516 | 644 | whenOutgoingSlot { publication -> |
|
|
517 | 645 | publication.artifactAttributes { |
|
|
518 | 646 | attribute(slotAttr, publication.artifactSlot.slot.name) |
|
|
519 | 647 | } |
|
|
520 | 648 | } |
|
|
521 | 649 | } |
|
|
522 | 650 | |
|
|
523 | 651 | } |
|
|
524 | 652 | |
|
|
525 | 653 | project(':consumer') { |
|
|
526 | 654 | configurations { |
|
|
527 | 655 | compileView { |
|
|
528 | 656 | canBeResolved = true |
|
|
529 | 657 | canBeConsumed = false |
|
|
530 | 658 | canBeDeclared = true |
|
|
531 | 659 | attributes { |
|
|
532 | 660 | attribute(variantAttr, 'browser') |
|
|
533 | 661 | attribute(slotAttr, 'typesPackage') |
|
|
534 | 662 | } |
|
|
535 | 663 | } |
|
|
536 | 664 | } |
|
|
537 | 665 | |
|
|
538 | 666 | dependencies { |
|
|
539 | 667 | compileView project(':producer') |
|
|
540 | 668 | } |
|
|
541 | 669 | |
|
|
542 | 670 | tasks.register('probe') { |
|
|
543 | 671 | doLast { |
|
|
544 | 672 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') |
|
|
545 | 673 | def jsFiles = configurations.compileView.incoming.artifactView { |
|
|
546 | 674 | attributes { |
|
|
547 | 675 | attribute(slotAttr, 'js') |
|
|
548 | 676 | } |
|
|
549 | 677 | }.files.files.collect { it.name }.sort().join(',') |
|
|
550 | 678 | |
|
|
551 | 679 | println('compileFiles=' + compileFiles) |
|
|
552 | 680 | println('jsFiles=' + jsFiles) |
|
|
553 | 681 | } |
|
|
554 | 682 | } |
|
|
555 | 683 | } |
|
|
556 | 684 | """); |
|
|
557 | 685 | |
|
|
558 | 686 | BuildResult result = runner(":consumer:probe").build(); |
|
|
559 | 687 | |
|
|
560 | 688 | assertTrue(result.getOutput().contains("compileFiles=typesPackage")); |
|
|
561 | 689 | assertTrue(result.getOutput().contains("jsFiles=js")); |
|
|
562 | 690 | } |
|
|
563 | 691 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
