variant-artifacts-plugin.md
311 lines
| 8.0 KiB
| text/x-minidsrc
|
MarkdownLexer
/ common / variant-artifacts-plugin.md
|
|
r34 | # Variant Artifacts Plugin | ||
| ## NAME | ||||
| `VariantsArtifactsPlugin` и extension `variantArtifacts`. | ||||
| ## SYNOPSIS | ||||
| ```groovy | ||||
| import org.gradle.api.attributes.Attribute | ||||
| plugins { | ||||
| id 'org.implab.gradle-variants-artifacts' | ||||
| } | ||||
| def variantAttr = Attribute.of('test.variant', String) | ||||
| def slotAttr = Attribute.of('test.slot', String) | ||||
| variants { | ||||
| layer('main') | ||||
| variant('browser') { | ||||
| role('main') { layers('main') } | ||||
| } | ||||
| } | ||||
| variantSources { | ||||
| bind('main') { | ||||
| configureSourceSet { | ||||
| declareOutputs('types', 'js', 'resources') | ||||
| } | ||||
| } | ||||
| } | ||||
| variantArtifacts { | ||||
| variant('browser') { | ||||
| primarySlot('typesPackage') { | ||||
| fromVariant { | ||||
| output('types') | ||||
| } | ||||
| } | ||||
| slot('js') { | ||||
| fromVariant { | ||||
| output('js') | ||||
| } | ||||
| } | ||||
| slot('resources') { | ||||
| fromVariant { | ||||
| output('resources') | ||||
| } | ||||
| } | ||||
| } | ||||
| whenOutgoingVariant { publication -> | ||||
| publication.configureConfiguration { | ||||
| attributes.attribute(variantAttr, publication.variantName()) | ||||
| } | ||||
| publication.primarySlot().configureArtifactAttributes { | ||||
| attribute(slotAttr, publication.primarySlot().slotName()) | ||||
| } | ||||
| publication.requireSlot('js').configureArtifactAttributes { | ||||
| attribute(slotAttr, 'js') | ||||
| } | ||||
| publication.requireSlot('resources').configureArtifactAttributes { | ||||
| attribute(slotAttr, 'resources') | ||||
| } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| ## DESCRIPTION | ||||
| `VariantsArtifactsPlugin` применяет `VariantsSourcesPlugin`, затем строит | ||||
| outgoing publication model поверх `variantSources`. | ||||
| ### publication model | ||||
| Для каждого `variantArtifacts.variant('<name>')` публикуется один outgoing | ||||
| build variant: | ||||
| - primary configuration `<variant>Elements`; | ||||
| - primary artifact slot на самой configuration; | ||||
| - secondary variants внутри `configuration.outgoing.variants` для остальных slots. | ||||
| Пример: | ||||
| - `browserElements` | ||||
| - primary slot: `typesPackage` | ||||
| - secondary variants: `js`, `resources` | ||||
| Это разделяет: | ||||
| - graph selection build variant-а; | ||||
| - artifact selection внутри уже выбранного variant-а. | ||||
| ### slot bindings | ||||
| `slot('<name>')` описывает, какие outputs из `variantSources` войдут в artifact | ||||
| representation этого slot-а. | ||||
| Binding rules: | ||||
| - `fromVariant { output(...) }` | ||||
| - `fromRole('<role>') { output(...) }` | ||||
| - `fromLayer('<layer>') { output(...) }` | ||||
| Каждый slot materialize-ится в отдельный `ArtifactAssembly`: | ||||
| - task: `process<Variant><Slot>`; | ||||
| - output dir: `build/variant-artifacts/<variant>/<slot>`. | ||||
| ### primary slot | ||||
| Primary slot задает artifact, который публикуется как основной artifact | ||||
| configuration `<variant>Elements`. | ||||
| Формы DSL: | ||||
| ```groovy | ||||
| variant('browser') { | ||||
| primarySlot('typesPackage') | ||||
| slot('typesPackage') { | ||||
| fromVariant { output('types') } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| или sugar: | ||||
| ```groovy | ||||
| variant('browser') { | ||||
| primarySlot('typesPackage') { | ||||
| fromVariant { output('types') } | ||||
| } | ||||
| } | ||||
| ``` | ||||
| Правила: | ||||
| - если slot один, он считается primary неявно; | ||||
| - если slots несколько, `primarySlot(...)` обязателен; | ||||
| - `primarySlot` должен ссылаться на существующий slot. | ||||
| ## LIFECYCLE | ||||
| - `VariantsArtifactsPlugin` ждет `variants.whenFinalized(...)`; | ||||
| - после этого валидирует `variantArtifacts`; | ||||
| - регистрирует `ArtifactAssembly` по каждому slot; | ||||
| - materialize-ит outgoing publications; | ||||
| - вызывает `whenOutgoingVariant(...)`; | ||||
| - callbacks replayable. | ||||
| После finalize мутации `variantArtifacts` запрещены. | ||||
| ## EVENTS | ||||
| ### whenOutgoingVariant | ||||
| Replayable callback на готовую outgoing publication variant-а. | ||||
| Подходит для: | ||||
| - настройки общих attributes build variant-а один раз; | ||||
| - настройки per-slot artifact attributes; | ||||
| - доконфигурации `ArtifactAssembly`. | ||||
| ## PAYLOAD TYPES | ||||
| ### OutgoingVariantPublication | ||||
| Содержит: | ||||
| - `variantName()`; | ||||
| - `topologyVariant()`; | ||||
| - `variantArtifact()`; | ||||
| - `configuration()` — primary `<variant>Elements`; | ||||
| - `primarySlot()`; | ||||
| - `slots()` — все slot publications; | ||||
| - `secondarySlots()`; | ||||
| - `findSlot(name)`, `requireSlot(name)`. | ||||
| Sugar: | ||||
| - `configureConfiguration(Action|Closure)`. | ||||
| ### OutgoingArtifactSlotPublication | ||||
| Содержит: | ||||
| - `slotName()`; | ||||
| - `primary()`; | ||||
| - `slot()` — модель `VariantArtifactSlot`; | ||||
| - `assembly()`. | ||||
| Sugar: | ||||
| - `configureAssembly(Action|Closure)`; | ||||
| - `configureArtifactAttributes(Action|Closure)`. | ||||
| `configureArtifactAttributes(...)` пишет attributes: | ||||
| - в `Configuration.attributes` для primary slot; | ||||
| - в `ConfigurationVariant.attributes` для secondary slot. | ||||
| ## CONSUMER SIDE | ||||
| ### primary resolution | ||||
| Обычное inter-project resolution выбирает primary artifact `<variant>Elements`. | ||||
| Пример: | ||||
| ```groovy | ||||
| configurations { | ||||
| compileView { | ||||
| canBeResolved = true | ||||
| canBeConsumed = false | ||||
| canBeDeclared = true | ||||
| attributes { | ||||
| attribute(variantAttr, 'browser') | ||||
| attribute(slotAttr, 'typesPackage') | ||||
| } | ||||
| } | ||||
| } | ||||
| dependencies { | ||||
| compileView project(':producer') | ||||
| } | ||||
| ``` | ||||
| ### artifact selection for secondary slots | ||||
| Secondary artifacts выбираются через `artifactView`. | ||||
| ```groovy | ||||
| def jsFiles = configurations.compileView.incoming.artifactView { | ||||
| attributes { | ||||
| attribute(slotAttr, 'js') | ||||
| } | ||||
| }.files | ||||
| ``` | ||||
| Здесь graph variant уже выбран, а `artifactView` выбирает нужный secondary | ||||
| artifact representation. | ||||
| ## VALIDATION | ||||
| Проверяется: | ||||
| - variant существует в topology model; | ||||
| - slot bindings не ссылаются на неизвестные role/layer; | ||||
| - при нескольких slots указан `primarySlot`; | ||||
| - `primarySlot` ссылается на существующий slot. | ||||
| ## API | ||||
| ### VariantArtifactsExtension | ||||
| - `variant(String)` — получить/создать variant artifact model; | ||||
| - `variant(String, Action|Closure)` — сконфигурировать variant artifact; | ||||
| - `getVariants()` — контейнер variant artifacts; | ||||
| - `findVariant(name)`, `requireVariant(name)`; | ||||
| - `whenOutgoingVariant(...)`. | ||||
| ### VariantArtifact | ||||
| - `slot(String)` — получить/создать slot; | ||||
| - `slot(String, Action|Closure)` — сконфигурировать slot; | ||||
| - `primarySlot(String)` — назначить primary slot; | ||||
| - `primarySlot(String, Action|Closure)` — sugar: configure slot + mark as primary; | ||||
| - `getSlots()`; | ||||
| - `findSlot(name)`, `requireSlot(name)`; | ||||
| - `findPrimarySlotName()`, `requirePrimarySlotName()`; | ||||
| - `findPrimarySlot()`, `requirePrimarySlot()`. | ||||
| ### VariantArtifactSlot | ||||
| - `fromVariant(...)`; | ||||
| - `fromRole(String, ...)`; | ||||
| - `fromLayer(String, ...)`. | ||||
| ### OutputSelectionSpec | ||||
| - `output(name)`; | ||||
| - `output(name, extra...)`. | ||||
| ## KEY CLASSES | ||||
| - `VariantsArtifactsPlugin` — plugin adapter и materialization outgoing variants. | ||||
| - `VariantArtifactsExtension` — root DSL и lifecycle. | ||||
| - `VariantArtifact` — outgoing build variant model. | ||||
| - `VariantArtifactSlot` — artifact representation slot. | ||||
| - `OutgoingVariantPublication` — payload variant-level publication callback. | ||||
| - `OutgoingArtifactSlotPublication` — payload per-slot publication callback. | ||||
| - `ArtifactAssembly` — assembled files for a slot. | ||||
| ## NOTES | ||||
| - `common` не навязывает доменную логику выбора primary slot. | ||||
| - `common` не фиксирует значения `usage`, `libraryelements` и прочих | ||||
| slot-specific attributes. | ||||
| - `common` не смешивает эту модель с отдельными publish осями вроде package | ||||
| metadata. | ||||
| - Closure callbacks используют delegate-first; для вложенных closure удобнее | ||||
| явный параметр (`publication -> ...`, `slotPublication -> ...`). | ||||
