# HG changeset patch # User cin # Date 2026-03-05 22:33:55 # Node ID 414a5d71eaa5a86517af00d4374d5e99bbd57c41 # Parent 2dd3356774b2de38c8c1ff9ab83b920e84d36b6d separated SourceSetRegistration, SourceSetUsageBinding diff --git a/common/readme.md b/common/readme.md --- a/common/readme.md +++ b/common/readme.md @@ -92,7 +92,8 @@ outputs. Это уже "физический" уровень, к которому удобно привязывать задачи, - `BuildRole` — роль внутри варианта, содержит ссылки на layer names. - `GenericSourceSet` — зарегистрированный набор исходников и outputs. - `BuildLayerBinding` — правила registration source set для конкретного layer. -- `SourceSetContext` — контекст callback-событий registration. +- `SourceSetRegistration` — payload события регистрации source set. +- `SourceSetUsageBinding` — payload события usage-binding. ## EVENT CONTRACT @@ -116,7 +117,8 @@ Closure callbacks работают в delegate-first режиме (`@DelegatesTo`). Для - `VariantsSourcesPlugin` — применяет `variants` + `sources` и запускает адаптер. - `VariantSourcesExtension` — API bind/events registration. - `BuildLayerBinding` — слой-конкретный DSL для имени и конфигурации source set. -- `SourceSetContext` — payload событий и sugar `configureSourceSet(...)`. +- `SourceSetRegistration` — payload `whenRegistered(...)`. +- `SourceSetUsageBinding` — payload `whenBound(...)`. ## NOTES diff --git a/common/src/main/java/org/implab/gradle/common/sources/BuildLayerBinding.java b/common/src/main/java/org/implab/gradle/common/sources/BuildLayerBinding.java --- a/common/src/main/java/org/implab/gradle/common/sources/BuildLayerBinding.java +++ b/common/src/main/java/org/implab/gradle/common/sources/BuildLayerBinding.java @@ -25,11 +25,11 @@ public abstract class BuildLayerBinding private final String name; private final List> sourceSetConfigureActions = new ArrayList<>(); - private final List> registeredActions = new ArrayList<>(); - private final List> boundActions = new ArrayList<>(); + private final List> registeredActions = new ArrayList<>(); + private final List> boundActions = new ArrayList<>(); private final List> registeredSourceSets = new ArrayList<>(); - private final List registeredContexts = new ArrayList<>(); - private final List boundContexts = new ArrayList<>(); + private final List registeredContexts = new ArrayList<>(); + private final List boundContexts = new ArrayList<>(); private final Set registeredSourceSetNames = new LinkedHashSet<>(); @Inject @@ -62,59 +62,59 @@ public abstract class BuildLayerBinding /** * Layer-local callback fired after source-set registration. - * Already emitted contexts are delivered immediately (replay). + * Already emitted registrations are delivered immediately (replay). * For simple callbacks you can use delegate-only style * (for example {@code whenRegistered { sourceSetName() }}). * For nested closures prefer explicit parameter * ({@code whenRegistered { ctx -> ... }}). */ - public void whenRegistered(Action action) { + public void whenRegistered(Action action) { registeredActions.add(action); for (var context : registeredContexts) action.execute(context); } public void whenRegistered( - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + @DelegatesTo(value = SourceSetRegistration.class, strategy = Closure.DELEGATE_FIRST) Closure action) { whenRegistered(Closures.action(action)); } /** * Layer-local callback fired for every resolved variant/role/layer usage. - * Already emitted contexts are delivered immediately (replay). + * Already emitted usage bindings are delivered immediately (replay). * For simple callbacks you can use delegate-only style * (for example {@code whenBound { variantName() }}). * For nested closures prefer explicit parameter * ({@code whenBound { ctx -> ... }}). */ - public void whenBound(Action action) { + public void whenBound(Action action) { boundActions.add(action); for (var context : boundContexts) action.execute(context); } public void whenBound( - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + @DelegatesTo(value = SourceSetUsageBinding.class, strategy = Closure.DELEGATE_FIRST) Closure action) { whenBound(Closures.action(action)); } - void notifyRegistered(SourceSetContext context) { - if (registeredSourceSetNames.add(context.sourceSetName())) { - var sourceSet = context.sourceSet(); + void notifyRegistered(SourceSetRegistration registration) { + if (registeredSourceSetNames.add(registration.sourceSetName())) { + var sourceSet = registration.sourceSet(); registeredSourceSets.add(sourceSet); for (var action : sourceSetConfigureActions) sourceSet.configure(action); } - registeredContexts.add(context); + registeredContexts.add(registration); for (var action : registeredActions) - action.execute(context); + action.execute(registration); } - void notifyBound(SourceSetContext context) { - boundContexts.add(context); + void notifyBound(SourceSetUsageBinding binding) { + boundContexts.add(binding); for (var action : boundActions) - action.execute(context); + action.execute(binding); } } diff --git a/common/src/main/java/org/implab/gradle/common/sources/SourceSetContext.java b/common/src/main/java/org/implab/gradle/common/sources/SourceSetContext.java deleted file mode 100644 --- a/common/src/main/java/org/implab/gradle/common/sources/SourceSetContext.java +++ /dev/null @@ -1,41 +0,0 @@ -package org.implab.gradle.common.sources; - -import org.implab.gradle.common.core.lang.Closures; -import org.gradle.api.Action; -import org.gradle.api.NamedDomainObjectProvider; - -import groovy.lang.Closure; -import groovy.lang.DelegatesTo; - -/** - * Immutable context for a {@link GenericSourceSet} registered for a resolved - * variant/layer pair. - * - *

