##// END OF EJS Templates
variants: move source set layout conventions out of model...
cin -
r59:780370baa54c default
parent child
Show More
@@ -0,0 +1,45
1 package org.implab.gradle.variants.sources.internal;
2
3 import org.gradle.api.Action;
4 import org.gradle.api.file.Directory;
5 import org.gradle.api.provider.Provider;
6 import org.implab.gradle.common.core.lang.FilePaths;
7 import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec;
8
9 /**
10 * Applies the default layout policy for source sets materialized from variant
11 * compile units.
12 *
13 * <p>
14 * The convention keeps sources grouped by layer and outputs grouped by
15 * variant/layer. It only sets directory properties on the materialized source
16 * set; declaring source directories and named outputs remains the
17 * responsibility of adapters or user DSL.
18 * </p>
19 */
20 public class CompileUnitLayoutConvention implements Action<CompileUnitSourceSetSpec> {
21 private final Provider<Directory> sourcesBaseDir;
22 private final Provider<Directory> outputsBaseDir;
23
24 public CompileUnitLayoutConvention(
25 Provider<Directory> baseDirectory,
26 Provider<Directory> outputsBaseDir) {
27 this.sourcesBaseDir = baseDirectory;
28 this.outputsBaseDir = outputsBaseDir;
29 }
30
31 @Override
32 public void execute(CompileUnitSourceSetSpec spec) {
33 var sourceSet = spec.getSourceSet();
34 var unit = spec.getCompileUnit();
35 var layerName = unit.layer().getName();
36 var variantName = unit.variant().getName();
37
38 sourceSet.getSourceSetDir()
39 .convention(sourcesBaseDir.map(base -> base.dir(FilePaths.cat(layerName))));
40
41 sourceSet.getOutputsDir()
42 .convention(outputsBaseDir.map(base -> base.dir(FilePaths.cat(variantName, layerName))));
43 }
44
45 }
@@ -1,9 +1,10
1 {
1 {
2 "java.configuration.updateBuildConfiguration": "automatic",
2 "java.configuration.updateBuildConfiguration": "automatic",
3 "java.compile.nullAnalysis.mode": "automatic",
3 "java.compile.nullAnalysis.mode": "automatic",
4 "cSpell.words": [
4 "cSpell.words": [
5 "implab",
5 "implab",
6 "materializer",
6 "materializer",
7 "rawtypes"
7 "rawtypes",
8 "Replayable"
8 ]
9 ]
9 } No newline at end of file
10 }
@@ -1,41 +1,58
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import org.gradle.api.GradleException;
3 import org.gradle.api.GradleException;
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.logging.Logger;
7 import org.gradle.api.logging.Logger;
8 import org.gradle.api.logging.Logging;
8 import org.gradle.api.logging.Logging;
9 import org.implab.gradle.common.core.lang.FilePaths;
9 import org.implab.gradle.variants.sources.GenericSourceSet;
10 import org.implab.gradle.variants.sources.GenericSourceSet;
10
11
11 /**
12 /**
12 * This plugin creates a {@code sources} extension which is
13 * This plugin creates a {@code sources} extension which is
13 * a container for {@link GenericSourceSet}.
14 * a container for {@link GenericSourceSet}.
14 *
15 *
15 */
16 */
16 public abstract class SourcesPlugin implements Plugin<Project> {
17 public abstract class SourcesPlugin implements Plugin<Project> {
17 private static final Logger logger = Logging.getLogger(SourcesPlugin.class);
18 private static final Logger logger = Logging.getLogger(SourcesPlugin.class);
18
19
19 private static final String SOURCES_EXTENSION_NAME = "sources";
20 public static final String SOURCES_EXTENSION_NAME = "sources";
21
22 private static final String SOURCES_DIRECTORY = "src";
23
24 private static final String OUTPUTS_DIRECTORY = "out";
20
25
21 @Override
26 @Override
22 public void apply(Project target) {
27 public void apply(Project target) {
23 logger.debug("Registering '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
28 logger.debug("Registering '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
29
30 var layout = target.getLayout();
31
24 var sources = target.getObjects().domainObjectContainer(GenericSourceSet.class);
32 var sources = target.getObjects().domainObjectContainer(GenericSourceSet.class);
33 sources.configureEach(sourceSet -> {
34 sourceSet.getSourceSetDir()
35 .convention(layout.getProjectDirectory()
36 .dir(FilePaths.cat(SOURCES_DIRECTORY, sourceSet.getName())));
37 sourceSet.getOutputsDir()
38 .convention(layout.getBuildDirectory()
39 .dir(FilePaths.cat(OUTPUTS_DIRECTORY, sourceSet.getName())));
40 });
25 target.getExtensions().add(SOURCES_EXTENSION_NAME, sources);
41 target.getExtensions().add(SOURCES_EXTENSION_NAME, sources);
26 }
42 }
27
43
28 public static NamedDomainObjectContainer<GenericSourceSet> getSourcesExtension(Project target) {
44 public static NamedDomainObjectContainer<GenericSourceSet> getSourcesExtension(Project target) {
29 var extensions = target.getExtensions();
45 var extensions = target.getExtensions();
30
46
31 @SuppressWarnings("unchecked")
47 @SuppressWarnings("unchecked")
32 var extension = (NamedDomainObjectContainer<GenericSourceSet>) extensions.findByName(SOURCES_EXTENSION_NAME);
48 var extension = (NamedDomainObjectContainer<GenericSourceSet>) extensions.findByName(SOURCES_EXTENSION_NAME);
33
49
34 if (extension == null) {
50 if (extension == null) {
35 logger.error("Sources extension '{}' isn't found on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
51 logger.error("Sources extension '{}' isn't found on project '{}'", SOURCES_EXTENSION_NAME,
52 target.getPath());
36 throw new GradleException("Sources extension isn't found");
53 throw new GradleException("Sources extension isn't found");
37 }
54 }
38 logger.debug("Resolved '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
55 logger.debug("Resolved '{}' extension on project '{}'", SOURCES_EXTENSION_NAME, target.getPath());
39 return extension;
56 return extension;
40 }
57 }
41 }
58 }
@@ -1,112 +1,113
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 public static final String VARIANT_ARTIFACTS_EXTENSION = "variantArtifacts";
24
25
25 @Override
26 @Override
26 public void apply(Project target) {
27 public void apply(Project target) {
27 var extensions = target.getExtensions();
28 var extensions = target.getExtensions();
28 var objects = target.getObjects();
29 var objects = target.getObjects();
29 var providers = target.getProviders();
30 var providers = target.getProviders();
30 var plugins = target.getPlugins();
31 var plugins = target.getPlugins();
31 var configurations = target.getConfigurations();
32 var configurations = target.getConfigurations();
32 var tasks = target.getTasks();
33 var tasks = target.getTasks();
33 var layout = target.getLayout();
34 var layout = target.getLayout();
34
35
35 // Apply the main VariantsPlugin to ensure the core variant model is available.
36 // Apply the main VariantsPlugin to ensure the core variant model is available.
36 plugins.apply(VariantsPlugin.class);
37 plugins.apply(VariantsPlugin.class);
37 plugins.apply(VariantSourcesPlugin.class);
38 plugins.apply(VariantSourcesPlugin.class);
38 // Access the VariantsExtension to configure variant sources.
39 // Access the VariantsExtension to configure variant sources.
39 var variantsExtension = extensions.getByType(VariantsExtension.class);
40 var variantsExtension = extensions.getByType(VariantsExtension.class);
40 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
41 var sourcesExtension = extensions.getByType(VariantSourcesExtension.class);
41
42
42 var deferred = new Deferred<OutgoingVariantsContext>();
43 var deferred = new Deferred<OutgoingVariantsContext>();
43
44
44 variantsExtension.whenFinalized(variants -> {
45 variantsExtension.whenFinalized(variants -> {
45
46
46 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 var outgoing = new OutgoingRegistry(configurations, objects, variants.getVariants());
47 var assemblies = new ArtifactAssemblyRegistry();
48 var assemblies = new ArtifactAssemblyRegistry();
48
49
49 // wire artifact assemblies to configuration variants
50 // wire artifact assemblies to configuration variants
50 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 var assembliesBridge = new ArtifactAssemblyBinder(assemblies);
51 var primarySlotConvention = new SingleSlotConvention(providers);
52 var primarySlotConvention = new SingleSlotConvention(providers);
52 var materializationHandler = new MaterializationPolicyHandler(objects);
53 var materializationHandler = new MaterializationPolicyHandler(objects);
53
54
54 // bind slot assemblies to outgoing variants
55 // bind slot assemblies to outgoing variants
55 outgoing.configureEach(assembliesBridge::execute);
56 outgoing.configureEach(assembliesBridge::execute);
56 // apply primary slot convention when outgoing variant has a single slot
57 // apply primary slot convention when outgoing variant has a single slot
57 outgoing.configureEach(primarySlotConvention::execute);
58 outgoing.configureEach(primarySlotConvention::execute);
58 // apply materialization policy hooks to outgoing variants
59 // apply materialization policy hooks to outgoing variants
59 outgoing.configureEach(materializationHandler::execute);
60 outgoing.configureEach(materializationHandler::execute);
60
61
61 sourcesExtension.whenAvailable(sources -> {
62 sourcesExtension.whenAvailable(sources -> {
62 var assemblyHandler = new ArtifactAssemblyHandler(
63 var assemblyHandler = new ArtifactAssemblyHandler(
63 objects,
64 objects,
64 tasks,
65 tasks,
65 assemblies,
66 assemblies,
66 sources.getCompileUnits(),
67 sources.getCompileUnits(),
67 sources.getRoleProjections(),
68 sources.getRoleProjections(),
68 sources.getSourceSets());
69 sources.getSourceSets());
69 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
70 assemblyHandler.getAssembliesDirectory().set(layout.getBuildDirectory().dir("variant-assemblies"));
70
71
71 deferred.resolve(new DefaultOutgoingVariantsContext(
72 deferred.resolve(new DefaultOutgoingVariantsContext(
72 assemblies,
73 assemblies,
73 outgoing,
74 outgoing,
74 assemblyHandler,
75 assemblyHandler,
75 materializationHandler));
76 materializationHandler));
76 });
77 });
77
78
78 });
79 });
79
80
80 var variantArtifacts = new VariantArtifactsExtension() {
81 var variantArtifacts = new VariantArtifactsExtension() {
81
82
82 @Override
83 @Override
83 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
84 public void variant(String variantName, Action<? super VariantArtifactsSpec> action) {
84 deferred.whenResolved(context -> {
85 deferred.whenResolved(context -> {
85 var variant = objects.named(Variant.class, variantName);
86 var variant = objects.named(Variant.class, variantName);
86 context.configureVariant(variant, action);
87 context.configureVariant(variant, action);
87 });
88 });
88 }
89 }
89
90
90 @Override
91 @Override
91 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 public void whenAvailable(Action<? super OutgoingVariantsContext> action) {
92 deferred.whenResolved(handler -> action.execute(handler));
93 deferred.whenResolved(handler -> action.execute(handler));
93 }
94 }
94
95
95 @Override
96 @Override
96 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 public void whenOutgoingConfiguration(Action<? super OutgoingConfigurationSpec> action) {
97 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
98 deferred.whenResolved(registry -> registry.whenOutgoingConfiguration(action));
98
99
99 }
100 }
100
101
101 @Override
102 @Override
102 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 public void whenOutgoingSlot(Action<? super OutgoingConfigurationSlotSpec> action) {
103 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
104 deferred.whenResolved(registry -> registry.whenOutgoingSlot(action));
104 }
105 }
105
106
106 };
107 };
107
108
108 extensions.add(VariantArtifactsExtension.class, "variantArtifacts", variantArtifacts);
109 extensions.add(VariantArtifactsExtension.class, VARIANT_ARTIFACTS_EXTENSION, variantArtifacts);
109
110
110 }
111 }
111
112
112 }
113 }
@@ -1,166 +1,155
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;
5
4
6 import org.eclipse.jdt.annotation.NonNullByDefault;
5 import org.eclipse.jdt.annotation.NonNullByDefault;
7 import org.gradle.api.Action;
6 import org.gradle.api.Action;
8 import org.gradle.api.InvalidUserDataException;
9 import org.gradle.api.Named;
10 import org.gradle.api.Plugin;
7 import org.gradle.api.Plugin;
11 import org.gradle.api.Project;
8 import org.gradle.api.Project;
12 import org.implab.gradle.common.core.lang.Deferred;
9 import org.implab.gradle.common.core.lang.Deferred;
13 import org.implab.gradle.common.core.lang.Strings;
10 import org.implab.gradle.common.core.lang.Strings;
14 import org.implab.gradle.variants.core.Layer;
11 import org.implab.gradle.variants.core.Layer;
15 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.Variant;
16 import org.implab.gradle.variants.core.VariantsExtension;
13 import org.implab.gradle.variants.core.VariantsExtension;
17 import org.implab.gradle.variants.core.VariantsView;
18 import org.implab.gradle.variants.sources.CompileUnit;
14 import org.implab.gradle.variants.sources.CompileUnit;
19 import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec;
15 import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec;
20 import org.implab.gradle.variants.sources.CompileUnitsView;
16 import org.implab.gradle.variants.sources.CompileUnitsView;
21 import org.implab.gradle.variants.sources.RoleProjectionsView;
17 import org.implab.gradle.variants.sources.RoleProjectionsView;
22 import org.implab.gradle.variants.sources.VariantSourcesContext;
18 import org.implab.gradle.variants.sources.VariantSourcesContext;
23 import org.implab.gradle.variants.sources.VariantSourcesExtension;
19 import org.implab.gradle.variants.sources.VariantSourcesExtension;
20 import org.implab.gradle.variants.sources.internal.CompileUnitLayoutConvention;
24 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
21 import org.implab.gradle.variants.sources.internal.CompileUnitNamer;
25 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
22 import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy;
26 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
23 import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec;
27 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
24 import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext;
28 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
25 import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry;
29 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
26 import org.implab.gradle.variants.sources.internal.SourceSetRegistry;
30
27
31 @NonNullByDefault
28 @NonNullByDefault
32 public abstract class VariantSourcesPlugin implements Plugin<Project> {
29 public abstract class VariantSourcesPlugin implements Plugin<Project> {
33 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
30 public static final String VARIANT_SOURCES_EXTENSION = "variantSources";
34
31
32 private static final String VARIANT_OUTPUTS_DIRECTORY = "variants";
33
34 private static final String VARIANT_SOURCES_DIRECTORY = "src";
35
35 @Override
36 @Override
36 public void apply(Project target) {
37 public void apply(Project target) {
37 var extensions = target.getExtensions();
38 var extensions = target.getExtensions();
38
39
39 // Apply the main VariantsPlugin to ensure the core variant model is available.
40 // Apply the main VariantsPlugin to ensure the core variant model is available.
40 target.getPlugins().apply(VariantsPlugin.class);
41 target.getPlugins().apply(VariantsPlugin.class);
41 // Access the VariantsExtension to configure variant sources.
42 // Access the VariantsExtension to configure variant sources.
42 var variantsExtension = extensions.getByType(VariantsExtension.class);
43 var variantsExtension = extensions.getByType(VariantsExtension.class);
43 var objectFactory = target.getObjects();
44 var objects = target.getObjects();
45 var providers = target.getProviders();
44
46
45 var deferred = new Deferred<VariantSourcesContext>();
47 var deferred = new Deferred<VariantSourcesContext>();
46
48
47 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
49 var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec();
48 var namingPolicy = new DefaultCompileUnitNamingPolicy();
50 var namingPolicy = new DefaultCompileUnitNamingPolicy();
49
51
50 variantsExtension.whenFinalized(variants -> {
52 variantsExtension.whenFinalized(variants -> {
51 // create variant views
53 // create variant views
52 var compileUnits = CompileUnitsView.of(variants);
54 var compileUnits = CompileUnitsView.of(variants);
53 var roleProjections = RoleProjectionsView.of(variants);
55 var roleProjections = RoleProjectionsView.of(variants);
54
56
55 // create registries
57 // create registries
56 var sourceSetRegistry = new SourceSetRegistry(objectFactory);
58 var sourceSetRegistry = new SourceSetRegistry(objects);
57 lateConfigurationPolicy.finalizePolicy();
59 lateConfigurationPolicy.finalizePolicy();
58 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
60 var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode);
59
61
60 // build compile unit namer
62 // build compile unit namer
61 var compileUnitNamer = CompileUnitNamer.builder()
63 var compileUnitNamer = CompileUnitNamer.builder()
62 .addUnits(compileUnits.getUnits())
64 .addUnits(compileUnits.getUnits())
63 .nameCollisionPolicy(namingPolicy.policy())
65 .nameCollisionPolicy(namingPolicy.policy())
64 .build();
66 .build();
67
68 var compileUnitLayoutConvention = new CompileUnitLayoutConvention(
69 providers.provider(() -> target.getLayout().getProjectDirectory().dir(VARIANT_SOURCES_DIRECTORY)),
70 target.getLayout().getBuildDirectory().dir(VARIANT_OUTPUTS_DIRECTORY));
71
72 // apply layout convention to all compile unit source sets
73 sourceSetConfiguration.addAction(compileUnitLayoutConvention);
65
74
66 // create the context
75 // create the context
67 var context = new DefaultVariantSourcesContext(
76 var context = new DefaultVariantSourcesContext(
68 variants,
77 variants,
69 compileUnits,
78 compileUnits,
70 roleProjections,
79 roleProjections,
71 compileUnitNamer,
80 compileUnitNamer,
72 sourceSetRegistry,
81 sourceSetRegistry,
73 sourceSetConfiguration
82 sourceSetConfiguration);
74 );
75 deferred.resolve(context);
83 deferred.resolve(context);
76 });
84 });
77
85
78 var variantSourcesExtension = new VariantSourcesExtension() {
86 var variantSourcesExtension = new VariantSourcesExtension() {
79 @Override
87 @Override
80 public void whenAvailable(Action<? super VariantSourcesContext> action) {
88 public void whenAvailable(Action<? super VariantSourcesContext> action) {
81 deferred.whenResolved(action::execute);
89 deferred.whenResolved(action::execute);
82 }
90 }
83
91
84 @Override
92 @Override
85 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
93 public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) {
86 action.execute(lateConfigurationPolicy);
94 action.execute(lateConfigurationPolicy);
87 }
95 }
88
96
89 @Override
97 @Override
90 public void namingPolicy(Action<? super NamingPolicySpec> action) {
98 public void namingPolicy(Action<? super NamingPolicySpec> action) {
91 action.execute(namingPolicy);
99 action.execute(namingPolicy);
92 }
100 }
93
101
94 @Override
102 @Override
95 public void variant(String variantName, Action<? super CompileUnitSourceSetSpec> action) {
103 public void variant(String variantName, Action<? super CompileUnitSourceSetSpec> action) {
96 Strings.argumentNotNullOrBlank(variantName, "variantName");
104 Strings.argumentNotNullOrBlank(variantName, "variantName");
97 Objects.requireNonNull(action, "action can't be null");
105 Objects.requireNonNull(action, "action can't be null");
98
106
99 lateConfigurationPolicy.finalizePolicy();
107 lateConfigurationPolicy.finalizePolicy();
100
108
101 whenAvailable(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action));
109 var variant = objects.named(Variant.class, variantName);
110
111 whenAvailable(ctx -> ctx.configureVariant(variant, action));
102 }
112 }
103
113
104 @Override
114 @Override
105 public void layer(String layerName, Action<? super CompileUnitSourceSetSpec> action) {
115 public void layer(String layerName, Action<? super CompileUnitSourceSetSpec> action) {
106 // protect external DSL
116 // protect external DSL
107 Strings.argumentNotNullOrBlank(layerName, "layerName");
117 Strings.argumentNotNullOrBlank(layerName, "layerName");
108 Objects.requireNonNull(action, "action can't be null");
118 Objects.requireNonNull(action, "action can't be null");
109
119
110 lateConfigurationPolicy.finalizePolicy();
120 lateConfigurationPolicy.finalizePolicy();
111
121
112 whenAvailable(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action));
122 var layer = objects.named(Layer.class, layerName);
123
124 whenAvailable(ctx -> ctx.configureLayer(layer, action));
113 }
125 }
114
126
115 @Override
127 @Override
116 public void unit(String variantName, String layerName, Action<? super CompileUnitSourceSetSpec> action) {
128 public void unit(String variantName, String layerName, Action<? super CompileUnitSourceSetSpec> action) {
117 Strings.argumentNotNullOrBlank(layerName, "layerName");
129 Strings.argumentNotNullOrBlank(layerName, "layerName");
118 Strings.argumentNotNullOrBlank(variantName, "variantName");
130 Strings.argumentNotNullOrBlank(variantName, "variantName");
119 Objects.requireNonNull(action, "action can't be null");
131 Objects.requireNonNull(action, "action can't be null");
120
132
121 lateConfigurationPolicy.finalizePolicy();
133 lateConfigurationPolicy.finalizePolicy();
122
134
123 whenAvailable(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action));
135 var variant = objects.named(Variant.class, variantName);
136 var layer = objects.named(Layer.class, layerName);
137 var unit = new CompileUnit(variant, layer);
138
139 whenAvailable(ctx -> ctx.configureUnit(unit, action));
124 }
140 }
125
141
126 @Override
142 @Override
127 public void configureEach(Action<? super CompileUnitSourceSetSpec> action) {
143 public void configureEach(Action<? super CompileUnitSourceSetSpec> action) {
128 Objects.requireNonNull(action, "action can't be null");
144 Objects.requireNonNull(action, "action can't be null");
129
145
130 lateConfigurationPolicy.finalizePolicy();
146 lateConfigurationPolicy.finalizePolicy();
131
147
132 whenAvailable(ctx -> ctx.configureEach(action));
148 whenAvailable(ctx -> ctx.configureEach(action));
133 }
149 }
134 };
150 };
135
151
136 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
152 extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension);
137
153
138 }
154 }
139
140 private static Layer resolveLayer(VariantsView variants, String name) {
141 return variants.getLayers().stream()
142 .filter(named(name))
143 .findAny()
144 .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared"));
145 }
146
147 private static Variant resolveVariant(VariantsView variants, String name) {
148 return variants.getVariants().stream()
149 .filter(named(name))
150 .findAny()
151 .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared"));
152 }
153
154 private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) {
155 return ctx.getCompileUnits().findUnit(
156 resolveVariant(ctx.getVariants(), variantName),
157 resolveLayer(ctx.getVariants(), layerName))
158 .orElseThrow(() -> new InvalidUserDataException(
159 "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'"));
160 }
161
162 private static Predicate<Named> named(String name) {
163 return named -> named.getName().equals(name);
164 }
165
166 }
155 }
@@ -1,102 +1,104
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.variants.core.Layer;
9 import org.implab.gradle.variants.core.Layer;
10 import org.implab.gradle.variants.core.Role;
10 import org.implab.gradle.variants.core.Role;
11 import org.implab.gradle.variants.core.Variant;
11 import org.implab.gradle.variants.core.Variant;
12 import org.implab.gradle.variants.core.VariantDefinition;
12 import org.implab.gradle.variants.core.VariantDefinition;
13 import org.implab.gradle.variants.core.VariantsExtension;
13 import org.implab.gradle.variants.core.VariantsExtension;
14 import org.implab.gradle.variants.core.VariantsView;
14 import org.implab.gradle.variants.core.VariantsView;
15 import org.implab.gradle.variants.internal.IdentityContainerFactory;
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 public static final String VARIANTS_EXTENSION = "variants";
27
26 @Override
28 @Override
27 public void apply(Project target) {
29 public void apply(Project target) {
28
30
29 var objectFactory = target.getObjects();
31 var objectFactory = target.getObjects();
30
32
31 var variantsExtension = new DefaultVariantsExtension(objectFactory);
33 var variantsExtension = new DefaultVariantsExtension(objectFactory);
32 target.getExtensions().add(VariantsExtension.class, "variants", variantsExtension);
34 target.getExtensions().add(VariantsExtension.class, VARIANTS_EXTENSION, variantsExtension);
33 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
35 target.afterEvaluate(t -> variantsExtension.finalizeExtension());
34
36
35 }
37 }
36
38
37 static class DefaultVariantsExtension implements VariantsExtension {
39 static class DefaultVariantsExtension implements VariantsExtension {
38
40
39 private final NamedDomainObjectContainer<Layer> layers;
41 private final NamedDomainObjectContainer<Layer> layers;
40 private final NamedDomainObjectContainer<Role> roles;
42 private final NamedDomainObjectContainer<Role> roles;
41 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
43 private final NamedDomainObjectContainer<VariantDefinition> variantDefinitions;
42 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
44 private final Deferred<VariantsView> finalizedResult = new Deferred<>();
43 private final ObjectFactory objectFactory;
45 private final ObjectFactory objectFactory;
44 private boolean finalized = false;
46 private boolean finalized = false;
45
47
46 public DefaultVariantsExtension(ObjectFactory objectFactory) {
48 public DefaultVariantsExtension(ObjectFactory objectFactory) {
47 this.objectFactory = objectFactory;
49 this.objectFactory = objectFactory;
48 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
50 this.layers = IdentityContainerFactory.create(objectFactory, Layer.class);
49 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
51 this.roles = IdentityContainerFactory.create(objectFactory, Role.class);
50 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
52 this.variantDefinitions = objectFactory.domainObjectContainer(VariantDefinition.class);
51 }
53 }
52
54
53 @Override
55 @Override
54 public NamedDomainObjectContainer<Layer> getLayers() {
56 public NamedDomainObjectContainer<Layer> getLayers() {
55 return layers;
57 return layers;
56 }
58 }
57
59
58 @Override
60 @Override
59 public NamedDomainObjectContainer<Role> getRoles() {
61 public NamedDomainObjectContainer<Role> getRoles() {
60 return roles;
62 return roles;
61 }
63 }
62
64
63 @Override
65 @Override
64 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
66 public NamedDomainObjectContainer<VariantDefinition> getVariantDefinitions() {
65 return variantDefinitions;
67 return variantDefinitions;
66 }
68 }
67
69
68 @Override
70 @Override
69 public void whenFinalized(Action<? super VariantsView> action) {
71 public void whenFinalized(Action<? super VariantsView> action) {
70 finalizedResult.whenResolved(action::execute);
72 finalizedResult.whenResolved(action::execute);
71 }
73 }
72
74
73 void finalizeExtension() {
75 void finalizeExtension() {
74 if (finalized)
76 if (finalized)
75 return;
77 return;
76
78
77 finalized = true;
79 finalized = true;
78
80
79 // freeze defined variants
81 // freeze defined variants
80 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
82 variantDefinitions.forEach(VariantDefinition::finalizeVariant);
81
83
82 // build a snapshot
84 // build a snapshot
83 var viewBuilder = VariantsView.builder();
85 var viewBuilder = VariantsView.builder();
84
86
85 // calculate and add variants
87 // calculate and add variants
86 variantDefinitions.stream()
88 variantDefinitions.stream()
87 .map(def -> objectFactory.named(Variant.class, def.getName()))
89 .map(def -> objectFactory.named(Variant.class, def.getName()))
88 .forEach(viewBuilder::addVariant);
90 .forEach(viewBuilder::addVariant);
89 // add layers
91 // add layers
90 layers.forEach(viewBuilder::addLayer);
92 layers.forEach(viewBuilder::addLayer);
91 // add roles
93 // add roles
92 roles.forEach(viewBuilder::addRole);
94 roles.forEach(viewBuilder::addRole);
93 // add definitions
95 // add definitions
94 variantDefinitions.forEach(viewBuilder::addDefinition);
96 variantDefinitions.forEach(viewBuilder::addDefinition);
95 // assemble the view
97 // assemble the view
96 var view = viewBuilder.build();
98 var view = viewBuilder.build();
97 // set the result and call hooks
99 // set the result and call hooks
98 finalizedResult.resolve(view);
100 finalizedResult.resolve(view);
99 }
101 }
100 }
102 }
101
103
102 }
104 }
@@ -1,191 +1,192
1 package org.implab.gradle.variants.sources;
1 package org.implab.gradle.variants.sources;
2
2
3 import java.io.File;
3 import java.io.File;
4 import java.nio.file.Paths;
5 import java.util.HashSet;
4 import java.util.HashSet;
6 import java.util.LinkedHashMap;
5 import java.util.LinkedHashMap;
7 import java.util.List;
6 import java.util.List;
8 import java.util.Map;
7 import java.util.Map;
9 import java.util.Objects;
8 import java.util.Objects;
10 import java.util.Set;
9 import java.util.Set;
11 import java.util.concurrent.Callable;
10 import java.util.concurrent.Callable;
12 import java.util.function.Function;
11 import java.util.function.Function;
13 import java.util.stream.Collectors;
12 import java.util.stream.Collectors;
14 import java.util.stream.Stream;
13 import java.util.stream.Stream;
15
14
16 import javax.inject.Inject;
15 import javax.inject.Inject;
17
16
18 import org.gradle.api.InvalidUserDataException;
17 import org.gradle.api.InvalidUserDataException;
19 import org.gradle.api.Named;
18 import org.gradle.api.Named;
20 import org.gradle.api.NamedDomainObjectContainer;
19 import org.gradle.api.NamedDomainObjectContainer;
21 import org.gradle.api.Task;
20 import org.gradle.api.Task;
22 import org.gradle.api.file.ConfigurableFileCollection;
21 import org.gradle.api.file.ConfigurableFileCollection;
23 import org.gradle.api.file.DirectoryProperty;
22 import org.gradle.api.file.DirectoryProperty;
24 import org.gradle.api.file.FileCollection;
23 import org.gradle.api.file.FileCollection;
25 import org.gradle.api.file.ProjectLayout;
26 import org.gradle.api.file.SourceDirectorySet;
24 import org.gradle.api.file.SourceDirectorySet;
27 import org.gradle.api.model.ObjectFactory;
25 import org.gradle.api.model.ObjectFactory;
28 import org.gradle.api.tasks.TaskProvider;
26 import org.gradle.api.tasks.TaskProvider;
29
27
30 /**
28 /**
31 * A configurable source set abstraction with named outputs.
29 * A configurable source set abstraction with named outputs.
32 *
30 *
33 * <p>
31 * <p>
34 * Each instance aggregates multiple {@link SourceDirectorySet source sets}
32 * Each instance aggregates multiple {@link SourceDirectorySet source sets}
35 * under a shared name and exposes typed outputs that must be declared up front.
33 * under a shared name and exposes typed outputs that must be declared up front.
36 * Default locations are {@code src/<name>} for sources and
34 * Source and output base directories are explicit model properties. They do
37 * {@code build/<name>} for outputs, both of which can be customized via the
35 * not define source directories or outputs by themselves; plugins and adapters
38 * exposed {@link DirectoryProperty} setters.
36 * may attach conventions or use them as layout hints for their own
37 * materialization rules.
39 * </p>
38 * </p>
40 *
39 *
41 * <p>
40 * <p>
42 * Outputs are grouped by names to make task wiring explicit. An output must be
41 * Outputs are grouped by names to make task wiring explicit. An output must be
43 * declared with {@link #declareOutputs(String, String...)} before files can be
42 * declared with {@link #declareOutputs(String, String...)} before files can be
44 * registered against it. Attempting to register or retrieve an undeclared
43 * registered against it. Attempting to register or retrieve an undeclared
45 * output results in
44 * output results in
46 * {@link InvalidUserDataException}.
45 * {@link InvalidUserDataException}.
47 * </p>
46 * </p>
48 */
47 */
49 public abstract class GenericSourceSet implements Named {
48 public abstract class GenericSourceSet implements Named {
50 private final String name;
49 private final String name;
51
50
52 private final NamedDomainObjectContainer<SourceDirectorySet> sourceDirectorySets;
51 private final NamedDomainObjectContainer<SourceDirectorySet> sourceDirectorySets;
53
52
54 private final Map<String, ConfigurableFileCollection> outputs;
53 private final Map<String, ConfigurableFileCollection> outputs;
55
54
56 private final FileCollection allOutputs;
55 private final FileCollection allOutputs;
57
56
58 private final FileCollection allSourceDirectories;
57 private final FileCollection allSourceDirectories;
59
58
60 private final ObjectFactory objects;
59 private final ObjectFactory objects;
61
60
62 private final Set<String> declaredOutputs = new HashSet<>();
61 private final Set<String> declaredOutputs = new HashSet<>();
63
62
64 @Inject
63 @Inject
65 public GenericSourceSet(String name, ObjectFactory objects, ProjectLayout layout) {
64 public GenericSourceSet(String name, ObjectFactory objects) {
66 this.name = name;
65 this.name = name;
67 this.objects = objects;
66 this.objects = objects;
68
67
69 sourceDirectorySets = objects.domainObjectContainer(
68 sourceDirectorySets = objects.domainObjectContainer(
70 SourceDirectorySet.class,
69 SourceDirectorySet.class,
71 this::createSourceDirectorySet);
70 this::createSourceDirectorySet);
72
71
73 outputs = new LinkedHashMap<>();
72 outputs = new LinkedHashMap<>();
74
73
75 allSourceDirectories = objects.fileCollection().from(sourceDirectoriesProvider());
74 allSourceDirectories = objects.fileCollection().from(sourceDirectoriesProvider());
76
75
77 allOutputs = objects.fileCollection().from(outputsProvider());
76 allOutputs = objects.fileCollection().from(outputsProvider());
78
79 getSourceSetDir().convention(layout
80 .getProjectDirectory()
81 .dir(Paths.get("src", name).toString()));
82
83 getOutputsDir().convention(layout
84 .getBuildDirectory()
85 .dir(name));
86 }
77 }
87
78
88 @Override
79 @Override
89 public String getName() {
80 public String getName() {
90 return name;
81 return name;
91 }
82 }
92
83
93 /**
84 /**
94 * Base directory for this source set. Defaults to {@code src/<name>} under
85 * Base directory associated with this source set.
95 * the project directory.
86 *
87 * <p>
88 * This property is intentionally convention-free in the base model. A
89 * plugin may attach a convention when it knows the surrounding layout
90 * policy, for example when materializing variant compile units.
91 * </p>
96 */
92 */
97 public abstract DirectoryProperty getSourceSetDir();
93 public abstract DirectoryProperty getSourceSetDir();
98
94
99 /**
95 /**
100 * Base directory for outputs of this source set. Defaults to
96 * Base directory associated with outputs of this source set.
101 * {@code build/<name>}.
97 *
98 * <p>
99 * This property is a layout hint. Actual named outputs are declared and
100 * populated through {@link #declareOutputs(String, String...)} and
101 * {@link #registerOutput(String, Object...)}.
102 * </p>
102 */
103 */
103 public abstract DirectoryProperty getOutputsDir();
104 public abstract DirectoryProperty getOutputsDir();
104
105
105 /**
106 /**
106 * The container of {@link SourceDirectorySet} instances that belong to this
107 * The container of {@link SourceDirectorySet} instances that belong to this
107 * logical source set.
108 * logical source set.
108 */
109 */
109 public NamedDomainObjectContainer<SourceDirectorySet> getSets() {
110 public NamedDomainObjectContainer<SourceDirectorySet> getSets() {
110 return sourceDirectorySets;
111 return sourceDirectorySets;
111 }
112 }
112
113
113 /**
114 /**
114 * All registered outputs grouped across output names.
115 * All registered outputs grouped across output names.
115 */
116 */
116 public FileCollection getAllOutputs() {
117 public FileCollection getAllOutputs() {
117 return allOutputs;
118 return allOutputs;
118 }
119 }
119
120
120 /**
121 /**
121 * All source directories from every contained {@link SourceDirectorySet}.
122 * All source directories from every contained {@link SourceDirectorySet}.
122 */
123 */
123 public FileCollection getAllSourceDirectories() {
124 public FileCollection getAllSourceDirectories() {
124 return allSourceDirectories;
125 return allSourceDirectories;
125 }
126 }
126
127
127 /**
128 /**
128 * Returns the file collection for the specified output name, creating it
129 * Returns the file collection for the specified output name, creating it
129 * if necessary.
130 * if necessary.
130 *
131 *
131 * @throws InvalidUserDataException if the output was not declared
132 * @throws InvalidUserDataException if the output was not declared
132 */
133 */
133 public FileCollection output(String name) {
134 public FileCollection output(String name) {
134 return configurableOutput(name);
135 return configurableOutput(name);
135 }
136 }
136
137
137 private ConfigurableFileCollection configurableOutput(String name) {
138 private ConfigurableFileCollection configurableOutput(String name) {
138 requireDeclaredOutput(name);
139 requireDeclaredOutput(name);
139 return outputs.computeIfAbsent(name, key -> objects.fileCollection());
140 return outputs.computeIfAbsent(name, key -> objects.fileCollection());
140 }
141 }
141
142
142 /**
143 /**
143 * Declares allowed output names. Outputs must be declared before registering
144 * Declares allowed output names. Outputs must be declared before registering
144 * files under them.
145 * files under them.
145 */
146 */
146 public void declareOutputs(String name, String... extra) {
147 public void declareOutputs(String name, String... extra) {
147 Stream.concat(Stream.of(name), Stream.of(extra))
148 Stream.concat(Stream.of(name), Stream.of(extra))
148 .map(Objects::requireNonNull)
149 .map(Objects::requireNonNull)
149 .forEach(declaredOutputs::add);
150 .forEach(declaredOutputs::add);
150 }
151 }
151
152
152 /**
153 /**
153 * Registers files produced elsewhere under the given output.
154 * Registers files produced elsewhere under the given output.
154 */
155 */
155 public void registerOutput(String name, Object... files) {
156 public void registerOutput(String name, Object... files) {
156 configurableOutput(name).from(files);
157 configurableOutput(name).from(files);
157 }
158 }
158
159
159 /**
160 /**
160 * Registers output files produced by a task, using a mapper to extract the
161 * Registers output files produced by a task, using a mapper to extract the
161 * output from the task. The task will be added as a build dependency of this
162 * output from the task. The task will be added as a build dependency of this
162 * output.
163 * output.
163 */
164 */
164 public <T extends Task> void registerOutput(String name, TaskProvider<T> task,
165 public <T extends Task> void registerOutput(String name, TaskProvider<T> task,
165 Function<? super T, ?> mapper) {
166 Function<? super T, ?> mapper) {
166 configurableOutput(name).from(task.map(mapper::apply))
167 configurableOutput(name).from(task.map(mapper::apply))
167 .builtBy(task);
168 .builtBy(task);
168 }
169 }
169
170
170 private SourceDirectorySet createSourceDirectorySet(String name) {
171 private SourceDirectorySet createSourceDirectorySet(String name) {
171 return objects.sourceDirectorySet(name, name);
172 return objects.sourceDirectorySet(name, name);
172 }
173 }
173
174
174 private void requireDeclaredOutput(String outputName) {
175 private void requireDeclaredOutput(String outputName) {
175 if (!declaredOutputs.contains(outputName)) {
176 if (!declaredOutputs.contains(outputName)) {
176 throw new InvalidUserDataException(
177 throw new InvalidUserDataException(
177 "Output '" + outputName + "' is not declared for source set '" + name + "'");
178 "Output '" + outputName + "' is not declared for source set '" + name + "'");
178 }
179 }
179 }
180 }
180
181
181 private Callable<List<? extends FileCollection>> outputsProvider() {
182 private Callable<List<? extends FileCollection>> outputsProvider() {
182 return () -> outputs.values().stream().toList();
183 return () -> outputs.values().stream().toList();
183 }
184 }
184
185
185 private Callable<Set<File>> sourceDirectoriesProvider() {
186 private Callable<Set<File>> sourceDirectoriesProvider() {
186 return () -> sourceDirectorySets.stream()
187 return () -> sourceDirectorySets.stream()
187 .flatMap(x -> x.getSrcDirs().stream())
188 .flatMap(x -> x.getSrcDirs().stream())
188 .collect(Collectors.toSet());
189 .collect(Collectors.toSet());
189 }
190 }
190
191
191 }
192 }
@@ -1,86 +1,90
1 package org.implab.gradle.variants;
1 package org.implab.gradle.variants;
2
2
3 import static org.junit.jupiter.api.Assertions.assertEquals;
3 import static org.junit.jupiter.api.Assertions.assertEquals;
4 import static org.junit.jupiter.api.Assertions.assertNotNull;
4 import static org.junit.jupiter.api.Assertions.assertNotNull;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
5 import static org.junit.jupiter.api.Assertions.assertTrue;
6
6
7 import java.io.IOException;
7 import java.io.IOException;
8 import java.util.Properties;
8 import java.util.Properties;
9
9
10 import org.gradle.testkit.runner.BuildResult;
10 import org.gradle.testkit.runner.BuildResult;
11 import org.junit.jupiter.api.Test;
11 import org.junit.jupiter.api.Test;
12
12
13 class PluginMarkerFunctionalTest extends AbstractFunctionalTest {
13 class PluginMarkerFunctionalTest extends AbstractFunctionalTest {
14
14
15 @Test
15 @Test
16 void pluginMarkersPointToCurrentImplementations() throws Exception {
16 void pluginMarkersPointToCurrentImplementations() throws Exception {
17 assertMarker("org.implab.gradle-variants.properties", VariantsPlugin.class);
17 assertMarker("org.implab.gradle-variants.properties", VariantsPlugin.class);
18 assertMarker("org.implab.gradle-sources.properties", SourcesPlugin.class);
18 assertMarker("org.implab.gradle-sources.properties", SourcesPlugin.class);
19 assertMarker("org.implab.gradle-variants-sources.properties", VariantSourcesPlugin.class);
19 assertMarker("org.implab.gradle-variants-sources.properties", VariantSourcesPlugin.class);
20 assertMarker("org.implab.gradle-variants-artifacts.properties", VariantArtifactsPlugin.class);
20 assertMarker("org.implab.gradle-variants-artifacts.properties", VariantArtifactsPlugin.class);
21 }
21 }
22
22
23 @Test
23 @Test
24 void pluginIdsApplyCurrentExtensions() throws Exception {
24 void pluginIdsApplyCurrentExtensions() throws Exception {
25 writeSettings("variants-plugin-ids");
25 writeSettings("variants-plugin-ids");
26 writeBuildFile("""
26 writeBuildFile("""
27 apply plugin: 'org.implab.gradle-variants'
27 apply plugin: 'org.implab.gradle-variants'
28 apply plugin: 'org.implab.gradle-sources'
28 apply plugin: 'org.implab.gradle-sources'
29 apply plugin: 'org.implab.gradle-variants-sources'
29 apply plugin: 'org.implab.gradle-variants-sources'
30 apply plugin: 'org.implab.gradle-variants-artifacts'
30 apply plugin: 'org.implab.gradle-variants-artifacts'
31
31
32 tasks.register('probe') {
32 tasks.register('probe') {
33 doLast {
33 doLast {
34 println("variantsExtension=" + (project.extensions.findByName('variants') != null))
34 println("variantsExtension=" + (project.extensions.findByName('variants') != null))
35 println("sourcesExtension=" + (project.extensions.findByName('sources') != null))
35 println("sourcesExtension=" + (project.extensions.findByName('sources') != null))
36 println("variantSourcesExtension=" + (project.extensions.findByName('variantSources') != null))
36 println("variantSourcesExtension=" + (project.extensions.findByName('variantSources') != null))
37 println("variantArtifactsExtension=" + (project.extensions.findByName('variantArtifacts') != null))
37 println("variantArtifactsExtension=" + (project.extensions.findByName('variantArtifacts') != null))
38 }
38 }
39 }
39 }
40 """);
40 """);
41
41
42 BuildResult result = runner("probe").build();
42 BuildResult result = runner("probe").build();
43
43
44 assertTrue(result.getOutput().contains("variantsExtension=true"));
44 assertTrue(result.getOutput().contains("variantsExtension=true"));
45 assertTrue(result.getOutput().contains("sourcesExtension=true"));
45 assertTrue(result.getOutput().contains("sourcesExtension=true"));
46 assertTrue(result.getOutput().contains("variantSourcesExtension=true"));
46 assertTrue(result.getOutput().contains("variantSourcesExtension=true"));
47 assertTrue(result.getOutput().contains("variantArtifactsExtension=true"));
47 assertTrue(result.getOutput().contains("variantArtifactsExtension=true"));
48 }
48 }
49
49
50 @Test
50 @Test
51 void sourcesPluginSupportsStandaloneSourceSetWorkflow() throws Exception {
51 void sourcesPluginSupportsStandaloneSourceSetWorkflow() throws Exception {
52 writeSettings("sources-plugin-standalone");
52 writeSettings("sources-plugin-standalone");
53 writeFile("inputs/main.js", "console.log('main')\n");
53 writeFile("inputs/main.js", "console.log('main')\n");
54 writeBuildFile("""
54 writeBuildFile("""
55 apply plugin: 'org.implab.gradle-sources'
55 apply plugin: 'org.implab.gradle-sources'
56
56
57 sources.create('main') {
57 sources.create('main') {
58 declareOutputs('js')
58 declareOutputs('js')
59 registerOutput('js', layout.projectDirectory.file('inputs/main.js'))
59 registerOutput('js', layout.projectDirectory.file('inputs/main.js'))
60 }
60 }
61
61
62 tasks.register('probe') {
62 tasks.register('probe') {
63 doLast {
63 doLast {
64 def main = sources.named('main').get()
64 def main = sources.named('main').get()
65 println("sourceSet=" + main.name)
65 println("sourceSet=" + main.name)
66 println("sourceSetDir=" + project.relativePath(main.sourceSetDir.get().asFile))
67 println("outputsDir=" + project.relativePath(main.outputsDir.get().asFile))
66 println("jsFiles=" + main.output('js').files.collect { it.name }.sort().join(','))
68 println("jsFiles=" + main.output('js').files.collect { it.name }.sort().join(','))
67 }
69 }
68 }
70 }
69 """);
71 """);
70
72
71 BuildResult result = runner("probe").build();
73 BuildResult result = runner("probe").build();
72
74
73 assertTrue(result.getOutput().contains("sourceSet=main"));
75 assertTrue(result.getOutput().contains("sourceSet=main"));
76 assertTrue(result.getOutput().contains("sourceSetDir=src/main"));
77 assertTrue(result.getOutput().contains("outputsDir=build/out/main"));
74 assertTrue(result.getOutput().contains("jsFiles=main.js"));
78 assertTrue(result.getOutput().contains("jsFiles=main.js"));
75 }
79 }
76
80
77 private static void assertMarker(String markerName, Class<?> implementationClass) throws IOException {
81 private static void assertMarker(String markerName, Class<?> implementationClass) throws IOException {
78 var resourceName = "META-INF/gradle-plugins/" + markerName;
82 var resourceName = "META-INF/gradle-plugins/" + markerName;
79 var resource = PluginMarkerFunctionalTest.class.getClassLoader().getResourceAsStream(resourceName);
83 var resource = PluginMarkerFunctionalTest.class.getClassLoader().getResourceAsStream(resourceName);
80 assertNotNull(resource, "Missing plugin marker " + resourceName);
84 assertNotNull(resource, "Missing plugin marker " + resourceName);
81
85
82 var properties = new Properties();
86 var properties = new Properties();
83 properties.load(resource);
87 properties.load(resource);
84 assertEquals(implementationClass.getName(), properties.getProperty("implementation-class"));
88 assertEquals(implementationClass.getName(), properties.getProperty("implementation-class"));
85 }
89 }
86 }
90 }
@@ -1,580 +1,585
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.whenAvailable { 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 def sourceSet = left.get()
43
44
44 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection)
45 .collect { it.layer().name }
46 .collect { it.layer().name }
46 .sort()
47 .sort()
47 .join(',')
48 .join(',')
48 lines << "mainSourceSet=" + left.name
49 lines << "mainSourceSet=" + sourceSet.name
50 lines << "mainSourceSetDir=" + project.relativePath(sourceSet.sourceSetDir.get().asFile)
51 lines << "mainOutputsDir=" + project.relativePath(sourceSet.outputsDir.get().asFile)
49 lines << "sameProvider=" + left.is(right)
52 lines << "sameProvider=" + left.is(right)
50 }
53 }
51
54
52 afterEvaluate {
55 afterEvaluate {
53 variantSources.whenAvailable { ctx ->
56 variantSources.whenAvailable { ctx ->
54 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
57 lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',')
55 }
58 }
56 }
59 }
57
60
58 tasks.register('probe') {
61 tasks.register('probe') {
59 doLast {
62 doLast {
60 lines.each { println(it) }
63 lines.each { println(it) }
61 }
64 }
62 }
65 }
63 """);
66 """);
64
67
65 BuildResult result = runner("probe").build();
68 BuildResult result = runner("probe").build();
66
69
67 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
70 assertTrue(result.getOutput().contains("units=browser:main,browser:test"));
68 assertTrue(result.getOutput().contains("projectionUnits=main"));
71 assertTrue(result.getOutput().contains("projectionUnits=main"));
69 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
72 assertTrue(result.getOutput().contains("mainSourceSet=browserMain"));
73 assertTrue(result.getOutput().contains("mainSourceSetDir=src/main"));
74 assertTrue(result.getOutput().contains("mainOutputsDir=build/variants/browser/main"));
70 assertTrue(result.getOutput().contains("sameProvider=true"));
75 assertTrue(result.getOutput().contains("sameProvider=true"));
71 assertTrue(result.getOutput().contains("late:variants=browser"));
76 assertTrue(result.getOutput().contains("late:variants=browser"));
72 }
77 }
73
78
74 @Test
79 @Test
75 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
80 void appliesSelectorPrecedenceForFutureMaterialization() throws Exception {
76 writeSettings("variant-sources-precedence");
81 writeSettings("variant-sources-precedence");
77 writeBuildFile("""
82 writeBuildFile("""
78 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
83 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
79
84
80 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
85 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
81 variantsExt.layers.create('main')
86 variantsExt.layers.create('main')
82 variantsExt.layers.create('test')
87 variantsExt.layers.create('test')
83 variantsExt.roles.create('production')
88 variantsExt.roles.create('production')
84 variantsExt.roles.create('test')
89 variantsExt.roles.create('test')
85
90
86 variantsExt.variant('browser') {
91 variantsExt.variant('browser') {
87 role('production') { layers('main') }
92 role('production') { layers('main') }
88 role('test') { layers('main', 'test') }
93 role('test') { layers('main', 'test') }
89 }
94 }
90
95
91 variantsExt.variant('node') {
96 variantsExt.variant('node') {
92 role('production') { layers('main') }
97 role('production') { layers('main') }
93 }
98 }
94
99
95 def events = []
100 def events = []
96
101
97 variantSources {
102 variantSources {
98 configureEach {
103 configureEach {
99 events << "all:" + sourceSet.name
104 events << "all:" + sourceSet.name
100 events << "context:" + sourceSet.name + ":" + variant.name + ":" + layer.name
105 events << "context:" + sourceSet.name + ":" + variant.name + ":" + layer.name
101 }
106 }
102 variant('browser') {
107 variant('browser') {
103 events << "variant:" + sourceSet.name
108 events << "variant:" + sourceSet.name
104 }
109 }
105 layer('main') {
110 layer('main') {
106 events << "layer:" + sourceSet.name
111 events << "layer:" + sourceSet.name
107 }
112 }
108 unit('browser', 'main') {
113 unit('browser', 'main') {
109 events << "unit:" + sourceSet.name
114 events << "unit:" + sourceSet.name
110 }
115 }
111 }
116 }
112
117
113 afterEvaluate {
118 afterEvaluate {
114 variantSources.whenAvailable { ctx ->
119 variantSources.whenAvailable { ctx ->
115 def browser = ctx.variants.variants.find { it.name == 'browser' }
120 def browser = ctx.variants.variants.find { it.name == 'browser' }
116 def node = ctx.variants.variants.find { it.name == 'node' }
121 def node = ctx.variants.variants.find { it.name == 'node' }
117 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
122 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
118 def testLayer = ctx.variants.layers.find { it.name == 'test' }
123 def testLayer = ctx.variants.layers.find { it.name == 'test' }
119
124
120 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
125 def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get()
121 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
126 def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get()
122 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
127 def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get()
123 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
128 def bySourceSet = events.groupBy { it.split(':', 2)[1] }
124
129
125 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
130 println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(','))
126 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
131 println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(','))
127 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
132 println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(','))
128 println("context=" + events.find { it.startsWith("context:" + browserMain.name + ":") })
133 println("context=" + events.find { it.startsWith("context:" + browserMain.name + ":") })
129 }
134 }
130 }
135 }
131 """);
136 """);
132
137
133 BuildResult result = runner("help").build();
138 BuildResult result = runner("help").build();
134
139
135 assertTrue(result.getOutput().contains("browserMain=all,variant,layer,unit"));
140 assertTrue(result.getOutput().contains("browserMain=all,variant,layer,unit"));
136 assertTrue(result.getOutput().contains("browserTest=all,variant"));
141 assertTrue(result.getOutput().contains("browserTest=all,variant"));
137 assertTrue(result.getOutput().contains("nodeMain=all,layer"));
142 assertTrue(result.getOutput().contains("nodeMain=all,layer"));
138 assertTrue(result.getOutput().contains("context=context:browserMain:browser:main"));
143 assertTrue(result.getOutput().contains("context=context:browserMain:browser:main"));
139 }
144 }
140
145
141 @Test
146 @Test
142 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
147 void failsLateConfigurationByDefaultAfterMaterialization() throws Exception {
143 writeSettings("variant-sources-late-fail");
148 writeSettings("variant-sources-late-fail");
144 writeBuildFile("""
149 writeBuildFile("""
145 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
150 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
146
151
147 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
152 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
148 variantsExt.layers.create('main')
153 variantsExt.layers.create('main')
149 variantsExt.roles.create('production')
154 variantsExt.roles.create('production')
150 variantsExt.variant('browser') {
155 variantsExt.variant('browser') {
151 role('production') { layers('main') }
156 role('production') { layers('main') }
152 }
157 }
153
158
154 afterEvaluate {
159 afterEvaluate {
155 variantSources.whenAvailable { ctx ->
160 variantSources.whenAvailable { ctx ->
156 def browser = ctx.variants.variants.find { it.name == 'browser' }
161 def browser = ctx.variants.variants.find { it.name == 'browser' }
157 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
162 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
158 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
163 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
159
164
160 ctx.sourceSets.getSourceSet(unit).get()
165 ctx.sourceSets.getSourceSet(unit).get()
161 variantSources.layer('main') {
166 variantSources.layer('main') {
162 sourceSet {
167 sourceSet {
163 declareOutputs('late')
168 declareOutputs('late')
164 }
169 }
165 }
170 }
166 }
171 }
167 }
172 }
168 """);
173 """);
169
174
170 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
175 assertBuildFails("Source sets for [layer=main] layer already materialized", "help");
171 }
176 }
172
177
173 @Test
178 @Test
174 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
179 void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception {
175 writeSettings("variant-sources-late-allow");
180 writeSettings("variant-sources-late-allow");
176 writeBuildFile("""
181 writeBuildFile("""
177 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
182 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
178
183
179 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
184 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
180 variantsExt.layers.create('main')
185 variantsExt.layers.create('main')
181 variantsExt.roles.create('production')
186 variantsExt.roles.create('production')
182 variantsExt.variant('browser') {
187 variantsExt.variant('browser') {
183 role('production') { layers('main') }
188 role('production') { layers('main') }
184 }
189 }
185
190
186 variantSources {
191 variantSources {
187 lateConfigurationPolicy {
192 lateConfigurationPolicy {
188 allowLateConfiguration()
193 allowLateConfiguration()
189 }
194 }
190 }
195 }
191
196
192 afterEvaluate {
197 afterEvaluate {
193 variantSources.whenAvailable { ctx ->
198 variantSources.whenAvailable { ctx ->
194 def browser = ctx.variants.variants.find { it.name == 'browser' }
199 def browser = ctx.variants.variants.find { it.name == 'browser' }
195 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
200 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
196 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
201 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
197
202
198 def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get()
203 def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get()
199 variantSources.layer('main') {
204 variantSources.layer('main') {
200 sourceSet {
205 sourceSet {
201 declareOutputs('late')
206 declareOutputs('late')
202 }
207 }
203 }
208 }
204 unitSourceSet.output('late')
209 unitSourceSet.output('late')
205 println('lateAllowed=ok')
210 println('lateAllowed=ok')
206 }
211 }
207 }
212 }
208 """);
213 """);
209
214
210 BuildResult result = runner("help").build();
215 BuildResult result = runner("help").build();
211 assertTrue(result.getOutput().contains("lateAllowed=ok"));
216 assertTrue(result.getOutput().contains("lateAllowed=ok"));
212 }
217 }
213
218
214 @Test
219 @Test
215 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
220 void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception {
216 writeSettings("variant-sources-late-warn");
221 writeSettings("variant-sources-late-warn");
217 writeBuildFile("""
222 writeBuildFile("""
218 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
223 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
219
224
220 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
225 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
221 variantsExt.layers.create('main')
226 variantsExt.layers.create('main')
222 variantsExt.roles.create('production')
227 variantsExt.roles.create('production')
223 variantsExt.variant('browser') {
228 variantsExt.variant('browser') {
224 role('production') { layers('main') }
229 role('production') { layers('main') }
225 }
230 }
226
231
227 variantSources {
232 variantSources {
228 lateConfigurationPolicy {
233 lateConfigurationPolicy {
229 warnOnLateConfiguration()
234 warnOnLateConfiguration()
230 }
235 }
231 }
236 }
232
237
233 afterEvaluate {
238 afterEvaluate {
234 variantSources.whenAvailable { ctx ->
239 variantSources.whenAvailable { ctx ->
235 def browser = ctx.variants.variants.find { it.name == 'browser' }
240 def browser = ctx.variants.variants.find { it.name == 'browser' }
236 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
241 def mainLayer = ctx.variants.layers.find { it.name == 'main' }
237 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
242 def unit = ctx.compileUnits.requireUnit(browser, mainLayer)
238
243
239 def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get()
244 def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get()
240 variantSources.layer('main') {
245 variantSources.layer('main') {
241 sourceSet {
246 sourceSet {
242 declareOutputs('late')
247 declareOutputs('late')
243 }
248 }
244 }
249 }
245 unitSourceSet.output('late')
250 unitSourceSet.output('late')
246 println('lateWarn=ok')
251 println('lateWarn=ok')
247 }
252 }
248 }
253 }
249 """);
254 """);
250
255
251 BuildResult result = runner("help").build();
256 BuildResult result = runner("help").build();
252
257
253 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
258 assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized"));
254 assertTrue(result.getOutput().contains("lateWarn=ok"));
259 assertTrue(result.getOutput().contains("lateWarn=ok"));
255 }
260 }
256
261
257 @Test
262 @Test
258 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
263 void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception {
259 writeSettings("variant-sources-late-policy-fixed");
264 writeSettings("variant-sources-late-policy-fixed");
260 writeBuildFile("""
265 writeBuildFile("""
261 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
266 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
262
267
263 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
268 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
264 variantsExt.layers.create('main')
269 variantsExt.layers.create('main')
265 variantsExt.roles.create('production')
270 variantsExt.roles.create('production')
266 variantsExt.variant('browser') {
271 variantsExt.variant('browser') {
267 role('production') { layers('main') }
272 role('production') { layers('main') }
268 }
273 }
269
274
270 variantSources {
275 variantSources {
271 variant('browser') {
276 variant('browser') {
272 sourceSet {
277 sourceSet {
273 declareOutputs('js')
278 declareOutputs('js')
274 }
279 }
275 }
280 }
276 lateConfigurationPolicy {
281 lateConfigurationPolicy {
277 allowLateConfiguration()
282 allowLateConfiguration()
278 }
283 }
279 }
284 }
280 """);
285 """);
281
286
282 assertBuildFails("Lazy configuration policy already applied", "help");
287 assertBuildFails("Lazy configuration policy already applied", "help");
283 }
288 }
284
289
285 @Test
290 @Test
286 void failsOnProjectedNameCollisionByDefault() throws Exception {
291 void failsOnProjectedNameCollisionByDefault() throws Exception {
287 writeSettings("variant-sources-name-collision-fail");
292 writeSettings("variant-sources-name-collision-fail");
288 writeBuildFile("""
293 writeBuildFile("""
289 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
294 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
290
295
291 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
296 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
292 variantsExt.layers.create('variantBar')
297 variantsExt.layers.create('variantBar')
293 variantsExt.layers.create('bar')
298 variantsExt.layers.create('bar')
294 variantsExt.roles.create('production')
299 variantsExt.roles.create('production')
295
300
296 variantsExt.variant('foo') {
301 variantsExt.variant('foo') {
297 role('production') { layers('variantBar') }
302 role('production') { layers('variantBar') }
298 }
303 }
299 variantsExt.variant('fooVariant') {
304 variantsExt.variant('fooVariant') {
300 role('production') { layers('bar') }
305 role('production') { layers('bar') }
301 }
306 }
302 """);
307 """);
303
308
304 assertBuildFails("The same source set names are produced by different compile units", "help");
309 assertBuildFails("The same source set names are produced by different compile units", "help");
305 }
310 }
306
311
307 @Test
312 @Test
308 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
313 void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception {
309 writeSettings("variant-sources-name-collision-resolve");
314 writeSettings("variant-sources-name-collision-resolve");
310 writeBuildFile("""
315 writeBuildFile("""
311 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
316 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
312
317
313 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
318 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
314 variantsExt.layers.create('variantBar')
319 variantsExt.layers.create('variantBar')
315 variantsExt.layers.create('bar')
320 variantsExt.layers.create('bar')
316 variantsExt.roles.create('production')
321 variantsExt.roles.create('production')
317
322
318 variantsExt.variant('foo') {
323 variantsExt.variant('foo') {
319 role('production') { layers('variantBar') }
324 role('production') { layers('variantBar') }
320 }
325 }
321 variantsExt.variant('fooVariant') {
326 variantsExt.variant('fooVariant') {
322 role('production') { layers('bar') }
327 role('production') { layers('bar') }
323 }
328 }
324
329
325 variantSources {
330 variantSources {
326 namingPolicy {
331 namingPolicy {
327 resolveNameCollision()
332 resolveNameCollision()
328 }
333 }
329 }
334 }
330
335
331 afterEvaluate {
336 afterEvaluate {
332 variantSources.whenAvailable { ctx ->
337 variantSources.whenAvailable { ctx ->
333 def foo = ctx.variants.variants.find { it.name == 'foo' }
338 def foo = ctx.variants.variants.find { it.name == 'foo' }
334 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
339 def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' }
335 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
340 def variantBar = ctx.variants.layers.find { it.name == 'variantBar' }
336 def bar = ctx.variants.layers.find { it.name == 'bar' }
341 def bar = ctx.variants.layers.find { it.name == 'bar' }
337
342
338 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
343 def later = ctx.compileUnits.requireUnit(fooVariant, bar)
339 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
344 def earlier = ctx.compileUnits.requireUnit(foo, variantBar)
340
345
341 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
346 println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name)
342 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
347 println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name)
343 }
348 }
344 }
349 }
345 """);
350 """);
346
351
347 BuildResult result = runner("help").build();
352 BuildResult result = runner("help").build();
348
353
349 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
354 assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2"));
350 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
355 assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar"));
351 }
356 }
352
357
353 @Test
358 @Test
354 void failsOnUnknownVariantSelectorTarget() throws Exception {
359 void failsOnUnknownVariantSelectorTarget() throws Exception {
355 writeSettings("variant-sources-missing-variant");
360 writeSettings("variant-sources-missing-variant");
356 writeBuildFile("""
361 writeBuildFile("""
357 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
362 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
358
363
359 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
364 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
360 variantsExt.layers.create('main')
365 variantsExt.layers.create('main')
361 variantsExt.roles.create('production')
366 variantsExt.roles.create('production')
362 variantsExt.variant('browser') {
367 variantsExt.variant('browser') {
363 role('production') { layers('main') }
368 role('production') { layers('main') }
364 }
369 }
365
370
366 variantSources {
371 variantSources {
367 variant('missing') {
372 variant('missing') {
368 sourceSet {
373 sourceSet {
369 declareOutputs('js')
374 declareOutputs('js')
370 }
375 }
371 }
376 }
372 }
377 }
373 """);
378 """);
374
379
375 assertBuildFails("Variant 'missing' isn't declared", "help");
380 assertBuildFails("The specified variant 'missing' isn't declared", "help");
376 }
381 }
377
382
378 @Test
383 @Test
379 void failsOnUnknownVariantContextSelectorTarget() throws Exception {
384 void failsOnUnknownVariantContextSelectorTarget() throws Exception {
380 writeSettings("variant-sources-context-missing-variant");
385 writeSettings("variant-sources-context-missing-variant");
381 writeBuildFile("""
386 writeBuildFile("""
382 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
387 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
383
388
384 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
389 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
385 variantsExt.layers.create('main')
390 variantsExt.layers.create('main')
386 variantsExt.roles.create('production')
391 variantsExt.roles.create('production')
387 variantsExt.variant('browser') {
392 variantsExt.variant('browser') {
388 role('production') { layers('main') }
393 role('production') { layers('main') }
389 }
394 }
390
395
391 variantSources.whenAvailable { ctx ->
396 variantSources.whenAvailable { ctx ->
392 def missing = objects.named(org.implab.gradle.variants.core.Variant, 'missing')
397 def missing = objects.named(org.implab.gradle.variants.core.Variant, 'missing')
393 ctx.configureVariant(missing) {
398 ctx.configureVariant(missing) {
394 sourceSet {
399 sourceSet {
395 declareOutputs('js')
400 declareOutputs('js')
396 }
401 }
397 }
402 }
398 }
403 }
399 """);
404 """);
400
405
401 assertBuildFails("The specified variant 'missing' isn't declared", "help");
406 assertBuildFails("The specified variant 'missing' isn't declared", "help");
402 }
407 }
403
408
404 @Test
409 @Test
405 void failsOnUnknownLayerSelectorTarget() throws Exception {
410 void failsOnUnknownLayerSelectorTarget() throws Exception {
406 writeSettings("variant-sources-missing-layer");
411 writeSettings("variant-sources-missing-layer");
407 writeBuildFile("""
412 writeBuildFile("""
408 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
413 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
409
414
410 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
415 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
411 variantsExt.layers.create('main')
416 variantsExt.layers.create('main')
412 variantsExt.roles.create('production')
417 variantsExt.roles.create('production')
413 variantsExt.variant('browser') {
418 variantsExt.variant('browser') {
414 role('production') { layers('main') }
419 role('production') { layers('main') }
415 }
420 }
416
421
417 variantSources {
422 variantSources {
418 layer('missing') {
423 layer('missing') {
419 sourceSet {
424 sourceSet {
420 declareOutputs('js')
425 declareOutputs('js')
421 }
426 }
422 }
427 }
423 }
428 }
424 """);
429 """);
425
430
426 assertBuildFails("Layer 'missing' isn't declared", "help");
431 assertBuildFails("The specified layer 'missing' isn't declared", "help");
427 }
432 }
428
433
429 @Test
434 @Test
430 void failsOnUnknownLayerContextSelectorTarget() throws Exception {
435 void failsOnUnknownLayerContextSelectorTarget() throws Exception {
431 writeSettings("variant-sources-context-missing-layer");
436 writeSettings("variant-sources-context-missing-layer");
432 writeBuildFile("""
437 writeBuildFile("""
433 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
438 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
434
439
435 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
440 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
436 variantsExt.layers.create('main')
441 variantsExt.layers.create('main')
437 variantsExt.roles.create('production')
442 variantsExt.roles.create('production')
438 variantsExt.variant('browser') {
443 variantsExt.variant('browser') {
439 role('production') { layers('main') }
444 role('production') { layers('main') }
440 }
445 }
441
446
442 variantSources.whenAvailable { ctx ->
447 variantSources.whenAvailable { ctx ->
443 def missing = objects.named(org.implab.gradle.variants.core.Layer, 'missing')
448 def missing = objects.named(org.implab.gradle.variants.core.Layer, 'missing')
444 ctx.configureLayer(missing) {
449 ctx.configureLayer(missing) {
445 sourceSet {
450 sourceSet {
446 declareOutputs('js')
451 declareOutputs('js')
447 }
452 }
448 }
453 }
449 }
454 }
450 """);
455 """);
451
456
452 assertBuildFails("The specified layer 'missing' isn't declared", "help");
457 assertBuildFails("The specified layer 'missing' isn't declared", "help");
453 }
458 }
454
459
455 @Test
460 @Test
456 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
461 void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception {
457 writeSettings("variant-sources-missing-unit");
462 writeSettings("variant-sources-missing-unit");
458 writeBuildFile("""
463 writeBuildFile("""
459 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
464 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
460
465
461 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
466 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
462 variantsExt.layers.create('main')
467 variantsExt.layers.create('main')
463 variantsExt.layers.create('test')
468 variantsExt.layers.create('test')
464 variantsExt.roles.create('production')
469 variantsExt.roles.create('production')
465 variantsExt.variant('browser') {
470 variantsExt.variant('browser') {
466 role('production') { layers('main') }
471 role('production') { layers('main') }
467 }
472 }
468
473
469 variantSources {
474 variantSources {
470 unit('browser', 'test') {
475 unit('browser', 'test') {
471 sourceSet {
476 sourceSet {
472 declareOutputs('js')
477 declareOutputs('js')
473 }
478 }
474 }
479 }
475 }
480 }
476 """);
481 """);
477
482
478 assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help");
483 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
479 }
484 }
480
485
481 @Test
486 @Test
482 void failsOnUndeclaredCompileUnitContextSelectorTarget() throws Exception {
487 void failsOnUndeclaredCompileUnitContextSelectorTarget() throws Exception {
483 writeSettings("variant-sources-context-missing-unit");
488 writeSettings("variant-sources-context-missing-unit");
484 writeBuildFile("""
489 writeBuildFile("""
485 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
490 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
486
491
487 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
492 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
488 variantsExt.layers.create('main')
493 variantsExt.layers.create('main')
489 variantsExt.layers.create('test')
494 variantsExt.layers.create('test')
490 variantsExt.roles.create('production')
495 variantsExt.roles.create('production')
491 variantsExt.variant('browser') {
496 variantsExt.variant('browser') {
492 role('production') { layers('main') }
497 role('production') { layers('main') }
493 }
498 }
494
499
495 variantSources.whenAvailable { ctx ->
500 variantSources.whenAvailable { ctx ->
496 def browser = ctx.variants.variants.find { it.name == 'browser' }
501 def browser = ctx.variants.variants.find { it.name == 'browser' }
497 def testLayer = ctx.variants.layers.find { it.name == 'test' }
502 def testLayer = ctx.variants.layers.find { it.name == 'test' }
498 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
503 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
499 ctx.configureUnit(unit) {
504 ctx.configureUnit(unit) {
500 sourceSet {
505 sourceSet {
501 declareOutputs('js')
506 declareOutputs('js')
502 }
507 }
503 }
508 }
504 }
509 }
505 """);
510 """);
506
511
507 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
512 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
508 }
513 }
509
514
510 @Test
515 @Test
511 void failsOnUndeclaredCompileUnitMaterializerTarget() throws Exception {
516 void failsOnUndeclaredCompileUnitMaterializerTarget() throws Exception {
512 writeSettings("variant-sources-materializer-missing-unit");
517 writeSettings("variant-sources-materializer-missing-unit");
513 writeBuildFile("""
518 writeBuildFile("""
514 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
519 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
515
520
516 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
521 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
517 variantsExt.layers.create('main')
522 variantsExt.layers.create('main')
518 variantsExt.layers.create('test')
523 variantsExt.layers.create('test')
519 variantsExt.roles.create('production')
524 variantsExt.roles.create('production')
520 variantsExt.variant('browser') {
525 variantsExt.variant('browser') {
521 role('production') { layers('main') }
526 role('production') { layers('main') }
522 }
527 }
523
528
524 variantSources.whenAvailable { ctx ->
529 variantSources.whenAvailable { ctx ->
525 def browser = ctx.variants.variants.find { it.name == 'browser' }
530 def browser = ctx.variants.variants.find { it.name == 'browser' }
526 def testLayer = ctx.variants.layers.find { it.name == 'test' }
531 def testLayer = ctx.variants.layers.find { it.name == 'test' }
527 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
532 def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer)
528 ctx.sourceSets.getSourceSet(unit)
533 ctx.sourceSets.getSourceSet(unit)
529 }
534 }
530 """);
535 """);
531
536
532 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
537 assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help");
533 }
538 }
534
539
535 @Test
540 @Test
536 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
541 void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception {
537 writeSettings("variant-sources-name-policy-fixed");
542 writeSettings("variant-sources-name-policy-fixed");
538 writeBuildFile("""
543 writeBuildFile("""
539 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
544 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
540
545
541 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
546 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
542 variantsExt.layers.create('main')
547 variantsExt.layers.create('main')
543 variantsExt.roles.create('production')
548 variantsExt.roles.create('production')
544 variantsExt.variant('browser') {
549 variantsExt.variant('browser') {
545 role('production') { layers('main') }
550 role('production') { layers('main') }
546 }
551 }
547
552
548 variantSources.whenAvailable {
553 variantSources.whenAvailable {
549 variantSources.namingPolicy {
554 variantSources.namingPolicy {
550 resolveNameCollision()
555 resolveNameCollision()
551 }
556 }
552 }
557 }
553 """);
558 """);
554
559
555 assertBuildFails("Naming policy already applied", "help");
560 assertBuildFails("Naming policy already applied", "help");
556 }
561 }
557
562
558 @Test
563 @Test
559 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
564 void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception {
560 writeSettings("variant-sources-late-policy-context-fixed");
565 writeSettings("variant-sources-late-policy-context-fixed");
561 writeBuildFile("""
566 writeBuildFile("""
562 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
567 apply plugin: org.implab.gradle.variants.VariantSourcesPlugin
563
568
564 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
569 def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension)
565 variantsExt.layers.create('main')
570 variantsExt.layers.create('main')
566 variantsExt.roles.create('production')
571 variantsExt.roles.create('production')
567 variantsExt.variant('browser') {
572 variantsExt.variant('browser') {
568 role('production') { layers('main') }
573 role('production') { layers('main') }
569 }
574 }
570
575
571 variantSources.whenAvailable {
576 variantSources.whenAvailable {
572 variantSources.lateConfigurationPolicy {
577 variantSources.lateConfigurationPolicy {
573 allowLateConfiguration()
578 allowLateConfiguration()
574 }
579 }
575 }
580 }
576 """);
581 """);
577
582
578 assertBuildFails("Lazy configuration policy already applied", "help");
583 assertBuildFails("Lazy configuration policy already applied", "help");
579 }
584 }
580 }
585 }
General Comments 0
You need to be logged in to leave comments. Login now