| @@ -281,6 +281,16 variantArtifacts { | |||
|
|
281 | 281 | } |
|
|
282 | 282 | ``` |
|
|
283 | 283 | |
|
|
284 | Slot bodies have two assembly modes: | |
|
|
285 | ||
|
|
286 | - contribution-based assembly with `from(...)`, `fromVariant(...)`, | |
|
|
287 | `fromRole(...)`, or `fromLayer(...)`; the plugin copies selected inputs into a | |
|
|
288 | managed directory and publishes that directory; | |
|
|
289 | - task-produced assembly with `producedBy(task) { outputFile }`; the mapped task | |
|
|
290 | output file or directory is published directly. | |
|
|
291 | ||
|
|
292 | These modes are mutually exclusive for one slot. | |
|
|
293 | ||
|
|
284 | 294 | The artifact API is still considered pre-1.0 and may change. |
|
|
285 | 295 | |
|
|
286 | 296 | ## Publication Status |
| @@ -279,6 +279,9 final class VariantArtifactsRegistry imp | |||
|
|
279 | 279 | |
|
|
280 | 280 | interface ArtifactAssemblyRules { |
|
|
281 | 281 | void from(Object input); |
|
|
282 | <T extends Task> void producedBy( | |
|
|
283 | TaskProvider<T> task, | |
|
|
284 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> output); | |
|
|
282 | 285 | void fromVariant(Action<? super OutputSelectionSpec> action); |
|
|
283 | 286 | void fromRole(String roleName, Action<? super OutputSelectionSpec> action); |
|
|
284 | 287 | void fromLayer(String layerName, Action<? super OutputSelectionSpec> action); |
| @@ -339,6 +342,8 This means: | |||
|
|
339 | 342 | - slot inputs remain live; |
|
|
340 | 343 | - `from(...)`, `fromVariant(...)`, `fromRole(...)`, `fromLayer(...)` may keep |
|
|
341 | 344 | contributing inputs until task execution; |
|
|
345 | - `producedBy(...)` publishes an existing task output directly and does not | |
|
|
346 | create the managed copy assembly for that slot; | |
|
|
342 | 347 | - `ArtifactAssembly` may expose live `FileCollection`, `Provider`, and task |
|
|
343 | 348 | wiring; |
|
|
344 | 349 | - external task outputs remain outside the control of this model and must be |
| @@ -412,8 +417,9 variantArtifacts { | |||
|
|
412 | 417 | } |
|
|
413 | 418 | |
|
|
414 | 419 | slot("bundleMetadata") { |
|
|
415 | from(someTask) | |
|
|
416 | from(layout.buildDirectory.file("generated/meta.json")) | |
|
|
420 | producedBy(writePackageMetadata) { | |
|
|
421 | outputFile | |
|
|
422 | } | |
|
|
417 | 423 | } |
|
|
418 | 424 | } |
|
|
419 | 425 | } |
| @@ -428,7 +434,10 variantArtifacts { | |||
|
|
428 | 434 | belong to the given role projection; |
|
|
429 | 435 | - `fromLayer(layer) { output(...) }` selects named outputs from the compile unit |
|
|
430 | 436 | of the current variant and the given layer, if such unit exists. |
|
|
437 | - `producedBy(task) { outputFile }` maps an existing producing task to the single | |
|
|
438 | file or directory published for the slot. | |
|
|
431 | 439 | |
|
|
440 | Contribution forms and `producedBy(...)` are mutually exclusive for one slot. | |
|
|
432 | 441 | The DSL stores declarations, not resolved file collections. |
|
|
433 | 442 | |
|
|
434 | 443 | --- |
| @@ -499,6 +508,9 For one outgoing variant: | |||
|
|
499 | 508 | - expands to one compile unit `(variant, layer)` when it exists; |
|
|
500 | 509 | - `from(Object)` |
|
|
501 | 510 | - bypasses `variantSources` completely. |
|
|
511 | - `producedBy(task)` | |
|
|
512 | - bypasses contribution resolution and registers the task output as the slot | |
|
|
513 | artifact directly. | |
|
|
502 | 514 | |
|
|
503 | 515 | After compile units are known, the bridge asks |
|
|
504 | 516 | `ctx.getSourceSets().getSourceSet(unit)` for each selected unit and resolves the |
| @@ -1,6 +1,13 | |||
|
|
1 | 1 | package org.implab.gradle.variants.artifacts; |
|
|
2 | 2 | |
|
|
3 | import java.util.function.Function; | |
|
|
4 | ||
|
|
3 | 5 | import org.gradle.api.Action; |
|
|
6 | import org.gradle.api.InvalidUserDataException; | |
|
|
7 | import org.gradle.api.Task; | |
|
|
8 | import org.gradle.api.file.FileSystemLocation; | |
|
|
9 | import org.gradle.api.provider.Provider; | |
|
|
10 | import org.gradle.api.tasks.TaskProvider; | |
|
|
4 | 11 | import groovy.lang.Closure; |
|
|
5 | 12 | import org.implab.gradle.common.core.lang.Closures; |
|
|
6 | 13 | |
| @@ -24,6 +31,45 public interface ArtifactAssemblySpec { | |||
|
|
24 | 31 | void from(Object artifact); |
|
|
25 | 32 | |
|
|
26 | 33 | /** |
|
|
34 | * Registers a task that directly produces the published slot artifact. | |
|
|
35 | * | |
|
|
36 | * <p>Use this method when the slot is produced as one file or directory by an | |
|
|
37 | * existing task, for example generated package metadata. Unlike {@link #from(Object)} | |
|
|
38 | * and topology-aware selectors, this does not copy inputs into a managed assembly | |
|
|
39 | * directory. The mapped task output becomes the published artifact itself. | |
|
|
40 | * | |
|
|
41 | * <p>This mode is mutually exclusive with contribution-based assembly methods | |
|
|
42 | * such as {@link #from(Object)}, {@link #fromVariant(Action)}, {@link #fromRole(String, Action)}, | |
|
|
43 | * and {@link #fromLayer(String, Action)} for the same slot. | |
|
|
44 | * | |
|
|
45 | * @param <T> task type | |
|
|
46 | * @param task task provider producing the artifact | |
|
|
47 | * @param artifact maps the producing task to its output file or directory provider | |
|
|
48 | */ | |
|
|
49 | <T extends Task> void producedBy( | |
|
|
50 | TaskProvider<T> task, | |
|
|
51 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> artifact); | |
|
|
52 | ||
|
|
53 | default <T extends Task> void producedBy(TaskProvider<T> task, Closure<?> closure) { | |
|
|
54 | producedBy(task, taskInstance -> producedArtifact(closure, taskInstance)); | |
|
|
55 | } | |
|
|
56 | ||
|
|
57 | @SuppressWarnings("unchecked") | |
|
|
58 | private static Provider<? extends FileSystemLocation> producedArtifact(Closure<?> closure, Task task) { | |
|
|
59 | var c = (Closure<?>) closure.clone(); | |
|
|
60 | c.setResolveStrategy(Closure.DELEGATE_FIRST); | |
|
|
61 | c.setDelegate(task); | |
|
|
62 | ||
|
|
63 | var artifact = c.call(task); | |
|
|
64 | if (artifact instanceof Provider<?>) { | |
|
|
65 | return (Provider<? extends FileSystemLocation>) artifact; | |
|
|
66 | } | |
|
|
67 | ||
|
|
68 | throw new InvalidUserDataException("Produced artifact mapper for task '" + task.getName() | |
|
|
69 | + "' must return Provider<? extends FileSystemLocation>"); | |
|
|
70 | } | |
|
|
71 | ||
|
|
72 | /** | |
|
|
27 | 73 | * Selects outputs from the whole variant scope. |
|
|
28 | 74 | * |
|
|
29 | 75 | * @param action output selection rule |
| @@ -32,7 +32,9 public class ArtifactAssemblyBinder impl | |||
|
|
32 | 32 | // Bind the primary artifact set to the root outgoing configuration. |
|
|
33 | 33 | resolver.when( |
|
|
34 | 34 | new ArtifactSlot(variant, primarySlot), |
|
|
35 |
assembly -> outgoing.artifact( |
|
|
|
35 | assembly -> outgoing.artifact( | |
|
|
36 | assembly.getArtifact(), | |
|
|
37 | artifact -> artifact.builtBy(assembly.getAssemblyTask()))); | |
|
|
36 | 38 | |
|
|
37 | 39 | // Bind non-primary slots to Gradle secondary artifact variants. |
|
|
38 | 40 | slots.all(slot -> { |
| @@ -46,7 +48,9 public class ArtifactAssemblyBinder impl | |||
|
|
46 | 48 | // otherwise be realized only after dependency resolution starts. |
|
|
47 | 49 | assembly -> outgoing.getVariants() |
|
|
48 | 50 | .create(slot.getName()) |
|
|
49 |
.artifact( |
|
|
|
51 | .artifact( | |
|
|
52 | assembly.getArtifact(), | |
|
|
53 | artifact -> artifact.builtBy(assembly.getAssemblyTask()))); | |
|
|
50 | 54 | }); |
|
|
51 | 55 | }); |
|
|
52 | 56 | } |
| @@ -4,38 +4,57 import java.util.HashMap; | |||
|
|
4 | 4 | import java.util.HashSet; |
|
|
5 | 5 | import java.util.Map; |
|
|
6 | 6 | import java.util.Set; |
|
|
7 | import java.util.function.Function; | |
|
|
8 | import java.util.stream.Stream; | |
|
|
7 | 9 | |
|
|
8 | 10 | import org.eclipse.jdt.annotation.NonNullByDefault; |
|
|
9 | 11 | import org.gradle.api.Action; |
|
|
12 | import org.gradle.api.InvalidUserDataException; | |
|
|
13 | import org.gradle.api.Task; | |
|
|
10 | 14 | import org.gradle.api.file.ConfigurableFileCollection; |
|
|
11 | 15 | import org.gradle.api.file.Directory; |
|
|
12 | 16 | import org.gradle.api.file.DirectoryProperty; |
|
|
13 | 17 | import org.gradle.api.file.FileCollection; |
|
|
18 | import org.gradle.api.file.FileSystemLocation; | |
|
|
14 | 19 | import org.gradle.api.model.ObjectFactory; |
|
|
15 | 20 | import org.gradle.api.provider.Provider; |
|
|
16 | 21 | import org.gradle.api.tasks.Sync; |
|
|
17 | 22 | import org.gradle.api.tasks.TaskContainer; |
|
|
23 | import org.gradle.api.tasks.TaskProvider; | |
|
|
18 | 24 | import org.gradle.language.base.plugins.LifecycleBasePlugin; |
|
|
19 | 25 | import org.implab.gradle.common.core.lang.FilePaths; |
|
|
26 | import org.implab.gradle.common.core.lang.Strings; | |
|
|
20 | 27 | import org.implab.gradle.variants.artifacts.ArtifactAssembly; |
|
|
21 | 28 | import org.implab.gradle.variants.artifacts.ArtifactAssemblySpec; |
|
|
22 | 29 | import org.implab.gradle.variants.artifacts.ArtifactSlot; |
|
|
30 | import org.implab.gradle.variants.artifacts.OutputSelectionSpec; | |
|
|
31 | import org.implab.gradle.variants.core.Layer; | |
|
|
32 | import org.implab.gradle.variants.core.Role; | |
|
|
23 | 33 | import org.implab.gradle.variants.sources.CompileUnit; |
|
|
24 | 34 | import org.implab.gradle.variants.sources.CompileUnitsView; |
|
|
25 | 35 | import org.implab.gradle.variants.sources.RoleProjectionsView; |
|
|
26 | 36 | import org.implab.gradle.variants.sources.SourceSetMaterializer; |
|
|
27 | 37 | |
|
|
28 | 38 | /** |
|
|
29 |
* Adapts slot contribution declarations to materialized |
|
|
|
39 | * Adapts slot contribution declarations to materialized | |
|
|
40 | * {@link ArtifactAssembly} | |
|
|
30 | 41 | * handles. |
|
|
31 | 42 | * |
|
|
32 | * <p>The handler creates one {@link Sync} task per {@link ArtifactSlot}. The task | |
|
|
33 | * copies all collected slot inputs into a single output directory. That output | |
|
|
34 | * directory is then registered in {@link ArtifactAssemblyRegistry} as the | |
|
|
35 | * published artifact for the slot. | |
|
|
43 | * <p> | |
|
|
44 | * Contribution-based assemblies create one {@link Sync} task per | |
|
|
45 | * {@link ArtifactSlot}. The task copies all collected slot inputs into a single | |
|
|
46 | * output directory. That output directory is then registered in | |
|
|
47 | * {@link ArtifactAssemblyRegistry} as the published artifact for the slot. | |
|
|
36 | 48 | * |
|
|
37 | * <p>Input collection uses {@link SlotContributionVisitor}. Each contribution is | |
|
|
38 | * converted to a {@link SlotInputKey}; duplicate keys are ignored so that repeated | |
|
|
49 | * <p> | |
|
|
50 | * Task-produced assemblies bypass the managed copy task. The producer task is | |
|
|
51 | * registered directly in {@link ArtifactAssemblyRegistry}, and its mapped output | |
|
|
52 | * file or directory becomes the published slot artifact. | |
|
|
53 | * | |
|
|
54 | * <p> | |
|
|
55 | * Input collection uses {@link SlotContributionVisitor}. Each contribution is | |
|
|
56 | * converted to a {@link SlotInputKey}; duplicate keys are ignored so that | |
|
|
57 | * repeated | |
|
|
39 | 58 | * topology-based selections do not add the same input twice. |
|
|
40 | 59 | */ |
|
|
41 | 60 | @NonNullByDefault |
| @@ -56,6 +75,8 public class ArtifactAssemblyHandler { | |||
|
|
56 | 75 | |
|
|
57 | 76 | private final Map<ArtifactSlot, SlotAssembly> slotInputs = new HashMap<>(); |
|
|
58 | 77 | |
|
|
78 | private final Map<ArtifactSlot, AssemblyMode> assemblyModes = new HashMap<>(); | |
|
|
79 | ||
|
|
59 | 80 | public ArtifactAssemblyHandler( |
|
|
60 | 81 | ObjectFactory objects, |
|
|
61 | 82 | TaskContainer tasks, |
| @@ -78,18 +99,21 public class ArtifactAssemblyHandler { | |||
|
|
78 | 99 | } |
|
|
79 | 100 | |
|
|
80 | 101 | public void configureAssembly(ArtifactSlot artifactSlot, Action<? super ArtifactAssemblySpec> action) { |
|
|
81 |
var |
|
|
|
82 | var spec = new DefaultArtifactAssemblySpec(objects, c -> c.accept(visitor)); | |
|
|
102 | var spec = new DefaultArtifactAssemblySpec(artifactSlot); | |
|
|
83 | 103 | action.execute(spec); |
|
|
84 | 104 | } |
|
|
85 | 105 | |
|
|
86 | public SlotContributionVisitor contributionVisitor(ArtifactSlot artifactSlot) { | |
|
|
87 |
var |
|
|
|
88 | return new ContributionVisitor(artifactSlot, assembly); | |
|
|
106 | private void useAssemblyMode(ArtifactSlot artifactSlot, AssemblyMode mode) { | |
|
|
107 | var previous = assemblyModes.putIfAbsent(artifactSlot, mode); | |
|
|
108 | if (previous != null && previous != mode) { | |
|
|
109 | throw new InvalidUserDataException("Artifact slot '" + artifactSlot | |
|
|
110 | + "' cannot mix task-produced artifact and contribution-based assembly"); | |
|
|
111 | } | |
|
|
89 | 112 | } |
|
|
90 | 113 | |
|
|
91 | 114 | /** |
|
|
92 |
* Creates the assembly task for the given slot and registers its output |
|
|
|
115 | * Creates the assembly task for the given slot and registers its output | |
|
|
116 | * artifact. | |
|
|
93 | 117 | */ |
|
|
94 | 118 | private SlotAssembly createSlotAssembly(ArtifactSlot artifactSlot) { |
|
|
95 | 119 | var assembly = new SlotAssembly(); |
| @@ -199,4 +223,99 public class ArtifactAssemblyHandler { | |||
|
|
199 | 223 | return inputs; |
|
|
200 | 224 | } |
|
|
201 | 225 | } |
|
|
226 | ||
|
|
227 | private enum AssemblyMode { | |
|
|
228 | CONTRIBUTIONS, | |
|
|
229 | TASK_PRODUCER | |
|
|
230 | } | |
|
|
231 | ||
|
|
232 | /** | |
|
|
233 | * Default DSL facade for collecting {@link SlotContribution} declarations. | |
|
|
234 | * | |
|
|
235 | * <p> | |
|
|
236 | * The spec does not validate topology references immediately. It translates DSL | |
|
|
237 | * calls to contribution objects and passes them to the supplied consumer; | |
|
|
238 | * semantic | |
|
|
239 | * validation happens later when the assembly handler resolves contributions | |
|
|
240 | * against the finalized source model. | |
|
|
241 | */ | |
|
|
242 | class DefaultArtifactAssemblySpec implements ArtifactAssemblySpec { | |
|
|
243 | ||
|
|
244 | private final ArtifactSlot artifactSlot; | |
|
|
245 | ||
|
|
246 | DefaultArtifactAssemblySpec(ArtifactSlot artifactSlot) { | |
|
|
247 | this.artifactSlot = artifactSlot; | |
|
|
248 | } | |
|
|
249 | ||
|
|
250 | @Override | |
|
|
251 | public void from(Object artifact) { | |
|
|
252 | contribute(new DirectContribution(artifact)); | |
|
|
253 | } | |
|
|
254 | ||
|
|
255 | @Override | |
|
|
256 | public <T extends Task> void producedBy( | |
|
|
257 | TaskProvider<T> task, | |
|
|
258 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> artifact) { | |
|
|
259 | registerProducedArtifact(task, artifact); | |
|
|
260 | } | |
|
|
261 | ||
|
|
262 | @Override | |
|
|
263 | public void fromVariant(Action<? super OutputSelectionSpec> action) { | |
|
|
264 | contribute(new VariantOutputsContribution(outputs(action))); | |
|
|
265 | } | |
|
|
266 | ||
|
|
267 | @Override | |
|
|
268 | public void fromRole(String roleName, Action<? super OutputSelectionSpec> action) { | |
|
|
269 | ||
|
|
270 | contribute(new RoleOutputsContribution( | |
|
|
271 | objects.named(Role.class, roleName), | |
|
|
272 | outputs(action))); | |
|
|
273 | } | |
|
|
274 | ||
|
|
275 | @Override | |
|
|
276 | public void fromLayer(String layerName, Action<? super OutputSelectionSpec> action) { | |
|
|
277 | contribute(new LayerOutputsContribution( | |
|
|
278 | objects.named(Layer.class, layerName), | |
|
|
279 | outputs(action))); | |
|
|
280 | } | |
|
|
281 | ||
|
|
282 | private static Set<String> outputs(Action<? super OutputSelectionSpec> action) { | |
|
|
283 | var spec = new OutputsSetSpec(); | |
|
|
284 | action.execute(spec); | |
|
|
285 | return spec.outputs(); | |
|
|
286 | } | |
|
|
287 | ||
|
|
288 | void contribute(SlotContribution contribution) { | |
|
|
289 | useAssemblyMode(artifactSlot, AssemblyMode.CONTRIBUTIONS); | |
|
|
290 | var assembly = slotInputs.computeIfAbsent(artifactSlot, ArtifactAssemblyHandler.this::createSlotAssembly); | |
|
|
291 | var contributionVisitor = new ContributionVisitor(artifactSlot, assembly); | |
|
|
292 | contribution.accept(contributionVisitor); | |
|
|
293 | } | |
|
|
294 | ||
|
|
295 | <T extends Task> void registerProducedArtifact( | |
|
|
296 | TaskProvider<T> task, | |
|
|
297 | Function<? super T, ? extends Provider<? extends FileSystemLocation>> artifact) { | |
|
|
298 | useAssemblyMode(artifactSlot, AssemblyMode.TASK_PRODUCER); | |
|
|
299 | assemblyRegistry.register(artifactSlot, task, artifact); | |
|
|
300 | } | |
|
|
301 | ||
|
|
302 | } | |
|
|
303 | ||
|
|
304 | /** Simple implementation of {@link OutputSelectionSpec}. */ | |
|
|
305 | static class OutputsSetSpec implements OutputSelectionSpec { | |
|
|
306 | private final Set<String> outputs = new HashSet<>(); | |
|
|
307 | ||
|
|
308 | @Override | |
|
|
309 | public void output(String name, String... extra) { | |
|
|
310 | Stream.concat(Stream.of(name), Stream.of(extra)) | |
|
|
311 | .map(Strings::requireNonBlank) | |
|
|
312 | .forEach(outputs::add); | |
|
|
313 | } | |
|
|
314 | ||
|
|
315 | Set<String> outputs() { | |
|
|
316 | return Set.copyOf(outputs); | |
|
|
317 | } | |
|
|
318 | ||
|
|
319 | } | |
|
|
320 | ||
|
|
202 | 321 | } |
| @@ -422,6 +422,170 class VariantArtifactsPluginFunctionalTe | |||
|
|
422 | 422 | } |
|
|
423 | 423 | |
|
|
424 | 424 | @Test |
|
|
425 | void publishesTaskProducedFileArtifactDirectly() throws Exception { | |
|
|
426 | writeFile("settings.gradle", """ | |
|
|
427 | rootProject.name = 'variant-artifacts-task-produced-file' | |
|
|
428 | include 'producer', 'consumer' | |
|
|
429 | """); | |
|
|
430 | writeBuildFile(""" | |
|
|
431 | import org.gradle.api.DefaultTask | |
|
|
432 | import org.gradle.api.attributes.Attribute | |
|
|
433 | import org.gradle.api.file.RegularFileProperty | |
|
|
434 | import org.gradle.api.tasks.OutputFile | |
|
|
435 | import org.gradle.api.tasks.TaskAction | |
|
|
436 | ||
|
|
437 | def variantAttr = Attribute.of('test.variant', String) | |
|
|
438 | def slotAttr = Attribute.of('test.slot', String) | |
|
|
439 | ||
|
|
440 | abstract class WritePackageMetadata extends DefaultTask { | |
|
|
441 | @OutputFile | |
|
|
442 | abstract RegularFileProperty getOutputFile() | |
|
|
443 | ||
|
|
444 | @TaskAction | |
|
|
445 | void write() { | |
|
|
446 | def file = outputFile.get().asFile | |
|
|
447 | file.parentFile.mkdirs() | |
|
|
448 | file.text = '{"name":"demo"}\\n' | |
|
|
449 | } | |
|
|
450 | } | |
|
|
451 | ||
|
|
452 | project(':producer') { | |
|
|
453 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin | |
|
|
454 | ||
|
|
455 | variants.layers.create('main') | |
|
|
456 | variants.roles.create('main') | |
|
|
457 | variants.variant('browser') { | |
|
|
458 | role('main') { | |
|
|
459 | layers('main') | |
|
|
460 | } | |
|
|
461 | } | |
|
|
462 | ||
|
|
463 | def writePackageMetadata = tasks.register('writePackageMetadata', WritePackageMetadata) { | |
|
|
464 | outputFile.set(layout.buildDirectory.file('generated/package.json')) | |
|
|
465 | } | |
|
|
466 | ||
|
|
467 | variantArtifacts { | |
|
|
468 | variant('browser') { | |
|
|
469 | primarySlot('packageMetadata') { | |
|
|
470 | producedBy(writePackageMetadata) { | |
|
|
471 | outputFile | |
|
|
472 | } | |
|
|
473 | } | |
|
|
474 | } | |
|
|
475 | ||
|
|
476 | whenOutgoingConfiguration { publication -> | |
|
|
477 | publication.configuration { | |
|
|
478 | attributes.attribute(variantAttr, publication.variant.name) | |
|
|
479 | } | |
|
|
480 | } | |
|
|
481 | ||
|
|
482 | whenOutgoingSlot { publication -> | |
|
|
483 | publication.artifactAttributes { | |
|
|
484 | attribute(slotAttr, publication.artifactSlot.slot.name) | |
|
|
485 | } | |
|
|
486 | } | |
|
|
487 | } | |
|
|
488 | ||
|
|
489 | tasks.register('checkNoManagedAssembly') { | |
|
|
490 | doLast { | |
|
|
491 | def assemblyTasks = tasks.names | |
|
|
492 | .findAll { it.startsWith('assembleVariantArtifactSlot') } | |
|
|
493 | .sort() | |
|
|
494 | println('producerAssemblyTasks=' + assemblyTasks.join(',')) | |
|
|
495 | assert assemblyTasks.empty | |
|
|
496 | } | |
|
|
497 | } | |
|
|
498 | } | |
|
|
499 | ||
|
|
500 | project(':consumer') { | |
|
|
501 | configurations { | |
|
|
502 | compileView { | |
|
|
503 | canBeResolved = true | |
|
|
504 | canBeConsumed = false | |
|
|
505 | canBeDeclared = true | |
|
|
506 | attributes { | |
|
|
507 | attribute(variantAttr, 'browser') | |
|
|
508 | attribute(slotAttr, 'packageMetadata') | |
|
|
509 | } | |
|
|
510 | } | |
|
|
511 | } | |
|
|
512 | ||
|
|
513 | dependencies { | |
|
|
514 | compileView project(':producer') | |
|
|
515 | } | |
|
|
516 | ||
|
|
517 | tasks.register('probe') { | |
|
|
518 | dependsOn configurations.compileView | |
|
|
519 | dependsOn ':producer:checkNoManagedAssembly' | |
|
|
520 | ||
|
|
521 | doLast { | |
|
|
522 | def files = configurations.compileView.files | |
|
|
523 | println('resolvedFiles=' + files.collect { it.name }.sort().join(',')) | |
|
|
524 | println('metadata=' + files.iterator().next().text.trim()) | |
|
|
525 | } | |
|
|
526 | } | |
|
|
527 | } | |
|
|
528 | """); | |
|
|
529 | ||
|
|
530 | BuildResult result = runner(":consumer:probe").build(); | |
|
|
531 | ||
|
|
532 | assertTrue(result.getOutput().contains("producerAssemblyTasks=")); | |
|
|
533 | assertTrue(result.getOutput().contains("resolvedFiles=package.json")); | |
|
|
534 | assertTrue(result.getOutput().contains("metadata={\"name\":\"demo\"}")); | |
|
|
535 | assertTrue(result.task(":producer:writePackageMetadata").getOutcome() == TaskOutcome.SUCCESS); | |
|
|
536 | assertTrue(result.task(":consumer:probe").getOutcome() == TaskOutcome.SUCCESS); | |
|
|
537 | } | |
|
|
538 | ||
|
|
539 | @Test | |
|
|
540 | void failsWhenTaskProducedArtifactIsMixedWithContributionAssembly() throws Exception { | |
|
|
541 | writeSettings("variant-artifacts-mixed-assembly-mode"); | |
|
|
542 | writeFile("inputs/marker.txt", "marker\n"); | |
|
|
543 | writeBuildFile(""" | |
|
|
544 | import org.gradle.api.DefaultTask | |
|
|
545 | import org.gradle.api.file.RegularFileProperty | |
|
|
546 | import org.gradle.api.tasks.OutputFile | |
|
|
547 | import org.gradle.api.tasks.TaskAction | |
|
|
548 | ||
|
|
549 | apply plugin: org.implab.gradle.variants.VariantArtifactsPlugin | |
|
|
550 | ||
|
|
551 | abstract class WritePackageMetadata extends DefaultTask { | |
|
|
552 | @OutputFile | |
|
|
553 | abstract RegularFileProperty getOutputFile() | |
|
|
554 | ||
|
|
555 | @TaskAction | |
|
|
556 | void write() { | |
|
|
557 | outputFile.get().asFile.text = '{}\\n' | |
|
|
558 | } | |
|
|
559 | } | |
|
|
560 | ||
|
|
561 | variants.layers.create('main') | |
|
|
562 | variants.roles.create('main') | |
|
|
563 | variants.variant('browser') { | |
|
|
564 | role('main') { | |
|
|
565 | layers('main') | |
|
|
566 | } | |
|
|
567 | } | |
|
|
568 | ||
|
|
569 | def writePackageMetadata = tasks.register('writePackageMetadata', WritePackageMetadata) { | |
|
|
570 | outputFile.set(layout.buildDirectory.file('generated/package.json')) | |
|
|
571 | } | |
|
|
572 | ||
|
|
573 | variantArtifacts { | |
|
|
574 | variant('browser') { | |
|
|
575 | primarySlot('packageMetadata') { | |
|
|
576 | producedBy(writePackageMetadata) { | |
|
|
577 | outputFile | |
|
|
578 | } | |
|
|
579 | from(layout.projectDirectory.file('inputs/marker.txt')) | |
|
|
580 | } | |
|
|
581 | } | |
|
|
582 | } | |
|
|
583 | """); | |
|
|
584 | ||
|
|
585 | assertBuildFails("cannot mix task-produced artifact and contribution-based assembly", "help"); | |
|
|
586 | } | |
|
|
587 | ||
|
|
588 | @Test | |
|
|
425 | 589 | void combinesDirectAndTopologyAwareSlotInputs() throws Exception { |
|
|
426 | 590 | writeSettings("variant-artifacts-combined-inputs"); |
|
|
427 | 591 | writeFile("inputs/base.js", "console.log('base')\n"); |
|
|
1 | NO CONTENT: file was removed |
General Comments 0
You need to be logged in to leave comments.
Login now