Used as callback payload when source sets are registered or bound in - * {@link VariantSourcesExtension} and then dispatched via - * {@link VariantSourcesExtension#whenRegistered(org.gradle.api.Action)}, - * {@link VariantSourcesExtension#whenBound(org.gradle.api.Action)}, - * {@link BuildLayerBinding#whenRegistered(org.gradle.api.Action)} and - * {@link BuildLayerBinding#whenBound(org.gradle.api.Action)}. - * - * @param variantName variant name from the build-variants model - * @param roleName role name inside the resolved variant - * @param layerName normalized layer name used to register the source set - * @param sourceSetName source-set name registered in the container - * @param sourceSet provider of the registered source set (realized later by Gradle on demand) - */ -public record SourceSetContext( - String variantName, - String roleName, - String layerName, - String sourceSetName, - NamedDomainObjectProvider sourceSet) { - public void configureSourceSet(Action action) { - sourceSet.configure(action); - } - - public void configureSourceSet( - @DelegatesTo(value = GenericSourceSet.class, strategy = Closure.DELEGATE_FIRST) Closure action) { - configureSourceSet(Closures.action(action)); - } -} diff --git a/common/src/main/java/org/implab/gradle/common/sources/SourceSetRegistration.java b/common/src/main/java/org/implab/gradle/common/sources/SourceSetRegistration.java new file mode 100644 --- /dev/null +++ b/common/src/main/java/org/implab/gradle/common/sources/SourceSetRegistration.java @@ -0,0 +1,33 @@ +package org.implab.gradle.common.sources; + +import org.implab.gradle.common.core.lang.Closures; +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectProvider; + +import groovy.lang.Closure; +import groovy.lang.DelegatesTo; + +/** + * Immutable payload for a newly registered {@link GenericSourceSet}. + * + *

Used as callback payload for + * {@link VariantSourcesExtension#whenRegistered(org.gradle.api.Action)} and + * {@link BuildLayerBinding#whenRegistered(org.gradle.api.Action)}. + * + * @param layerName normalized layer name that owns the registration + * @param sourceSetName source-set name registered in the container + * @param sourceSet provider of the registered source set (realized later by Gradle on demand) + */ +public record SourceSetRegistration( + String layerName, + String sourceSetName, + NamedDomainObjectProvider sourceSet) { + public void configureSourceSet(Action action) { + sourceSet.configure(action); + } + + public void configureSourceSet( + @DelegatesTo(value = GenericSourceSet.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + configureSourceSet(Closures.action(action)); + } +} diff --git a/common/src/main/java/org/implab/gradle/common/sources/SourceSetUsageBinding.java b/common/src/main/java/org/implab/gradle/common/sources/SourceSetUsageBinding.java new file mode 100644 --- /dev/null +++ b/common/src/main/java/org/implab/gradle/common/sources/SourceSetUsageBinding.java @@ -0,0 +1,37 @@ +package org.implab.gradle.common.sources; + +import org.implab.gradle.common.core.lang.Closures; +import org.gradle.api.Action; +import org.gradle.api.NamedDomainObjectProvider; + +import groovy.lang.Closure; +import groovy.lang.DelegatesTo; + +/** + * Immutable payload for a resolved variant/role/layer usage bound to a source set. + * + *

Used as callback payload for + * {@link VariantSourcesExtension#whenBound(org.gradle.api.Action)} and + * {@link BuildLayerBinding#whenBound(org.gradle.api.Action)}. + * + * @param variantName variant name from the build-variants model + * @param roleName role name inside the resolved variant + * @param layerName normalized layer name used to resolve the source set + * @param sourceSetName source-set name registered in the container + * @param sourceSet provider of the registered source set (realized later by Gradle on demand) + */ +public record SourceSetUsageBinding( + String variantName, + String roleName, + String layerName, + String sourceSetName, + NamedDomainObjectProvider sourceSet) { + public void configureSourceSet(Action action) { + sourceSet.configure(action); + } + + public void configureSourceSet( + @DelegatesTo(value = GenericSourceSet.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + configureSourceSet(Closures.action(action)); + } +} diff --git a/common/src/main/java/org/implab/gradle/common/sources/VariantSourcesExtension.java b/common/src/main/java/org/implab/gradle/common/sources/VariantSourcesExtension.java --- a/common/src/main/java/org/implab/gradle/common/sources/VariantSourcesExtension.java +++ b/common/src/main/java/org/implab/gradle/common/sources/VariantSourcesExtension.java @@ -35,10 +35,10 @@ public abstract class VariantSourcesExte private static final Pattern SOURCE_SET_NAME_TOKEN = Pattern.compile("\\{([A-Za-z][A-Za-z0-9]*)\\}"); private final NamedDomainObjectContainer bindings; - private final List> registeredActions = new ArrayList<>(); - private final List> boundActions = new ArrayList<>(); - private final List registeredContexts = new ArrayList<>(); - private final List boundContexts = new ArrayList<>(); + private final List> registeredActions = new ArrayList<>(); + private final List> boundActions = new ArrayList<>(); + private final List registeredContexts = new ArrayList<>(); + private final List boundContexts = new ArrayList<>(); private final LinkedHashMap> sourceSetsByName = new LinkedHashMap<>(); private final LinkedHashMap sourceSetLayersByName = new LinkedHashMap<>(); private boolean sourceSetsRegistered; @@ -80,60 +80,50 @@ public abstract class VariantSourcesExte } /** - * Global callback fired for each registered source-set context. - * Already emitted contexts are delivered immediately (replay). + * Global callback fired for each registered source set. + * Already emitted registrations are delivered immediately (replay). * For simple callbacks you can use delegate-only style * (for example {@code whenRegistered { sourceSetName() }}). * For nested closures prefer explicit parameter * ({@code whenRegistered { ctx -> ... }}). */ - public void whenRegistered(Action action) { + public void whenRegistered(Action action) { registeredActions.add(action); for (var context : registeredContexts) action.execute(context); } public void whenRegistered( - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + @DelegatesTo(value = SourceSetRegistration.class, strategy = Closure.DELEGATE_FIRST) Closure action) { whenRegistered(Closures.action(action)); } - public void whenRegistered(String variantName, Action action) { - var normalizedVariantName = normalize(variantName, "variantName must not be null or blank"); - whenRegistered(filterByVariant(normalizedVariantName, action)); - } - - public void whenRegistered(String variantName, - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { - whenRegistered(variantName, Closures.action(action)); - } - /** * Global callback fired for every resolved variant/role/layer usage. - * Already emitted contexts are delivered immediately (replay). + * Already emitted usage bindings are delivered immediately (replay). * For simple callbacks you can use delegate-only style * (for example {@code whenBound { variantName() }}). * For nested closures prefer explicit parameter * ({@code whenBound { ctx -> ... }}). */ - public void whenBound(Action action) { + public void whenBound(Action action) { boundActions.add(action); for (var context : boundContexts) action.execute(context); } public void whenBound( - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + @DelegatesTo(value = SourceSetUsageBinding.class, strategy = Closure.DELEGATE_FIRST) Closure action) { whenBound(Closures.action(action)); } - public void whenBound(String variantName, Action action) { + public void whenBound(String variantName, Action action) { var normalizedVariantName = normalize(variantName, "variantName must not be null or blank"); whenBound(filterByVariant(normalizedVariantName, action)); } public void whenBound(String variantName, - @DelegatesTo(value = SourceSetContext.class, strategy = Closure.DELEGATE_FIRST) Closure action) { + @DelegatesTo(value = SourceSetUsageBinding.class, strategy = Closure.DELEGATE_FIRST) Closure action) { whenBound(variantName, Closures.action(action)); } @@ -188,7 +178,7 @@ public abstract class VariantSourcesExte var sourceSet = sourceSetsByName.computeIfAbsent(sourceSetName, name -> sources.register(name)); - var context = new SourceSetContext( + var binding = new SourceSetUsageBinding( usage.variantName(), usage.roleName(), usage.layerName(), @@ -196,31 +186,35 @@ public abstract class VariantSourcesExte sourceSet); if (isNewSourceSet) { - resolvedBinding.notifyRegistered(context); - notifyRegistered(context); + var registration = new SourceSetRegistration( + usage.layerName(), + sourceSetName, + sourceSet); + resolvedBinding.notifyRegistered(registration); + notifyRegistered(registration); } - resolvedBinding.notifyBound(context); - notifyBound(context); + resolvedBinding.notifyBound(binding); + notifyBound(binding); } - private void notifyRegistered(SourceSetContext context) { - registeredContexts.add(context); + private void notifyRegistered(SourceSetRegistration registration) { + registeredContexts.add(registration); for (var action : registeredActions) - action.execute(context); + action.execute(registration); } - private void notifyBound(SourceSetContext context) { - boundContexts.add(context); + private void notifyBound(SourceSetUsageBinding binding) { + boundContexts.add(binding); for (var action : boundActions) - action.execute(context); + action.execute(binding); } - private static Action filterByVariant(String variantName, - Action action) { - return context -> { - if (variantName.equals(context.variantName())) - action.execute(context); + private static Action filterByVariant(String variantName, + Action action) { + return binding -> { + if (variantName.equals(binding.variantName())) + action.execute(binding); }; } diff --git a/common/src/test/java/org/implab/gradle/common/sources/VariantsSourcesPluginFunctionalTest.java b/common/src/test/java/org/implab/gradle/common/sources/VariantsSourcesPluginFunctionalTest.java --- a/common/src/test/java/org/implab/gradle/common/sources/VariantsSourcesPluginFunctionalTest.java +++ b/common/src/test/java/org/implab/gradle/common/sources/VariantsSourcesPluginFunctionalTest.java @@ -61,10 +61,10 @@ class VariantsSourcesPluginFunctionalTes } } bind('mainAmd').whenRegistered { ctx -> - localEvents << "${ctx.variantName()}:${ctx.roleName()}:${ctx.layerName()}:${ctx.sourceSetName()}" + localEvents << "${ctx.layerName()}:${ctx.sourceSetName()}" } whenRegistered { ctx -> - events << "${ctx.variantName()}:${ctx.roleName()}:${ctx.layerName()}:${ctx.sourceSetName()}" + events << "${ctx.layerName()}:${ctx.sourceSetName()}" } } @@ -90,9 +90,8 @@ class VariantsSourcesPluginFunctionalTes BuildResult result = runner("probe").build(); assertTrue(result.getOutput().contains("sources=browserMainAmd,browserMainBase,nodeMainBase")); - assertTrue(result.getOutput().contains( - "events=browser:main:mainAmd:browserMainAmd|browser:main:mainBase:browserMainBase|node:main:mainBase:nodeMainBase")); - assertTrue(result.getOutput().contains("local=browser:main:mainAmd:browserMainAmd")); + assertTrue(result.getOutput().contains("events=mainAmd:browserMainAmd|mainBase:browserMainBase|mainBase:nodeMainBase")); + assertTrue(result.getOutput().contains("local=mainAmd:browserMainAmd")); assertTrue(result.getOutput().contains("outputs=ok")); assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); } @@ -158,7 +157,7 @@ class VariantsSourcesPluginFunctionalTes } @Test - void exposesProviderInSourceSetRegisteredContext() throws Exception { + void exposesProviderInSourceSetRegistration() throws Exception { writeFile(SETTINGS_FILE, ROOT_NAME); writeFile(BUILD_FILE, """ plugins { @@ -264,9 +263,9 @@ class VariantsSourcesPluginFunctionalTes } def registeredEvents = [] - def browserRegisteredEvents = [] def boundEvents = [] def browserBoundEvents = [] + def localRegisteredEvents = [] def localBoundEvents = [] variantSources { @@ -279,17 +278,19 @@ class VariantsSourcesPluginFunctionalTes } bind('main') { + whenRegistered { + localRegisteredEvents << "${layerName()}:${sourceSetName()}" + } + } + + bind('main') { whenBound { localBoundEvents << "${variantName()}:${roleName()}:${layerName()}:${sourceSetName()}" } } whenRegistered { ctx -> - registeredEvents << "${ctx.variantName()}:${ctx.roleName()}:${ctx.layerName()}:${ctx.sourceSetName()}" - } - - whenRegistered('browser') { ctx -> - browserRegisteredEvents << "${ctx.variantName()}:${ctx.roleName()}:${ctx.layerName()}:${ctx.sourceSetName()}" + registeredEvents << "${ctx.layerName()}:${ctx.sourceSetName()}" } whenBound { ctx -> @@ -309,7 +310,7 @@ class VariantsSourcesPluginFunctionalTes main.output('compiled') println("registered=" + registeredEvents.sort().join('|')) - println("browserRegistered=" + browserRegisteredEvents.sort().join('|')) + println("localRegistered=" + localRegisteredEvents.sort().join('|')) println("bound=" + boundEvents.sort().join('|')) println("browserBound=" + browserBoundEvents.sort().join('|')) println("localBound=" + localBoundEvents.sort().join('|')) @@ -320,8 +321,8 @@ class VariantsSourcesPluginFunctionalTes BuildResult result = runner("probe").build(); assertTrue(result.getOutput().contains("sources=main")); - assertTrue(result.getOutput().contains("registered=browser:main:main:main")); - assertTrue(result.getOutput().contains("browserRegistered=browser:main:main:main")); + assertTrue(result.getOutput().contains("registered=main:main")); + assertTrue(result.getOutput().contains("localRegistered=main:main")); assertTrue(result.getOutput().contains("bound=browser:main:main:main|node:main:main:main")); assertTrue(result.getOutput().contains("browserBound=browser:main:main:main")); assertTrue(result.getOutput().contains("localBound=browser:main:main:main|node:main:main:main")); diff --git a/common/variant-sources-plugin.md b/common/variant-sources-plugin.md --- a/common/variant-sources-plugin.md +++ b/common/variant-sources-plugin.md @@ -97,14 +97,22 @@ Tokens: ### variant filter -Глобальные callbacks поддерживают фильтр по варианту: +Фильтр по варианту поддерживает только usage-binding: -- `whenRegistered(String variantName, ...)` - `whenBound(String variantName, ...)` -## SOURCE SET CONTEXT +## PAYLOAD TYPES + +`SourceSetRegistration` содержит: -`SourceSetContext` содержит: +- `layerName`, `sourceSetName`; +- `sourceSet` (`NamedDomainObjectProvider`). + +Sugar: + +- `configureSourceSet(Action|Closure)`. + +`SourceSetUsageBinding` содержит: - `variantName`, `roleName`, `layerName`, `sourceSetName`; - `sourceSet` (`NamedDomainObjectProvider`). @@ -122,6 +130,7 @@ Sugar: - `bindings(Action|Closure)` — контейнерная конфигурация bindings. - `whenRegistered(...)` — глобальные callbacks регистрации source set. - `whenBound(...)` — глобальные callbacks usage-binding. +- `whenBound(String variantName, ...)` — usage-binding callbacks с variant-filter. ### BuildLayerBinding @@ -135,7 +144,8 @@ Sugar: - `VariantsSourcesPlugin` — точка входа plugin adapter. - `VariantSourcesExtension` — глобальный DSL bind/events. - `BuildLayerBinding` — layer-local policy и callbacks. -- `SourceSetContext` — payload callbacks и sugar-конфигурирование. +- `SourceSetRegistration` — payload регистрации source set. +- `SourceSetUsageBinding` — payload usage-binding. ## NOTES