##// END OF EJS Templates
Working on separating variants as standalone plugin
cin -
r38:87d6128f0bc8 default
parent child
Show More
@@ -0,0 +1,41
1 package org.implab.gradle.common.sources;
2
3 import org.implab.gradle.common.core.lang.Closures;
4 import org.gradle.api.Action;
5 import org.gradle.api.Named;
6 import org.gradle.api.provider.Property;
7
8 import groovy.lang.Closure;
9 import groovy.lang.DelegatesTo;
10
11 /**
12 * Public DSL contract for per-layer source-set policy and callbacks.
13 */
14 public interface LayerBindingSpec extends Named {
15 Property<String> getSourceSetNamePattern();
16
17 default void setSourceSetNamePattern(String pattern) {
18 getSourceSetNamePattern().set(pattern);
19 }
20
21 void configureSourceSet(Action<? super GenericSourceSet> configure);
22
23 default void configureSourceSet(
24 @DelegatesTo(value = GenericSourceSet.class, strategy = Closure.DELEGATE_FIRST) Closure<?> configure) {
25 configureSourceSet(Closures.action(configure));
26 }
27
28 void whenRegistered(Action<? super SourceSetRegistration> action);
29
30 default void whenRegistered(
31 @DelegatesTo(value = SourceSetRegistration.class, strategy = Closure.DELEGATE_FIRST) Closure<?> action) {
32 whenRegistered(Closures.action(action));
33 }
34
35 void whenBound(Action<? super SourceSetUsageBinding> action);
36
37 default void whenBound(
38 @DelegatesTo(value = SourceSetUsageBinding.class, strategy = Closure.DELEGATE_FIRST) Closure<?> action) {
39 whenBound(Closures.action(action));
40 }
41 }
@@ -0,0 +1,53
1 plugins {
2 id "java-library"
3 id "ivy-publish"
4 }
5
6 java {
7 withJavadocJar()
8 withSourcesJar()
9 toolchain {
10 languageVersion = JavaLanguageVersion.of(21)
11 }
12 }
13
14 dependencies {
15 compileOnly libs.jdt.annotations
16
17 api gradleApi(),
18 libs.bundles.jackson
19
20 implementation project(":common")
21
22 testImplementation gradleTestKit()
23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
24 testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:5.11.4"
25 testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.4"
26 }
27
28 task printVersion{
29 doLast {
30 println "project: $project.group:$project.name:$project.version"
31 println "jar: ${->jar.archiveFileName.get()}"
32 }
33 }
34
35 test {
36 useJUnitPlatform()
37 }
38
39 publishing {
40 repositories {
41 ivy {
42 url "${System.properties["user.home"]}/ivy-repo"
43 }
44 }
45 publications {
46 ivy(IvyPublication) {
47 from components.java
48 descriptor.description {
49 text = providers.provider({ description })
50 }
51 }
52 }
53 }
@@ -0,0 +1,17
1 package org.implab.gradle.variants;
2
3 import org.gradle.api.Plugin;
4 import org.gradle.api.Project;
5 import org.implab.gradle.variants.model.VariantsExtension;
6
7 public abstract class VariantsPlugin implements Plugin<Project> {
8 @Override
9 public void apply(Project target) {
10 var extension = target.getExtensions().create("variants", VariantsExtension.class);
11
12 target.afterEvaluate(project -> {
13
14 });
15
16 }
17 }
@@ -0,0 +1,9
1 package org.implab.gradle.variants.model;
2
3 import org.gradle.api.Named;
4
5 /**
6 * Identity-only domain object.
7 */
8 public interface Layer extends Named {
9 } No newline at end of file
@@ -0,0 +1,9
1 package org.implab.gradle.variants.model;
2
3 import org.gradle.api.Named;
4
5 /**
6 * Identity-only domain object.
7 */
8 public interface Role extends Named {
9 } No newline at end of file
@@ -0,0 +1,35
1 package org.implab.gradle.variants.model;
2
3 import org.gradle.api.Named;
4 import org.gradle.api.provider.SetProperty;
5
6 /**
7 * Binds a role to a set of layers inside a particular variant.
8 *
9 * The binding name is the role name, e.g. "production", "test", "tool".
10 */
11 public interface RoleBinding extends Named {
12
13 /**
14 * Layer names participating in this (variant, role) selection.
15 *
16 * Core model keeps names here deliberately:
17 * source/materialization semantics live elsewhere.
18 */
19 SetProperty<String> getLayerNames();
20
21 /**
22 * Adds one layer to this binding.
23 */
24 void layer(String name);
25
26 /**
27 * Adds several layers to this binding.
28 */
29 void layers(String... names);
30
31 /**
32 * Adds several layers to this binding.
33 */
34 void layers(Iterable<String> names);
35 } No newline at end of file
@@ -0,0 +1,44
1 package org.implab.gradle.variants.model;
2
3 import org.gradle.api.Action;
4 import org.gradle.api.Named;
5 import org.gradle.api.NamedDomainObjectContainer;
6 import org.implab.gradle.common.core.lang.Closures;
7
8 import groovy.lang.Closure;
9
10 /**
11 * A named variant, e.g. "browser", "electron".
12 *
13 * A variant does not "have a role" directly.
14 * It owns a set of role bindings.
15 */
16 public interface Variant extends Named {
17
18 /**
19 * Role bindings declared inside this variant.
20 *
21 * The binding name is the role name.
22 */
23 NamedDomainObjectContainer<RoleBinding> getRoleBindings();
24
25 /**
26 * Creates or returns an existing role binding and configures it.
27 */
28 default RoleBinding role(String name) {
29 return getRoleBindings().maybeCreate(name);
30 }
31
32 /**
33 * Creates or returns an existing role binding and configures it.
34 */
35 default RoleBinding role(String name, Action<? super RoleBinding> action) {
36 var role = role(name);
37 action.execute(role);
38 return role;
39 }
40
41 default RoleBinding role(String name, Closure<?> closure) {
42 return role(name, Closures.action(closure));
43 }
44 } No newline at end of file
@@ -0,0 +1,59
1 package org.implab.gradle.variants.model;
2
3 import org.gradle.api.Action;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.implab.gradle.common.core.lang.Closures;
6
7 import groovy.lang.Closure;
8
9 /**
10 * Root extension:
11 *
12 * variants {
13 * layers { ... }
14 * roles { ... }
15 *
16 * variant("browser") {
17 * role("production") {
18 * layers("main", "generated", "mainRjs")
19 * }
20 * }
21 * }
22 */
23 public interface VariantsExtension {
24
25 /**
26 * Domain of layers.
27 */
28 NamedDomainObjectContainer<Layer> getLayers();
29
30 /**
31 * Domain of roles.
32 */
33 NamedDomainObjectContainer<Role> getRoles();
34
35 /**
36 * Declared variants.
37 */
38 NamedDomainObjectContainer<Variant> getVariantDefinitions();
39
40 /**
41 * Creates or returns an existing variant and configures it.
42 */
43 default Variant variant(String name) {
44 return getVariantDefinitions().maybeCreate(name);
45 }
46
47 /**
48 * Creates or returns an existing variant and configures it.
49 */
50 default Variant variant(String name, Action<? super Variant> action) {
51 var variant = variant(name);
52 action.execute(variant);
53 return variant;
54 }
55
56 default Variant variant(String name, Closure<?> closure) {
57 return variant(name, Closures.action(closure));
58 }
59 } No newline at end of file
@@ -87,11 +87,11 outputs. Это уже "физический" уровень, к которому удобно привязывать задачи,
87
87
88 ## DOMAIN MODEL
88 ## DOMAIN MODEL
89
89
90 - `BuildLayer` — глобальный идентификатор слоя.
90 - `BuildLayer` — canonical identity-model объявленного слоя.
91 - `BuildVariant` — агрегат ролей, атрибутов, артефактных слотов.
91 - `BuildVariant` — агрегат ролей, атрибутов, артефактных слотов.
92 - `BuildRole` — роль внутри варианта, содержит ссылки на layer names.
92 - `BuildRole` — роль внутри варианта, содержит ссылки на declared layer names.
93 - `GenericSourceSet` — зарегистрированный набор исходников и outputs.
93 - `GenericSourceSet` — зарегистрированный набор исходников и outputs.
94 - `BuildLayerBinding` — правила registration source set для конкретного layer.
94 - `LayerBindingSpec` — публичный DSL-contract adapter policy/callbacks для слоя.
95 - `SourceSetRegistration` — payload события регистрации source set.
95 - `SourceSetRegistration` — payload события регистрации source set.
96 - `SourceSetUsageBinding` — payload события usage-binding.
96 - `SourceSetUsageBinding` — payload события usage-binding.
97
97
@@ -116,7 +116,7 Closure callbacks работают в delegate-first режиме (`@DelegatesTo`). Для
116 - `BuildVariant` — API ролей, attributes и artifact slots варианта.
116 - `BuildVariant` — API ролей, attributes и artifact slots варианта.
117 - `VariantsSourcesPlugin` — применяет `variants` + `sources` и запускает адаптер.
117 - `VariantsSourcesPlugin` — применяет `variants` + `sources` и запускает адаптер.
118 - `VariantSourcesExtension` — API bind/events registration.
118 - `VariantSourcesExtension` — API bind/events registration.
119 - `BuildLayerBinding` — слой-конкретный DSL для имени и конфигурации source set.
119 - `LayerBindingSpec` — слой-конкретный DSL для policy/configuration source set.
120 - `SourceSetRegistration` — payload `whenRegistered(...)`.
120 - `SourceSetRegistration` — payload `whenRegistered(...)`.
121 - `SourceSetUsageBinding` — payload `whenBound(...)`.
121 - `SourceSetUsageBinding` — payload `whenBound(...)`.
122
122
@@ -20,22 +20,17 import org.gradle.api.model.ObjectFactor
20 import groovy.lang.Closure;
20 import groovy.lang.Closure;
21
21
22 public abstract class BuildVariantsExtension {
22 public abstract class BuildVariantsExtension {
23 private final NamedDomainObjectContainer<BuildLayer> layers;
23 private final ObjectFactory objects;
24 private final LinkedHashMap<String, LayoutLayer> layersByName = new LinkedHashMap<>();
24 private final NamedDomainObjectContainer<BuildVariant> variants;
25 private final NamedDomainObjectContainer<BuildVariant> variants;
25 private final List<Action<? super BuildVariantsExtension>> finalizedActions = new ArrayList<>();
26 private final List<Action<? super BuildVariantsExtension>> finalizedActions = new ArrayList<>();
26 private boolean finalized;
27 private boolean finalized;
27
28
28 @Inject
29 @Inject
29 public BuildVariantsExtension(ObjectFactory objects) {
30 public BuildVariantsExtension(ObjectFactory objects) {
30 layers = objects.domainObjectContainer(BuildLayer.class);
31 this.objects = objects;
31 variants = objects.domainObjectContainer(BuildVariant.class);
32 variants = objects.domainObjectContainer(BuildVariant.class);
32
33
33 layers.all(layer -> {
34 if (finalized)
35 throw new InvalidUserDataException(
36 "Variants model is finalized and cannot add layer '" + layer.getName() + "'");
37 });
38
39 variants.all(variant -> {
34 variants.all(variant -> {
40 if (finalized)
35 if (finalized)
41 throw new InvalidUserDataException(
36 throw new InvalidUserDataException(
@@ -43,44 +38,26 public abstract class BuildVariantsExten
43 });
38 });
44 }
39 }
45
40
46 public NamedDomainObjectContainer<BuildLayer> getLayers() {
41 public Collection<LayoutLayer> getLayers() {
47 return layers;
42 return Collections.unmodifiableCollection(layersByName.values());
48 }
43 }
49
44
50 public NamedDomainObjectContainer<BuildVariant> getVariants() {
45 public NamedDomainObjectContainer<BuildVariant> getVariants() {
51 return variants;
46 return variants;
52 }
47 }
53
48
54 public void layers(Action<? super NamedDomainObjectContainer<BuildLayer>> action) {
49 public LayoutLayer layer(String name, Action<? super LayoutLayer> configure) {
55 ensureMutable("configure layers");
50 ensureMutable("configure layers");
56 action.execute(layers);
51 var layer = layersByName.computeIfAbsent(requireName(name, "Layer name must not be null or blank"), this::newLayer);
57 }
58
59 public void layers(Closure<?> configure) {
60 layers(Closures.action(configure));
61 }
62
63 public void variants(Action<? super NamedDomainObjectContainer<BuildVariant>> action) {
64 ensureMutable("configure variants");
65 action.execute(variants);
66 }
67
68 public void variants(Closure<?> configure) {
69 variants(Closures.action(configure));
70 }
71
72 public BuildLayer layer(String name, Action<? super BuildLayer> configure) {
73 ensureMutable("configure layers");
74 var layer = layers.maybeCreate(name);
75 configure.execute(layer);
52 configure.execute(layer);
76 return layer;
53 return layer;
77 }
54 }
78
55
79 public BuildLayer layer(String name, Closure<?> configure) {
56 public LayoutLayer layer(String name, Closure<?> configure) {
80 return layer(name, Closures.action(configure));
57 return layer(name, Closures.action(configure));
81 }
58 }
82
59
83 public BuildLayer layer(String name) {
60 public LayoutLayer layer(String name) {
84 return layer(name, it -> {
61 return layer(name, it -> {
85 });
62 });
86 }
63 }
@@ -115,6 +92,16 public abstract class BuildVariantsExten
115 return Collections.unmodifiableList(all);
92 return Collections.unmodifiableList(all);
116 }
93 }
117
94
95 public Optional<LayoutLayer> findLayer(String name) {
96 var normalizedName = normalize(name);
97 return normalizedName == null ? Optional.empty() : Optional.ofNullable(layersByName.get(normalizedName));
98 }
99
100 public LayoutLayer requireLayer(String name) {
101 return findLayer(name)
102 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't defined"));
103 }
104
118 public Optional<BuildVariant> find(String name) {
105 public Optional<BuildVariant> find(String name) {
119 return Optional.ofNullable(variants.findByName(name));
106 return Optional.ofNullable(variants.findByName(name));
120 }
107 }
@@ -160,20 +147,6 public abstract class BuildVariantsExten
160 public void validate() {
147 public void validate() {
161 var errors = new ArrayList<String>();
148 var errors = new ArrayList<String>();
162
149
163 var layersByName = new LinkedHashMap<String, BuildLayer>();
164 for (var layer : layers) {
165 var layerName = normalize(layer.getName());
166 if (layerName == null) {
167 errors.add("Layer name must not be blank");
168 continue;
169 }
170
171 var previous = layersByName.putIfAbsent(layerName, layer);
172 if (previous != null) {
173 errors.add("Layer '" + layerName + "' is declared more than once");
174 }
175 }
176
177 for (var variant : variants)
150 for (var variant : variants)
178 validateVariant(variant, layersByName, errors);
151 validateVariant(variant, layersByName, errors);
179
152
@@ -186,7 +159,7 public abstract class BuildVariantsExten
186 }
159 }
187 }
160 }
188
161
189 private static void validateVariant(BuildVariant variant, Map<String, BuildLayer> layersByName, List<String> errors) {
162 private static void validateVariant(BuildVariant variant, Map<String, LayoutLayer> layersByName, List<String> errors) {
190 var variantName = normalize(variant.getName());
163 var variantName = normalize(variant.getName());
191 if (variantName == null) {
164 if (variantName == null) {
192 errors.add("Variant name must not be blank");
165 errors.add("Variant name must not be blank");
@@ -211,7 +184,7 public abstract class BuildVariantsExten
211 }
184 }
212 }
185 }
213
186
214 private static void validateRoleMappings(BuildVariant variant, Map<String, BuildLayer> layersByName,
187 private static void validateRoleMappings(BuildVariant variant, Map<String, LayoutLayer> layersByName,
215 List<String> errors) {
188 List<String> errors) {
216 for (var role : variant.getRoles()) {
189 for (var role : variant.getRoles()) {
217 var seenLayers = new LinkedHashSet<String>();
190 var seenLayers = new LinkedHashSet<String>();
@@ -244,6 +217,17 public abstract class BuildVariantsExten
244 return trimmed.isEmpty() ? null : trimmed;
217 return trimmed.isEmpty() ? null : trimmed;
245 }
218 }
246
219
220 private static String requireName(String value, String errorMessage) {
221 var normalized = normalize(value);
222 if (normalized == null)
223 throw new InvalidUserDataException(errorMessage);
224 return normalized;
225 }
226
227 private LayoutLayer newLayer(String name) {
228 return objects.newInstance(LayoutLayer.class, name);
229 }
230
247 private void ensureMutable(String operation) {
231 private void ensureMutable(String operation) {
248 if (finalized)
232 if (finalized)
249 throw new InvalidUserDataException("Variants model is finalized and cannot " + operation);
233 throw new InvalidUserDataException("Variants model is finalized and cannot " + operation);
@@ -5,24 +5,16 import java.util.LinkedHashSet;
5 import java.util.List;
5 import java.util.List;
6 import java.util.Set;
6 import java.util.Set;
7
7
8 import javax.inject.Inject;
9
10 import org.implab.gradle.common.core.lang.Closures;
11 import org.gradle.api.Action;
8 import org.gradle.api.Action;
12 import org.gradle.api.Named;
13 import org.gradle.api.NamedDomainObjectProvider;
9 import org.gradle.api.NamedDomainObjectProvider;
10 import org.gradle.api.model.ObjectFactory;
14 import org.gradle.api.provider.Property;
11 import org.gradle.api.provider.Property;
15
12
16 import groovy.lang.Closure;
13 final class LayerBinding implements LayerBindingSpec {
17 import groovy.lang.DelegatesTo;
14 static final String DEFAULT_SOURCE_SET_NAME_PATTERN = "{variant}{layerCap}";
18
19 /**
20 * Maps a logical layer to per-source-set hooks.
21 */
22 public abstract class BuildLayerBinding implements Named {
23 public static final String DEFAULT_SOURCE_SET_NAME_PATTERN = "{variant}{layerCap}";
24
15
25 private final String name;
16 private final String name;
17 private final Property<String> sourceSetNamePattern;
26
18
27 private final List<Action<? super GenericSourceSet>> sourceSetConfigureActions = new ArrayList<>();
19 private final List<Action<? super GenericSourceSet>> sourceSetConfigureActions = new ArrayList<>();
28 private final List<Action<? super SourceSetRegistration>> registeredActions = new ArrayList<>();
20 private final List<Action<? super SourceSetRegistration>> registeredActions = new ArrayList<>();
@@ -32,10 +24,10 public abstract class BuildLayerBinding
32 private final List<SourceSetUsageBinding> boundContexts = new ArrayList<>();
24 private final List<SourceSetUsageBinding> boundContexts = new ArrayList<>();
33 private final Set<String> registeredSourceSetNames = new LinkedHashSet<>();
25 private final Set<String> registeredSourceSetNames = new LinkedHashSet<>();
34
26
35 @Inject
27 LayerBinding(String name, ObjectFactory objects) {
36 public BuildLayerBinding(String name) {
37 this.name = name;
28 this.name = name;
38 getSourceSetNamePattern().convention(DEFAULT_SOURCE_SET_NAME_PATTERN);
29 sourceSetNamePattern = objects.property(String.class);
30 sourceSetNamePattern.convention(DEFAULT_SOURCE_SET_NAME_PATTERN);
39 }
31 }
40
32
41 @Override
33 @Override
@@ -43,61 +35,32 public abstract class BuildLayerBinding
43 return name;
35 return name;
44 }
36 }
45
37
46 public abstract Property<String> getSourceSetNamePattern();
38 @Override
39 public Property<String> getSourceSetNamePattern() {
40 return sourceSetNamePattern;
41 }
47
42
48 /**
43 @Override
49 * Action applied to every registered source set for this layer.
50 * Already registered source sets are configured immediately (replay).
51 */
52 public void configureSourceSet(Action<? super GenericSourceSet> configure) {
44 public void configureSourceSet(Action<? super GenericSourceSet> configure) {
53 sourceSetConfigureActions.add(configure);
45 sourceSetConfigureActions.add(configure);
54 for (var sourceSet : registeredSourceSets)
46 for (var sourceSet : registeredSourceSets)
55 sourceSet.configure(configure);
47 sourceSet.configure(configure);
56 }
48 }
57
49
58 public void configureSourceSet(
50 @Override
59 @DelegatesTo(value = GenericSourceSet.class, strategy = Closure.DELEGATE_FIRST) Closure<?> configure) {
60 configureSourceSet(Closures.action(configure));
61 }
62
63 /**
64 * Layer-local callback fired after source-set registration.
65 * Already emitted registrations are delivered immediately (replay).
66 * For simple callbacks you can use delegate-only style
67 * (for example {@code whenRegistered { sourceSetName() }}).
68 * For nested closures prefer explicit parameter
69 * ({@code whenRegistered { ctx -> ... }}).
70 */
71 public void whenRegistered(Action<? super SourceSetRegistration> action) {
51 public void whenRegistered(Action<? super SourceSetRegistration> action) {
72 registeredActions.add(action);
52 registeredActions.add(action);
73 for (var context : registeredContexts)
53 for (var context : registeredContexts)
74 action.execute(context);
54 action.execute(context);
75 }
55 }
76
56
77 public void whenRegistered(
57 @Override
78 @DelegatesTo(value = SourceSetRegistration.class, strategy = Closure.DELEGATE_FIRST) Closure<?> action) {
79 whenRegistered(Closures.action(action));
80 }
81
82 /**
83 * Layer-local callback fired for every resolved variant/role/layer usage.
84 * Already emitted usage bindings are delivered immediately (replay).
85 * For simple callbacks you can use delegate-only style
86 * (for example {@code whenBound { variantName() }}).
87 * For nested closures prefer explicit parameter
88 * ({@code whenBound { ctx -> ... }}).
89 */
90 public void whenBound(Action<? super SourceSetUsageBinding> action) {
58 public void whenBound(Action<? super SourceSetUsageBinding> action) {
91 boundActions.add(action);
59 boundActions.add(action);
92 for (var context : boundContexts)
60 for (var context : boundContexts)
93 action.execute(context);
61 action.execute(context);
94 }
62 }
95
63
96 public void whenBound(
97 @DelegatesTo(value = SourceSetUsageBinding.class, strategy = Closure.DELEGATE_FIRST) Closure<?> action) {
98 whenBound(Closures.action(action));
99 }
100
101 void notifyRegistered(SourceSetRegistration registration) {
64 void notifyRegistered(SourceSetRegistration registration) {
102 if (registeredSourceSetNames.add(registration.sourceSetName())) {
65 if (registeredSourceSetNames.add(registration.sourceSetName())) {
103 var sourceSet = registration.sourceSet();
66 var sourceSet = registration.sourceSet();
@@ -5,13 +5,13 import javax.inject.Inject;
5 import org.gradle.api.Named;
5 import org.gradle.api.Named;
6
6
7 /**
7 /**
8 * Global layer declaration used by build variants.
8 * Canonical identity model for a declared layout layer.
9 */
9 */
10 public abstract class BuildLayer implements Named {
10 public abstract class LayoutLayer implements Named {
11 private final String name;
11 private final String name;
12
12
13 @Inject
13 @Inject
14 public BuildLayer(String name) {
14 public LayoutLayer(String name) {
15 this.name = name;
15 this.name = name;
16 }
16 }
17
17
@@ -12,7 +12,7 import groovy.lang.DelegatesTo;
12 *
12 *
13 * <p>Used as callback payload for
13 * <p>Used as callback payload for
14 * {@link VariantSourcesExtension#whenRegistered(org.gradle.api.Action)} and
14 * {@link VariantSourcesExtension#whenRegistered(org.gradle.api.Action)} and
15 * {@link BuildLayerBinding#whenRegistered(org.gradle.api.Action)}.
15 * {@link LayerBindingSpec#whenRegistered(org.gradle.api.Action)}.
16 *
16 *
17 * @param layerName normalized layer name that owns the registration
17 * @param layerName normalized layer name that owns the registration
18 * @param sourceSetName source-set name registered in the container
18 * @param sourceSetName source-set name registered in the container
@@ -12,7 +12,7 import groovy.lang.DelegatesTo;
12 *
12 *
13 * <p>Used as callback payload for
13 * <p>Used as callback payload for
14 * {@link VariantSourcesExtension#whenBound(org.gradle.api.Action)} and
14 * {@link VariantSourcesExtension#whenBound(org.gradle.api.Action)} and
15 * {@link BuildLayerBinding#whenBound(org.gradle.api.Action)}.
15 * {@link LayerBindingSpec#whenBound(org.gradle.api.Action)}.
16 *
16 *
17 * @param variantName variant name from the build-variants model
17 * @param variantName variant name from the build-variants model
18 * @param roleName role name inside the resolved variant
18 * @param roleName role name inside the resolved variant
@@ -124,6 +124,10 public class VariantArtifactSlot impleme
124 return List.copyOf(bindings);
124 return List.copyOf(bindings);
125 }
125 }
126
126
127 void acceptBindings(Consumer<? super BindingResolver> consumer) {
128 bindings.forEach(consumer);
129 }
130
127 Set<String> referencedRoleNames() {
131 Set<String> referencedRoleNames() {
128 return Set.copyOf(referencedRoleNames);
132 return Set.copyOf(referencedRoleNames);
129 }
133 }
@@ -33,7 +33,8 public abstract class VariantArtifactsPl
33 var variantArtifactsResolver = new VariantArtifactsResolver(target.getObjects());
33 var variantArtifactsResolver = new VariantArtifactsResolver(target.getObjects());
34 var artifactAssemblies = new ArtifactAssemblyRegistry(target.getObjects(), target.getTasks());
34 var artifactAssemblies = new ArtifactAssemblyRegistry(target.getObjects(), target.getTasks());
35
35
36 // Bind variant artifacts resolution to variant sources registration, so that artifact resolution can be performed
36 // Bind variant artifacts resolution to variant sources registration, so that
37 // artifact resolution can be performed
37 variantSources.whenBound(variantArtifactsResolver::recordBinding);
38 variantSources.whenBound(variantArtifactsResolver::recordBinding);
38
39
39 variants.whenFinalized(model -> {
40 variants.whenFinalized(model -> {
@@ -86,7 +87,8 public abstract class VariantArtifactsPl
86 var assemblies = variantArtifact.getSlots().stream()
87 var assemblies = variantArtifact.getSlots().stream()
87 .collect(Collectors.toMap(
88 .collect(Collectors.toMap(
88 VariantArtifactSlot::getName,
89 VariantArtifactSlot::getName,
89 slot -> registerAssembly(project, variantArtifactsResolver, artifactAssemblies, variantArtifact, slot),
90 slot -> registerAssembly(project, variantArtifactsResolver, artifactAssemblies, variantArtifact,
91 slot),
90 (left, right) -> left,
92 (left, right) -> left,
91 LinkedHashMap::new));
93 LinkedHashMap::new));
92
94
@@ -136,9 +138,10 public abstract class VariantArtifactsPl
136 ArtifactAssemblyRegistry artifactAssemblies,
138 ArtifactAssemblyRegistry artifactAssemblies,
137 VariantArtifact variantArtifact,
139 VariantArtifact variantArtifact,
138 VariantArtifactSlot slot) {
140 VariantArtifactSlot slot) {
141 String assemblyName = variantArtifact.getName() + Strings.capitalize(slot.getName());
139 return artifactAssemblies.register(
142 return artifactAssemblies.register(
140 variantArtifact.getName() + Strings.capitalize(slot.getName()),
143 assemblyName,
141 "process" + Strings.capitalize(variantArtifact.getName()) + Strings.capitalize(slot.getName()),
144 "process" + Strings.capitalize(assemblyName),
142 project.getLayout().getBuildDirectory()
145 project.getLayout().getBuildDirectory()
143 .dir("variant-artifacts/" + variantArtifact.getName() + "/" + slot.getName()),
146 .dir("variant-artifacts/" + variantArtifact.getName() + "/" + slot.getName()),
144 files -> files.from(variantArtifactsResolver.files(variantArtifact.getName(), slot)));
147 files -> files.from(variantArtifactsResolver.files(variantArtifact.getName(), slot)));
@@ -1,6 +1,7
1 package org.implab.gradle.common.sources;
1 package org.implab.gradle.common.sources;
2
2
3 import java.util.ArrayList;
3 import java.util.ArrayList;
4 import java.util.Collection;
4 import java.util.LinkedHashSet;
5 import java.util.LinkedHashSet;
5 import java.util.List;
6 import java.util.List;
6 import java.util.Set;
7 import java.util.Set;
@@ -86,13 +87,11 public final class VariantArtifactsResol
86 * @return lazily wired file collection for the selected outputs
87 * @return lazily wired file collection for the selected outputs
87 */
88 */
88 public FileCollection files(String variantName, VariantArtifactSlot slot) {
89 public FileCollection files(String variantName, VariantArtifactSlot slot) {
89 var builder = new FileCollectionBuilder();
90 var contexts = boundContexts.stream()
90 var contexts = boundContexts.stream()
91 .filter(context -> variantName.equals(context.variantName()))
91 .filter(context -> variantName.equals(context.variantName()))
92 .toList();
92 .toList();
93
93 var builder = new FileCollectionBuilder(contexts);
94 slot.bindings().forEach(binding -> binding.resolve(contexts, builder::addOutput));
94 slot.acceptBindings(builder::visitBinding);
95
96 return builder.build();
95 return builder.build();
97 }
96 }
98
97
@@ -103,9 +102,11 public final class VariantArtifactsResol
103 class FileCollectionBuilder {
102 class FileCollectionBuilder {
104 private final ConfigurableFileCollection files;
103 private final ConfigurableFileCollection files;
105 private final Set<BindingKey> boundOutputs = new LinkedHashSet<>();
104 private final Set<BindingKey> boundOutputs = new LinkedHashSet<>();
105 private final Collection<SourceSetUsageBinding> contexts;
106
106
107 FileCollectionBuilder() {
107 FileCollectionBuilder(Collection<SourceSetUsageBinding> contexts) {
108 this.files = objects.fileCollection();
108 this.files = objects.fileCollection();
109 this.contexts = contexts;
109 }
110 }
110
111
111 FileCollection build() {
112 FileCollection build() {
@@ -116,6 +117,10 public final class VariantArtifactsResol
116 if (boundOutputs.add(binding.key()))
117 if (boundOutputs.add(binding.key()))
117 files.from(binding.files());
118 files.from(binding.files());
118 }
119 }
120
121 void visitBinding(VariantArtifactSlot.BindingResolver resolver) {
122 resolver.resolve(contexts, this::addOutput);
123 }
119 }
124 }
120
125
121 }
126 }
@@ -15,7 +15,6 import org.eclipse.jdt.annotation.NonNul
15 import org.eclipse.jdt.annotation.Nullable;
15 import org.eclipse.jdt.annotation.Nullable;
16 import org.gradle.api.Action;
16 import org.gradle.api.Action;
17 import org.gradle.api.InvalidUserDataException;
17 import org.gradle.api.InvalidUserDataException;
18 import org.gradle.api.NamedDomainObjectContainer;
19 import org.gradle.api.NamedDomainObjectProvider;
18 import org.gradle.api.NamedDomainObjectProvider;
20 import org.gradle.api.file.ProjectLayout;
19 import org.gradle.api.file.ProjectLayout;
21 import org.gradle.api.model.ObjectFactory;
20 import org.gradle.api.model.ObjectFactory;
@@ -35,8 +34,9 public abstract class VariantSourcesExte
35 private static final Logger logger = Logging.getLogger(VariantSourcesExtension.class);
34 private static final Logger logger = Logging.getLogger(VariantSourcesExtension.class);
36 private static final Pattern SOURCE_SET_NAME_TOKEN = Pattern.compile("\\{([A-Za-z][A-Za-z0-9]*)\\}");
35 private static final Pattern SOURCE_SET_NAME_TOKEN = Pattern.compile("\\{([A-Za-z][A-Za-z0-9]*)\\}");
37
36
37 private final ObjectFactory objects;
38 private final ProjectLayout layout;
38 private final ProjectLayout layout;
39 private final NamedDomainObjectContainer<BuildLayerBinding> bindings;
39 private final LinkedHashMap<String, LayerBinding> bindingsByName = new LinkedHashMap<>();
40 private final List<Action<? super SourceSetRegistration>> registeredActions = new ArrayList<>();
40 private final List<Action<? super SourceSetRegistration>> registeredActions = new ArrayList<>();
41 private final List<Action<? super SourceSetUsageBinding>> boundActions = new ArrayList<>();
41 private final List<Action<? super SourceSetUsageBinding>> boundActions = new ArrayList<>();
42 private final List<SourceSetRegistration> registeredContexts = new ArrayList<>();
42 private final List<SourceSetRegistration> registeredContexts = new ArrayList<>();
@@ -47,38 +47,46 public abstract class VariantSourcesExte
47
47
48 @Inject
48 @Inject
49 public VariantSourcesExtension(ObjectFactory objects, ProjectLayout layout) {
49 public VariantSourcesExtension(ObjectFactory objects, ProjectLayout layout) {
50 this.objects = objects;
50 this.layout = layout;
51 this.layout = layout;
51 bindings = objects.domainObjectContainer(BuildLayerBinding.class);
52 }
52 }
53
53
54 public NamedDomainObjectContainer<BuildLayerBinding> getBindings() {
54 public List<LayerBindingSpec> getBindings() {
55 return bindings;
55 return bindingsByName.values().stream().map(x -> (LayerBindingSpec)x).toList();
56 }
56 }
57
57
58 public void bindings(Action<? super NamedDomainObjectContainer<BuildLayerBinding>> action) {
58 public LayerBindingSpec bind(String layer) {
59 action.execute(bindings);
59 return bindingsByName.computeIfAbsent(
60 normalize(layer, "Layer name must not be null or blank"),
61 name -> new LayerBinding(name, objects));
60 }
62 }
61
63
62 public void bindings(
64 public LayerBindingSpec bind(LayoutLayer layer) {
63 @DelegatesTo(value = NamedDomainObjectContainer.class, strategy = Closure.DELEGATE_FIRST) Closure<?> action) {
65 return bind(layer.getName());
64 bindings(Closures.action(action));
65 }
66
67 public BuildLayerBinding bind(String layer) {
68 return bindings.maybeCreate(normalize(layer, "Layer name must not be null or blank"));
69 }
66 }
70
67
71 /**
68 /**
72 * Configures per-layer binding.
69 * Configures per-layer binding.
73 */
70 */
74 public BuildLayerBinding bind(String layer, Action<? super BuildLayerBinding> configure) {
71 public LayerBindingSpec bind(String layer, Action<? super LayerBindingSpec> configure) {
75 var binding = bind(layer);
72 var binding = bind(layer);
76 configure.execute(binding);
73 configure.execute(binding);
77 return binding;
74 return binding;
78 }
75 }
79
76
80 public BuildLayerBinding bind(String layer,
77 public LayerBindingSpec bind(LayoutLayer layer, Action<? super LayerBindingSpec> configure) {
81 @DelegatesTo(value = BuildLayerBinding.class, strategy = Closure.DELEGATE_FIRST) Closure<?> configure) {
78 var binding = bind(layer);
79 configure.execute(binding);
80 return binding;
81 }
82
83 public LayerBindingSpec bind(String layer,
84 @DelegatesTo(value = LayerBindingSpec.class, strategy = Closure.DELEGATE_FIRST) Closure<?> configure) {
85 return bind(layer, Closures.action(configure));
86 }
87
88 public LayerBindingSpec bind(LayoutLayer layer,
89 @DelegatesTo(value = LayerBindingSpec.class, strategy = Closure.DELEGATE_FIRST) Closure<?> configure) {
82 return bind(layer, Closures.action(configure));
90 return bind(layer, Closures.action(configure));
83 }
91 }
84
92
@@ -130,12 +138,12 public abstract class VariantSourcesExte
130 whenBound(variantName, Closures.action(action));
138 whenBound(variantName, Closures.action(action));
131 }
139 }
132
140
133 void registerSourceSets(BuildVariantsExtension variants, NamedDomainObjectContainer<GenericSourceSet> sources) {
141 void registerSourceSets(BuildVariantsExtension variants, org.gradle.api.NamedDomainObjectContainer<GenericSourceSet> sources) {
134 if (sourceSetsRegistered) {
142 if (sourceSetsRegistered) {
135 throw new InvalidUserDataException("variantSources source sets are already registered");
143 throw new InvalidUserDataException("variantSources source sets are already registered");
136 }
144 }
137
145
138 validateBindings(variants);
146 resolveBindings(variants);
139
147
140 var usages = layerUsages(variants).toList();
148 var usages = layerUsages(variants).toList();
141 var registeredBefore = registeredContexts.size();
149 var registeredBefore = registeredContexts.size();
@@ -145,7 +153,7 public abstract class VariantSourcesExte
145 "Starting variant source-set registration (variants={}, layers={}, bindings={}, usages={})",
153 "Starting variant source-set registration (variants={}, layers={}, bindings={}, usages={})",
146 variants.getVariants().size(),
154 variants.getVariants().size(),
147 variants.getLayers().size(),
155 variants.getLayers().size(),
148 bindings.size(),
156 bindingsByName.size(),
149 usages.size());
157 usages.size());
150
158
151 usages.forEach(usage -> registerLayerUsage(usage, sources));
159 usages.forEach(usage -> registerLayerUsage(usage, sources));
@@ -166,24 +174,25 public abstract class VariantSourcesExte
166 .map(layerName -> new LayerUsage(
174 .map(layerName -> new LayerUsage(
167 variant.getName(),
175 variant.getName(),
168 role.getName(),
176 role.getName(),
169 normalize(layerName, "Layer name in variant '" + variant.getName()
177 variants.requireLayer(normalize(layerName, "Layer name in variant '"
170 + "' and role '" + role.getName() + "' must not be null or blank")))));
178 + variant.getName() + "' and role '" + role.getName()
179 + "' must not be null or blank"))))));
171 }
180 }
172
181
173 private void registerLayerUsage(LayerUsage usage, NamedDomainObjectContainer<GenericSourceSet> sources) {
182 private void registerLayerUsage(LayerUsage usage, org.gradle.api.NamedDomainObjectContainer<GenericSourceSet> sources) {
174 var resolvedBinding = bind(usage.layerName());
183 var resolvedBinding = binding(usage.layer().getName());
175 var sourceSetNamePattern = resolvedBinding.getSourceSetNamePattern();
184 var sourceSetNamePattern = resolvedBinding.getSourceSetNamePattern();
176 sourceSetNamePattern.finalizeValueOnRead();
185 sourceSetNamePattern.finalizeValueOnRead();
177
186
178 var sourceSetName = sourceSetName(usage, sourceSetNamePattern.get());
187 var sourceSetName = sourceSetName(usage, sourceSetNamePattern.get());
179
188
180 ensureSourceSetNameBoundToSingleLayer(sourceSetName, usage.layerName());
189 ensureSourceSetNameBoundToSingleLayer(sourceSetName, usage.layer().getName());
181 var isNewSourceSet = !sourceSetsByName.containsKey(sourceSetName);
190 var isNewSourceSet = !sourceSetsByName.containsKey(sourceSetName);
182 var sourceSet = sourceSetsByName.computeIfAbsent(sourceSetName,
191 var sourceSet = sourceSetsByName.computeIfAbsent(sourceSetName,
183 name -> {
192 name -> {
184 var ssp = sources.register(name);
193 var ssp = sources.register(name);
185 ssp.configure(x -> {
194 ssp.configure(x -> {
186 x.getSourceSetDir().set(layout.getProjectDirectory().dir("src/" + usage.layerName()));
195 x.getSourceSetDir().set(layout.getProjectDirectory().dir("src/" + usage.layer().getName()));
187 });
196 });
188 return ssp;
197 return ssp;
189 });
198 });
@@ -191,13 +200,13 public abstract class VariantSourcesExte
191 var binding = new SourceSetUsageBinding(
200 var binding = new SourceSetUsageBinding(
192 usage.variantName(),
201 usage.variantName(),
193 usage.roleName(),
202 usage.roleName(),
194 usage.layerName(),
203 usage.layer().getName(),
195 sourceSetName,
204 sourceSetName,
196 sourceSet);
205 sourceSet);
197
206
198 if (isNewSourceSet) {
207 if (isNewSourceSet) {
199 var registration = new SourceSetRegistration(
208 var registration = new SourceSetRegistration(
200 usage.layerName(),
209 usage.layer().getName(),
201 sourceSetName,
210 sourceSetName,
202 sourceSet);
211 sourceSet);
203 resolvedBinding.notifyRegistered(registration);
212 resolvedBinding.notifyRegistered(registration);
@@ -236,14 +245,10 public abstract class VariantSourcesExte
236 }
245 }
237 }
246 }
238
247
239 private void validateBindings(BuildVariantsExtension variants) {
248 private void resolveBindings(BuildVariantsExtension variants) {
240 var knownLayerNames = new java.util.LinkedHashSet<String>();
241 for (var layer : variants.getLayers())
242 knownLayerNames.add(layer.getName());
243
244 var errors = new ArrayList<String>();
249 var errors = new ArrayList<String>();
245 for (var binding : bindings) {
250 for (var binding : bindingsByName.values()) {
246 if (!knownLayerNames.contains(binding.getName())) {
251 if (variants.findLayer(binding.getName()).isEmpty()) {
247 errors.add("Layer binding '" + binding.getName() + "' references unknown layer");
252 errors.add("Layer binding '" + binding.getName() + "' references unknown layer");
248 }
253 }
249 }
254 }
@@ -256,6 +261,10 public abstract class VariantSourcesExte
256 }
261 }
257 }
262 }
258
263
264 private LayerBinding binding(String layerName) {
265 return bindingsByName.computeIfAbsent(layerName, name -> new LayerBinding(name, objects));
266 }
267
259 private static String sourceSetName(LayerUsage usage, String pattern) {
268 private static String sourceSetName(LayerUsage usage, String pattern) {
260 var normalizedPattern = normalize(pattern, "sourceSetNamePattern must not be null or blank");
269 var normalizedPattern = normalize(pattern, "sourceSetNamePattern must not be null or blank");
261 var resolved = resolveSourceSetNamePattern(normalizedPattern, usage);
270 var resolved = resolveSourceSetNamePattern(normalizedPattern, usage);
@@ -287,8 +296,8 public abstract class VariantSourcesExte
287 case "variantCap" -> Strings.capitalize(sanitizeName(usage.variantName()));
296 case "variantCap" -> Strings.capitalize(sanitizeName(usage.variantName()));
288 case "role" -> sanitizeName(usage.roleName());
297 case "role" -> sanitizeName(usage.roleName());
289 case "roleCap" -> Strings.capitalize(sanitizeName(usage.roleName()));
298 case "roleCap" -> Strings.capitalize(sanitizeName(usage.roleName()));
290 case "layer" -> sanitizeName(usage.layerName());
299 case "layer" -> sanitizeName(usage.layer().getName());
291 case "layerCap" -> Strings.capitalize(sanitizeName(usage.layerName()));
300 case "layerCap" -> Strings.capitalize(sanitizeName(usage.layer().getName()));
292 default -> throw new InvalidUserDataException(
301 default -> throw new InvalidUserDataException(
293 "sourceSetNamePattern contains unsupported token '{" + token + "}'");
302 "sourceSetNamePattern contains unsupported token '{" + token + "}'");
294 };
303 };
@@ -303,6 +312,6 public abstract class VariantSourcesExte
303 return trimmed;
312 return trimmed;
304 }
313 }
305
314
306 private record LayerUsage(String variantName, String roleName, String layerName) {
315 private record LayerUsage(String variantName, String roleName, LayoutLayer layer) {
307 }
316 }
308 }
317 }
@@ -41,8 +41,8 class VariantsArtifactsPluginFunctionalT
41 }
41 }
42
42
43 variants {
43 variants {
44 layer('mainBase')
44 layers('mainBase', 'mainAmd')
45 layer('mainAmd')
45 roles('main', 'test')
46
46
47 variant('browser') {
47 variant('browser') {
48 role('main') {
48 role('main') {
@@ -64,6 +64,41 class VariantsPluginFunctionalTest {
64 }
64 }
65
65
66 @Test
66 @Test
67 void supportsLayerRegistryDslAndLookupApi() throws Exception {
68 writeFile(SETTINGS_FILE, ROOT_NAME);
69 writeFile(BUILD_FILE, """
70 plugins {
71 id 'org.implab.gradle-variants'
72 }
73
74 variants {
75 layer('mainBase')
76 layer('mainAmd')
77
78 variant('browser') {
79 role('main') {
80 layers('mainBase', 'mainAmd')
81 }
82 }
83 }
84
85 tasks.register('probe') {
86 doLast {
87 println('layers=' + variants.layers.collect { it.name }.sort().join(','))
88 println('requireLayer=' + variants.requireLayer('mainBase').name)
89 println('findLayer=' + variants.findLayer('missing').isPresent())
90 }
91 }
92 """);
93
94 BuildResult result = runner("probe").build();
95
96 assertTrue(result.getOutput().contains("layers=mainAmd,mainBase"));
97 assertTrue(result.getOutput().contains("requireLayer=mainBase"));
98 assertTrue(result.getOutput().contains("findLayer=false"));
99 }
100
101 @Test
67 void failsOnUnknownLayerReference() throws Exception {
102 void failsOnUnknownLayerReference() throws Exception {
68 assertBuildFails("""
103 assertBuildFails("""
69 plugins {
104 plugins {
@@ -133,6 +133,83 class VariantsSourcesPluginFunctionalTes
133 }
133 }
134
134
135 @Test
135 @Test
136 void supportsBindingByBuildLayerIdentity() throws Exception {
137 writeFile(SETTINGS_FILE, ROOT_NAME);
138 writeFile(BUILD_FILE, """
139 plugins {
140 id 'org.implab.gradle-variants-sources'
141 }
142
143 variants {
144 layer('main')
145 variant('browser') {
146 role('main') { layers('main') }
147 }
148 }
149
150 variantSources {
151 bind(variants.requireLayer('main')) {
152 configureSourceSet {
153 declareOutputs('compiled')
154 }
155 }
156 }
157
158 tasks.register('probe') {
159 doLast {
160 def ss = sources.getByName('browserMain')
161 ss.output('compiled')
162 println('bindLayerIdentity=ok')
163 }
164 }
165 """);
166
167 BuildResult result = runner("probe").build();
168 assertTrue(result.getOutput().contains("bindLayerIdentity=ok"));
169 }
170
171 @Test
172 void exposesBindingsSnapshot() throws Exception {
173 writeFile(SETTINGS_FILE, ROOT_NAME);
174 writeFile(BUILD_FILE, """
175 plugins {
176 id 'org.implab.gradle-variants-sources'
177 }
178
179 variants {
180 layer('main')
181 layer('extra')
182 variant('browser') {
183 role('main') { layers('main') }
184 }
185 }
186
187 variantSources {
188 bind('main') {
189 sourceSetNamePattern = '{layer}'
190 configureSourceSet {
191 declareOutputs('compiled')
192 }
193 }
194 bind('extra')
195 }
196
197 tasks.register('probe') {
198 doLast {
199 def ss = sources.getByName('main')
200 ss.output('compiled')
201 println("bindings=" + variantSources.bindings.collect { it.name }.sort().join(','))
202 println('bindingsSnapshot=ok')
203 }
204 }
205 """);
206
207 BuildResult result = runner("probe").build();
208 assertTrue(result.getOutput().contains("bindings=extra,main"));
209 assertTrue(result.getOutput().contains("bindingsSnapshot=ok"));
210 }
211
212 @Test
136 void failsOnUnknownLayerBinding() throws Exception {
213 void failsOnUnknownLayerBinding() throws Exception {
137 writeFile(SETTINGS_FILE, ROOT_NAME);
214 writeFile(SETTINGS_FILE, ROOT_NAME);
138 writeFile(BUILD_FILE, """
215 writeFile(BUILD_FILE, """
@@ -54,7 +54,7 variantSources {
54
54
55 ### binding
55 ### binding
56
56
57 `bind('<layer>')` возвращает `BuildLayerBinding` и задает policy для этого
57 `bind('<layer>')` возвращает `LayerBindingSpec` и задает policy для этого
58 слоя:
58 слоя:
59
59
60 - как именовать source set;
60 - как именовать source set;
@@ -125,14 +125,16 Sugar:
125
125
126 ### VariantSourcesExtension
126 ### VariantSourcesExtension
127
127
128 - `bind(BuildLayer)` — получить/создать binding для canonical layer identity.
128 - `bind(String)` — получить/создать binding по имени слоя.
129 - `bind(String)` — получить/создать binding по имени слоя.
129 - `bind(String, Action|Closure)` — сконфигурировать binding.
130 - `bind(String, Action|Closure)` — сконфигурировать binding.
130 - `bindings(Action|Closure)`контейнерная конфигурация bindings.
131 - `bind(BuildLayer, Action|Closure)`сконфигурировать binding по `BuildLayer`.
132 - `getBindings()` — read-only snapshot текущих bindings.
131 - `whenRegistered(...)` — глобальные callbacks регистрации source set.
133 - `whenRegistered(...)` — глобальные callbacks регистрации source set.
132 - `whenBound(...)` — глобальные callbacks usage-binding.
134 - `whenBound(...)` — глобальные callbacks usage-binding.
133 - `whenBound(String variantName, ...)` — usage-binding callbacks с variant-filter.
135 - `whenBound(String variantName, ...)` — usage-binding callbacks с variant-filter.
134
136
135 ### BuildLayerBinding
137 ### LayerBindingSpec
136
138
137 - `sourceSetNamePattern` — naming policy для source set слоя.
139 - `sourceSetNamePattern` — naming policy для source set слоя.
138 - `configureSourceSet(...)` — слойная конфигурация `GenericSourceSet`.
140 - `configureSourceSet(...)` — слойная конфигурация `GenericSourceSet`.
@@ -143,7 +145,7 Sugar:
143
145
144 - `VariantsSourcesPlugin` — точка входа plugin adapter.
146 - `VariantsSourcesPlugin` — точка входа plugin adapter.
145 - `VariantSourcesExtension` — глобальный DSL bind/events.
147 - `VariantSourcesExtension` — глобальный DSL bind/events.
146 - `BuildLayerBinding` — layer-local policy и callbacks.
148 - `LayerBindingSpec`публичный DSL-contract layer-local policy/callbacks.
147 - `SourceSetRegistration` — payload регистрации source set.
149 - `SourceSetRegistration` — payload регистрации source set.
148 - `SourceSetUsageBinding` — payload usage-binding.
150 - `SourceSetUsageBinding` — payload usage-binding.
149
151
@@ -151,5 +153,7 Sugar:
151
153
152 - `sourceSetNamePattern` фиксируется при первом чтении в registration
154 - `sourceSetNamePattern` фиксируется при первом чтении в registration
153 (`finalizeValueOnRead`).
155 (`finalizeValueOnRead`).
156 - runtime state bindings скрыт внутри adapter implementation (`LayerBinding`).
157 - name-based bindings резолвятся к canonical `BuildLayer` через registry `variants`.
154 - Closure callbacks используют delegate-first.
158 - Closure callbacks используют delegate-first.
155 - Для вложенных closure лучше явный параметр (`ctx -> ...`).
159 - Для вложенных closure лучше явный параметр (`ctx -> ...`).
@@ -81,8 +81,9 Typed-атрибуты (`Attribute<T> -> Provider<T>`) для передачи параметров в
81
81
82 - `layer(...)` — объявление или конфигурация `BuildLayer`.
82 - `layer(...)` — объявление или конфигурация `BuildLayer`.
83 - `variant(...)` — объявление или конфигурация `BuildVariant`.
83 - `variant(...)` — объявление или конфигурация `BuildVariant`.
84 - `layers { ... }`, `variants { ... }` — контейнерный DSL.
84 - `layers { layer(...) }`, `variants { ... }` — grouped DSL без публикации container API наружу.
85 - `all(...)` — callback для всех вариантов.
85 - `all(...)` — callback для всех вариантов.
86 - `findLayer(name)`, `requireLayer(name)`, `getAllLayers()` — доступ к registry слоев.
86 - `getAll()`, `find(name)`, `require(name)` — доступ к вариантам.
87 - `getAll()`, `find(name)`, `require(name)` — доступ к вариантам.
87 - `validate()` — явный запуск валидации.
88 - `validate()` — явный запуск валидации.
88 - `finalizeModel()` — валидация + финализация модели.
89 - `finalizeModel()` — валидация + финализация модели.
@@ -99,7 +100,7 Typed-атрибуты (`Attribute<T> -> Provider<T>`) для передачи параметров в
99 - `VariantsPlugin` — точка входа плагина.
100 - `VariantsPlugin` — точка входа плагина.
100 - `BuildVariantsExtension` — root extension и lifecycle.
101 - `BuildVariantsExtension` — root extension и lifecycle.
101 - `BuildVariant` — агрегатная модель варианта.
102 - `BuildVariant` — агрегатная модель варианта.
102 - `BuildLayer`модель слоя.
103 - `BuildLayer`canonical identity-model слоя.
103 - `BuildRole` — модель роли.
104 - `BuildRole` — модель роли.
104 - `BuildArtifactSlot` — модель артефактного слота.
105 - `BuildArtifactSlot` — модель артефактного слота.
105 - `VariantAttributes` — typed wrapper для variant attributes.
106 - `VariantAttributes` — typed wrapper для variant attributes.
@@ -107,4 +108,5 Typed-атрибуты (`Attribute<T> -> Provider<T>`) для передачи параметров в
107 ## NOTES
108 ## NOTES
108
109
109 - Модель `variants` intentionally agnostic к toolchain.
110 - Модель `variants` intentionally agnostic к toolchain.
111 - Layer registry хранится как явная identity-map, а не как публичный `NamedDomainObjectContainer`.
110 - Интеграция с задачами выполняется через `variantSources` и адаптеры.
112 - Интеграция с задачами выполняется через `variantSources` и адаптеры.
@@ -21,3 +21,4 dependencyResolutionManagement {
21 rootProject.name = 'gradle-common'
21 rootProject.name = 'gradle-common'
22
22
23 include 'common'
23 include 'common'
24 include 'variants'
General Comments 0
You need to be logged in to leave comments. Login now