| @@ -0,0 +1,69 | |||
|
|
1 | package org.implab.gradle.variants.sources; | |
|
|
2 | ||
|
|
3 | import org.gradle.api.Action; | |
|
|
4 | import org.implab.gradle.common.core.lang.Closures; | |
|
|
5 | import org.implab.gradle.variants.core.Layer; | |
|
|
6 | import org.implab.gradle.variants.core.Variant; | |
|
|
7 | ||
|
|
8 | import groovy.lang.Closure; | |
|
|
9 | ||
|
|
10 | /** | |
|
|
11 | * Source-set materialization context for one compile unit. | |
|
|
12 | * | |
|
|
13 | * <p>This type keeps compile-unit identity next to the materialized | |
|
|
14 | * {@link GenericSourceSet} without turning the selector DSL into a second | |
|
|
15 | * source-set API. Use {@link #getCompileUnit()} for selection metadata and | |
|
|
16 | * {@link #sourceSet(Action)} when the underlying source set body must be | |
|
|
17 | * configured. | |
|
|
18 | */ | |
|
|
19 | public interface CompileUnitSourceSetSpec { | |
|
|
20 | /** | |
|
|
21 | * Returns the compile unit represented by this source set. | |
|
|
22 | * | |
|
|
23 | * @return compile-unit identity | |
|
|
24 | */ | |
|
|
25 | CompileUnit getCompileUnit(); | |
|
|
26 | ||
|
|
27 | /** | |
|
|
28 | * Returns the variant part of the compile-unit identity. | |
|
|
29 | * | |
|
|
30 | * @return variant identity | |
|
|
31 | */ | |
|
|
32 | default Variant getVariant() { | |
|
|
33 | return getCompileUnit().variant(); | |
|
|
34 | } | |
|
|
35 | ||
|
|
36 | /** | |
|
|
37 | * Returns the layer part of the compile-unit identity. | |
|
|
38 | * | |
|
|
39 | * @return layer identity | |
|
|
40 | */ | |
|
|
41 | default Layer getLayer() { | |
|
|
42 | return getCompileUnit().layer(); | |
|
|
43 | } | |
|
|
44 | ||
|
|
45 | /** | |
|
|
46 | * Returns the materialized source set body. | |
|
|
47 | * | |
|
|
48 | * @return source set body | |
|
|
49 | */ | |
|
|
50 | GenericSourceSet getSourceSet(); | |
|
|
51 | ||
|
|
52 | /** | |
|
|
53 | * Configures the materialized source set body. | |
|
|
54 | * | |
|
|
55 | * @param action source set configuration action | |
|
|
56 | */ | |
|
|
57 | default void sourceSet(Action<? super GenericSourceSet> action) { | |
|
|
58 | action.execute(getSourceSet()); | |
|
|
59 | } | |
|
|
60 | ||
|
|
61 | /** | |
|
|
62 | * Configures the materialized source set body. | |
|
|
63 | * | |
|
|
64 | * @param closure source set configuration closure | |
|
|
65 | */ | |
|
|
66 | default void sourceSet(Closure<?> closure) { | |
|
|
67 | sourceSet(Closures.action(closure)); | |
|
|
68 | } | |
|
|
69 | } | |
| @@ -0,0 +1,25 | |||
|
|
1 | package org.implab.gradle.variants.sources.internal; | |
|
|
2 | ||
|
|
3 | import org.implab.gradle.variants.sources.CompileUnit; | |
|
|
4 | import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec; | |
|
|
5 | import org.implab.gradle.variants.sources.GenericSourceSet; | |
|
|
6 | ||
|
|
7 | class DefaultCompileUnitSourceSetSpec implements CompileUnitSourceSetSpec { | |
|
|
8 | private final CompileUnit compileUnit; | |
|
|
9 | private final GenericSourceSet sourceSet; | |
|
|
10 | ||
|
|
11 | DefaultCompileUnitSourceSetSpec(CompileUnit compileUnit, GenericSourceSet sourceSet) { | |
|
|
12 | this.compileUnit = compileUnit; | |
|
|
13 | this.sourceSet = sourceSet; | |
|
|
14 | } | |
|
|
15 | ||
|
|
16 | @Override | |
|
|
17 | public CompileUnit getCompileUnit() { | |
|
|
18 | return compileUnit; | |
|
|
19 | } | |
|
|
20 | ||
|
|
21 | @Override | |
|
|
22 | public GenericSourceSet getSourceSet() { | |
|
|
23 | return sourceSet; | |
|
|
24 | } | |
|
|
25 | } | |
| @@ -1,157 +1,166 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.Objects; |
|
|
4 | 4 | import java.util.function.Predicate; |
|
|
5 | 5 | |
|
|
6 | 6 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
7 | 7 | import org.gradle.api.Action; |
|
|
8 | 8 | import org.gradle.api.InvalidUserDataException; |
|
|
9 | 9 | import org.gradle.api.Named; |
|
|
10 | 10 | import org.gradle.api.Plugin; |
|
|
11 | 11 | import org.gradle.api.Project; |
|
|
12 | 12 | import org.implab.gradle.common.core.lang.Deferred; |
|
|
13 | 13 | import org.implab.gradle.common.core.lang.Strings; |
|
|
14 | 14 | import org.implab.gradle.variants.core.Layer; |
|
|
15 | 15 | import org.implab.gradle.variants.core.Variant; |
|
|
16 | 16 | import org.implab.gradle.variants.core.VariantsExtension; |
|
|
17 | 17 | import org.implab.gradle.variants.core.VariantsView; |
|
|
18 | 18 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
19 | import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec; | |
|
|
19 | 20 | import org.implab.gradle.variants.sources.CompileUnitsView; |
|
|
20 | import org.implab.gradle.variants.sources.GenericSourceSet; | |
|
|
21 | 21 | import org.implab.gradle.variants.sources.RoleProjectionsView; |
|
|
22 | 22 | import org.implab.gradle.variants.sources.VariantSourcesContext; |
|
|
23 | 23 | import org.implab.gradle.variants.sources.VariantSourcesExtension; |
|
|
24 | 24 | import org.implab.gradle.variants.sources.internal.CompileUnitNamer; |
|
|
25 | 25 | import org.implab.gradle.variants.sources.internal.DefaultCompileUnitNamingPolicy; |
|
|
26 | 26 | import org.implab.gradle.variants.sources.internal.DefaultLateConfigurationPolicySpec; |
|
|
27 | 27 | import org.implab.gradle.variants.sources.internal.DefaultVariantSourcesContext; |
|
|
28 | 28 | import org.implab.gradle.variants.sources.internal.SourceSetConfigurationRegistry; |
|
|
29 | 29 | import org.implab.gradle.variants.sources.internal.SourceSetRegistry; |
|
|
30 | 30 | |
|
|
31 | 31 | @NonNullByDefault |
|
|
32 | 32 | public abstract class VariantSourcesPlugin implements Plugin<Project> { |
|
|
33 | 33 | public static final String VARIANT_SOURCES_EXTENSION = "variantSources"; |
|
|
34 | 34 | |
|
|
35 | 35 | @Override |
|
|
36 | 36 | public void apply(Project target) { |
|
|
37 | 37 | var extensions = target.getExtensions(); |
|
|
38 | 38 | |
|
|
39 | 39 | // Apply the main VariantsPlugin to ensure the core variant model is available. |
|
|
40 | 40 | target.getPlugins().apply(VariantsPlugin.class); |
|
|
41 | 41 | // Access the VariantsExtension to configure variant sources. |
|
|
42 | 42 | var variantsExtension = extensions.getByType(VariantsExtension.class); |
|
|
43 | 43 | var objectFactory = target.getObjects(); |
|
|
44 | 44 | |
|
|
45 | 45 | var deferred = new Deferred<VariantSourcesContext>(); |
|
|
46 | 46 | |
|
|
47 | 47 | var lateConfigurationPolicy = new DefaultLateConfigurationPolicySpec(); |
|
|
48 | 48 | var namingPolicy = new DefaultCompileUnitNamingPolicy(); |
|
|
49 | 49 | |
|
|
50 | 50 | variantsExtension.whenFinalized(variants -> { |
|
|
51 | 51 | // create variant views |
|
|
52 | 52 | var compileUnits = CompileUnitsView.of(variants); |
|
|
53 | 53 | var roleProjections = RoleProjectionsView.of(variants); |
|
|
54 | 54 | |
|
|
55 | 55 | // create registries |
|
|
56 | 56 | var sourceSetRegistry = new SourceSetRegistry(objectFactory); |
|
|
57 | 57 | lateConfigurationPolicy.finalizePolicy(); |
|
|
58 | 58 | var sourceSetConfiguration = new SourceSetConfigurationRegistry(lateConfigurationPolicy::mode); |
|
|
59 | 59 | |
|
|
60 | 60 | // build compile unit namer |
|
|
61 | 61 | var compileUnitNamer = CompileUnitNamer.builder() |
|
|
62 | 62 | .addUnits(compileUnits.getUnits()) |
|
|
63 | 63 | .nameCollisionPolicy(namingPolicy.policy()) |
|
|
64 | 64 | .build(); |
|
|
65 | 65 | |
|
|
66 | 66 | // create the context |
|
|
67 | 67 | var context = new DefaultVariantSourcesContext( |
|
|
68 | 68 | variants, |
|
|
69 | 69 | compileUnits, |
|
|
70 | 70 | roleProjections, |
|
|
71 | 71 | compileUnitNamer, |
|
|
72 | 72 | sourceSetRegistry, |
|
|
73 | 73 | sourceSetConfiguration |
|
|
74 | 74 | ); |
|
|
75 | 75 | deferred.resolve(context); |
|
|
76 | 76 | }); |
|
|
77 | 77 | |
|
|
78 | 78 | var variantSourcesExtension = new VariantSourcesExtension() { |
|
|
79 | 79 | @Override |
|
|
80 | 80 | public void whenAvailable(Action<? super VariantSourcesContext> action) { |
|
|
81 | 81 | deferred.whenResolved(action::execute); |
|
|
82 | 82 | } |
|
|
83 | 83 | |
|
|
84 | 84 | @Override |
|
|
85 | 85 | public void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action) { |
|
|
86 | 86 | action.execute(lateConfigurationPolicy); |
|
|
87 | 87 | } |
|
|
88 | 88 | |
|
|
89 | 89 | @Override |
|
|
90 | 90 | public void namingPolicy(Action<? super NamingPolicySpec> action) { |
|
|
91 | 91 | action.execute(namingPolicy); |
|
|
92 | 92 | } |
|
|
93 | 93 | |
|
|
94 | 94 | @Override |
|
|
95 |
public void variant(String variantName, Action<? super |
|
|
|
95 | public void variant(String variantName, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
96 | 96 | Strings.argumentNotNullOrBlank(variantName, "variantName"); |
|
|
97 | 97 | Objects.requireNonNull(action, "action can't be null"); |
|
|
98 | 98 | |
|
|
99 | 99 | lateConfigurationPolicy.finalizePolicy(); |
|
|
100 | 100 | |
|
|
101 | 101 | whenAvailable(ctx -> ctx.configureVariant(resolveVariant(ctx.getVariants(), variantName), action)); |
|
|
102 | 102 | } |
|
|
103 | 103 | |
|
|
104 | 104 | @Override |
|
|
105 |
public void layer(String layerName, Action<? super |
|
|
|
105 | public void layer(String layerName, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
106 | 106 | // protect external DSL |
|
|
107 | 107 | Strings.argumentNotNullOrBlank(layerName, "layerName"); |
|
|
108 | 108 | Objects.requireNonNull(action, "action can't be null"); |
|
|
109 | 109 | |
|
|
110 | 110 | lateConfigurationPolicy.finalizePolicy(); |
|
|
111 | 111 | |
|
|
112 | 112 | whenAvailable(ctx -> ctx.configureLayer(resolveLayer(ctx.getVariants(), layerName), action)); |
|
|
113 | 113 | } |
|
|
114 | 114 | |
|
|
115 | 115 | @Override |
|
|
116 |
public void unit(String variantName, String layerName, Action<? super |
|
|
|
116 | public void unit(String variantName, String layerName, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
117 | 117 | Strings.argumentNotNullOrBlank(layerName, "layerName"); |
|
|
118 | 118 | Strings.argumentNotNullOrBlank(variantName, "variantName"); |
|
|
119 | 119 | Objects.requireNonNull(action, "action can't be null"); |
|
|
120 | 120 | |
|
|
121 | 121 | lateConfigurationPolicy.finalizePolicy(); |
|
|
122 | 122 | |
|
|
123 | 123 | whenAvailable(ctx -> ctx.configureUnit(resolveCompileUnit(ctx, variantName, layerName), action)); |
|
|
124 | 124 | } |
|
|
125 | ||
|
|
126 | @Override | |
|
|
127 | public void configureEach(Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
128 | Objects.requireNonNull(action, "action can't be null"); | |
|
|
129 | ||
|
|
130 | lateConfigurationPolicy.finalizePolicy(); | |
|
|
131 | ||
|
|
132 | whenAvailable(ctx -> ctx.configureEach(action)); | |
|
|
133 | } | |
|
|
125 | 134 | }; |
|
|
126 | 135 | |
|
|
127 | 136 | extensions.add(VariantSourcesExtension.class, VARIANT_SOURCES_EXTENSION, variantSourcesExtension); |
|
|
128 | 137 | |
|
|
129 | 138 | } |
|
|
130 | 139 | |
|
|
131 | 140 | private static Layer resolveLayer(VariantsView variants, String name) { |
|
|
132 | 141 | return variants.getLayers().stream() |
|
|
133 | 142 | .filter(named(name)) |
|
|
134 | 143 | .findAny() |
|
|
135 | 144 | .orElseThrow(() -> new InvalidUserDataException("Layer '" + name + "' isn't declared")); |
|
|
136 | 145 | } |
|
|
137 | 146 | |
|
|
138 | 147 | private static Variant resolveVariant(VariantsView variants, String name) { |
|
|
139 | 148 | return variants.getVariants().stream() |
|
|
140 | 149 | .filter(named(name)) |
|
|
141 | 150 | .findAny() |
|
|
142 | 151 | .orElseThrow(() -> new InvalidUserDataException("Variant '" + name + "' isn't declared")); |
|
|
143 | 152 | } |
|
|
144 | 153 | |
|
|
145 | 154 | private static CompileUnit resolveCompileUnit(VariantSourcesContext ctx, String variantName, String layerName) { |
|
|
146 | 155 | return ctx.getCompileUnits().findUnit( |
|
|
147 | 156 | resolveVariant(ctx.getVariants(), variantName), |
|
|
148 | 157 | resolveLayer(ctx.getVariants(), layerName)) |
|
|
149 | 158 | .orElseThrow(() -> new InvalidUserDataException( |
|
|
150 | 159 | "The CompileUnit isn't declared for variant '" + variantName + "', layer '" + layerName + "'")); |
|
|
151 | 160 | } |
|
|
152 | 161 | |
|
|
153 | 162 | private static Predicate<Named> named(String name) { |
|
|
154 | 163 | return named -> named.getName().equals(name); |
|
|
155 | 164 | } |
|
|
156 | 165 | |
|
|
157 | 166 | } |
| @@ -1,206 +1,191 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.io.File; |
|
|
4 | 4 | import java.nio.file.Paths; |
|
|
5 | 5 | import java.util.HashSet; |
|
|
6 | 6 | import java.util.LinkedHashMap; |
|
|
7 | 7 | import java.util.List; |
|
|
8 | 8 | import java.util.Map; |
|
|
9 | 9 | import java.util.Objects; |
|
|
10 | 10 | import java.util.Set; |
|
|
11 | 11 | import java.util.concurrent.Callable; |
|
|
12 | 12 | import java.util.function.Function; |
|
|
13 | 13 | import java.util.stream.Collectors; |
|
|
14 | 14 | import java.util.stream.Stream; |
|
|
15 | 15 | |
|
|
16 | 16 | import javax.inject.Inject; |
|
|
17 | 17 | |
|
|
18 | 18 | import org.gradle.api.InvalidUserDataException; |
|
|
19 | 19 | import org.gradle.api.Named; |
|
|
20 | 20 | import org.gradle.api.NamedDomainObjectContainer; |
|
|
21 | 21 | import org.gradle.api.Task; |
|
|
22 | 22 | import org.gradle.api.file.ConfigurableFileCollection; |
|
|
23 | 23 | import org.gradle.api.file.DirectoryProperty; |
|
|
24 | 24 | import org.gradle.api.file.FileCollection; |
|
|
25 | 25 | import org.gradle.api.file.ProjectLayout; |
|
|
26 | 26 | import org.gradle.api.file.SourceDirectorySet; |
|
|
27 | 27 | import org.gradle.api.model.ObjectFactory; |
|
|
28 | 28 | import org.gradle.api.tasks.TaskProvider; |
|
|
29 | import org.gradle.util.Configurable; | |
|
|
30 | import org.implab.gradle.common.core.lang.Closures; | |
|
|
31 | ||
|
|
32 | import groovy.lang.Closure; | |
|
|
33 | 29 | |
|
|
34 | 30 | /** |
|
|
35 | 31 | * A configurable source set abstraction with named outputs. |
|
|
36 | 32 | * |
|
|
37 | 33 | * <p> |
|
|
38 | 34 | * Each instance aggregates multiple {@link SourceDirectorySet source sets} |
|
|
39 | 35 | * under a shared name and exposes typed outputs that must be declared up front. |
|
|
40 | 36 | * Default locations are {@code src/<name>} for sources and |
|
|
41 | 37 | * {@code build/<name>} for outputs, both of which can be customized via the |
|
|
42 | 38 | * exposed {@link DirectoryProperty} setters. |
|
|
43 | 39 | * </p> |
|
|
44 | 40 | * |
|
|
45 | 41 | * <p> |
|
|
46 | 42 | * Outputs are grouped by names to make task wiring explicit. An output must be |
|
|
47 | 43 | * declared with {@link #declareOutputs(String, String...)} before files can be |
|
|
48 | 44 | * registered against it. Attempting to register or retrieve an undeclared |
|
|
49 | 45 | * output results in |
|
|
50 | 46 | * {@link InvalidUserDataException}. |
|
|
51 | 47 | * </p> |
|
|
52 | 48 | */ |
|
|
53 | public abstract class GenericSourceSet | |
|
|
54 | implements Named, Configurable<GenericSourceSet> { | |
|
|
49 | public abstract class GenericSourceSet implements Named { | |
|
|
55 | 50 | private final String name; |
|
|
56 | 51 | |
|
|
57 | 52 | private final NamedDomainObjectContainer<SourceDirectorySet> sourceDirectorySets; |
|
|
58 | 53 | |
|
|
59 | 54 | private final Map<String, ConfigurableFileCollection> outputs; |
|
|
60 | 55 | |
|
|
61 | 56 | private final FileCollection allOutputs; |
|
|
62 | 57 | |
|
|
63 | 58 | private final FileCollection allSourceDirectories; |
|
|
64 | 59 | |
|
|
65 | 60 | private final ObjectFactory objects; |
|
|
66 | 61 | |
|
|
67 | 62 | private final Set<String> declaredOutputs = new HashSet<>(); |
|
|
68 | 63 | |
|
|
69 | 64 | @Inject |
|
|
70 | 65 | public GenericSourceSet(String name, ObjectFactory objects, ProjectLayout layout) { |
|
|
71 | 66 | this.name = name; |
|
|
72 | 67 | this.objects = objects; |
|
|
73 | 68 | |
|
|
74 | 69 | sourceDirectorySets = objects.domainObjectContainer( |
|
|
75 | 70 | SourceDirectorySet.class, |
|
|
76 | 71 | this::createSourceDirectorySet); |
|
|
77 | 72 | |
|
|
78 | 73 | outputs = new LinkedHashMap<>(); |
|
|
79 | 74 | |
|
|
80 | 75 | allSourceDirectories = objects.fileCollection().from(sourceDirectoriesProvider()); |
|
|
81 | 76 | |
|
|
82 | 77 | allOutputs = objects.fileCollection().from(outputsProvider()); |
|
|
83 | 78 | |
|
|
84 | 79 | getSourceSetDir().convention(layout |
|
|
85 | 80 | .getProjectDirectory() |
|
|
86 | 81 | .dir(Paths.get("src", name).toString())); |
|
|
87 | 82 | |
|
|
88 | 83 | getOutputsDir().convention(layout |
|
|
89 | 84 | .getBuildDirectory() |
|
|
90 | 85 | .dir(name)); |
|
|
91 | 86 | } |
|
|
92 | 87 | |
|
|
93 | 88 | @Override |
|
|
94 | 89 | public String getName() { |
|
|
95 | 90 | return name; |
|
|
96 | 91 | } |
|
|
97 | 92 | |
|
|
98 | 93 | /** |
|
|
99 | 94 | * Base directory for this source set. Defaults to {@code src/<name>} under |
|
|
100 | 95 | * the project directory. |
|
|
101 | 96 | */ |
|
|
102 | 97 | public abstract DirectoryProperty getSourceSetDir(); |
|
|
103 | 98 | |
|
|
104 | 99 | /** |
|
|
105 | 100 | * Base directory for outputs of this source set. Defaults to |
|
|
106 | 101 | * {@code build/<name>}. |
|
|
107 | 102 | */ |
|
|
108 | 103 | public abstract DirectoryProperty getOutputsDir(); |
|
|
109 | 104 | |
|
|
110 | 105 | /** |
|
|
111 | 106 | * The container of {@link SourceDirectorySet} instances that belong to this |
|
|
112 | 107 | * logical source set. |
|
|
113 | 108 | */ |
|
|
114 | 109 | public NamedDomainObjectContainer<SourceDirectorySet> getSets() { |
|
|
115 | 110 | return sourceDirectorySets; |
|
|
116 | 111 | } |
|
|
117 | 112 | |
|
|
118 | 113 | /** |
|
|
119 | 114 | * All registered outputs grouped across output names. |
|
|
120 | 115 | */ |
|
|
121 | 116 | public FileCollection getAllOutputs() { |
|
|
122 | 117 | return allOutputs; |
|
|
123 | 118 | } |
|
|
124 | 119 | |
|
|
125 | 120 | /** |
|
|
126 | 121 | * All source directories from every contained {@link SourceDirectorySet}. |
|
|
127 | 122 | */ |
|
|
128 | 123 | public FileCollection getAllSourceDirectories() { |
|
|
129 | 124 | return allSourceDirectories; |
|
|
130 | 125 | } |
|
|
131 | 126 | |
|
|
132 | 127 | /** |
|
|
133 | 128 | * Returns the file collection for the specified output name, creating it |
|
|
134 | 129 | * if necessary. |
|
|
135 | 130 | * |
|
|
136 | 131 | * @throws InvalidUserDataException if the output was not declared |
|
|
137 | 132 | */ |
|
|
138 | 133 | public FileCollection output(String name) { |
|
|
139 | 134 | return configurableOutput(name); |
|
|
140 | 135 | } |
|
|
141 | 136 | |
|
|
142 | 137 | private ConfigurableFileCollection configurableOutput(String name) { |
|
|
143 | 138 | requireDeclaredOutput(name); |
|
|
144 | 139 | return outputs.computeIfAbsent(name, key -> objects.fileCollection()); |
|
|
145 | 140 | } |
|
|
146 | 141 | |
|
|
147 | 142 | /** |
|
|
148 | 143 | * Declares allowed output names. Outputs must be declared before registering |
|
|
149 | 144 | * files under them. |
|
|
150 | 145 | */ |
|
|
151 | 146 | public void declareOutputs(String name, String... extra) { |
|
|
152 | 147 | Stream.concat(Stream.of(name), Stream.of(extra)) |
|
|
153 | 148 | .map(Objects::requireNonNull) |
|
|
154 | 149 | .forEach(declaredOutputs::add); |
|
|
155 | 150 | } |
|
|
156 | 151 | |
|
|
157 | 152 | /** |
|
|
158 | 153 | * Registers files produced elsewhere under the given output. |
|
|
159 | 154 | */ |
|
|
160 | 155 | public void registerOutput(String name, Object... files) { |
|
|
161 | 156 | configurableOutput(name).from(files); |
|
|
162 | 157 | } |
|
|
163 | 158 | |
|
|
164 | 159 | /** |
|
|
165 | 160 | * Registers output files produced by a task, using a mapper to extract the |
|
|
166 | 161 | * output from the task. The task will be added as a build dependency of this |
|
|
167 | 162 | * output. |
|
|
168 | 163 | */ |
|
|
169 | 164 | public <T extends Task> void registerOutput(String name, TaskProvider<T> task, |
|
|
170 | 165 | Function<? super T, ?> mapper) { |
|
|
171 | 166 | configurableOutput(name).from(task.map(mapper::apply)) |
|
|
172 | 167 | .builtBy(task); |
|
|
173 | 168 | } |
|
|
174 | 169 | |
|
|
175 | /** | |
|
|
176 | * Applies a Groovy closure to this source set, enabling DSL-style | |
|
|
177 | * configuration. | |
|
|
178 | */ | |
|
|
179 | @Override | |
|
|
180 | public GenericSourceSet configure(Closure configure) { | |
|
|
181 | Closures.apply(configure, this); | |
|
|
182 | return this; | |
|
|
183 | } | |
|
|
184 | ||
|
|
185 | 170 | private SourceDirectorySet createSourceDirectorySet(String name) { |
|
|
186 | 171 | return objects.sourceDirectorySet(name, name); |
|
|
187 | 172 | } |
|
|
188 | 173 | |
|
|
189 | 174 | private void requireDeclaredOutput(String outputName) { |
|
|
190 | 175 | if (!declaredOutputs.contains(outputName)) { |
|
|
191 | 176 | throw new InvalidUserDataException( |
|
|
192 | 177 | "Output '" + outputName + "' is not declared for source set '" + name + "'"); |
|
|
193 | 178 | } |
|
|
194 | 179 | } |
|
|
195 | 180 | |
|
|
196 | 181 | private Callable<List<? extends FileCollection>> outputsProvider() { |
|
|
197 | 182 | return () -> outputs.values().stream().toList(); |
|
|
198 | 183 | } |
|
|
199 | 184 | |
|
|
200 | 185 | private Callable<Set<File>> sourceDirectoriesProvider() { |
|
|
201 | 186 | return () -> sourceDirectorySets.stream() |
|
|
202 | 187 | .flatMap(x -> x.getSrcDirs().stream()) |
|
|
203 | 188 | .collect(Collectors.toSet()); |
|
|
204 | 189 | } |
|
|
205 | 190 | |
|
|
206 | 191 | } |
| @@ -1,81 +1,93 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.gradle.api.Action; |
|
|
4 | 4 | import org.implab.gradle.variants.core.Layer; |
|
|
5 | 5 | import org.implab.gradle.variants.core.Variant; |
|
|
6 | 6 | import org.implab.gradle.variants.core.VariantsView; |
|
|
7 | 7 | |
|
|
8 | 8 | /** |
|
|
9 | 9 | * Registry of symbolic source set names produced by sources projection. |
|
|
10 | 10 | * |
|
|
11 | 11 | * <p>Identity in this registry is the {@link GenericSourceSet} name assigned |
|
|
12 | 12 | * by the finalized |
|
|
13 | 13 | * {@link VariantSourcesExtension#namingPolicy(org.gradle.api.Action)}. |
|
|
14 | 14 | */ |
|
|
15 | 15 | public interface VariantSourcesContext { |
|
|
16 | 16 | |
|
|
17 | 17 | /** |
|
|
18 | 18 | * Finalized core model. |
|
|
19 | 19 | */ |
|
|
20 | 20 | VariantsView getVariants(); |
|
|
21 | 21 | |
|
|
22 | 22 | /** |
|
|
23 | 23 | * Derived compile-side view. |
|
|
24 | 24 | */ |
|
|
25 | 25 | CompileUnitsView getCompileUnits(); |
|
|
26 | 26 | |
|
|
27 | 27 | /** |
|
|
28 | 28 | * Derived role-side view. |
|
|
29 | 29 | */ |
|
|
30 | 30 | RoleProjectionsView getRoleProjections(); |
|
|
31 | 31 | |
|
|
32 | 32 | /** |
|
|
33 | 33 | * Lazy source set provider service. |
|
|
34 | 34 | */ |
|
|
35 | 35 | SourceSetMaterializer getSourceSets(); |
|
|
36 | 36 | |
|
|
37 | 37 | /** |
|
|
38 |
* Configures all |
|
|
|
38 | * Configures all source sets produced from the given layer. | |
|
|
39 | 39 | * |
|
|
40 | 40 | * The action is applied: |
|
|
41 | 41 | * - to already materialized source sets of this layer |
|
|
42 | 42 | * - to all future source sets of this layer |
|
|
43 | 43 | * |
|
|
44 | 44 | * <p>For future source sets, selector precedence and registration order are |
|
|
45 | 45 | * preserved by the materializer. |
|
|
46 | 46 | * |
|
|
47 | 47 | * <p>For already materialized source sets, behavior is governed by |
|
|
48 | 48 | * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}. |
|
|
49 | 49 | * In warn/allow modes the action is applied as a late imperative step and does |
|
|
50 | 50 | * not retroactively restore selector precedence. |
|
|
51 | 51 | * |
|
|
52 | 52 | * @throws org.gradle.api.InvalidUserDataException if the layer is not part of |
|
|
53 | 53 | * the finalized variant model |
|
|
54 | 54 | */ |
|
|
55 |
void configureLayer(Layer layer, Action<? super |
|
|
|
55 | void configureLayer(Layer layer, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
56 | 56 | |
|
|
57 | 57 | /** |
|
|
58 |
* Configures all |
|
|
|
58 | * Configures all source sets produced from the given variant. | |
|
|
59 | 59 | * |
|
|
60 | 60 | * <p>Late application semantics for already materialized source sets are |
|
|
61 | 61 | * governed by |
|
|
62 | 62 | * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}. |
|
|
63 | 63 | * |
|
|
64 | 64 | * @throws org.gradle.api.InvalidUserDataException if the variant is not part |
|
|
65 | 65 | * of the finalized variant model |
|
|
66 | 66 | */ |
|
|
67 |
void configureVariant(Variant variant, Action<? super |
|
|
|
67 | void configureVariant(Variant variant, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
68 | 68 | |
|
|
69 | 69 | /** |
|
|
70 |
* Configures the |
|
|
|
70 | * Configures the source set produced from the given compile unit. | |
|
|
71 | 71 | * |
|
|
72 | 72 | * <p>Late application semantics for already materialized source sets are |
|
|
73 | 73 | * governed by |
|
|
74 | 74 | * {@link VariantSourcesExtension#lateConfigurationPolicy(org.gradle.api.Action)}. |
|
|
75 | 75 | * |
|
|
76 | 76 | * @throws org.gradle.api.InvalidUserDataException if the compile unit is not |
|
|
77 | 77 | * part of the finalized variant model |
|
|
78 | 78 | */ |
|
|
79 |
void configureUnit(CompileUnit unit, Action<? super |
|
|
|
79 | void configureUnit(CompileUnit unit, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
80 | ||
|
|
81 | /** | |
|
|
82 | * Configures every source set materialized from the finalized compile-unit | |
|
|
83 | * model. | |
|
|
84 | * | |
|
|
85 | * <p>This selector is applied before variant-, layer-, and unit-specific | |
|
|
86 | * selectors. | |
|
|
87 | * | |
|
|
88 | * @throws org.gradle.api.InvalidUserDataException if late configuration is | |
|
|
89 | * rejected by the selected policy | |
|
|
90 | */ | |
|
|
91 | void configureEach(Action<? super CompileUnitSourceSetSpec> action); | |
|
|
80 | 92 | |
|
|
81 | 93 | } |
| @@ -1,170 +1,184 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources; |
|
|
2 | 2 | |
|
|
3 | 3 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
4 | 4 | import org.gradle.api.Action; |
|
|
5 | 5 | import org.implab.gradle.common.core.lang.Closures; |
|
|
6 | 6 | import groovy.lang.Closure; |
|
|
7 | 7 | |
|
|
8 | 8 | @NonNullByDefault |
|
|
9 | 9 | public interface VariantSourcesExtension { |
|
|
10 | 10 | |
|
|
11 | 11 | /** |
|
|
12 | 12 | * Selects how selector rules behave when they target an already materialized |
|
|
13 |
* |
|
|
|
13 | * source set. | |
|
|
14 | 14 | * |
|
|
15 | 15 | * <p>This policy is single-valued: |
|
|
16 | 16 | * <ul> |
|
|
17 | 17 | * <li>it must be selected before the first selector rule is registered via |
|
|
18 | 18 | * {@link #variant(String, Action)}, {@link #layer(String, Action)} or |
|
|
19 | 19 | * {@link #unit(String, String, Action)};</li> |
|
|
20 | 20 | * <li>it must be selected before the source context becomes observable via |
|
|
21 | 21 | * {@link #whenAvailable(Action)};</li> |
|
|
22 | 22 | * <li>once selected or once source context creation begins, it cannot |
|
|
23 | 23 | * be changed later;</li> |
|
|
24 | 24 | * <li>the policy controls both diagnostics and late-application semantics.</li> |
|
|
25 | 25 | * </ul> |
|
|
26 | 26 | * |
|
|
27 | 27 | * <p>If not selected explicitly, the default is |
|
|
28 | 28 | * {@link LateConfigurationPolicySpec#failOnLateConfiguration()}. |
|
|
29 | 29 | */ |
|
|
30 | 30 | void lateConfigurationPolicy(Action<? super LateConfigurationPolicySpec> action); |
|
|
31 | 31 | |
|
|
32 | 32 | default void lateConfigurationPolicy(Closure<?> closure) { |
|
|
33 | 33 | lateConfigurationPolicy(Closures.action(closure)); |
|
|
34 | 34 | } |
|
|
35 | 35 | |
|
|
36 | 36 | /** |
|
|
37 | 37 | * Selects how compile-unit name collisions are handled when the source |
|
|
38 | 38 | * context is created from the finalized variant model. |
|
|
39 | 39 | * |
|
|
40 | 40 | * <p>This policy is single-valued: |
|
|
41 | 41 | * <ul> |
|
|
42 | 42 | * <li>it must be selected before the |
|
|
43 | 43 | * {@link VariantSourcesContext} becomes observable through |
|
|
44 | 44 | * {@link #whenAvailable(Action)};</li> |
|
|
45 | 45 | * <li>once the context is being created, the policy is fixed and cannot be |
|
|
46 | 46 | * changed later;</li> |
|
|
47 | 47 | * <li>the policy governs validation of compile-unit names produced by the |
|
|
48 | 48 | * source-set materializer.</li> |
|
|
49 | 49 | * </ul> |
|
|
50 | 50 | * |
|
|
51 | 51 | * <p>If not selected explicitly, the default is |
|
|
52 | 52 | * {@link NamingPolicySpec#failOnNameCollision()}. |
|
|
53 | 53 | */ |
|
|
54 | 54 | void namingPolicy(Action<? super NamingPolicySpec> action); |
|
|
55 | 55 | |
|
|
56 | 56 | default void namingPolicy(Closure<?> closure) { |
|
|
57 | 57 | namingPolicy(Closures.action(closure)); |
|
|
58 | 58 | } |
|
|
59 | 59 | |
|
|
60 | 60 | /** |
|
|
61 | 61 | * Registers a selector rule for all compile units of the given layer. |
|
|
62 | 62 | * |
|
|
63 | 63 | * <p>Registering the first selector rule fixes the selected |
|
|
64 | 64 | * {@link #lateConfigurationPolicy(Action)} for the remaining extension |
|
|
65 | 65 | * lifecycle. |
|
|
66 | 66 | */ |
|
|
67 |
void layer(String layerName, Action<? super |
|
|
|
67 | void layer(String layerName, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
68 | 68 | |
|
|
69 | 69 | default void layer(String layerName, Closure<?> closure) { |
|
|
70 | 70 | layer(layerName, Closures.action(closure)); |
|
|
71 | 71 | } |
|
|
72 | 72 | |
|
|
73 | 73 | /** |
|
|
74 | 74 | * Registers a selector rule for all compile units of the given variant. |
|
|
75 | 75 | * |
|
|
76 | 76 | * <p>Registering the first selector rule fixes the selected |
|
|
77 | 77 | * {@link #lateConfigurationPolicy(Action)} for the remaining extension |
|
|
78 | 78 | * lifecycle. |
|
|
79 | 79 | */ |
|
|
80 |
void variant(String variantName, Action<? super |
|
|
|
80 | void variant(String variantName, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
81 | 81 | |
|
|
82 | 82 | default void variant(String variantName, Closure<?> closure) { |
|
|
83 | 83 | variant(variantName, Closures.action(closure)); |
|
|
84 | 84 | } |
|
|
85 | 85 | |
|
|
86 | 86 | /** |
|
|
87 | 87 | * Registers a selector rule for one exact compile unit. |
|
|
88 | 88 | * |
|
|
89 | 89 | * <p>Registering the first selector rule fixes the selected |
|
|
90 | 90 | * {@link #lateConfigurationPolicy(Action)} for the remaining extension |
|
|
91 | 91 | * lifecycle. |
|
|
92 | 92 | */ |
|
|
93 |
void unit(String variantName, String layerName, Action<? super |
|
|
|
93 | void unit(String variantName, String layerName, Action<? super CompileUnitSourceSetSpec> action); | |
|
|
94 | 94 | |
|
|
95 | 95 | default void unit(String variantName, String layerName, Closure<?> closure) { |
|
|
96 | 96 | unit(variantName, layerName, Closures.action(closure)); |
|
|
97 | 97 | } |
|
|
98 | 98 | |
|
|
99 | 99 | /** |
|
|
100 | * Registers a selector rule for every materialized compile-unit source set. | |
|
|
101 | * | |
|
|
102 | * <p>Registering the first selector rule fixes the selected | |
|
|
103 | * {@link #lateConfigurationPolicy(Action)} for the remaining extension | |
|
|
104 | * lifecycle. This selector is applied before variant-, layer-, and unit-specific | |
|
|
105 | * selectors. | |
|
|
106 | */ | |
|
|
107 | void configureEach(Action<? super CompileUnitSourceSetSpec> action); | |
|
|
108 | ||
|
|
109 | default void configureEach(Closure<?> closure) { | |
|
|
110 | configureEach(Closures.action(closure)); | |
|
|
111 | } | |
|
|
112 | ||
|
|
113 | /** | |
|
|
100 | 114 | * Invoked when the variants-derived source context becomes available. |
|
|
101 | 115 | * |
|
|
102 | 116 | * Replayable: |
|
|
103 | 117 | * <ul> |
|
|
104 | 118 | * <li>if called before variants finalization, action is queued |
|
|
105 | 119 | * <li>if called after variants finalization, action is invoked immediately |
|
|
106 | 120 | * </ul> |
|
|
107 | 121 | * |
|
|
108 | 122 | * <p>By the time this callback becomes observable, compile-unit naming |
|
|
109 | 123 | * policy has already been fixed and symbolic source-set names for finalized |
|
|
110 | 124 | * compile units are determined. |
|
|
111 | 125 | */ |
|
|
112 | 126 | void whenAvailable(Action<? super VariantSourcesContext> action); |
|
|
113 | 127 | |
|
|
114 | 128 | default void whenAvailable(Closure<?> closure) { |
|
|
115 | 129 | whenAvailable(Closures.action(closure)); |
|
|
116 | 130 | } |
|
|
117 | 131 | |
|
|
118 | 132 | |
|
|
119 | 133 | /** |
|
|
120 | 134 | * Imperative selector for the late-configuration mode. |
|
|
121 | 135 | * |
|
|
122 | 136 | * <p>Exactly one mode is expected to be chosen for the extension lifecycle. |
|
|
123 | 137 | */ |
|
|
124 | 138 | interface LateConfigurationPolicySpec { |
|
|
125 | 139 | /** |
|
|
126 | 140 | * Rejects selector registration if it targets any already materialized |
|
|
127 | 141 | * source set. |
|
|
128 | 142 | */ |
|
|
129 | 143 | void failOnLateConfiguration(); |
|
|
130 | 144 | |
|
|
131 | 145 | /** |
|
|
132 | 146 | * Allows late selector registration, but emits a warning when it targets an |
|
|
133 | 147 | * already materialized source set. |
|
|
134 | 148 | * |
|
|
135 | 149 | * <p>For such targets, selector precedence is not re-established |
|
|
136 | 150 | * retroactively. The action is applied as a late imperative step, after the |
|
|
137 | 151 | * state already produced at the materialization moment. |
|
|
138 | 152 | */ |
|
|
139 | 153 | void warnOnLateConfiguration(); |
|
|
140 | 154 | |
|
|
141 | 155 | /** |
|
|
142 | 156 | * Allows late selector registration without a warning when it targets an |
|
|
143 | 157 | * already materialized source set. |
|
|
144 | 158 | * |
|
|
145 | 159 | * <p>For such targets, selector precedence is not re-established |
|
|
146 | 160 | * retroactively. The action is applied as a late imperative step, after the |
|
|
147 | 161 | * state already produced at the materialization moment. |
|
|
148 | 162 | */ |
|
|
149 | 163 | void allowLateConfiguration(); |
|
|
150 | 164 | } |
|
|
151 | 165 | |
|
|
152 | 166 | interface NamingPolicySpec { |
|
|
153 | 167 | /** |
|
|
154 | 168 | * Rejects finalized compile-unit models that project the same source-set |
|
|
155 | 169 | * name for different compile units. |
|
|
156 | 170 | */ |
|
|
157 | 171 | void failOnNameCollision(); |
|
|
158 | 172 | |
|
|
159 | 173 | /** |
|
|
160 | 174 | * Resolves name collisions deterministically for the finalized |
|
|
161 | 175 | * compile-unit model. |
|
|
162 | 176 | * |
|
|
163 | 177 | * <p>Conflicting compile units are ordered canonically by |
|
|
164 | 178 | * {@code (variant.name, layer.name)}. The first unit keeps the base |
|
|
165 | 179 | * projected name, and each next unit receives a numeric suffix |
|
|
166 | 180 | * ({@code 2}, {@code 3}, ...). |
|
|
167 | 181 | */ |
|
|
168 | 182 | void resolveNameCollision(); |
|
|
169 | 183 | } |
|
|
170 | 184 | } |
| @@ -1,107 +1,114 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.util.HashMap; |
|
|
4 | 4 | import java.util.Map; |
|
|
5 | 5 | import java.util.Objects; |
|
|
6 | 6 | import org.gradle.api.Action; |
|
|
7 | 7 | import org.gradle.api.NamedDomainObjectProvider; |
|
|
8 | 8 | import org.implab.gradle.variants.core.Layer; |
|
|
9 | 9 | import org.implab.gradle.variants.core.Variant; |
|
|
10 | 10 | import org.implab.gradle.variants.core.VariantsView; |
|
|
11 | 11 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
12 | import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec; | |
|
|
12 | 13 | import org.implab.gradle.variants.sources.CompileUnitsView; |
|
|
13 | 14 | import org.implab.gradle.variants.sources.GenericSourceSet; |
|
|
14 | 15 | import org.implab.gradle.variants.sources.RoleProjectionsView; |
|
|
15 | 16 | import org.implab.gradle.variants.sources.SourceSetMaterializer; |
|
|
16 | 17 | import org.implab.gradle.variants.sources.VariantSourcesContext; |
|
|
17 | 18 | |
|
|
18 | 19 | public class DefaultVariantSourcesContext implements VariantSourcesContext { |
|
|
19 | 20 | private final VariantsView variantsView; |
|
|
20 | 21 | private final CompileUnitsView compileUnitsView; |
|
|
21 | 22 | private final RoleProjectionsView roleProjectionsView; |
|
|
22 | 23 | private final SourceSetMaterializer sourceSetMaterializer; |
|
|
23 | 24 | private final SourceSetRegistry sourceSetRegistry; |
|
|
24 | 25 | private final CompileUnitNamer compileUnitNamer; |
|
|
25 | 26 | private final SourceSetConfigurationRegistry sourceSetConfigurationRegistry; |
|
|
26 | 27 | |
|
|
27 | 28 | public DefaultVariantSourcesContext( |
|
|
28 | 29 | VariantsView variantsView, |
|
|
29 | 30 | CompileUnitsView compileUnitsView, |
|
|
30 | 31 | RoleProjectionsView roleProjectionsView, |
|
|
31 | 32 | CompileUnitNamer compileUnitNamer, |
|
|
32 | 33 | SourceSetRegistry sourceSetRegistry, |
|
|
33 | 34 | SourceSetConfigurationRegistry sourceSetConfigurationRegistry) { |
|
|
34 | 35 | this.variantsView = variantsView; |
|
|
35 | 36 | this.compileUnitNamer = compileUnitNamer; |
|
|
36 | 37 | this.compileUnitsView = compileUnitsView; |
|
|
37 | 38 | this.roleProjectionsView = roleProjectionsView; |
|
|
38 | 39 | this.sourceSetRegistry = sourceSetRegistry; |
|
|
39 | 40 | this.sourceSetConfigurationRegistry = sourceSetConfigurationRegistry; |
|
|
40 | 41 | |
|
|
41 | 42 | sourceSetMaterializer = new LocalSourceSetMaterializer(); |
|
|
42 | 43 | } |
|
|
43 | 44 | |
|
|
44 | 45 | @Override |
|
|
45 | 46 | public VariantsView getVariants() { |
|
|
46 | 47 | return variantsView; |
|
|
47 | 48 | } |
|
|
48 | 49 | |
|
|
49 | 50 | @Override |
|
|
50 | 51 | public CompileUnitsView getCompileUnits() { |
|
|
51 | 52 | return compileUnitsView; |
|
|
52 | 53 | } |
|
|
53 | 54 | |
|
|
54 | 55 | @Override |
|
|
55 | 56 | public RoleProjectionsView getRoleProjections() { |
|
|
56 | 57 | return roleProjectionsView; |
|
|
57 | 58 | } |
|
|
58 | 59 | |
|
|
59 | 60 | @Override |
|
|
60 | 61 | public SourceSetMaterializer getSourceSets() { |
|
|
61 | 62 | return sourceSetMaterializer; |
|
|
62 | 63 | } |
|
|
63 | 64 | |
|
|
64 | 65 | @Override |
|
|
65 |
public void configureLayer(Layer layer, Action<? super |
|
|
|
66 | public void configureLayer(Layer layer, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
66 | 67 | variantsView.assertLayer(layer); |
|
|
67 | 68 | Objects.requireNonNull(action, "action can't be null"); |
|
|
68 | 69 | sourceSetConfigurationRegistry.addLayerAction(layer, action); |
|
|
69 | 70 | } |
|
|
70 | 71 | |
|
|
71 | 72 | @Override |
|
|
72 |
public void configureVariant(Variant variant, Action<? super |
|
|
|
73 | public void configureVariant(Variant variant, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
73 | 74 | variantsView.assertVariant(variant); |
|
|
74 | 75 | Objects.requireNonNull(action, "action can't be null"); |
|
|
75 | 76 | sourceSetConfigurationRegistry.addVariantAction(variant, action); |
|
|
76 | 77 | } |
|
|
77 | 78 | |
|
|
78 | 79 | @Override |
|
|
79 |
public void configureUnit(CompileUnit unit, Action<? super |
|
|
|
80 | public void configureUnit(CompileUnit unit, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
80 | 81 | assertCompileUnit(unit); |
|
|
81 | 82 | Objects.requireNonNull(action, "action can't be null"); |
|
|
82 | 83 | sourceSetConfigurationRegistry.addCompileUnitAction(unit, action); |
|
|
83 | 84 | } |
|
|
84 | 85 | |
|
|
86 | @Override | |
|
|
87 | public void configureEach(Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
88 | Objects.requireNonNull(action, "action can't be null"); | |
|
|
89 | sourceSetConfigurationRegistry.addAction(action); | |
|
|
90 | } | |
|
|
91 | ||
|
|
85 | 92 | private void assertCompileUnit(CompileUnit unit) { |
|
|
86 | 93 | Objects.requireNonNull(unit, "unit can't be null"); |
|
|
87 | 94 | variantsView.assertVariant(unit.variant()); |
|
|
88 | 95 | variantsView.assertLayer(unit.layer()); |
|
|
89 | 96 | compileUnitsView.requireUnit(unit.variant(), unit.layer()); |
|
|
90 | 97 | } |
|
|
91 | 98 | |
|
|
92 | 99 | class LocalSourceSetMaterializer implements SourceSetMaterializer { |
|
|
93 | 100 | private final Map<CompileUnit, NamedDomainObjectProvider<GenericSourceSet>> registeredSources = new HashMap<>(); |
|
|
94 | 101 | |
|
|
95 | 102 | @Override |
|
|
96 | 103 | public NamedDomainObjectProvider<GenericSourceSet> getSourceSet(CompileUnit unit) { |
|
|
97 | 104 | assertCompileUnit(unit); |
|
|
98 | 105 | return registeredSources.computeIfAbsent(unit, k -> { |
|
|
99 | 106 | var sourcesName = compileUnitNamer.resolveName(unit); |
|
|
100 | 107 | sourceSetRegistry.whenMaterialized(sourcesName, |
|
|
101 | 108 | sourceSet -> sourceSetConfigurationRegistry.applyConfiguration(unit, sourceSet)); |
|
|
102 | 109 | return sourceSetRegistry.sourceSets().register(sourcesName); |
|
|
103 | 110 | }); |
|
|
104 | 111 | } |
|
|
105 | 112 | |
|
|
106 | 113 | } |
|
|
107 | 114 | } |
| @@ -1,95 +1,109 | |||
|
|
1 | 1 | package org.implab.gradle.variants.sources.internal; |
|
|
2 | 2 | |
|
|
3 | 3 | import java.text.MessageFormat; |
|
|
4 | 4 | import java.util.LinkedHashMap; |
|
|
5 | 5 | import java.util.List; |
|
|
6 | 6 | import java.util.Map; |
|
|
7 | 7 | import java.util.function.Supplier; |
|
|
8 | 8 | import java.util.stream.Collectors; |
|
|
9 | 9 | |
|
|
10 | 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
11 | 11 | import org.gradle.api.Action; |
|
|
12 | 12 | import org.gradle.api.InvalidUserDataException; |
|
|
13 | 13 | import org.gradle.api.Named; |
|
|
14 | 14 | import org.gradle.api.logging.Logger; |
|
|
15 | 15 | import org.gradle.api.logging.Logging; |
|
|
16 | 16 | import org.implab.gradle.common.core.lang.ReplayableQueue; |
|
|
17 | 17 | import org.implab.gradle.variants.core.Layer; |
|
|
18 | 18 | import org.implab.gradle.variants.core.Variant; |
|
|
19 | 19 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
20 | import org.implab.gradle.variants.sources.CompileUnitSourceSetSpec; | |
|
|
20 | 21 | import org.implab.gradle.variants.sources.GenericSourceSet; |
|
|
21 | 22 | |
|
|
22 | 23 | @NonNullByDefault |
|
|
23 | 24 | public class SourceSetConfigurationRegistry { |
|
|
24 | 25 | private static final Logger logger = Logging.getLogger(SourceSetConfigurationRegistry.class); |
|
|
25 | 26 | |
|
|
26 | private final Map<Layer, ReplayableQueue<GenericSourceSet>> sourcesByLayer = new LinkedHashMap<>(); | |
|
|
27 |
private final Map< |
|
|
|
28 |
private final Map< |
|
|
|
27 | private final ReplayableQueue<CompileUnitSourceSetSpec> sources = new ReplayableQueue<>(); | |
|
|
28 | private final Map<Layer, ReplayableQueue<CompileUnitSourceSetSpec>> sourcesByLayer = new LinkedHashMap<>(); | |
|
|
29 | private final Map<Variant, ReplayableQueue<CompileUnitSourceSetSpec>> sourcesByVariant = new LinkedHashMap<>(); | |
|
|
30 | private final Map<CompileUnit, ReplayableQueue<CompileUnitSourceSetSpec>> sourcesByUnit = new LinkedHashMap<>(); | |
|
|
29 | 31 | |
|
|
30 | 32 | private final Supplier<LateConfigurationMode> lateConfigurationMode; |
|
|
31 | 33 | |
|
|
32 | 34 | public SourceSetConfigurationRegistry(Supplier<LateConfigurationMode> lateConfigurationMode) { |
|
|
33 | 35 | this.lateConfigurationMode = lateConfigurationMode; |
|
|
34 | 36 | } |
|
|
35 | 37 | |
|
|
36 |
public void add |
|
|
|
38 | public void addAction(Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
39 | addToActions( | |
|
|
40 | sources, | |
|
|
41 | action, | |
|
|
42 | "Source sets already materialized"); | |
|
|
43 | } | |
|
|
44 | ||
|
|
45 | public void addLayerAction(Layer layer, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
37 | 46 | addToActions( |
|
|
38 | 47 | sourcesByLayer.computeIfAbsent(layer, key -> new ReplayableQueue<>()), |
|
|
39 | 48 | action, |
|
|
40 | 49 | MessageFormat.format( |
|
|
41 | 50 | "Source sets for [layer={0}] layer already materialized", |
|
|
42 | 51 | layer.getName())); |
|
|
43 | 52 | } |
|
|
44 | 53 | |
|
|
45 |
public void addVariantAction(Variant variant, Action<? super |
|
|
|
54 | public void addVariantAction(Variant variant, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
46 | 55 | addToActions( |
|
|
47 | 56 | sourcesByVariant.computeIfAbsent(variant, key -> new ReplayableQueue<>()), |
|
|
48 | 57 | action, |
|
|
49 | 58 | MessageFormat.format( |
|
|
50 | 59 | "Source sets for [variant={0}] variant already materialized", |
|
|
51 | 60 | variant.getName())); |
|
|
52 | 61 | |
|
|
53 | 62 | } |
|
|
54 | 63 | |
|
|
55 |
public void addCompileUnitAction(CompileUnit unit, Action<? super |
|
|
|
64 | public void addCompileUnitAction(CompileUnit unit, Action<? super CompileUnitSourceSetSpec> action) { | |
|
|
56 | 65 | addToActions( |
|
|
57 | 66 | sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()), |
|
|
58 | 67 | action, |
|
|
59 | 68 | MessageFormat.format( |
|
|
60 | 69 | "Source set for [variant={0}, layer={1}] already materialized", |
|
|
61 | 70 | unit.variant().getName(), |
|
|
62 | 71 | unit.layer().getName())); |
|
|
63 | 72 | } |
|
|
64 | 73 | |
|
|
65 | 74 | private void addToActions( |
|
|
66 |
ReplayableQueue< |
|
|
|
67 |
Action<? super |
|
|
|
75 | ReplayableQueue<CompileUnitSourceSetSpec> actions, | |
|
|
76 | Action<? super CompileUnitSourceSetSpec> action, | |
|
|
68 | 77 | String assertMessage) { |
|
|
69 | 78 | assertLazyConfiguration(actions.values(), assertMessage); |
|
|
70 | 79 | actions.forEach(action::execute); |
|
|
71 | 80 | } |
|
|
72 | 81 | |
|
|
73 |
void assertLazyConfiguration(List< |
|
|
|
82 | void assertLazyConfiguration(List<CompileUnitSourceSetSpec> sets, String message) { | |
|
|
74 | 83 | if (sets.size() == 0) |
|
|
75 | 84 | return; |
|
|
76 | 85 | |
|
|
77 | var names = sets.stream().map(Named::getName).collect(Collectors.joining(", ")); | |
|
|
86 | var names = sets.stream() | |
|
|
87 | .map(CompileUnitSourceSetSpec::getSourceSet) | |
|
|
88 | .map(Named::getName) | |
|
|
89 | .collect(Collectors.joining(", ")); | |
|
|
78 | 90 | |
|
|
79 | 91 | switch (lateConfigurationMode.get()) { |
|
|
80 | 92 | case FAIL: |
|
|
81 | 93 | throw new InvalidUserDataException(message + " [" + names + "]"); |
|
|
82 | 94 | case WARN: |
|
|
83 | 95 | logger.warn(message + "\n\t" + names); |
|
|
84 | 96 | break; |
|
|
85 | 97 | default: |
|
|
86 | 98 | break; |
|
|
87 | 99 | } |
|
|
88 | 100 | } |
|
|
89 | 101 | |
|
|
90 | 102 | public void applyConfiguration(CompileUnit unit, GenericSourceSet sourceSet) { |
|
|
91 | sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(sourceSet); | |
|
|
92 | sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(sourceSet); | |
|
|
93 |
sourcesBy |
|
|
|
103 | var spec = new DefaultCompileUnitSourceSetSpec(unit, sourceSet); | |
|
|
104 | sources.add(spec); | |
|
|
105 | sourcesByVariant.computeIfAbsent(unit.variant(), key -> new ReplayableQueue<>()).add(spec); | |
|
|
106 | sourcesByLayer.computeIfAbsent(unit.layer(), key -> new ReplayableQueue<>()).add(spec); | |
|
|
107 | sourcesByUnit.computeIfAbsent(unit, key -> new ReplayableQueue<>()).add(spec); | |
|
|
94 | 108 | } |
|
|
95 | 109 | } |
| @@ -1,691 +1,703 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.gradle.testkit.runner.BuildResult; |
|
|
6 | 6 | import org.gradle.testkit.runner.TaskOutcome; |
|
|
7 | 7 | import org.junit.jupiter.api.Test; |
|
|
8 | 8 | |
|
|
9 | 9 | class VariantArtifactsPluginFunctionalTest extends AbstractFunctionalTest { |
|
|
10 | 10 | |
|
|
11 | 11 | @Test |
|
|
12 | 12 | void gradleReferenceLazyOutgoingConfigurationAllowsSecondaryArtifactSelection() throws Exception { |
|
|
13 | 13 | writeFile("settings.gradle", """ |
|
|
14 | 14 | rootProject.name = 'gradle-reference-outgoing-resolution' |
|
|
15 | 15 | include 'producer', 'consumer' |
|
|
16 | 16 | """); |
|
|
17 | 17 | writeFile("producer/inputs/typesPackage", "types\n"); |
|
|
18 | 18 | writeFile("producer/inputs/js", "js\n"); |
|
|
19 | 19 | writeBuildFile(""" |
|
|
20 | 20 | import org.gradle.api.attributes.Attribute |
|
|
21 | 21 | |
|
|
22 | 22 | def variantAttr = Attribute.of('test.variant', String) |
|
|
23 | 23 | def slotAttr = Attribute.of('test.slot', String) |
|
|
24 | 24 | |
|
|
25 | 25 | project(':producer') { |
|
|
26 | 26 | def browserElements = configurations.consumable('browserElements') |
|
|
27 | 27 | |
|
|
28 | 28 | println('reference: registered browserElements provider') |
|
|
29 | 29 | |
|
|
30 | 30 | browserElements.configure { configuration -> |
|
|
31 | 31 | println('reference: configuring browserElements') |
|
|
32 | 32 | |
|
|
33 | 33 | configuration.attributes.attribute(variantAttr, 'browser') |
|
|
34 | 34 | configuration.outgoing.attributes.attribute(slotAttr, 'typesPackage') |
|
|
35 | 35 | configuration.outgoing.artifact(layout.projectDirectory.file('inputs/typesPackage')) |
|
|
36 | 36 | |
|
|
37 | 37 | configuration.outgoing.variants.create('js') { secondary -> |
|
|
38 | 38 | println('reference: creating js outgoing variant') |
|
|
39 | 39 | |
|
|
40 | 40 | secondary.attributes.attribute(slotAttr, 'js') |
|
|
41 | 41 | secondary.artifact(layout.projectDirectory.file('inputs/js')) |
|
|
42 | 42 | } |
|
|
43 | 43 | } |
|
|
44 | 44 | } |
|
|
45 | 45 | |
|
|
46 | 46 | project(':consumer') { |
|
|
47 | 47 | configurations { |
|
|
48 | 48 | compileView { |
|
|
49 | 49 | canBeResolved = true |
|
|
50 | 50 | canBeConsumed = false |
|
|
51 | 51 | canBeDeclared = true |
|
|
52 | 52 | attributes { |
|
|
53 | 53 | attribute(variantAttr, 'browser') |
|
|
54 | 54 | attribute(slotAttr, 'typesPackage') |
|
|
55 | 55 | } |
|
|
56 | 56 | } |
|
|
57 | 57 | } |
|
|
58 | 58 | |
|
|
59 | 59 | dependencies { |
|
|
60 | 60 | compileView project(':producer') |
|
|
61 | 61 | } |
|
|
62 | 62 | |
|
|
63 | 63 | tasks.register('probe') { |
|
|
64 | 64 | doLast { |
|
|
65 | 65 | println('reference: resolving primary files') |
|
|
66 | 66 | |
|
|
67 | 67 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') |
|
|
68 | 68 | |
|
|
69 | 69 | println('reference: resolving secondary files') |
|
|
70 | 70 | |
|
|
71 | 71 | def jsFiles = configurations.compileView.incoming.artifactView { |
|
|
72 | 72 | attributes { |
|
|
73 | 73 | attribute(slotAttr, 'js') |
|
|
74 | 74 | } |
|
|
75 | 75 | }.files.files.collect { it.name }.sort().join(',') |
|
|
76 | 76 | |
|
|
77 | 77 | println('compileFiles=' + compileFiles) |
|
|
78 | 78 | println('jsFiles=' + jsFiles) |
|
|
79 | 79 | } |
|
|
80 | 80 | } |
|
|
81 | 81 | } |
|
|
82 | 82 | """); |
|
|
83 | 83 | |
|
|
84 | 84 | BuildResult result = runner(":consumer:probe").build(); |
|
|
85 | 85 | var output = result.getOutput(); |
|
|
86 | 86 | var registered = output.indexOf("reference: registered browserElements provider"); |
|
|
87 | 87 | var resolvingPrimary = output.indexOf("reference: resolving primary files"); |
|
|
88 | 88 | var configuring = output.indexOf("reference: configuring browserElements"); |
|
|
89 | 89 | var creatingSecondary = output.indexOf("reference: creating js outgoing variant"); |
|
|
90 | 90 | var resolvingSecondary = output.indexOf("reference: resolving secondary files"); |
|
|
91 | 91 | |
|
|
92 | 92 | assertTrue(registered >= 0); |
|
|
93 | 93 | assertTrue(resolvingPrimary >= 0); |
|
|
94 | 94 | assertTrue(configuring >= 0); |
|
|
95 | 95 | assertTrue(creatingSecondary >= 0); |
|
|
96 | 96 | assertTrue(resolvingSecondary >= 0); |
|
|
97 | 97 | assertTrue(registered < resolvingPrimary); |
|
|
98 | 98 | assertTrue(resolvingPrimary < configuring); |
|
|
99 | 99 | assertTrue(configuring < creatingSecondary); |
|
|
100 | 100 | assertTrue(creatingSecondary < resolvingSecondary); |
|
|
101 | 101 | assertTrue(output.contains("compileFiles=typesPackage")); |
|
|
102 | 102 | assertTrue(output.contains("jsFiles=js")); |
|
|
103 | 103 | } |
|
|
104 | 104 | |
|
|
105 | 105 | @Test |
|
|
106 | 106 | void gradleReferenceRegisteredSecondaryArtifactVariantIsNotRealizedBeforeResolution() throws Exception { |
|
|
107 | 107 | // Gradle issue: https://github.com/gradle/gradle/issues/27441 |
|
|
108 | 108 | // Registered outgoing artifact variants are not realized before dependency resolution. |
|
|
109 | 109 | writeFile("settings.gradle", """ |
|
|
110 | 110 | rootProject.name = 'gradle-reference-registered-secondary-variant' |
|
|
111 | 111 | include 'producer', 'consumer' |
|
|
112 | 112 | """); |
|
|
113 | 113 | writeFile("producer/inputs/typesPackage", "types\n"); |
|
|
114 | 114 | writeFile("producer/inputs/js", "js\n"); |
|
|
115 | 115 | writeFile("build.gradle", """ |
|
|
116 | 116 | import org.gradle.api.attributes.Attribute |
|
|
117 | 117 | |
|
|
118 | 118 | def variantAttr = Attribute.of('test.variant', String) |
|
|
119 | 119 | def slotAttr = Attribute.of('test.slot', String) |
|
|
120 | 120 | |
|
|
121 | 121 | project(':producer') { |
|
|
122 | 122 | def browserElements = configurations.consumable('browserElements') |
|
|
123 | 123 | |
|
|
124 | 124 | browserElements.configure { configuration -> |
|
|
125 | 125 | configuration.attributes.attribute(variantAttr, 'browser') |
|
|
126 | 126 | configuration.outgoing.attributes.attribute(slotAttr, 'typesPackage') |
|
|
127 | 127 | configuration.outgoing.artifact(layout.projectDirectory.file('inputs/typesPackage')) |
|
|
128 | 128 | |
|
|
129 | 129 | configuration.outgoing.variants.register('js') { secondary -> |
|
|
130 | 130 | secondary.attributes.attribute(slotAttr, 'js') |
|
|
131 | 131 | secondary.artifact(layout.projectDirectory.file('inputs/js')) |
|
|
132 | 132 | } |
|
|
133 | 133 | } |
|
|
134 | 134 | } |
|
|
135 | 135 | |
|
|
136 | 136 | project(':consumer') { |
|
|
137 | 137 | configurations { |
|
|
138 | 138 | compileView { |
|
|
139 | 139 | canBeResolved = true |
|
|
140 | 140 | canBeConsumed = false |
|
|
141 | 141 | canBeDeclared = true |
|
|
142 | 142 | attributes { |
|
|
143 | 143 | attribute(variantAttr, 'browser') |
|
|
144 | 144 | attribute(slotAttr, 'typesPackage') |
|
|
145 | 145 | } |
|
|
146 | 146 | } |
|
|
147 | 147 | } |
|
|
148 | 148 | |
|
|
149 | 149 | dependencies { |
|
|
150 | 150 | compileView project(':producer') |
|
|
151 | 151 | } |
|
|
152 | 152 | |
|
|
153 | 153 | tasks.register('probe') { |
|
|
154 | 154 | doLast { |
|
|
155 | 155 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') |
|
|
156 | 156 | def jsFiles = configurations.compileView.incoming.artifactView { |
|
|
157 | 157 | attributes { |
|
|
158 | 158 | attribute(slotAttr, 'js') |
|
|
159 | 159 | } |
|
|
160 | 160 | }.files.files.collect { it.name }.sort().join(',') |
|
|
161 | 161 | |
|
|
162 | 162 | println('compileFiles=' + compileFiles) |
|
|
163 | 163 | println('jsFiles=' + jsFiles) |
|
|
164 | 164 | } |
|
|
165 | 165 | } |
|
|
166 | 166 | } |
|
|
167 | 167 | """); |
|
|
168 | 168 | |
|
|
169 | 169 | assertBuildFails("Cannot create variant 'js' after dependency configuration ':producer:browserElements' has been resolved", |
|
|
170 | 170 | ":consumer:probe"); |
|
|
171 | 171 | } |
|
|
172 | 172 | |
|
|
173 | 173 | @Test |
|
|
174 | 174 | void materializesPrimaryAndSecondarySlotsAndInvokesOutgoingHooks() throws Exception { |
|
|
175 | 175 | writeSettings("variant-artifacts-slots"); |
|
|
176 | 176 | writeFile("inputs/base.js", "console.log('base')\n"); |
|
|
177 | 177 | writeFile("inputs/amd.js", "console.log('amd')\n"); |
|
|
178 | 178 | writeFile("inputs/mainJs.txt", "mainJs marker\n"); |
|
|
179 | 179 | writeFile("inputs/amdJs.txt", "amdJs marker\n"); |
|
|
180 | 180 | writeBuildFile(""" |
|
|
181 | 181 | import org.gradle.api.attributes.Attribute |
|
|
182 | 182 | |
|
|
183 | 183 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
184 | 184 | |
|
|
185 | 185 | def variantAttr = Attribute.of('test.variant', String) |
|
|
186 | 186 | def slotAttr = Attribute.of('test.slot', String) |
|
|
187 | 187 | |
|
|
188 | 188 | variants.layers.create('mainBase') |
|
|
189 | 189 | variants.layers.create('mainAmd') |
|
|
190 | 190 | variants.roles.create('main') |
|
|
191 | 191 | variants.roles.create('test') |
|
|
192 | 192 | variants.variant('browser') { |
|
|
193 | 193 | role('main') { |
|
|
194 | 194 | layers('mainBase', 'mainAmd') |
|
|
195 | 195 | } |
|
|
196 | 196 | } |
|
|
197 | 197 | |
|
|
198 | 198 | variantSources { |
|
|
199 | 199 | layer('mainBase') { |
|
|
200 |
|
|
|
|
201 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) | |
|
|
200 | sourceSet { | |
|
|
201 | declareOutputs('js') | |
|
|
202 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) | |
|
|
203 | } | |
|
|
202 | 204 | } |
|
|
203 | 205 | layer('mainAmd') { |
|
|
204 |
|
|
|
|
205 | registerOutput('js', layout.projectDirectory.file('inputs/amd.js')) | |
|
|
206 | sourceSet { | |
|
|
207 | declareOutputs('js') | |
|
|
208 | registerOutput('js', layout.projectDirectory.file('inputs/amd.js')) | |
|
|
209 | } | |
|
|
206 | 210 | } |
|
|
207 | 211 | } |
|
|
208 | 212 | |
|
|
209 | 213 | variantArtifacts { |
|
|
210 | 214 | variant('browser') { |
|
|
211 | 215 | primarySlot('mainJs') { |
|
|
212 | 216 | fromRole('main') { |
|
|
213 | 217 | output('js') |
|
|
214 | 218 | } |
|
|
215 | 219 | from(layout.projectDirectory.file('inputs/mainJs.txt')) |
|
|
216 | 220 | } |
|
|
217 | 221 | slot('amdJs') { |
|
|
218 | 222 | fromLayer('mainAmd') { |
|
|
219 | 223 | output('js') |
|
|
220 | 224 | } |
|
|
221 | 225 | from(layout.projectDirectory.file('inputs/amdJs.txt')) |
|
|
222 | 226 | } |
|
|
223 | 227 | } |
|
|
224 | 228 | |
|
|
225 | 229 | whenOutgoingConfiguration { publication -> |
|
|
226 | 230 | publication.configuration { |
|
|
227 | 231 | attributes.attribute(variantAttr, publication.variant.name) |
|
|
228 | 232 | } |
|
|
229 | 233 | } |
|
|
230 | 234 | |
|
|
231 | 235 | whenOutgoingSlot { publication -> |
|
|
232 | 236 | def slotName = publication.artifactSlot.slot.name |
|
|
233 | 237 | publication.artifactAttributes { |
|
|
234 | 238 | attribute(slotAttr, slotName) |
|
|
235 | 239 | } |
|
|
236 | 240 | } |
|
|
237 | 241 | } |
|
|
238 | 242 | |
|
|
239 | 243 | tasks.register('probe') { |
|
|
240 | 244 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_mainJs' |
|
|
241 | 245 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s5_amdJs' |
|
|
242 | 246 | |
|
|
243 | 247 | doLast { |
|
|
244 | 248 | def mainDir = layout.buildDirectory.dir('variant-assemblies/browser/mainJs').get().asFile |
|
|
245 | 249 | def amdDir = layout.buildDirectory.dir('variant-assemblies/browser/amdJs').get().asFile |
|
|
246 | 250 | |
|
|
247 | 251 | assert new File(mainDir, 'base.js').exists() |
|
|
248 | 252 | assert new File(mainDir, 'amd.js').exists() |
|
|
249 | 253 | assert new File(mainDir, 'mainJs.txt').exists() |
|
|
250 | 254 | |
|
|
251 | 255 | assert !new File(amdDir, 'base.js').exists() |
|
|
252 | 256 | assert new File(amdDir, 'amd.js').exists() |
|
|
253 | 257 | assert new File(amdDir, 'amdJs.txt').exists() |
|
|
254 | 258 | |
|
|
255 | 259 | def elements = configurations.getByName('browserElements') |
|
|
256 | 260 | def amdVariant = elements.outgoing.variants.getByName('amdJs') |
|
|
257 | 261 | |
|
|
258 | 262 | println('variantAttr=' + elements.attributes.getAttribute(variantAttr)) |
|
|
259 | 263 | println('primarySlotAttr=' + elements.outgoing.attributes.getAttribute(slotAttr)) |
|
|
260 | 264 | println('amdSlotAttr=' + amdVariant.attributes.getAttribute(slotAttr)) |
|
|
261 | 265 | println('configurations=' + configurations.matching { it.name == 'browserElements' }.collect { it.name }.join(',')) |
|
|
262 | 266 | println('secondaryVariants=' + elements.outgoing.variants.collect { it.name }.sort().join(',')) |
|
|
263 | 267 | } |
|
|
264 | 268 | } |
|
|
265 | 269 | """); |
|
|
266 | 270 | |
|
|
267 | 271 | BuildResult result = runner("probe").build(); |
|
|
268 | 272 | |
|
|
269 | 273 | assertTrue(result.getOutput().contains("variantAttr=browser")); |
|
|
270 | 274 | assertTrue(result.getOutput().contains("primarySlotAttr=mainJs")); |
|
|
271 | 275 | assertTrue(result.getOutput().contains("amdSlotAttr=amdJs")); |
|
|
272 | 276 | assertTrue(result.getOutput().contains("configurations=browserElements")); |
|
|
273 | 277 | assertTrue(result.getOutput().contains("secondaryVariants=amdJs")); |
|
|
274 | 278 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
275 | 279 | } |
|
|
276 | 280 | |
|
|
277 | 281 | @Test |
|
|
278 | 282 | void outgoingSlotHookFollowsMaterializedGradleArtifactVariants() throws Exception { |
|
|
279 | 283 | writeSettings("variant-artifacts-materialized-gradle-variant"); |
|
|
280 | 284 | writeFile("inputs/typesPackage", "types\n"); |
|
|
281 | 285 | writeFile("inputs/js", "js\n"); |
|
|
282 | 286 | writeBuildFile(""" |
|
|
283 | 287 | import org.gradle.api.attributes.Attribute |
|
|
284 | 288 | |
|
|
285 | 289 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
286 | 290 | |
|
|
287 | 291 | def slotAttr = Attribute.of('test.slot', String) |
|
|
288 | 292 | |
|
|
289 | 293 | variants.layers.create('main') |
|
|
290 | 294 | variants.roles.create('main') |
|
|
291 | 295 | variants.variant('browser') { |
|
|
292 | 296 | role('main') { |
|
|
293 | 297 | layers('main') |
|
|
294 | 298 | } |
|
|
295 | 299 | } |
|
|
296 | 300 | |
|
|
297 | 301 | variantArtifacts { |
|
|
298 | 302 | variant('browser') { |
|
|
299 | 303 | primarySlot('typesPackage') { |
|
|
300 | 304 | from(layout.projectDirectory.file('inputs/typesPackage')) |
|
|
301 | 305 | } |
|
|
302 | 306 | } |
|
|
303 | 307 | |
|
|
304 | 308 | whenOutgoingConfiguration { publication -> |
|
|
305 | 309 | publication.configuration { |
|
|
306 | 310 | outgoing.variants.create('js') { secondary -> |
|
|
307 | 311 | secondary.artifact(layout.projectDirectory.file('inputs/js')) |
|
|
308 | 312 | } |
|
|
309 | 313 | } |
|
|
310 | 314 | } |
|
|
311 | 315 | |
|
|
312 | 316 | whenOutgoingSlot { publication -> |
|
|
313 | 317 | publication.artifactAttributes { |
|
|
314 | 318 | attribute(slotAttr, publication.artifactSlot.slot.name) |
|
|
315 | 319 | } |
|
|
316 | 320 | } |
|
|
317 | 321 | } |
|
|
318 | 322 | |
|
|
319 | 323 | tasks.register('probe') { |
|
|
320 | 324 | doLast { |
|
|
321 | 325 | def elements = configurations.getByName('browserElements') |
|
|
322 | 326 | def jsVariant = elements.outgoing.variants.getByName('js') |
|
|
323 | 327 | |
|
|
324 | 328 | println('primarySlotAttr=' + elements.outgoing.attributes.getAttribute(slotAttr)) |
|
|
325 | 329 | println('jsSlotAttr=' + jsVariant.attributes.getAttribute(slotAttr)) |
|
|
326 | 330 | } |
|
|
327 | 331 | } |
|
|
328 | 332 | """); |
|
|
329 | 333 | |
|
|
330 | 334 | BuildResult result = runner("probe").build(); |
|
|
331 | 335 | |
|
|
332 | 336 | assertTrue(result.getOutput().contains("primarySlotAttr=typesPackage")); |
|
|
333 | 337 | assertTrue(result.getOutput().contains("jsSlotAttr=js")); |
|
|
334 | 338 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
335 | 339 | } |
|
|
336 | 340 | |
|
|
337 | 341 | @Test |
|
|
338 | 342 | void allowsSingleSlotVariantWithoutExplicitPrimarySlot() throws Exception { |
|
|
339 | 343 | writeSettings("variant-artifacts-single-slot"); |
|
|
340 | 344 | writeBuildFile(""" |
|
|
341 | 345 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
342 | 346 | |
|
|
343 | 347 | variants.layers.create('main') |
|
|
344 | 348 | variants.roles.create('main') |
|
|
345 | 349 | variants.variant('browser') { |
|
|
346 | 350 | role('main') { |
|
|
347 | 351 | layers('main') |
|
|
348 | 352 | } |
|
|
349 | 353 | } |
|
|
350 | 354 | |
|
|
351 | 355 | variantSources.layer('main') { |
|
|
352 |
|
|
|
|
356 | sourceSet { | |
|
|
357 | declareOutputs('types') | |
|
|
358 | } | |
|
|
353 | 359 | } |
|
|
354 | 360 | |
|
|
355 | 361 | variantArtifacts { |
|
|
356 | 362 | variant('browser') { |
|
|
357 | 363 | slot('typesPackage') { |
|
|
358 | 364 | fromVariant { |
|
|
359 | 365 | output('types') |
|
|
360 | 366 | } |
|
|
361 | 367 | } |
|
|
362 | 368 | } |
|
|
363 | 369 | } |
|
|
364 | 370 | |
|
|
365 | 371 | tasks.register('probe') { |
|
|
366 | 372 | doLast { |
|
|
367 | 373 | variantArtifacts.whenAvailable { ctx -> |
|
|
368 | 374 | def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser') |
|
|
369 | 375 | println('primary=' + ctx.findOutgoing(browser).get().primarySlot.get().name) |
|
|
370 | 376 | } |
|
|
371 | 377 | } |
|
|
372 | 378 | } |
|
|
373 | 379 | """); |
|
|
374 | 380 | |
|
|
375 | 381 | BuildResult result = runner("probe").build(); |
|
|
376 | 382 | |
|
|
377 | 383 | assertTrue(result.getOutput().contains("primary=typesPackage")); |
|
|
378 | 384 | } |
|
|
379 | 385 | |
|
|
380 | 386 | @Test |
|
|
381 | 387 | void materializesDirectSlotInputsWithoutVariantSourceBindings() throws Exception { |
|
|
382 | 388 | writeSettings("variant-artifacts-direct-input"); |
|
|
383 | 389 | writeFile("inputs/bundle.js", "console.log('bundle')\n"); |
|
|
384 | 390 | writeBuildFile(""" |
|
|
385 | 391 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
386 | 392 | |
|
|
387 | 393 | variants.layers.create('main') |
|
|
388 | 394 | variants.roles.create('main') |
|
|
389 | 395 | variants.variant('browser') { |
|
|
390 | 396 | role('main') { |
|
|
391 | 397 | layers('main') |
|
|
392 | 398 | } |
|
|
393 | 399 | } |
|
|
394 | 400 | |
|
|
395 | 401 | variantArtifacts { |
|
|
396 | 402 | variant('browser') { |
|
|
397 | 403 | primarySlot('bundle') { |
|
|
398 | 404 | from(layout.projectDirectory.file('inputs/bundle.js')) |
|
|
399 | 405 | } |
|
|
400 | 406 | } |
|
|
401 | 407 | } |
|
|
402 | 408 | |
|
|
403 | 409 | tasks.register('probe') { |
|
|
404 | 410 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle' |
|
|
405 | 411 | |
|
|
406 | 412 | doLast { |
|
|
407 | 413 | def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile |
|
|
408 | 414 | assert new File(bundleDir, 'bundle.js').exists() |
|
|
409 | 415 | } |
|
|
410 | 416 | } |
|
|
411 | 417 | """); |
|
|
412 | 418 | |
|
|
413 | 419 | BuildResult result = runner("probe").build(); |
|
|
414 | 420 | |
|
|
415 | 421 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
416 | 422 | } |
|
|
417 | 423 | |
|
|
418 | 424 | @Test |
|
|
419 | 425 | void combinesDirectAndTopologyAwareSlotInputs() throws Exception { |
|
|
420 | 426 | writeSettings("variant-artifacts-combined-inputs"); |
|
|
421 | 427 | writeFile("inputs/base.js", "console.log('base')\n"); |
|
|
422 | 428 | writeFile("inputs/marker.txt", "marker\n"); |
|
|
423 | 429 | writeBuildFile(""" |
|
|
424 | 430 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
425 | 431 | |
|
|
426 | 432 | variants.layers.create('main') |
|
|
427 | 433 | variants.roles.create('main') |
|
|
428 | 434 | variants.variant('browser') { |
|
|
429 | 435 | role('main') { |
|
|
430 | 436 | layers('main') |
|
|
431 | 437 | } |
|
|
432 | 438 | } |
|
|
433 | 439 | |
|
|
434 | 440 | variantSources.layer('main') { |
|
|
435 |
|
|
|
|
436 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) | |
|
|
441 | sourceSet { | |
|
|
442 | declareOutputs('js') | |
|
|
443 | registerOutput('js', layout.projectDirectory.file('inputs/base.js')) | |
|
|
444 | } | |
|
|
437 | 445 | } |
|
|
438 | 446 | |
|
|
439 | 447 | variantArtifacts { |
|
|
440 | 448 | variant('browser') { |
|
|
441 | 449 | primarySlot('bundle') { |
|
|
442 | 450 | fromVariant { |
|
|
443 | 451 | output('js') |
|
|
444 | 452 | } |
|
|
445 | 453 | from(layout.projectDirectory.file('inputs/marker.txt')) |
|
|
446 | 454 | } |
|
|
447 | 455 | } |
|
|
448 | 456 | } |
|
|
449 | 457 | |
|
|
450 | 458 | tasks.register('probe') { |
|
|
451 | 459 | dependsOn 'assembleVariantArtifactSlot_v7_browser_s6_bundle' |
|
|
452 | 460 | |
|
|
453 | 461 | doLast { |
|
|
454 | 462 | def bundleDir = layout.buildDirectory.dir('variant-assemblies/browser/bundle').get().asFile |
|
|
455 | 463 | assert new File(bundleDir, 'base.js').exists() |
|
|
456 | 464 | assert new File(bundleDir, 'marker.txt').exists() |
|
|
457 | 465 | } |
|
|
458 | 466 | } |
|
|
459 | 467 | """); |
|
|
460 | 468 | |
|
|
461 | 469 | BuildResult result = runner("probe").build(); |
|
|
462 | 470 | |
|
|
463 | 471 | assertTrue(result.task(":probe").getOutcome() == TaskOutcome.SUCCESS); |
|
|
464 | 472 | } |
|
|
465 | 473 | |
|
|
466 | 474 | @Test |
|
|
467 | 475 | void failsOnUnknownVariantReference() throws Exception { |
|
|
468 | 476 | writeSettings("variant-artifacts-missing-variant"); |
|
|
469 | 477 | writeBuildFile(""" |
|
|
470 | 478 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
471 | 479 | |
|
|
472 | 480 | variants.layers.create('main') |
|
|
473 | 481 | |
|
|
474 | 482 | variantArtifacts { |
|
|
475 | 483 | variant('browser') { |
|
|
476 | 484 | slot('mainJs') { |
|
|
477 | 485 | fromVariant { |
|
|
478 | 486 | output('js') |
|
|
479 | 487 | } |
|
|
480 | 488 | } |
|
|
481 | 489 | } |
|
|
482 | 490 | } |
|
|
483 | 491 | """); |
|
|
484 | 492 | |
|
|
485 | 493 | assertBuildFails("isn't declared", "help"); |
|
|
486 | 494 | } |
|
|
487 | 495 | |
|
|
488 | 496 | @Test |
|
|
489 | 497 | void failsOnUnknownRoleReference() throws Exception { |
|
|
490 | 498 | writeSettings("variant-artifacts-missing-role"); |
|
|
491 | 499 | writeBuildFile(""" |
|
|
492 | 500 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
493 | 501 | |
|
|
494 | 502 | variants.layers.create('main') |
|
|
495 | 503 | variants.roles.create('main') |
|
|
496 | 504 | variants.variant('browser') { |
|
|
497 | 505 | role('main') { |
|
|
498 | 506 | layers('main') |
|
|
499 | 507 | } |
|
|
500 | 508 | } |
|
|
501 | 509 | |
|
|
502 | 510 | variantArtifacts { |
|
|
503 | 511 | variant('browser') { |
|
|
504 | 512 | slot('mainJs') { |
|
|
505 | 513 | fromRole('test') { |
|
|
506 | 514 | output('js') |
|
|
507 | 515 | } |
|
|
508 | 516 | } |
|
|
509 | 517 | } |
|
|
510 | 518 | } |
|
|
511 | 519 | """); |
|
|
512 | 520 | |
|
|
513 | 521 | assertBuildFails("Role projection for variant 'browser' and role 'test' not found", "help"); |
|
|
514 | 522 | } |
|
|
515 | 523 | |
|
|
516 | 524 | @Test |
|
|
517 | 525 | void failsWhenPrimarySlotIsMissingForMultipleSlots() throws Exception { |
|
|
518 | 526 | writeSettings("variant-artifacts-missing-primary"); |
|
|
519 | 527 | writeBuildFile(""" |
|
|
520 | 528 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
521 | 529 | |
|
|
522 | 530 | variants.layers.create('main') |
|
|
523 | 531 | variants.roles.create('main') |
|
|
524 | 532 | variants.variant('browser') { |
|
|
525 | 533 | role('main') { |
|
|
526 | 534 | layers('main') |
|
|
527 | 535 | } |
|
|
528 | 536 | } |
|
|
529 | 537 | |
|
|
530 | 538 | variantSources.layer('main') { |
|
|
531 |
|
|
|
|
539 | sourceSet { | |
|
|
540 | declareOutputs('types', 'js') | |
|
|
541 | } | |
|
|
532 | 542 | } |
|
|
533 | 543 | |
|
|
534 | 544 | variantArtifacts { |
|
|
535 | 545 | variant('browser') { |
|
|
536 | 546 | slot('typesPackage') { |
|
|
537 | 547 | fromVariant { |
|
|
538 | 548 | output('types') |
|
|
539 | 549 | } |
|
|
540 | 550 | } |
|
|
541 | 551 | slot('js') { |
|
|
542 | 552 | fromVariant { |
|
|
543 | 553 | output('js') |
|
|
544 | 554 | } |
|
|
545 | 555 | } |
|
|
546 | 556 | } |
|
|
547 | 557 | } |
|
|
548 | 558 | |
|
|
549 | 559 | tasks.register('probe') { |
|
|
550 | 560 | doLast { |
|
|
551 | 561 | variantArtifacts.whenAvailable { ctx -> |
|
|
552 | 562 | def browser = objects.named(org.implab.gradle.variants.core.Variant, 'browser') |
|
|
553 | 563 | ctx.findOutgoing(browser).get().primarySlot.get() |
|
|
554 | 564 | } |
|
|
555 | 565 | } |
|
|
556 | 566 | } |
|
|
557 | 567 | """); |
|
|
558 | 568 | |
|
|
559 | 569 | assertBuildFails("Multiple slots declared for browser, please specify primary slot explicitly", "probe"); |
|
|
560 | 570 | } |
|
|
561 | 571 | |
|
|
562 | 572 | @Test |
|
|
563 | 573 | void failsOnLayerReferenceOutsideVariantTopology() throws Exception { |
|
|
564 | 574 | writeSettings("variant-artifacts-layer-outside-topology"); |
|
|
565 | 575 | writeBuildFile(""" |
|
|
566 | 576 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
567 | 577 | |
|
|
568 | 578 | variants.layers.create('mainBase') |
|
|
569 | 579 | variants.layers.create('extra') |
|
|
570 | 580 | variants.roles.create('main') |
|
|
571 | 581 | variants.variant('browser') { |
|
|
572 | 582 | role('main') { |
|
|
573 | 583 | layers('mainBase') |
|
|
574 | 584 | } |
|
|
575 | 585 | } |
|
|
576 | 586 | |
|
|
577 | 587 | variantArtifacts { |
|
|
578 | 588 | variant('browser') { |
|
|
579 | 589 | slot('extraJs') { |
|
|
580 | 590 | fromLayer('extra') { |
|
|
581 | 591 | output('js') |
|
|
582 | 592 | } |
|
|
583 | 593 | } |
|
|
584 | 594 | } |
|
|
585 | 595 | } |
|
|
586 | 596 | """); |
|
|
587 | 597 | |
|
|
588 | 598 | assertBuildFails("Compile unit for variant 'browser' and layer 'extra' not found", "help"); |
|
|
589 | 599 | } |
|
|
590 | 600 | |
|
|
591 | 601 | @Test |
|
|
592 | 602 | void preservesPrimaryResolutionAndAllowsSecondaryArtifactSelection() throws Exception { |
|
|
593 | 603 | writeFile("settings.gradle", """ |
|
|
594 | 604 | rootProject.name = 'variant-artifacts-resolution' |
|
|
595 | 605 | include 'producer', 'consumer' |
|
|
596 | 606 | """); |
|
|
597 | 607 | writeFile("producer/inputs/types.d.ts", "export type Foo = string\n"); |
|
|
598 | 608 | writeFile("producer/inputs/index.js", "export const foo = 'bar'\n"); |
|
|
599 | 609 | writeBuildFile(""" |
|
|
600 | 610 | import org.gradle.api.attributes.Attribute |
|
|
601 | 611 | |
|
|
602 | 612 | def variantAttr = Attribute.of('test.variant', String) |
|
|
603 | 613 | def slotAttr = Attribute.of('test.slot', String) |
|
|
604 | 614 | |
|
|
605 | 615 | subprojects { |
|
|
606 | 616 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin |
|
|
607 | 617 | } |
|
|
608 | 618 | |
|
|
609 | 619 | project(':producer') { |
|
|
610 | 620 | variants.layers.create('main') |
|
|
611 | 621 | variants.roles.create('main') |
|
|
612 | 622 | variants.variant('browser') { |
|
|
613 | 623 | role('main') { |
|
|
614 | 624 | layers('main') |
|
|
615 | 625 | } |
|
|
616 | 626 | } |
|
|
617 | 627 | |
|
|
618 | 628 | variantSources.layer('main') { |
|
|
619 |
|
|
|
|
620 | registerOutput('types', layout.projectDirectory.file('inputs/types.d.ts')) | |
|
|
621 |
registerOutput(' |
|
|
|
629 | sourceSet { | |
|
|
630 | declareOutputs('types', 'js') | |
|
|
631 | registerOutput('types', layout.projectDirectory.file('inputs/types.d.ts')) | |
|
|
632 | registerOutput('js', layout.projectDirectory.file('inputs/index.js')) | |
|
|
633 | } | |
|
|
622 | 634 | } |
|
|
623 | 635 | |
|
|
624 | 636 | variantArtifacts { |
|
|
625 | 637 | variant('browser') { |
|
|
626 | 638 | primarySlot('typesPackage') { |
|
|
627 | 639 | fromVariant { |
|
|
628 | 640 | output('types') |
|
|
629 | 641 | } |
|
|
630 | 642 | } |
|
|
631 | 643 | slot('js') { |
|
|
632 | 644 | fromVariant { |
|
|
633 | 645 | output('js') |
|
|
634 | 646 | } |
|
|
635 | 647 | } |
|
|
636 | 648 | } |
|
|
637 | 649 | |
|
|
638 | 650 | whenOutgoingConfiguration { publication -> |
|
|
639 | 651 | publication.configuration { |
|
|
640 | 652 | attributes.attribute(variantAttr, publication.variant.name) |
|
|
641 | 653 | } |
|
|
642 | 654 | } |
|
|
643 | 655 | |
|
|
644 | 656 | whenOutgoingSlot { publication -> |
|
|
645 | 657 | publication.artifactAttributes { |
|
|
646 | 658 | attribute(slotAttr, publication.artifactSlot.slot.name) |
|
|
647 | 659 | } |
|
|
648 | 660 | } |
|
|
649 | 661 | } |
|
|
650 | 662 | |
|
|
651 | 663 | } |
|
|
652 | 664 | |
|
|
653 | 665 | project(':consumer') { |
|
|
654 | 666 | configurations { |
|
|
655 | 667 | compileView { |
|
|
656 | 668 | canBeResolved = true |
|
|
657 | 669 | canBeConsumed = false |
|
|
658 | 670 | canBeDeclared = true |
|
|
659 | 671 | attributes { |
|
|
660 | 672 | attribute(variantAttr, 'browser') |
|
|
661 | 673 | attribute(slotAttr, 'typesPackage') |
|
|
662 | 674 | } |
|
|
663 | 675 | } |
|
|
664 | 676 | } |
|
|
665 | 677 | |
|
|
666 | 678 | dependencies { |
|
|
667 | 679 | compileView project(':producer') |
|
|
668 | 680 | } |
|
|
669 | 681 | |
|
|
670 | 682 | tasks.register('probe') { |
|
|
671 | 683 | doLast { |
|
|
672 | 684 | def compileFiles = configurations.compileView.files.collect { it.name }.sort().join(',') |
|
|
673 | 685 | def jsFiles = configurations.compileView.incoming.artifactView { |
|
|
674 | 686 | attributes { |
|
|
675 | 687 | attribute(slotAttr, 'js') |
|
|
676 | 688 | } |
|
|
677 | 689 | }.files.files.collect { it.name }.sort().join(',') |
|
|
678 | 690 | |
|
|
679 | 691 | println('compileFiles=' + compileFiles) |
|
|
680 | 692 | println('jsFiles=' + jsFiles) |
|
|
681 | 693 | } |
|
|
682 | 694 | } |
|
|
683 | 695 | } |
|
|
684 | 696 | """); |
|
|
685 | 697 | |
|
|
686 | 698 | BuildResult result = runner(":consumer:probe").build(); |
|
|
687 | 699 | |
|
|
688 | 700 | assertTrue(result.getOutput().contains("compileFiles=typesPackage")); |
|
|
689 | 701 | assertTrue(result.getOutput().contains("jsFiles=js")); |
|
|
690 | 702 | } |
|
|
691 | 703 | } |
| @@ -1,554 +1,580 | |||
|
|
1 | 1 | package org.implab.gradle.variants; |
|
|
2 | 2 | |
|
|
3 | 3 | import static org.junit.jupiter.api.Assertions.assertTrue; |
|
|
4 | 4 | |
|
|
5 | 5 | import org.gradle.testkit.runner.BuildResult; |
|
|
6 | 6 | import org.junit.jupiter.api.Test; |
|
|
7 | 7 | |
|
|
8 | 8 | class VariantSourcesPluginFunctionalTest extends AbstractFunctionalTest { |
|
|
9 | 9 | |
|
|
10 | 10 | @Test |
|
|
11 | 11 | void exposesDerivedViewsAndStableSourceSetProvider() throws Exception { |
|
|
12 | 12 | writeSettings("variant-sources-derived-views"); |
|
|
13 | 13 | writeBuildFile(""" |
|
|
14 | 14 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
15 | 15 | |
|
|
16 | 16 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
17 | 17 | variantsExt.layers.create('main') |
|
|
18 | 18 | variantsExt.layers.create('test') |
|
|
19 | 19 | variantsExt.roles.create('production') |
|
|
20 | 20 | variantsExt.roles.create('test') |
|
|
21 | 21 | |
|
|
22 | 22 | variantsExt.variant('browser') { |
|
|
23 | 23 | role('production') { layers('main') } |
|
|
24 | 24 | role('test') { layers('main', 'test') } |
|
|
25 | 25 | } |
|
|
26 | 26 | |
|
|
27 | 27 | def lines = [] |
|
|
28 | 28 | |
|
|
29 | 29 | variantSources.whenAvailable { ctx -> |
|
|
30 | 30 | lines << "units=" + ctx.compileUnits.units |
|
|
31 | 31 | .collect { "${it.variant().name}:${it.layer().name}" } |
|
|
32 | 32 | .sort() |
|
|
33 | 33 | .join(',') |
|
|
34 | 34 | |
|
|
35 | 35 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
36 | 36 | def production = ctx.variants.roles.find { it.name == 'production' } |
|
|
37 | 37 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
38 | 38 | def projection = ctx.roleProjections.requireProjection(browser, production) |
|
|
39 | 39 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) |
|
|
40 | 40 | |
|
|
41 | 41 | def left = ctx.sourceSets.getSourceSet(unit) |
|
|
42 | 42 | def right = ctx.sourceSets.getSourceSet(unit) |
|
|
43 | 43 | |
|
|
44 | 44 | lines << "projectionUnits=" + ctx.roleProjections.getUnits(projection) |
|
|
45 | 45 | .collect { it.layer().name } |
|
|
46 | 46 | .sort() |
|
|
47 | 47 | .join(',') |
|
|
48 | 48 | lines << "mainSourceSet=" + left.name |
|
|
49 | 49 | lines << "sameProvider=" + left.is(right) |
|
|
50 | 50 | } |
|
|
51 | 51 | |
|
|
52 | 52 | afterEvaluate { |
|
|
53 | 53 | variantSources.whenAvailable { ctx -> |
|
|
54 | 54 | lines << "late:variants=" + ctx.variants.variants.collect { it.name }.sort().join(',') |
|
|
55 | 55 | } |
|
|
56 | 56 | } |
|
|
57 | 57 | |
|
|
58 | 58 | tasks.register('probe') { |
|
|
59 | 59 | doLast { |
|
|
60 | 60 | lines.each { println(it) } |
|
|
61 | 61 | } |
|
|
62 | 62 | } |
|
|
63 | 63 | """); |
|
|
64 | 64 | |
|
|
65 | 65 | BuildResult result = runner("probe").build(); |
|
|
66 | 66 | |
|
|
67 | 67 | assertTrue(result.getOutput().contains("units=browser:main,browser:test")); |
|
|
68 | 68 | assertTrue(result.getOutput().contains("projectionUnits=main")); |
|
|
69 | 69 | assertTrue(result.getOutput().contains("mainSourceSet=browserMain")); |
|
|
70 | 70 | assertTrue(result.getOutput().contains("sameProvider=true")); |
|
|
71 | 71 | assertTrue(result.getOutput().contains("late:variants=browser")); |
|
|
72 | 72 | } |
|
|
73 | 73 | |
|
|
74 | 74 | @Test |
|
|
75 | 75 | void appliesSelectorPrecedenceForFutureMaterialization() throws Exception { |
|
|
76 | 76 | writeSettings("variant-sources-precedence"); |
|
|
77 | 77 | writeBuildFile(""" |
|
|
78 | 78 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
79 | 79 | |
|
|
80 | 80 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
81 | 81 | variantsExt.layers.create('main') |
|
|
82 | 82 | variantsExt.layers.create('test') |
|
|
83 | 83 | variantsExt.roles.create('production') |
|
|
84 | 84 | variantsExt.roles.create('test') |
|
|
85 | 85 | |
|
|
86 | 86 | variantsExt.variant('browser') { |
|
|
87 | 87 | role('production') { layers('main') } |
|
|
88 | 88 | role('test') { layers('main', 'test') } |
|
|
89 | 89 | } |
|
|
90 | 90 | |
|
|
91 | 91 | variantsExt.variant('node') { |
|
|
92 | 92 | role('production') { layers('main') } |
|
|
93 | 93 | } |
|
|
94 | 94 | |
|
|
95 | 95 | def events = [] |
|
|
96 | 96 | |
|
|
97 | 97 | variantSources { |
|
|
98 | configureEach { | |
|
|
99 | events << "all:" + sourceSet.name | |
|
|
100 | events << "context:" + sourceSet.name + ":" + variant.name + ":" + layer.name | |
|
|
101 | } | |
|
|
98 | 102 | variant('browser') { |
|
|
99 | events << "variant:" + name | |
|
|
103 | events << "variant:" + sourceSet.name | |
|
|
100 | 104 | } |
|
|
101 | 105 | layer('main') { |
|
|
102 | events << "layer:" + name | |
|
|
106 | events << "layer:" + sourceSet.name | |
|
|
103 | 107 | } |
|
|
104 | 108 | unit('browser', 'main') { |
|
|
105 | events << "unit:" + name | |
|
|
109 | events << "unit:" + sourceSet.name | |
|
|
106 | 110 | } |
|
|
107 | 111 | } |
|
|
108 | 112 | |
|
|
109 | 113 | afterEvaluate { |
|
|
110 | 114 | variantSources.whenAvailable { ctx -> |
|
|
111 | 115 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
112 | 116 | def node = ctx.variants.variants.find { it.name == 'node' } |
|
|
113 | 117 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
114 | 118 | def testLayer = ctx.variants.layers.find { it.name == 'test' } |
|
|
115 | 119 | |
|
|
116 | 120 | def browserMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, mainLayer)).get() |
|
|
117 | 121 | def browserTest = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(browser, testLayer)).get() |
|
|
118 | 122 | def nodeMain = ctx.sourceSets.getSourceSet(ctx.compileUnits.requireUnit(node, mainLayer)).get() |
|
|
119 | 123 | def bySourceSet = events.groupBy { it.split(':', 2)[1] } |
|
|
120 | 124 | |
|
|
121 | 125 | println("browserMain=" + bySourceSet[browserMain.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
122 | 126 | println("browserTest=" + bySourceSet[browserTest.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
123 | 127 | println("nodeMain=" + bySourceSet[nodeMain.name].collect { it.split(':', 2)[0] }.join(',')) |
|
|
128 | println("context=" + events.find { it.startsWith("context:" + browserMain.name + ":") }) | |
|
|
124 | 129 | } |
|
|
125 | 130 | } |
|
|
126 | 131 | """); |
|
|
127 | 132 | |
|
|
128 | 133 | BuildResult result = runner("help").build(); |
|
|
129 | 134 | |
|
|
130 | assertTrue(result.getOutput().contains("browserMain=variant,layer,unit")); | |
|
|
131 | assertTrue(result.getOutput().contains("browserTest=variant")); | |
|
|
132 | assertTrue(result.getOutput().contains("nodeMain=layer")); | |
|
|
135 | assertTrue(result.getOutput().contains("browserMain=all,variant,layer,unit")); | |
|
|
136 | assertTrue(result.getOutput().contains("browserTest=all,variant")); | |
|
|
137 | assertTrue(result.getOutput().contains("nodeMain=all,layer")); | |
|
|
138 | assertTrue(result.getOutput().contains("context=context:browserMain:browser:main")); | |
|
|
133 | 139 | } |
|
|
134 | 140 | |
|
|
135 | 141 | @Test |
|
|
136 | 142 | void failsLateConfigurationByDefaultAfterMaterialization() throws Exception { |
|
|
137 | 143 | writeSettings("variant-sources-late-fail"); |
|
|
138 | 144 | writeBuildFile(""" |
|
|
139 | 145 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
140 | 146 | |
|
|
141 | 147 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
142 | 148 | variantsExt.layers.create('main') |
|
|
143 | 149 | variantsExt.roles.create('production') |
|
|
144 | 150 | variantsExt.variant('browser') { |
|
|
145 | 151 | role('production') { layers('main') } |
|
|
146 | 152 | } |
|
|
147 | 153 | |
|
|
148 | 154 | afterEvaluate { |
|
|
149 | 155 | variantSources.whenAvailable { ctx -> |
|
|
150 | 156 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
151 | 157 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
152 | 158 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) |
|
|
153 | 159 | |
|
|
154 | 160 | ctx.sourceSets.getSourceSet(unit).get() |
|
|
155 | 161 | variantSources.layer('main') { |
|
|
156 |
|
|
|
|
162 | sourceSet { | |
|
|
163 | declareOutputs('late') | |
|
|
164 | } | |
|
|
157 | 165 | } |
|
|
158 | 166 | } |
|
|
159 | 167 | } |
|
|
160 | 168 | """); |
|
|
161 | 169 | |
|
|
162 | 170 | assertBuildFails("Source sets for [layer=main] layer already materialized", "help"); |
|
|
163 | 171 | } |
|
|
164 | 172 | |
|
|
165 | 173 | @Test |
|
|
166 | 174 | void allowsLateConfigurationWhenSelectedBeforeFirstSelector() throws Exception { |
|
|
167 | 175 | writeSettings("variant-sources-late-allow"); |
|
|
168 | 176 | writeBuildFile(""" |
|
|
169 | 177 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
170 | 178 | |
|
|
171 | 179 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
172 | 180 | variantsExt.layers.create('main') |
|
|
173 | 181 | variantsExt.roles.create('production') |
|
|
174 | 182 | variantsExt.variant('browser') { |
|
|
175 | 183 | role('production') { layers('main') } |
|
|
176 | 184 | } |
|
|
177 | 185 | |
|
|
178 | 186 | variantSources { |
|
|
179 | 187 | lateConfigurationPolicy { |
|
|
180 | 188 | allowLateConfiguration() |
|
|
181 | 189 | } |
|
|
182 | 190 | } |
|
|
183 | 191 | |
|
|
184 | 192 | afterEvaluate { |
|
|
185 | 193 | variantSources.whenAvailable { ctx -> |
|
|
186 | 194 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
187 | 195 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
188 | 196 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) |
|
|
189 | 197 | |
|
|
190 |
def |
|
|
|
198 | def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get() | |
|
|
191 | 199 | variantSources.layer('main') { |
|
|
192 |
|
|
|
|
200 | sourceSet { | |
|
|
201 | declareOutputs('late') | |
|
|
202 | } | |
|
|
193 | 203 | } |
|
|
194 |
|
|
|
|
204 | unitSourceSet.output('late') | |
|
|
195 | 205 | println('lateAllowed=ok') |
|
|
196 | 206 | } |
|
|
197 | 207 | } |
|
|
198 | 208 | """); |
|
|
199 | 209 | |
|
|
200 | 210 | BuildResult result = runner("help").build(); |
|
|
201 | 211 | assertTrue(result.getOutput().contains("lateAllowed=ok")); |
|
|
202 | 212 | } |
|
|
203 | 213 | |
|
|
204 | 214 | @Test |
|
|
205 | 215 | void warnsAndAppliesLateConfigurationWhenWarnModeSelected() throws Exception { |
|
|
206 | 216 | writeSettings("variant-sources-late-warn"); |
|
|
207 | 217 | writeBuildFile(""" |
|
|
208 | 218 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
209 | 219 | |
|
|
210 | 220 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
211 | 221 | variantsExt.layers.create('main') |
|
|
212 | 222 | variantsExt.roles.create('production') |
|
|
213 | 223 | variantsExt.variant('browser') { |
|
|
214 | 224 | role('production') { layers('main') } |
|
|
215 | 225 | } |
|
|
216 | 226 | |
|
|
217 | 227 | variantSources { |
|
|
218 | 228 | lateConfigurationPolicy { |
|
|
219 | 229 | warnOnLateConfiguration() |
|
|
220 | 230 | } |
|
|
221 | 231 | } |
|
|
222 | 232 | |
|
|
223 | 233 | afterEvaluate { |
|
|
224 | 234 | variantSources.whenAvailable { ctx -> |
|
|
225 | 235 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
226 | 236 | def mainLayer = ctx.variants.layers.find { it.name == 'main' } |
|
|
227 | 237 | def unit = ctx.compileUnits.requireUnit(browser, mainLayer) |
|
|
228 | 238 | |
|
|
229 |
def |
|
|
|
239 | def unitSourceSet = ctx.sourceSets.getSourceSet(unit).get() | |
|
|
230 | 240 | variantSources.layer('main') { |
|
|
231 |
|
|
|
|
241 | sourceSet { | |
|
|
242 | declareOutputs('late') | |
|
|
243 | } | |
|
|
232 | 244 | } |
|
|
233 |
|
|
|
|
245 | unitSourceSet.output('late') | |
|
|
234 | 246 | println('lateWarn=ok') |
|
|
235 | 247 | } |
|
|
236 | 248 | } |
|
|
237 | 249 | """); |
|
|
238 | 250 | |
|
|
239 | 251 | BuildResult result = runner("help").build(); |
|
|
240 | 252 | |
|
|
241 | 253 | assertTrue(result.getOutput().contains("Source sets for [layer=main] layer already materialized")); |
|
|
242 | 254 | assertTrue(result.getOutput().contains("lateWarn=ok")); |
|
|
243 | 255 | } |
|
|
244 | 256 | |
|
|
245 | 257 | @Test |
|
|
246 | 258 | void rejectsChangingLateConfigurationPolicyAfterFirstSelector() throws Exception { |
|
|
247 | 259 | writeSettings("variant-sources-late-policy-fixed"); |
|
|
248 | 260 | writeBuildFile(""" |
|
|
249 | 261 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
250 | 262 | |
|
|
251 | 263 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
252 | 264 | variantsExt.layers.create('main') |
|
|
253 | 265 | variantsExt.roles.create('production') |
|
|
254 | 266 | variantsExt.variant('browser') { |
|
|
255 | 267 | role('production') { layers('main') } |
|
|
256 | 268 | } |
|
|
257 | 269 | |
|
|
258 | 270 | variantSources { |
|
|
259 | 271 | variant('browser') { |
|
|
260 |
|
|
|
|
272 | sourceSet { | |
|
|
273 | declareOutputs('js') | |
|
|
274 | } | |
|
|
261 | 275 | } |
|
|
262 | 276 | lateConfigurationPolicy { |
|
|
263 | 277 | allowLateConfiguration() |
|
|
264 | 278 | } |
|
|
265 | 279 | } |
|
|
266 | 280 | """); |
|
|
267 | 281 | |
|
|
268 | 282 | assertBuildFails("Lazy configuration policy already applied", "help"); |
|
|
269 | 283 | } |
|
|
270 | 284 | |
|
|
271 | 285 | @Test |
|
|
272 | 286 | void failsOnProjectedNameCollisionByDefault() throws Exception { |
|
|
273 | 287 | writeSettings("variant-sources-name-collision-fail"); |
|
|
274 | 288 | writeBuildFile(""" |
|
|
275 | 289 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
276 | 290 | |
|
|
277 | 291 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
278 | 292 | variantsExt.layers.create('variantBar') |
|
|
279 | 293 | variantsExt.layers.create('bar') |
|
|
280 | 294 | variantsExt.roles.create('production') |
|
|
281 | 295 | |
|
|
282 | 296 | variantsExt.variant('foo') { |
|
|
283 | 297 | role('production') { layers('variantBar') } |
|
|
284 | 298 | } |
|
|
285 | 299 | variantsExt.variant('fooVariant') { |
|
|
286 | 300 | role('production') { layers('bar') } |
|
|
287 | 301 | } |
|
|
288 | 302 | """); |
|
|
289 | 303 | |
|
|
290 | 304 | assertBuildFails("The same source set names are produced by different compile units", "help"); |
|
|
291 | 305 | } |
|
|
292 | 306 | |
|
|
293 | 307 | @Test |
|
|
294 | 308 | void resolvesProjectedNameCollisionDeterministicallyWhenConfigured() throws Exception { |
|
|
295 | 309 | writeSettings("variant-sources-name-collision-resolve"); |
|
|
296 | 310 | writeBuildFile(""" |
|
|
297 | 311 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
298 | 312 | |
|
|
299 | 313 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
300 | 314 | variantsExt.layers.create('variantBar') |
|
|
301 | 315 | variantsExt.layers.create('bar') |
|
|
302 | 316 | variantsExt.roles.create('production') |
|
|
303 | 317 | |
|
|
304 | 318 | variantsExt.variant('foo') { |
|
|
305 | 319 | role('production') { layers('variantBar') } |
|
|
306 | 320 | } |
|
|
307 | 321 | variantsExt.variant('fooVariant') { |
|
|
308 | 322 | role('production') { layers('bar') } |
|
|
309 | 323 | } |
|
|
310 | 324 | |
|
|
311 | 325 | variantSources { |
|
|
312 | 326 | namingPolicy { |
|
|
313 | 327 | resolveNameCollision() |
|
|
314 | 328 | } |
|
|
315 | 329 | } |
|
|
316 | 330 | |
|
|
317 | 331 | afterEvaluate { |
|
|
318 | 332 | variantSources.whenAvailable { ctx -> |
|
|
319 | 333 | def foo = ctx.variants.variants.find { it.name == 'foo' } |
|
|
320 | 334 | def fooVariant = ctx.variants.variants.find { it.name == 'fooVariant' } |
|
|
321 | 335 | def variantBar = ctx.variants.layers.find { it.name == 'variantBar' } |
|
|
322 | 336 | def bar = ctx.variants.layers.find { it.name == 'bar' } |
|
|
323 | 337 | |
|
|
324 | 338 | def later = ctx.compileUnits.requireUnit(fooVariant, bar) |
|
|
325 | 339 | def earlier = ctx.compileUnits.requireUnit(foo, variantBar) |
|
|
326 | 340 | |
|
|
327 | 341 | println("map1=" + later.variant().name + ":" + later.layer().name + "->" + ctx.sourceSets.getSourceSet(later).name) |
|
|
328 | 342 | println("map2=" + earlier.variant().name + ":" + earlier.layer().name + "->" + ctx.sourceSets.getSourceSet(earlier).name) |
|
|
329 | 343 | } |
|
|
330 | 344 | } |
|
|
331 | 345 | """); |
|
|
332 | 346 | |
|
|
333 | 347 | BuildResult result = runner("help").build(); |
|
|
334 | 348 | |
|
|
335 | 349 | assertTrue(result.getOutput().contains("map1=fooVariant:bar->fooVariantBar2")); |
|
|
336 | 350 | assertTrue(result.getOutput().contains("map2=foo:variantBar->fooVariantBar")); |
|
|
337 | 351 | } |
|
|
338 | 352 | |
|
|
339 | 353 | @Test |
|
|
340 | 354 | void failsOnUnknownVariantSelectorTarget() throws Exception { |
|
|
341 | 355 | writeSettings("variant-sources-missing-variant"); |
|
|
342 | 356 | writeBuildFile(""" |
|
|
343 | 357 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
344 | 358 | |
|
|
345 | 359 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
346 | 360 | variantsExt.layers.create('main') |
|
|
347 | 361 | variantsExt.roles.create('production') |
|
|
348 | 362 | variantsExt.variant('browser') { |
|
|
349 | 363 | role('production') { layers('main') } |
|
|
350 | 364 | } |
|
|
351 | 365 | |
|
|
352 | 366 | variantSources { |
|
|
353 | 367 | variant('missing') { |
|
|
354 |
|
|
|
|
368 | sourceSet { | |
|
|
369 | declareOutputs('js') | |
|
|
370 | } | |
|
|
355 | 371 | } |
|
|
356 | 372 | } |
|
|
357 | 373 | """); |
|
|
358 | 374 | |
|
|
359 | 375 | assertBuildFails("Variant 'missing' isn't declared", "help"); |
|
|
360 | 376 | } |
|
|
361 | 377 | |
|
|
362 | 378 | @Test |
|
|
363 | 379 | void failsOnUnknownVariantContextSelectorTarget() throws Exception { |
|
|
364 | 380 | writeSettings("variant-sources-context-missing-variant"); |
|
|
365 | 381 | writeBuildFile(""" |
|
|
366 | 382 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
367 | 383 | |
|
|
368 | 384 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
369 | 385 | variantsExt.layers.create('main') |
|
|
370 | 386 | variantsExt.roles.create('production') |
|
|
371 | 387 | variantsExt.variant('browser') { |
|
|
372 | 388 | role('production') { layers('main') } |
|
|
373 | 389 | } |
|
|
374 | 390 | |
|
|
375 | 391 | variantSources.whenAvailable { ctx -> |
|
|
376 | 392 | def missing = objects.named(org.implab.gradle.variants.core.Variant, 'missing') |
|
|
377 | 393 | ctx.configureVariant(missing) { |
|
|
378 |
|
|
|
|
394 | sourceSet { | |
|
|
395 | declareOutputs('js') | |
|
|
396 | } | |
|
|
379 | 397 | } |
|
|
380 | 398 | } |
|
|
381 | 399 | """); |
|
|
382 | 400 | |
|
|
383 | 401 | assertBuildFails("The specified variant 'missing' isn't declared", "help"); |
|
|
384 | 402 | } |
|
|
385 | 403 | |
|
|
386 | 404 | @Test |
|
|
387 | 405 | void failsOnUnknownLayerSelectorTarget() throws Exception { |
|
|
388 | 406 | writeSettings("variant-sources-missing-layer"); |
|
|
389 | 407 | writeBuildFile(""" |
|
|
390 | 408 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
391 | 409 | |
|
|
392 | 410 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
393 | 411 | variantsExt.layers.create('main') |
|
|
394 | 412 | variantsExt.roles.create('production') |
|
|
395 | 413 | variantsExt.variant('browser') { |
|
|
396 | 414 | role('production') { layers('main') } |
|
|
397 | 415 | } |
|
|
398 | 416 | |
|
|
399 | 417 | variantSources { |
|
|
400 | 418 | layer('missing') { |
|
|
401 |
|
|
|
|
419 | sourceSet { | |
|
|
420 | declareOutputs('js') | |
|
|
421 | } | |
|
|
402 | 422 | } |
|
|
403 | 423 | } |
|
|
404 | 424 | """); |
|
|
405 | 425 | |
|
|
406 | 426 | assertBuildFails("Layer 'missing' isn't declared", "help"); |
|
|
407 | 427 | } |
|
|
408 | 428 | |
|
|
409 | 429 | @Test |
|
|
410 | 430 | void failsOnUnknownLayerContextSelectorTarget() throws Exception { |
|
|
411 | 431 | writeSettings("variant-sources-context-missing-layer"); |
|
|
412 | 432 | writeBuildFile(""" |
|
|
413 | 433 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
414 | 434 | |
|
|
415 | 435 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
416 | 436 | variantsExt.layers.create('main') |
|
|
417 | 437 | variantsExt.roles.create('production') |
|
|
418 | 438 | variantsExt.variant('browser') { |
|
|
419 | 439 | role('production') { layers('main') } |
|
|
420 | 440 | } |
|
|
421 | 441 | |
|
|
422 | 442 | variantSources.whenAvailable { ctx -> |
|
|
423 | 443 | def missing = objects.named(org.implab.gradle.variants.core.Layer, 'missing') |
|
|
424 | 444 | ctx.configureLayer(missing) { |
|
|
425 |
|
|
|
|
445 | sourceSet { | |
|
|
446 | declareOutputs('js') | |
|
|
447 | } | |
|
|
426 | 448 | } |
|
|
427 | 449 | } |
|
|
428 | 450 | """); |
|
|
429 | 451 | |
|
|
430 | 452 | assertBuildFails("The specified layer 'missing' isn't declared", "help"); |
|
|
431 | 453 | } |
|
|
432 | 454 | |
|
|
433 | 455 | @Test |
|
|
434 | 456 | void failsOnUndeclaredCompileUnitSelectorTarget() throws Exception { |
|
|
435 | 457 | writeSettings("variant-sources-missing-unit"); |
|
|
436 | 458 | writeBuildFile(""" |
|
|
437 | 459 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
438 | 460 | |
|
|
439 | 461 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
440 | 462 | variantsExt.layers.create('main') |
|
|
441 | 463 | variantsExt.layers.create('test') |
|
|
442 | 464 | variantsExt.roles.create('production') |
|
|
443 | 465 | variantsExt.variant('browser') { |
|
|
444 | 466 | role('production') { layers('main') } |
|
|
445 | 467 | } |
|
|
446 | 468 | |
|
|
447 | 469 | variantSources { |
|
|
448 | 470 | unit('browser', 'test') { |
|
|
449 |
|
|
|
|
471 | sourceSet { | |
|
|
472 | declareOutputs('js') | |
|
|
473 | } | |
|
|
450 | 474 | } |
|
|
451 | 475 | } |
|
|
452 | 476 | """); |
|
|
453 | 477 | |
|
|
454 | 478 | assertBuildFails("The CompileUnit isn't declared for variant 'browser', layer 'test'", "help"); |
|
|
455 | 479 | } |
|
|
456 | 480 | |
|
|
457 | 481 | @Test |
|
|
458 | 482 | void failsOnUndeclaredCompileUnitContextSelectorTarget() throws Exception { |
|
|
459 | 483 | writeSettings("variant-sources-context-missing-unit"); |
|
|
460 | 484 | writeBuildFile(""" |
|
|
461 | 485 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
462 | 486 | |
|
|
463 | 487 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
464 | 488 | variantsExt.layers.create('main') |
|
|
465 | 489 | variantsExt.layers.create('test') |
|
|
466 | 490 | variantsExt.roles.create('production') |
|
|
467 | 491 | variantsExt.variant('browser') { |
|
|
468 | 492 | role('production') { layers('main') } |
|
|
469 | 493 | } |
|
|
470 | 494 | |
|
|
471 | 495 | variantSources.whenAvailable { ctx -> |
|
|
472 | 496 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
473 | 497 | def testLayer = ctx.variants.layers.find { it.name == 'test' } |
|
|
474 | 498 | def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer) |
|
|
475 | 499 | ctx.configureUnit(unit) { |
|
|
476 |
|
|
|
|
500 | sourceSet { | |
|
|
501 | declareOutputs('js') | |
|
|
502 | } | |
|
|
477 | 503 | } |
|
|
478 | 504 | } |
|
|
479 | 505 | """); |
|
|
480 | 506 | |
|
|
481 | 507 | assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help"); |
|
|
482 | 508 | } |
|
|
483 | 509 | |
|
|
484 | 510 | @Test |
|
|
485 | 511 | void failsOnUndeclaredCompileUnitMaterializerTarget() throws Exception { |
|
|
486 | 512 | writeSettings("variant-sources-materializer-missing-unit"); |
|
|
487 | 513 | writeBuildFile(""" |
|
|
488 | 514 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
489 | 515 | |
|
|
490 | 516 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
491 | 517 | variantsExt.layers.create('main') |
|
|
492 | 518 | variantsExt.layers.create('test') |
|
|
493 | 519 | variantsExt.roles.create('production') |
|
|
494 | 520 | variantsExt.variant('browser') { |
|
|
495 | 521 | role('production') { layers('main') } |
|
|
496 | 522 | } |
|
|
497 | 523 | |
|
|
498 | 524 | variantSources.whenAvailable { ctx -> |
|
|
499 | 525 | def browser = ctx.variants.variants.find { it.name == 'browser' } |
|
|
500 | 526 | def testLayer = ctx.variants.layers.find { it.name == 'test' } |
|
|
501 | 527 | def unit = new org.implab.gradle.variants.sources.CompileUnit(browser, testLayer) |
|
|
502 | 528 | ctx.sourceSets.getSourceSet(unit) |
|
|
503 | 529 | } |
|
|
504 | 530 | """); |
|
|
505 | 531 | |
|
|
506 | 532 | assertBuildFails("Compile unit for variant 'browser' and layer 'test' not found", "help"); |
|
|
507 | 533 | } |
|
|
508 | 534 | |
|
|
509 | 535 | @Test |
|
|
510 | 536 | void rejectsChangingNamingPolicyAfterContextBecomesObservable() throws Exception { |
|
|
511 | 537 | writeSettings("variant-sources-name-policy-fixed"); |
|
|
512 | 538 | writeBuildFile(""" |
|
|
513 | 539 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
514 | 540 | |
|
|
515 | 541 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
516 | 542 | variantsExt.layers.create('main') |
|
|
517 | 543 | variantsExt.roles.create('production') |
|
|
518 | 544 | variantsExt.variant('browser') { |
|
|
519 | 545 | role('production') { layers('main') } |
|
|
520 | 546 | } |
|
|
521 | 547 | |
|
|
522 | 548 | variantSources.whenAvailable { |
|
|
523 | 549 | variantSources.namingPolicy { |
|
|
524 | 550 | resolveNameCollision() |
|
|
525 | 551 | } |
|
|
526 | 552 | } |
|
|
527 | 553 | """); |
|
|
528 | 554 | |
|
|
529 | 555 | assertBuildFails("Naming policy already applied", "help"); |
|
|
530 | 556 | } |
|
|
531 | 557 | |
|
|
532 | 558 | @Test |
|
|
533 | 559 | void rejectsChangingLateConfigurationPolicyAfterContextBecomesObservable() throws Exception { |
|
|
534 | 560 | writeSettings("variant-sources-late-policy-context-fixed"); |
|
|
535 | 561 | writeBuildFile(""" |
|
|
536 | 562 | apply plugin: org.implab.gradle.variants.VariantSourcesPlugin |
|
|
537 | 563 | |
|
|
538 | 564 | def variantsExt = extensions.getByType(org.implab.gradle.variants.core.VariantsExtension) |
|
|
539 | 565 | variantsExt.layers.create('main') |
|
|
540 | 566 | variantsExt.roles.create('production') |
|
|
541 | 567 | variantsExt.variant('browser') { |
|
|
542 | 568 | role('production') { layers('main') } |
|
|
543 | 569 | } |
|
|
544 | 570 | |
|
|
545 | 571 | variantSources.whenAvailable { |
|
|
546 | 572 | variantSources.lateConfigurationPolicy { |
|
|
547 | 573 | allowLateConfiguration() |
|
|
548 | 574 | } |
|
|
549 | 575 | } |
|
|
550 | 576 | """); |
|
|
551 | 577 | |
|
|
552 | 578 | assertBuildFails("Lazy configuration policy already applied", "help"); |
|
|
553 | 579 | } |
|
|
554 | 580 | } |
General Comments 0
You need to be logged in to leave comments.
Login now
