##// END OF EJS Templates
variants: refine public API boundary
cin -
r56:c41a563716ec default
parent child
Show More
@@ -0,0 +1,14
1 package org.implab.gradle.variants.internal;
2
3 import org.gradle.api.Named;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.gradle.api.model.ObjectFactory;
6
7 public final class IdentityContainerFactory {
8 private IdentityContainerFactory() {
9 }
10
11 public static <T extends Named> NamedDomainObjectContainer<T> create(ObjectFactory objectFactory, Class<T> type) {
12 return objectFactory.domainObjectContainer(type, name -> objectFactory.named(type, name));
13 }
14 }
@@ -0,0 +1,7
1 /**
2 * Internal implementation utilities of the variants module.
3 *
4 * <p>Types in this package are not part of the public API. They may change,
5 * move, or be removed without compatibility guarantees.
6 */
7 package org.implab.gradle.variants.internal;
@@ -0,0 +1,9
1 /**
2 * Internal implementation of the variant sources plugin.
3 *
4 * <p>Types in this package are not part of the public API. They may change,
5 * move, or be removed without compatibility guarantees. Build logic should use
6 * the public contracts from {@link org.implab.gradle.variants.sources}
7 * instead.
8 */
9 package org.implab.gradle.variants.sources.internal;
@@ -1,53 +1,57
1 plugins {
1 plugins {
2 id "java-library"
2 id "java-library"
3 id "ivy-publish"
3 id "ivy-publish"
4 }
4 }
5
5
6 java {
6 java {
7 withJavadocJar()
7 withJavadocJar()
8 withSourcesJar()
8 withSourcesJar()
9 toolchain {
9 toolchain {
10 languageVersion = JavaLanguageVersion.of(21)
10 languageVersion = JavaLanguageVersion.of(21)
11 }
11 }
12 }
12 }
13
13
14 dependencies {
14 dependencies {
15 compileOnly libs.jdt.annotations
15 compileOnly libs.jdt.annotations
16
16
17 api gradleApi(),
17 api gradleApi(),
18 libs.bundles.jackson
18 libs.bundles.jackson
19
19
20 implementation project(":common")
20 implementation project(":common")
21
21
22 testImplementation gradleTestKit()
22 testImplementation gradleTestKit()
23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
23 testImplementation "org.junit.jupiter:junit-jupiter-api:5.11.4"
24 testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine: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"
25 testRuntimeOnly "org.junit.platform:junit-platform-launcher:1.11.4"
26 }
26 }
27
27
28 task printVersion{
28 task printVersion{
29 doLast {
29 doLast {
30 println "project: $project.group:$project.name:$project.version"
30 println "project: $project.group:$project.name:$project.version"
31 println "jar: ${->jar.archiveFileName.get()}"
31 println "jar: ${->jar.archiveFileName.get()}"
32 }
32 }
33 }
33 }
34
34
35 test {
35 test {
36 useJUnitPlatform()
36 useJUnitPlatform()
37 }
37 }
38
38
39 javadoc {
40 exclude "**/internal/**"
41 }
42
39 publishing {
43 publishing {
40 repositories {
44 repositories {
41 ivy {
45 ivy {
42 url "${System.properties["user.home"]}/ivy-repo"
46 url "${System.properties["user.home"]}/ivy-repo"
43 }
47 }
44 }
48 }
45 publications {
49 publications {
46 ivy(IvyPublication) {
50 ivy(IvyPublication) {
47 from components.java
51 from components.java
48 descriptor.description {
52 descriptor.description {
49 text = providers.provider({ description })
53 text = providers.provider({ description })
50 }
54 }
51 }
55 }
52 }
56 }
53 }
57 }
@@ -1,112 +1,112
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.Plugin;
4 import org.gradle.api.Plugin;
5 import org.gradle.api.Project;
5 import org.gradle.api.Project;
6 import org.implab.gradle.common.core.lang.Deferred;
6 import org.implab.gradle.common.core.lang.Deferred;
7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
7 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSpec;
8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
8 import org.implab.gradle.variants.artifacts.OutgoingVariantsContext;
9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
9 import org.implab.gradle.variants.artifacts.VariantArtifactsExtension;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
10 import org.implab.gradle.variants.artifacts.VariantArtifactsSpec;
11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
11 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyBinder;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
12 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyHandler;
13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
13 import org.implab.gradle.variants.artifacts.internal.ArtifactAssemblyRegistry;
14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
14 import org.implab.gradle.variants.artifacts.internal.DefaultOutgoingVariantsContext;
15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
15 import org.implab.gradle.variants.artifacts.internal.MaterializationPolicyHandler;
16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
16 import org.implab.gradle.variants.artifacts.internal.OutgoingRegistry;
17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
17 import org.implab.gradle.variants.artifacts.internal.SingleSlotConvention;
18 import org.implab.gradle.variants.core.Variant;
18 import org.implab.gradle.variants.core.Variant;
19 import org.implab.gradle.variants.core.VariantsExtension;
19 import org.implab.gradle.variants.core.VariantsExtension;
20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
20 import org.implab.gradle.variants.sources.VariantSourcesExtension;
21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
21 import org.implab.gradle.variants.artifacts.OutgoingConfigurationSlotSpec;
22
22
23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
23 public abstract class VariantArtifactsPlugin implements Plugin<Project> {
24
24
25 @Override
25 @Override
26 public void apply(Project target) {
26 public void apply(Project target) {
27 var extensions = target.getExtensions();
27 var extensions = target.getExtensions();
28 var objects = target.getObjects();
28 var objects = target.getObjects();
29 var providers = target.getProviders();
29 var providers = target.getProviders();
30 var plugins = target.getPlugins();
30 var plugins = target.getPlugins();
31 var configurations = target.getConfigurations();
31 var configurations = target.getConfigurations();
32 var tasks = target.getTasks();
32 var tasks = target.getTasks();
33 var layout = target.getLayout();
33 var layout = target.getLayout();
34
34
35 // Apply the main VariantsPlugin to ensure the core variant model is available.
35 // Apply the main VariantsPlugin to ensure the core variant model is available.
36 plugins.apply(VariantsPlugin.class);
36 plugins.apply(VariantsPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
38 // Access the VariantsExtension to configure variant sources.
38 // Access the VariantsExtension to configure variant sources.
39 var variantsExtension = extensions.getByType(VariantsExtension.class);
39 var variantsExtension = extensions.getByType(VariantsExtension.class);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
41
41
42 var deferred = new Deferred<OutgoingVariantsContext>();
42 var deferred = new Deferred<OutgoingVariantsContext>();
43
43
44 variantsExtension.whenFinalized(variants -> {
44 variantsExtension.whenFinalized(variants -> {
45
45
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 var assemblies = new ArtifactAssemblyRegistry();
47 var assemblies = new ArtifactAssemblyRegistry();
48
48
49 // wire artifact assemblies to configuration variants
49 // wire artifact assemblies to configuration variants
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 var primarySlotConvention = new SingleSlotConvention(providers);
51 var primarySlotConvention = new SingleSlotConvention(providers);
52 var materializationHandler = new MaterializationPolicyHandler(objects);
52 var materializationHandler = new MaterializationPolicyHandler(objects);
53
53
54 // bind slot assemblies to outgoing variants
54 // bind slot assemblies to outgoing variants
55 outgoing.configureEach(assembliesBridge::execute);
55 outgoing.configureEach(assembliesBridge::execute);
56 // apply primary slot convention when outgoing variant has a single slot
56 // apply primary slot convention when outgoing variant has a single slot
57 outgoing.configureEach(primarySlotConvention::execute);
57 outgoing.configureEach(primarySlotConvention::execute);
58 // apply materialization policy hooks to outgoing variants
58 // apply materialization policy hooks to outgoing variants
59 outgoing.configureEach(materializationHandler::execute);
59 outgoing.configureEach(materializationHandler::execute);
60
60
61 sourcesExtension.whenFinalized(sources -> {
61 sourcesExtension.whenAvailable(sources -> {
62 var assemblyHandler = new ArtifactAssemblyHandler(
62 var assemblyHandler = new ArtifactAssemblyHandler(
63 objects,
63 objects,
64 tasks,
64 tasks,
65 assemblies,
65 assemblies,
66 sources.getCompileUnits(),
66 sources.getCompileUnits(),
67 sources.getRoleProjections(),
67 sources.getRoleProjections(),
68 sources.getSourceSets());
68 sources.getSourceSets());
69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
70
70
71 deferred.resolve(new DefaultOutgoingVariantsContext(
71 deferred.resolve(new DefaultOutgoingVariantsContext(
72 assemblies,
72 assemblies,
73 outgoing,
73 outgoing,
74 assemblyHandler,
74 assemblyHandler,
75 materializationHandler));
75 materializationHandler));
76 });
76 });
77
77
78 });
78 });
79
79
80 var variantArtifacts = new VariantArtifactsExtension() {
80 var variantArtifacts = new VariantArtifactsExtension() {
81
81
82 @Override
82 @Override
83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
84 deferred.whenResolved(context -> {
84 deferred.whenResolved(context -> {
85 var variant = objects.named(Variant.class, variantName);
85 var variant = objects.named(Variant.class, variantName);
86 context.configureVariant(variant, action);
86 context.configureVariant(variant, action);
87 });
87 });
88 }
88 }
89
89
90 @Override
90 @Override
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 deferred.whenResolved(handler -> action.execute(handler));
92 deferred.whenResolved(handler -> action.execute(handler));
93 }
93 }
94
94
95 @Override
95 @Override
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
98
98
99 }
99 }
100
100
101 @Override
101 @Override
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
104 }
104 }
105
105
106 };
106 };
107
107
108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
109
109
110 }
110 }
111
111
112 }
112 }
@@ -1,157 +1,157
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import java.util.Objects;
3 import java.util.Objects;
4 import java.util.function.Predicate;
4 import java.util.function.Predicate;
5
5
6 import org.eclipse.jdt.annotation.NonNullByDefault;
6 import org.eclipse.jdt.annotation.NonNullByDefault;
7 import org.gradle.api.Action;
7 import org.gradle.api.Action;
8 import org.gradle.api.InvalidUserDataException;
8 import org.gradle.api.InvalidUserDataException;
9 import org.gradle.api.Named;
9 import org.gradle.api.Named;
10 import org.gradle.api.Plugin;
10 import org.gradle.api.Plugin;
11 import org.gradle.api.Project;
11 import org.gradle.api.Project;
12 import org.implab.gradle.common.core.lang.Deferred;
12 import org.implab.gradle.common.core.lang.Deferred;
13 import org.implab.gradle.common.core.lang.Strings;
13 import org.implab.gradle.common.core.lang.Strings;
14 import org.implab.gradle.variants.core.Layer;
14 import org.implab.gradle.variants.core.Layer;
15 import org.implab.gradle.variants.core.Variant;
15 import org.implab.gradle.variants.core.Variant;
16 import org.implab.gradle.variants.core.VariantsExtension;
16 import org.implab.gradle.variants.core.VariantsExtension;
17 import org.implab.gradle.variants.core.VariantsView;
17 import org.implab.gradle.variants.core.VariantsView;
18 import org.implab.gradle.variants.sources.CompileUnit;
18 import org.implab.gradle.variants.sources.CompileUnit;
19 import org.implab.gradle.variants.sources.CompileUnitsView;
19 import org.implab.gradle.variants.sources.CompileUnitsView;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
20 import org.implab.gradle.variants.sources.GenericSourceSet;
21 import org.implab.gradle.variants.sources.RoleProjectionsView;
21 import org.implab.gradle.variants.sources.RoleProjectionsView;
22 import org.implab.gradle.variants.sources.VariantSourcesContext;
22 import org.implab.gradle.variants.sources.VariantSourcesContext;
23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
30
30
31 @NonNullByDefault
31 @NonNullByDefault
32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
34
34
35 @Override
35 @Override
36 public void apply(Project target) {
36 public void apply(Project target) {
37 var extensions = target.getExtensions();
37 var extensions = target.getExtensions();
38
38
39 // Apply the main VariantsPlugin to ensure the core variant model is available.
39 // Apply the main VariantsPlugin to ensure the core variant model is available.
40 target.getPlugins().apply(VariantsPlugin.class);
40 target.getPlugins().apply(VariantsPlugin.class);
41 // Access the VariantsExtension to configure variant sources.
41 // Access the VariantsExtension to configure variant sources.
42 var variantsExtension = extensions.getByType(VariantsExtension.class);
42 var variantsExtension = extensions.getByType(VariantsExtension.class);
43 var objectFactory = target.getObjects();
43 var objectFactory = target.getObjects();
44
44
45 var deferred = new Deferred<VariantSourcesContext>();
45 var deferred = new Deferred<VariantSourcesContext>();
46
46
47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
49
49
50 variantsExtension.whenFinalized(variants -> {
50 variantsExtension.whenFinalized(variants -> {
51 // create variant views
51 // create variant views
52 var compileUnits = CompileUnitsView.of(variants);
52 var compileUnits = CompileUnitsView.of(variants);
53 var roleProjections = RoleProjectionsView.of(variants);
53 var roleProjections = RoleProjectionsView.of(variants);
54
54
55 // create registries
55 // create registries
56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
57 lateConfigurationPolicy.finalizePolicy();
57 lateConfigurationPolicy.finalizePolicy();
58 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
58 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
59
59
60 // build compile unit namer
60 // build compile unit namer
61 var compileUnitNamer = CompileUnitNamer.builder()
61 var compileUnitNamer = CompileUnitNamer.builder()
62 .addUnits(compileUnits.getUnits())
62 .addUnits(compileUnits.getUnits())
63 .nameCollisionPolicy(namingPolicy.policy())
63 .nameCollisionPolicy(namingPolicy.policy())
64 .build();
64 .build();
65
65
66 // create the context
66 // create the context
67 var context = new DefaultVariantSourcesContext(
67 var context = new DefaultVariantSourcesContext(
68 variants,
68 variants,
69 compileUnits,
69 compileUnits,
70 roleProjections,
70 roleProjections,
71 compileUnitNamer,
71 compileUnitNamer,
72 sourceSetRegistry,
72 sourceSetRegistry,
73 sourceSetConfiguration
73 sourceSetConfiguration
74 );
74 );
75 deferred.resolve(context);
75 deferred.resolve(context);
76 });
76 });
77
77
78 var variantSourcesExtension = new VariantSourcesExtension() {
78 var variantSourcesExtension = new VariantSourcesExtension() {
79 @Override
79 @Override
80 public void whenFinalized(Action<? super VariantSourcesContext> action) {
80 public void whenAvailable(Action<? super VariantSourcesContext> action) {
81 deferred.whenResolved(action::execute);
81 deferred.whenResolved(action::execute);
82 }
82 }
83
83
84 @Override
84 @Override
85 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
85 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
86 action.execute(lateConfigurationPolicy);
86 action.execute(lateConfigurationPolicy);
87 }
87 }
88
88
89 @Override
89 @Override
90 public void namingPolicy(Action<? super NamingPolicySpec> action) {
90 public void namingPolicy(Action<? super NamingPolicySpec> action) {
91 action.execute(namingPolicy);
91 action.execute(namingPolicy);
92 }
92 }
93
93
94 @Override
94 @Override
95 public void variant(String variantName, Action<? super GenericSourceSet> action) {
95 public void variant(String variantName, Action<? super GenericSourceSet> action) {
96 Strings.argumentNotNullOrBlank(variantName, "variantName");
96 Strings.argumentNotNullOrBlank(variantName, "variantName");
97 Objects.requireNonNull(action, "action can't be null");
97 Objects.requireNonNull(action, "action can't be null");
98
98
99 lateConfigurationPolicy.finalizePolicy();
99 lateConfigurationPolicy.finalizePolicy();
100
100
101 whenFinalized(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
101 whenAvailable(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
102 }
102 }
103
103
104 @Override
104 @Override
105 public void layer(String layerName, Action<? super GenericSourceSet> action) {
105 public void layer(String layerName, Action<? super GenericSourceSet> action) {
106 // protect external DSL
106 // protect external DSL
107 Strings.argumentNotNullOrBlank(layerName, "layerName");
107 Strings.argumentNotNullOrBlank(layerName, "layerName");
108 Objects.requireNonNull(action, "action can't be null");
108 Objects.requireNonNull(action, "action can't be null");
109
109
110 lateConfigurationPolicy.finalizePolicy();
110 lateConfigurationPolicy.finalizePolicy();
111
111
112 whenFinalized(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
112 whenAvailable(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
113 }
113 }
114
114
115 @Override
115 @Override
116 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
116 public void unit(String variantName, String layerName, Action<? super GenericSourceSet> action) {
117 Strings.argumentNotNullOrBlank(layerName, "layerName");
117 Strings.argumentNotNullOrBlank(layerName, "layerName");
118 Strings.argumentNotNullOrBlank(variantName, "variantName");
118 Strings.argumentNotNullOrBlank(variantName, "variantName");
119 Objects.requireNonNull(action, "action can't be null");
119 Objects.requireNonNull(action, "action can't be null");
120
120
121 lateConfigurationPolicy.finalizePolicy();
121 lateConfigurationPolicy.finalizePolicy();
122
122
123 whenFinalized(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
123 whenAvailable(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
124 }
124 }
125 };
125 };
126
126
127 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
127 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
128
128
129 }
129 }
130
130
131 private static Layer resolveLayer(VariantsView variants, String name) {
131 private static Layer resolveLayer(VariantsView variants, String name) {
132 return variants.getLayers().stream()
132 return variants.getLayers().stream()
133 .filter(named(name))
133 .filter(named(name))
134 .findAny()
134 .findAny()
135 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared"));
135 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared"));
136 }
136 }
137
137
138 private static Variant resolveVariant(VariantsView variants, String name) {
138 private static Variant resolveVariant(VariantsView variants, String name) {
139 return variants.getVariants().stream()
139 return variants.getVariants().stream()
140 .filter(named(name))
140 .filter(named(name))
141 .findAny()
141 .findAny()
142 .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared"));
142 .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared"));
143 }
143 }
144
144
145 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
145 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
146 return ctx.getCompileUnits().findUnit(
146 return ctx.getCompileUnits().findUnit(
147 resolveVariant(ctx.getVariants(), variantName),
147 resolveVariant(ctx.getVariants(), variantName),
148 resolveLayer(ctx.getVariants(), layerName))
148 resolveLayer(ctx.getVariants(), layerName))
149 .orElseThrow(() -> new InvalidUserDataException(
149 .orElseThrow(() -> new InvalidUserDataException(
150 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
150 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
151 }
151 }
152
152
153 private static Predicate<Named> named(String name) {
153 private static Predicate<Named> named(String name) {
154 return named -> named.getName().equals(name);
154 return named -> named.getName().equals(name);
155 }
155 }
156
156
157 }
157 }
@@ -1,102 +1,102
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.Action;
3 import org.gradle.api.Action;
4 import org.gradle.api.NamedDomainObjectContainer;
4 import org.gradle.api.NamedDomainObjectContainer;
5 import org.gradle.api.Plugin;
5 import org.gradle.api.Plugin;
6 import org.gradle.api.Project;
6 import org.gradle.api.Project;
7 import org.gradle.api.model.ObjectFactory;
7 import org.gradle.api.model.ObjectFactory;
8 import org.implab.gradle.common.core.lang.Deferred;
8 import org.implab.gradle.common.core.lang.Deferred;
9 import org.implab.gradle.internal.IdentityContainerFactory;
10 import org.implab.gradle.variants.core.Layer;
9 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Role;
10 import org.implab.gradle.variants.core.Role;
12 import org.implab.gradle.variants.core.Variant;
11 import org.implab.gradle.variants.core.Variant;
13 import org.implab.gradle.variants.core.VariantDefinition;
12 import org.implab.gradle.variants.core.VariantDefinition;
14 import org.implab.gradle.variants.core.VariantsExtension;
13 import org.implab.gradle.variants.core.VariantsExtension;
15 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.internal.IdentityContainerFactory;
16
16
17 /**
17 /**
18 * <ul>
18 * <ul>
19 * <li> {@link Variant} defines compilation semantics
19 * <li> {@link Variant} defines compilation semantics
20 * <li> {@link Layer} defines compilation partition
20 * <li> {@link Layer} defines compilation partition
21 * <li> {@link Role} defines result grouping / publication intent
21 * <li> {@link Role} defines result grouping / publication intent
22 * </ul>
22 * </ul>
23 *
23 *
24 */
24 */
25 public abstract class VariantsPlugin implements Plugin<Project> {
25 public abstract class VariantsPlugin implements Plugin<Project> {
26 @Override
26 @Override
27 public void apply(Project target) {
27 public void apply(Project target) {
28
28
29 var objectFactory = target.getObjects();
29 var objectFactory = target.getObjects();
30
30
31 var variantsExtension = new DefaultVariantsExtension(objectFactory);
31 var variantsExtension = new DefaultVariantsExtension(objectFactory);
32 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
32 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
33 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
33 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
34
34
35 }
35 }
36
36
37 static class DefaultVariantsExtension implements VariantsExtension {
37 static class DefaultVariantsExtension implements VariantsExtension {
38
38
39 private final NamedDomainObjectContainer<Layer> layers;
39 private final NamedDomainObjectContainer<Layer> layers;
40 private final NamedDomainObjectContainer<Role> roles;
40 private final NamedDomainObjectContainer<Role> roles;
41 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
41 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
42 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
42 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
43 private final ObjectFactory objectFactory;
43 private final ObjectFactory objectFactory;
44 private boolean finalized = false;
44 private boolean finalized = false;
45
45
46 public DefaultVariantsExtension(ObjectFactory objectFactory) {
46 public DefaultVariantsExtension(ObjectFactory objectFactory) {
47 this.objectFactory = objectFactory;
47 this.objectFactory = objectFactory;
48 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
48 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
49 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
49 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
50 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
50 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
51 }
51 }
52
52
53 @Override
53 @Override
54 public NamedDomainObjectContainer<Layer> getLayers() {
54 public NamedDomainObjectContainer<Layer> getLayers() {
55 return layers;
55 return layers;
56 }
56 }
57
57
58 @Override
58 @Override
59 public NamedDomainObjectContainer<Role> getRoles() {
59 public NamedDomainObjectContainer<Role> getRoles() {
60 return roles;
60 return roles;
61 }
61 }
62
62
63 @Override
63 @Override
64 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
64 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
65 return variantDefinitions;
65 return variantDefinitions;
66 }
66 }
67
67
68 @Override
68 @Override
69 public void whenFinalized(Action<? super VariantsView> action) {
69 public void whenFinalized(Action<? super VariantsView> action) {
70 finalizedResult.whenResolved(action::execute);
70 finalizedResult.whenResolved(action::execute);
71 }
71 }
72
72
73 void finalizeExtension() {
73 void finalizeExtension() {
74 if (finalized)
74 if (finalized)
75 return;
75 return;
76
76
77 finalized = true;
77 finalized = true;
78
78
79 // freeze defined variants
79 // freeze defined variants
80 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
80 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
81
81
82 // build a snapshot
82 // build a snapshot
83 var viewBuilder = VariantsView.builder();
83 var viewBuilder = VariantsView.builder();
84
84
85 // calculate and add variants
85 // calculate and add variants
86 variantDefinitions.stream()
86 variantDefinitions.stream()
87 .map(def -> objectFactory.named(Variant.class, def.getName()))
87 .map(def -> objectFactory.named(Variant.class, def.getName()))
88 .forEach(viewBuilder::addVariant);
88 .forEach(viewBuilder::addVariant);
89 // add layers
89 // add layers
90 layers.forEach(viewBuilder::addLayer);
90 layers.forEach(viewBuilder::addLayer);
91 // add roles
91 // add roles
92 roles.forEach(viewBuilder::addRole);
92 roles.forEach(viewBuilder::addRole);
93 // add definitions
93 // add definitions
94 variantDefinitions.forEach(viewBuilder::addDefinition);
94 variantDefinitions.forEach(viewBuilder::addDefinition);
95 // assemble the view
95 // assemble the view
96 var view = viewBuilder.build();
96 var view = viewBuilder.build();
97 // set the result and call hooks
97 // set the result and call hooks
98 finalizedResult.resolve(view);
98 finalizedResult.resolve(view);
99 }
99 }
100 }
100 }
101
101
102 }
102 }
@@ -1,124 +1,124
1 package org.implab.gradle.variants.artifacts.internal;
1 package org.implab.gradle.variants.artifacts.internal;
2
2
3 import java.util.LinkedHashMap;
3 import java.util.LinkedHashMap;
4 import java.util.Map;
4 import java.util.Map;
5 import java.util.Optional;
5 import java.util.Optional;
6 import java.util.Set;
6 import java.util.Set;
7 import java.util.function.Consumer;
7 import java.util.function.Consumer;
8
8
9 import org.eclipse.jdt.annotation.NonNullByDefault;
9 import org.eclipse.jdt.annotation.NonNullByDefault;
10 import org.gradle.api.InvalidUserDataException;
10 import org.gradle.api.InvalidUserDataException;
11 import org.gradle.api.NamedDomainObjectContainer;
11 import org.gradle.api.NamedDomainObjectContainer;
12 import org.gradle.api.NamedDomainObjectProvider;
12 import org.gradle.api.NamedDomainObjectProvider;
13 import org.gradle.api.artifacts.Configuration;
13 import org.gradle.api.artifacts.Configuration;
14 import org.gradle.api.artifacts.ConfigurationContainer;
14 import org.gradle.api.artifacts.ConfigurationContainer;
15 import org.gradle.api.model.ObjectFactory;
15 import org.gradle.api.model.ObjectFactory;
16 import org.gradle.api.provider.Property;
16 import org.gradle.api.provider.Property;
17 import org.implab.gradle.internal.IdentityContainerFactory;
18 import org.implab.gradle.common.core.lang.ReplayableQueue;
17 import org.implab.gradle.common.core.lang.ReplayableQueue;
19 import org.implab.gradle.variants.artifacts.OutgoingVariant;
18 import org.implab.gradle.variants.artifacts.OutgoingVariant;
20 import org.implab.gradle.variants.artifacts.Slot;
19 import org.implab.gradle.variants.artifacts.Slot;
21 import org.implab.gradle.variants.core.Variant;
20 import org.implab.gradle.variants.core.Variant;
21 import org.implab.gradle.variants.internal.IdentityContainerFactory;
22
22
23 /**
23 /**
24 * Registry of variant-level outgoing models.
24 * Registry of variant-level outgoing models.
25 *
25 *
26 * <p>Each declared outgoing model owns one lazy Gradle consumable configuration
26 * <p>Each declared outgoing model owns one lazy Gradle consumable configuration
27 * and one live slot identity container.
27 * and one live slot identity container.
28 */
28 */
29 @NonNullByDefault
29 @NonNullByDefault
30 public class OutgoingRegistry {
30 public class OutgoingRegistry {
31 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
31 private final Map<Variant, OutgoingVariant> outgoingByVariant = new LinkedHashMap<>();
32 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
32 private final ReplayableQueue<OutgoingVariant> outgoingVariants = new ReplayableQueue<>();
33 private final ConfigurationContainer configurations;
33 private final ConfigurationContainer configurations;
34 private final ObjectFactory objects;
34 private final ObjectFactory objects;
35 private final Set<Variant> declaredVariants;
35 private final Set<Variant> declaredVariants;
36
36
37 public OutgoingRegistry(
37 public OutgoingRegistry(
38 ConfigurationContainer configurations,
38 ConfigurationContainer configurations,
39 ObjectFactory objects,
39 ObjectFactory objects,
40 Set<Variant> declaredVariants) {
40 Set<Variant> declaredVariants) {
41 this.configurations = configurations;
41 this.configurations = configurations;
42 this.objects = objects;
42 this.objects = objects;
43 this.declaredVariants = declaredVariants;
43 this.declaredVariants = declaredVariants;
44 }
44 }
45
45
46 public Optional<OutgoingVariant> find(Variant variant) {
46 public Optional<OutgoingVariant> find(Variant variant) {
47 return Optional.ofNullable(outgoingByVariant.get(variant));
47 return Optional.ofNullable(outgoingByVariant.get(variant));
48 }
48 }
49
49
50 public OutgoingVariant maybeCreate(Variant variant) {
50 public OutgoingVariant maybeCreate(Variant variant) {
51 return find(variant).orElseGet(() -> create(variant));
51 return find(variant).orElseGet(() -> create(variant));
52 }
52 }
53
53
54 public OutgoingVariant create(Variant variant) {
54 public OutgoingVariant create(Variant variant) {
55 if (!declaredVariants.contains(variant))
55 if (!declaredVariants.contains(variant))
56 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
56 throw new InvalidUserDataException("Variant " + variant + " isn't declared");
57 if (outgoingByVariant.containsKey(variant))
57 if (outgoingByVariant.containsKey(variant))
58 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
58 throw new InvalidUserDataException("Outgoing variant " + variant + " already exists");
59
59
60 var configuration = configurations.consumable(outgoingConfigurationName(variant));
60 var configuration = configurations.consumable(outgoingConfigurationName(variant));
61 var outgoing = new Outgoing(variant, configuration);
61 var outgoing = new Outgoing(variant, configuration);
62
62
63 outgoingByVariant.put(variant, outgoing);
63 outgoingByVariant.put(variant, outgoing);
64
64
65 outgoingVariants.add(outgoing);
65 outgoingVariants.add(outgoing);
66
66
67 return outgoing;
67 return outgoing;
68 }
68 }
69
69
70 /**
70 /**
71 * Registers a replayable hook for outgoing variant declarations.
71 * Registers a replayable hook for outgoing variant declarations.
72 *
72 *
73 * @param action outgoing variant action
73 * @param action outgoing variant action
74 */
74 */
75 public void configureEach(Consumer<? super OutgoingVariant> action) {
75 public void configureEach(Consumer<? super OutgoingVariant> action) {
76 outgoingVariants.forEach(action);
76 outgoingVariants.forEach(action);
77 }
77 }
78
78
79 private String outgoingConfigurationName(Variant variant) {
79 private String outgoingConfigurationName(Variant variant) {
80 return variant.getName() + "Elements";
80 return variant.getName() + "Elements";
81 }
81 }
82
82
83 private class Outgoing implements OutgoingVariant {
83 private class Outgoing implements OutgoingVariant {
84
84
85 private final Variant variant;
85 private final Variant variant;
86
86
87 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
87 private final NamedDomainObjectProvider<? extends Configuration> configurationProvider;
88
88
89 private final NamedDomainObjectContainer<Slot> slots;
89 private final NamedDomainObjectContainer<Slot> slots;
90
90
91 private final Property<Slot> primarySlot;
91 private final Property<Slot> primarySlot;
92
92
93 public Outgoing(
93 public Outgoing(
94 Variant variant,
94 Variant variant,
95 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
95 NamedDomainObjectProvider<? extends Configuration> configurationProvider) {
96 this.variant = variant;
96 this.variant = variant;
97 this.configurationProvider = configurationProvider;
97 this.configurationProvider = configurationProvider;
98 this.slots = IdentityContainerFactory.create(objects, Slot.class);
98 this.slots = IdentityContainerFactory.create(objects, Slot.class);
99 this.primarySlot = objects.property(Slot.class);
99 this.primarySlot = objects.property(Slot.class);
100 primarySlot.finalizeValueOnRead();
100 primarySlot.finalizeValueOnRead();
101 }
101 }
102
102
103 @Override
103 @Override
104 public Property<Slot> getPrimarySlot() {
104 public Property<Slot> getPrimarySlot() {
105 return primarySlot;
105 return primarySlot;
106 }
106 }
107
107
108 @Override
108 @Override
109 public Variant getVariant() {
109 public Variant getVariant() {
110 return variant;
110 return variant;
111 }
111 }
112
112
113 @Override
113 @Override
114 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
114 public NamedDomainObjectProvider<? extends Configuration> getConfiguration() {
115 return configurationProvider;
115 return configurationProvider;
116 }
116 }
117
117
118 @Override
118 @Override
119 public NamedDomainObjectContainer<Slot> getSlots() {
119 public NamedDomainObjectContainer<Slot> getSlots() {
120 return slots;
120 return slots;
121 }
121 }
122 }
122 }
123
123
124 }
124 }
@@ -1,170 +1,170
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import org.eclipse.jdt.annotation.NonNullByDefault;
3 import org.eclipse.jdt.annotation.NonNullByDefault;
4 import org.gradle.api.Action;
4 import org.gradle.api.Action;
5 import org.implab.gradle.common.core.lang.Closures;
5 import org.implab.gradle.common.core.lang.Closures;
6 import groovy.lang.Closure;
6 import groovy.lang.Closure;
7
7
8 @NonNullByDefault
8 @NonNullByDefault
9 public interface VariantSourcesExtension {
9 public interface VariantSourcesExtension {
10
10
11 /**
11 /**
12 * Selects how selector rules behave when they target an already materialized
12 * Selects how selector rules behave when they target an already materialized
13 * {@link GenericSourceSet}.
13 * {@link GenericSourceSet}.
14 *
14 *
15 * <p>This policy is single-valued:
15 * <p>This policy is single-valued:
16 * <ul>
16 * <ul>
17 * <li>it must be selected before the first selector rule is registered via
17 * <li>it must be selected before the first selector rule is registered via
18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
18 * {@link #variant(String, Action)}, {@link #layer(String, Action)} or
19 * {@link #unit(String, String, Action)};</li>
19 * {@link #unit(String, String, Action)};</li>
20 * <li>it must be selected before the finalized context becomes observable via
20 * <li>it must be selected before the source context becomes observable via
21 * {@link #whenFinalized(Action)};</li>
21 * {@link #whenAvailable(Action)};</li>
22 * <li>once selected or once the finalized context is being created, it cannot
22 * <li>once selected or once source context creation begins, it cannot
23 * be changed later;</li>
23 * be changed later;</li>
24 * <li>the policy controls both diagnostics and late-application semantics.</li>
24 * <li>the policy controls both diagnostics and late-application semantics.</li>
25 * </ul>
25 * </ul>
26 *
26 *
27 * <p>If not selected explicitly, the default is
27 * <p>If not selected explicitly, the default is
28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
28 * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}.
29 */
29 */
30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
30 void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action);
31
31
32 default void lateConfigurationPolicy(Closure<?> closure) {
32 default void lateConfigurationPolicy(Closure<?> closure) {
33 lateConfigurationPolicy(Closures.action(closure));
33 lateConfigurationPolicy(Closures.action(closure));
34 }
34 }
35
35
36 /**
36 /**
37 * Selects how compile-unit name collisions are handled when the finalized
37 * Selects how compile-unit name collisions are handled when the source
38 * source context is created.
38 * context is created from the finalized variant model.
39 *
39 *
40 * <p>This policy is single-valued:
40 * <p>This policy is single-valued:
41 * <ul>
41 * <ul>
42 * <li>it must be selected before the finalized
42 * <li>it must be selected before the finalized
43 * {@link VariantSourcesContext} becomes observable through
43 * {@link VariantSourcesContext} becomes observable through
44 * {@link #whenFinalized(Action)};</li>
44 * {@link #whenAvailable(Action)};</li>
45 * <li>once the context is being created, the policy is fixed and cannot be
45 * <li>once the context is being created, the policy is fixed and cannot be
46 * changed later;</li>
46 * changed later;</li>
47 * <li>the policy governs validation of compile-unit names produced by the
47 * <li>the policy governs validation of compile-unit names produced by the
48 * source-set materializer.</li>
48 * source-set materializer.</li>
49 * </ul>
49 * </ul>
50 *
50 *
51 * <p>If not selected explicitly, the default is
51 * <p>If not selected explicitly, the default is
52 * {@link NamingPolicySpec#failOnNameCollision()}.
52 * {@link NamingPolicySpec#failOnNameCollision()}.
53 */
53 */
54 void namingPolicy(Action<? super NamingPolicySpec> action);
54 void namingPolicy(Action<? super NamingPolicySpec> action);
55
55
56 default void namingPolicy(Closure<?> closure) {
56 default void namingPolicy(Closure<?> closure) {
57 namingPolicy(Closures.action(closure));
57 namingPolicy(Closures.action(closure));
58 }
58 }
59
59
60 /**
60 /**
61 * Registers a selector rule for all compile units of the given layer.
61 * Registers a selector rule for all compile units of the given layer.
62 *
62 *
63 * <p>Registering the first selector rule fixes the selected
63 * <p>Registering the first selector rule fixes the selected
64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
64 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
65 * lifecycle.
65 * lifecycle.
66 */
66 */
67 void layer(String layerName, Action<? super GenericSourceSet> action);
67 void layer(String layerName, Action<? super GenericSourceSet> action);
68
68
69 default void layer(String layerName, Closure<?> closure) {
69 default void layer(String layerName, Closure<?> closure) {
70 layer(layerName, Closures.action(closure));
70 layer(layerName, Closures.action(closure));
71 }
71 }
72
72
73 /**
73 /**
74 * Registers a selector rule for all compile units of the given variant.
74 * Registers a selector rule for all compile units of the given variant.
75 *
75 *
76 * <p>Registering the first selector rule fixes the selected
76 * <p>Registering the first selector rule fixes the selected
77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
77 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
78 * lifecycle.
78 * lifecycle.
79 */
79 */
80 void variant(String variantName, Action<? super GenericSourceSet> action);
80 void variant(String variantName, Action<? super GenericSourceSet> action);
81
81
82 default void variant(String variantName, Closure<?> closure) {
82 default void variant(String variantName, Closure<?> closure) {
83 variant(variantName, Closures.action(closure));
83 variant(variantName, Closures.action(closure));
84 }
84 }
85
85
86 /**
86 /**
87 * Registers a selector rule for one exact compile unit.
87 * Registers a selector rule for one exact compile unit.
88 *
88 *
89 * <p>Registering the first selector rule fixes the selected
89 * <p>Registering the first selector rule fixes the selected
90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
90 * {@link #lateConfigurationPolicy(Action)} for the remaining extension
91 * lifecycle.
91 * lifecycle.
92 */
92 */
93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
93 void unit(String variantName, String layerName, Action<? super GenericSourceSet> action);
94
94
95 default void unit(String variantName, String layerName, Closure<?> closure) {
95 default void unit(String variantName, String layerName, Closure<?> closure) {
96 unit(variantName, layerName, Closures.action(closure));
96 unit(variantName, layerName, Closures.action(closure));
97 }
97 }
98
98
99 /**
99 /**
100 * Invoked when finalized variants-derived source context becomes available.
100 * Invoked when the variants-derived source context becomes available.
101 *
101 *
102 * Replayable:
102 * Replayable:
103 * <ul>
103 * <ul>
104 * <li>if called before variants finalization, action is queued
104 * <li>if called before variants finalization, action is queued
105 * <li>if called after variants finalization, action is invoked immediately
105 * <li>if called after variants finalization, action is invoked immediately
106 * </ul>
106 * </ul>
107 *
107 *
108 * <p>By the time this callback becomes observable, compile-unit naming
108 * <p>By the time this callback becomes observable, compile-unit naming
109 * policy has already been fixed and symbolic source-set names for finalized
109 * policy has already been fixed and symbolic source-set names for finalized
110 * compile units are determined.
110 * compile units are determined.
111 */
111 */
112 void whenFinalized(Action<? super VariantSourcesContext> action);
112 void whenAvailable(Action<? super VariantSourcesContext> action);
113
113
114 default void whenFinalized(Closure<?> closure) {
114 default void whenAvailable(Closure<?> closure) {
115 whenFinalized(Closures.action(closure));
115 whenAvailable(Closures.action(closure));
116 }
116 }
117
117
118
118
119 /**
119 /**
120 * Imperative selector for the late-configuration mode.
120 * Imperative selector for the late-configuration mode.
121 *
121 *
122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
122 * <p>Exactly one mode is expected to be chosen for the extension lifecycle.
123 */
123 */
124 interface LateConfigurationPolicySpec {
124 interface LateConfigurationPolicySpec {
125 /**
125 /**
126 * Rejects selector registration if it targets any already materialized
126 * Rejects selector registration if it targets any already materialized
127 * source set.
127 * source set.
128 */
128 */
129 void failOnLateConfiguration();
129 void failOnLateConfiguration();
130
130
131 /**
131 /**
132 * Allows late selector registration, but emits a warning when it targets an
132 * Allows late selector registration, but emits a warning when it targets an
133 * already materialized source set.
133 * already materialized source set.
134 *
134 *
135 * <p>For such targets, selector precedence is not re-established
135 * <p>For such targets, selector precedence is not re-established
136 * retroactively. The action is applied as a late imperative step, after the
136 * retroactively. The action is applied as a late imperative step, after the
137 * state already produced at the materialization moment.
137 * state already produced at the materialization moment.
138 */
138 */
139 void warnOnLateConfiguration();
139 void warnOnLateConfiguration();
140
140
141 /**
141 /**
142 * Allows late selector registration without a warning when it targets an
142 * Allows late selector registration without a warning when it targets an
143 * already materialized source set.
143 * already materialized source set.
144 *
144 *
145 * <p>For such targets, selector precedence is not re-established
145 * <p>For such targets, selector precedence is not re-established
146 * retroactively. The action is applied as a late imperative step, after the
146 * retroactively. The action is applied as a late imperative step, after the
147 * state already produced at the materialization moment.
147 * state already produced at the materialization moment.
148 */
148 */
149 void allowLateConfiguration();
149 void allowLateConfiguration();
150 }
150 }
151
151
152 interface NamingPolicySpec {
152 interface NamingPolicySpec {
153 /**
153 /**
154 * Rejects finalized compile-unit models that project the same source-set
154 * Rejects finalized compile-unit models that project the same source-set
155 * name for different compile units.
155 * name for different compile units.
156 */
156 */
157 void failOnNameCollision();
157 void failOnNameCollision();
158
158
159 /**
159 /**
160 * Resolves name collisions deterministically for the finalized
160 * Resolves name collisions deterministically for the finalized
161 * compile-unit model.
161 * compile-unit model.
162 *
162 *
163 * <p>Conflicting compile units are ordered canonically by
163 * <p>Conflicting compile units are ordered canonically by
164 * {@code (variant.name, layer.name)}. The first unit keeps the base
164 * {@code (variant.name, layer.name)}. The first unit keeps the base
165 * projected name, and each next unit receives a numeric suffix
165 * projected name, and each next unit receives a numeric suffix
166 * ({@code 2}, {@code 3}, ...).
166 * ({@code 2}, {@code 3}, ...).
167 */
167 */
168 void resolveNameCollision();
168 void resolveNameCollision();
169 }
169 }
170 }
170 }
@@ -1,454 +1,454
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import static org.junit.jupiter.api.Assertions.assertTrue;
3 import static org.junit.jupiter.api.Assertions.assertTrue;
4
4
5 import org.gradle.testkit.runner.BuildResult;
5 import org.gradle.testkit.runner.BuildResult;
6 import org.junit.jupiter.api.Test;
6 import org.junit.jupiter.api.Test;
7
7
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
8 class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest {
9
9
10 @Test
10 @Test
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
11 void exposesDerivedViewsAndStableSourceSetProvider() throws Exception {
12 writeSettings("variant-sources-derived-views");
12 writeSettings("variant-sources-derived-views");
13 writeBuildFile("""
13 writeBuildFile("""
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
14 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
15
15
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
16 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
17 variantsExt.layers.create('main')
17 variantsExt.layers.create('main')
18 variantsExt.layers.create('test')
18 variantsExt.layers.create('test')
19 variantsExt.roles.create('production')
19 variantsExt.roles.create('production')
20 variantsExt.roles.create('test')
20 variantsExt.roles.create('test')
21
21
22 variantsExt.variant('browser') {
22 variantsExt.variant('browser') {
23 role('production') { layers('main') }
23 role('production') { layers('main') }
24 role('test') { layers('main', 'test') }
24 role('test') { layers('main', 'test') }
25 }
25 }
26
26
27 def lines = []
27 def lines = []
28
28
29 variantSources.whenFinalized { ctx ->
29 variantSources.whenAvailable { ctx ->
30 lines << "units=" + ctx.compileUnits.units
30 lines << "units=" + ctx.compileUnits.units
31 .collect { "${it.variant().name}:${it.layer().name}" }
31 .collect { "${it.variant().name}:${it.layer().name}" }
32 .sort()
32 .sort()
33 .join(',')
33 .join(',')
34
34
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
35 def browser = ctx.variants.variants.find { it.name == 'browser' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
36 def production = ctx.variants.roles.find { it.name == 'production' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
37 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
38 def projection = ctx.roleProjections.requireProjection(browser, production)
38 def projection = ctx.roleProjections.requireProjection(browser, production)
39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
39 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
40
40
41 def left = ctx.sourceSets.getSourceSet(unit)
41 def left = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
42 def right = ctx.sourceSets.getSourceSet(unit)
43
43
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 .collect { it.layer().name }
45 .collect { it.layer().name }
46 .sort()
46 .sort()
47 .join(',')
47 .join(',')
48 lines << "mainSourceSet=" + left.name
48 lines << "mainSourceSet=" + left.name
49 lines << "sameProvider=" + left.is(right)
49 lines << "sameProvider=" + left.is(right)
50 }
50 }
51
51
52 afterEvaluate {
52 afterEvaluate {
53 variantSources.whenFinalized { ctx ->
53 variantSources.whenAvailable { ctx ->
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
55 }
55 }
56 }
56 }
57
57
58 tasks.register('probe') {
58 tasks.register('probe') {
59 doLast {
59 doLast {
60 lines.each { println(it) }
60 lines.each { println(it) }
61 }
61 }
62 }
62 }
63 """);
63 """);
64
64
65 BuildResult result = runner("probe").build();
65 BuildResult result = runner("probe").build();
66
66
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
72 }
72 }
73
73
74 @Test
74 @Test
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
76 writeSettings("variant-sources-precedence");
76 writeSettings("variant-sources-precedence");
77 writeBuildFile("""
77 writeBuildFile("""
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
79
79
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
81 variantsExt.layers.create('main')
81 variantsExt.layers.create('main')
82 variantsExt.layers.create('test')
82 variantsExt.layers.create('test')
83 variantsExt.roles.create('production')
83 variantsExt.roles.create('production')
84 variantsExt.roles.create('test')
84 variantsExt.roles.create('test')
85
85
86 variantsExt.variant('browser') {
86 variantsExt.variant('browser') {
87 role('production') { layers('main') }
87 role('production') { layers('main') }
88 role('test') { layers('main', 'test') }
88 role('test') { layers('main', 'test') }
89 }
89 }
90
90
91 variantsExt.variant('node') {
91 variantsExt.variant('node') {
92 role('production') { layers('main') }
92 role('production') { layers('main') }
93 }
93 }
94
94
95 def events = []
95 def events = []
96
96
97 variantSources {
97 variantSources {
98 variant('browser') {
98 variant('browser') {
99 events << "variant:" + name
99 events << "variant:" + name
100 }
100 }
101 layer('main') {
101 layer('main') {
102 events << "layer:" + name
102 events << "layer:" + name
103 }
103 }
104 unit('browser', 'main') {
104 unit('browser', 'main') {
105 events << "unit:" + name
105 events << "unit:" + name
106 }
106 }
107 }
107 }
108
108
109 afterEvaluate {
109 afterEvaluate {
110 variantSources.whenFinalized { ctx ->
110 variantSources.whenAvailable { ctx ->
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
111 def browser = ctx.variants.variants.find { it.name == 'browser' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
112 def node = ctx.variants.variants.find { it.name == 'node' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
113 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
114 def testLayer = ctx.variants.layers.find { it.name == 'test' }
115
115
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
116 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
117 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
118 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
119 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
120
120
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
121 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
122 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
123 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
124 }
124 }
125 }
125 }
126 """);
126 """);
127
127
128 BuildResult result = runner("help").build();
128 BuildResult result = runner("help").build();
129
129
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
130 assertTrue(result.getOutput().contains("browserMain=variant,layer,unit"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
131 assertTrue(result.getOutput().contains("browserTest=variant"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
132 assertTrue(result.getOutput().contains("nodeMain=layer"));
133 }
133 }
134
134
135 @Test
135 @Test
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
136 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
137 writeSettings("variant-sources-late-fail");
137 writeSettings("variant-sources-late-fail");
138 writeBuildFile("""
138 writeBuildFile("""
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
139 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
140
140
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
141 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
142 variantsExt.layers.create('main')
142 variantsExt.layers.create('main')
143 variantsExt.roles.create('production')
143 variantsExt.roles.create('production')
144 variantsExt.variant('browser') {
144 variantsExt.variant('browser') {
145 role('production') { layers('main') }
145 role('production') { layers('main') }
146 }
146 }
147
147
148 afterEvaluate {
148 afterEvaluate {
149 variantSources.whenFinalized { ctx ->
149 variantSources.whenAvailable { ctx ->
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
150 def browser = ctx.variants.variants.find { it.name == 'browser' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
151 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
152 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
153
153
154 ctx.sourceSets.getSourceSet(unit).get()
154 ctx.sourceSets.getSourceSet(unit).get()
155 variantSources.layer('main') {
155 variantSources.layer('main') {
156 declareOutputs('late')
156 declareOutputs('late')
157 }
157 }
158 }
158 }
159 }
159 }
160 """);
160 """);
161
161
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
162 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
163 }
163 }
164
164
165 @Test
165 @Test
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
166 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
167 writeSettings("variant-sources-late-allow");
167 writeSettings("variant-sources-late-allow");
168 writeBuildFile("""
168 writeBuildFile("""
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
169 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
170
170
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
171 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
172 variantsExt.layers.create('main')
172 variantsExt.layers.create('main')
173 variantsExt.roles.create('production')
173 variantsExt.roles.create('production')
174 variantsExt.variant('browser') {
174 variantsExt.variant('browser') {
175 role('production') { layers('main') }
175 role('production') { layers('main') }
176 }
176 }
177
177
178 variantSources {
178 variantSources {
179 lateConfigurationPolicy {
179 lateConfigurationPolicy {
180 allowLateConfiguration()
180 allowLateConfiguration()
181 }
181 }
182 }
182 }
183
183
184 afterEvaluate {
184 afterEvaluate {
185 variantSources.whenFinalized { ctx ->
185 variantSources.whenAvailable { ctx ->
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
186 def browser = ctx.variants.variants.find { it.name == 'browser' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
187 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
188 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
189
189
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
190 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
191 variantSources.layer('main') {
191 variantSources.layer('main') {
192 declareOutputs('late')
192 declareOutputs('late')
193 }
193 }
194 sourceSet.output('late')
194 sourceSet.output('late')
195 println('lateAllowed=ok')
195 println('lateAllowed=ok')
196 }
196 }
197 }
197 }
198 """);
198 """);
199
199
200 BuildResult result = runner("help").build();
200 BuildResult result = runner("help").build();
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
201 assertTrue(result.getOutput().contains("lateAllowed=ok"));
202 }
202 }
203
203
204 @Test
204 @Test
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
205 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
206 writeSettings("variant-sources-late-warn");
206 writeSettings("variant-sources-late-warn");
207 writeBuildFile("""
207 writeBuildFile("""
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
208 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
209
209
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
210 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
211 variantsExt.layers.create('main')
211 variantsExt.layers.create('main')
212 variantsExt.roles.create('production')
212 variantsExt.roles.create('production')
213 variantsExt.variant('browser') {
213 variantsExt.variant('browser') {
214 role('production') { layers('main') }
214 role('production') { layers('main') }
215 }
215 }
216
216
217 variantSources {
217 variantSources {
218 lateConfigurationPolicy {
218 lateConfigurationPolicy {
219 warnOnLateConfiguration()
219 warnOnLateConfiguration()
220 }
220 }
221 }
221 }
222
222
223 afterEvaluate {
223 afterEvaluate {
224 variantSources.whenFinalized { ctx ->
224 variantSources.whenAvailable { ctx ->
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
225 def browser = ctx.variants.variants.find { it.name == 'browser' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
226 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
227 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
228
228
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
229 def sourceSet = ctx.sourceSets.getSourceSet(unit).get()
230 variantSources.layer('main') {
230 variantSources.layer('main') {
231 declareOutputs('late')
231 declareOutputs('late')
232 }
232 }
233 sourceSet.output('late')
233 sourceSet.output('late')
234 println('lateWarn=ok')
234 println('lateWarn=ok')
235 }
235 }
236 }
236 }
237 """);
237 """);
238
238
239 BuildResult result = runner("help").build();
239 BuildResult result = runner("help").build();
240
240
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
241 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
242 assertTrue(result.getOutput().contains("lateWarn=ok"));
243 }
243 }
244
244
245 @Test
245 @Test
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
246 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
247 writeSettings("variant-sources-late-policy-fixed");
247 writeSettings("variant-sources-late-policy-fixed");
248 writeBuildFile("""
248 writeBuildFile("""
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
249 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
250
250
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
251 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
252 variantsExt.layers.create('main')
252 variantsExt.layers.create('main')
253 variantsExt.roles.create('production')
253 variantsExt.roles.create('production')
254 variantsExt.variant('browser') {
254 variantsExt.variant('browser') {
255 role('production') { layers('main') }
255 role('production') { layers('main') }
256 }
256 }
257
257
258 variantSources {
258 variantSources {
259 variant('browser') {
259 variant('browser') {
260 declareOutputs('js')
260 declareOutputs('js')
261 }
261 }
262 lateConfigurationPolicy {
262 lateConfigurationPolicy {
263 allowLateConfiguration()
263 allowLateConfiguration()
264 }
264 }
265 }
265 }
266 """);
266 """);
267
267
268 assertBuildFails("Lazy configuration policy already applied", "help");
268 assertBuildFails("Lazy configuration policy already applied", "help");
269 }
269 }
270
270
271 @Test
271 @Test
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
272 void failsOnProjectedNameCollisionByDefault() throws Exception {
273 writeSettings("variant-sources-name-collision-fail");
273 writeSettings("variant-sources-name-collision-fail");
274 writeBuildFile("""
274 writeBuildFile("""
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
275 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
276
276
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
277 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
278 variantsExt.layers.create('variantBar')
278 variantsExt.layers.create('variantBar')
279 variantsExt.layers.create('bar')
279 variantsExt.layers.create('bar')
280 variantsExt.roles.create('production')
280 variantsExt.roles.create('production')
281
281
282 variantsExt.variant('foo') {
282 variantsExt.variant('foo') {
283 role('production') { layers('variantBar') }
283 role('production') { layers('variantBar') }
284 }
284 }
285 variantsExt.variant('fooVariant') {
285 variantsExt.variant('fooVariant') {
286 role('production') { layers('bar') }
286 role('production') { layers('bar') }
287 }
287 }
288 """);
288 """);
289
289
290 assertBuildFails("The same source set names are produced by different compile units", "help");
290 assertBuildFails("The same source set names are produced by different compile units", "help");
291 }
291 }
292
292
293 @Test
293 @Test
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
294 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
295 writeSettings("variant-sources-name-collision-resolve");
295 writeSettings("variant-sources-name-collision-resolve");
296 writeBuildFile("""
296 writeBuildFile("""
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
297 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
298
298
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
299 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
300 variantsExt.layers.create('variantBar')
300 variantsExt.layers.create('variantBar')
301 variantsExt.layers.create('bar')
301 variantsExt.layers.create('bar')
302 variantsExt.roles.create('production')
302 variantsExt.roles.create('production')
303
303
304 variantsExt.variant('foo') {
304 variantsExt.variant('foo') {
305 role('production') { layers('variantBar') }
305 role('production') { layers('variantBar') }
306 }
306 }
307 variantsExt.variant('fooVariant') {
307 variantsExt.variant('fooVariant') {
308 role('production') { layers('bar') }
308 role('production') { layers('bar') }
309 }
309 }
310
310
311 variantSources {
311 variantSources {
312 namingPolicy {
312 namingPolicy {
313 resolveNameCollision()
313 resolveNameCollision()
314 }
314 }
315 }
315 }
316
316
317 afterEvaluate {
317 afterEvaluate {
318 variantSources.whenFinalized { ctx ->
318 variantSources.whenAvailable { ctx ->
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
319 def foo = ctx.variants.variants.find { it.name == 'foo' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
320 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
321 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
322 def bar = ctx.variants.layers.find { it.name == 'bar' }
323
323
324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
324 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
325 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
326
326
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
327 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
328 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
329 }
329 }
330 }
330 }
331 """);
331 """);
332
332
333 BuildResult result = runner("help").build();
333 BuildResult result = runner("help").build();
334
334
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
335 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
336 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
337 }
337 }
338
338
339 @Test
339 @Test
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
340 void failsOnUnknownVariantSelectorTarget() throws Exception {
341 writeSettings("variant-sources-missing-variant");
341 writeSettings("variant-sources-missing-variant");
342 writeBuildFile("""
342 writeBuildFile("""
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
343 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
344
344
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
345 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
346 variantsExt.layers.create('main')
346 variantsExt.layers.create('main')
347 variantsExt.roles.create('production')
347 variantsExt.roles.create('production')
348 variantsExt.variant('browser') {
348 variantsExt.variant('browser') {
349 role('production') { layers('main') }
349 role('production') { layers('main') }
350 }
350 }
351
351
352 variantSources {
352 variantSources {
353 variant('missing') {
353 variant('missing') {
354 declareOutputs('js')
354 declareOutputs('js')
355 }
355 }
356 }
356 }
357 """);
357 """);
358
358
359 assertBuildFails("Variant 'missing' isn't declared", "help");
359 assertBuildFails("Variant 'missing' isn't declared", "help");
360 }
360 }
361
361
362 @Test
362 @Test
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
363 void failsOnUnknownLayerSelectorTarget() throws Exception {
364 writeSettings("variant-sources-missing-layer");
364 writeSettings("variant-sources-missing-layer");
365 writeBuildFile("""
365 writeBuildFile("""
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
366 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
367
367
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
368 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
369 variantsExt.layers.create('main')
369 variantsExt.layers.create('main')
370 variantsExt.roles.create('production')
370 variantsExt.roles.create('production')
371 variantsExt.variant('browser') {
371 variantsExt.variant('browser') {
372 role('production') { layers('main') }
372 role('production') { layers('main') }
373 }
373 }
374
374
375 variantSources {
375 variantSources {
376 layer('missing') {
376 layer('missing') {
377 declareOutputs('js')
377 declareOutputs('js')
378 }
378 }
379 }
379 }
380 """);
380 """);
381
381
382 assertBuildFails("Layer 'missing' isn't declared", "help");
382 assertBuildFails("Layer 'missing' isn't declared", "help");
383 }
383 }
384
384
385 @Test
385 @Test
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
386 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
387 writeSettings("variant-sources-missing-unit");
387 writeSettings("variant-sources-missing-unit");
388 writeBuildFile("""
388 writeBuildFile("""
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
389 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
390
390
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
391 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
392 variantsExt.layers.create('main')
392 variantsExt.layers.create('main')
393 variantsExt.layers.create('test')
393 variantsExt.layers.create('test')
394 variantsExt.roles.create('production')
394 variantsExt.roles.create('production')
395 variantsExt.variant('browser') {
395 variantsExt.variant('browser') {
396 role('production') { layers('main') }
396 role('production') { layers('main') }
397 }
397 }
398
398
399 variantSources {
399 variantSources {
400 unit('browser', 'test') {
400 unit('browser', 'test') {
401 declareOutputs('js')
401 declareOutputs('js')
402 }
402 }
403 }
403 }
404 """);
404 """);
405
405
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
406 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
407 }
407 }
408
408
409 @Test
409 @Test
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
410 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
411 writeSettings("variant-sources-name-policy-fixed");
411 writeSettings("variant-sources-name-policy-fixed");
412 writeBuildFile("""
412 writeBuildFile("""
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
414
414
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
416 variantsExt.layers.create('main')
416 variantsExt.layers.create('main')
417 variantsExt.roles.create('production')
417 variantsExt.roles.create('production')
418 variantsExt.variant('browser') {
418 variantsExt.variant('browser') {
419 role('production') { layers('main') }
419 role('production') { layers('main') }
420 }
420 }
421
421
422 variantSources.whenFinalized {
422 variantSources.whenAvailable {
423 variantSources.namingPolicy {
423 variantSources.namingPolicy {
424 resolveNameCollision()
424 resolveNameCollision()
425 }
425 }
426 }
426 }
427 """);
427 """);
428
428
429 assertBuildFails("Naming policy already applied", "help");
429 assertBuildFails("Naming policy already applied", "help");
430 }
430 }
431
431
432 @Test
432 @Test
433 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
433 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
434 writeSettings("variant-sources-late-policy-context-fixed");
434 writeSettings("variant-sources-late-policy-context-fixed");
435 writeBuildFile("""
435 writeBuildFile("""
436 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
436 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
437
437
438 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
438 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
439 variantsExt.layers.create('main')
439 variantsExt.layers.create('main')
440 variantsExt.roles.create('production')
440 variantsExt.roles.create('production')
441 variantsExt.variant('browser') {
441 variantsExt.variant('browser') {
442 role('production') { layers('main') }
442 role('production') { layers('main') }
443 }
443 }
444
444
445 variantSources.whenFinalized {
445 variantSources.whenAvailable {
446 variantSources.lateConfigurationPolicy {
446 variantSources.lateConfigurationPolicy {
447 allowLateConfiguration()
447 allowLateConfiguration()
448 }
448 }
449 }
449 }
450 """);
450 """);
451
451
452 assertBuildFails("Lazy configuration policy already applied", "help");
452 assertBuildFails("Lazy configuration policy already applied", "help");
453 }
453 }
454 }
454 }
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now