Variant Artifacts Plugin
NAME
VariantsArtifactsPlugin и extension variantArtifacts.
SYNOPSIS
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 contributions и DSL
slot('<name>') описывает artifact representation не как один файл или одну
задачу, а как набор contributions, которые потом materialize-ятся в отдельный
ArtifactAssembly.
Текущий DSL поддерживает два вида contributions:
- topology-aware:
fromVariant { output(...) }fromRole('<role>') { output(...) }fromLayer('<layer>') { output(...) }- direct:
from(someFileOrProviderOrTaskOutput)
Смысл DSL по слоям:
fromVariant/fromRole/fromLayerвыбирают область topology model, в которой contribution активен;output(...)выбирает named output соответствующегоGenericSourceSet;from(Object)добавляет direct contribution, не зависящий отvariantSourcesbindings;- итоговый contribution при materialization:
- проверяет, подходит ли текущий
SourceSetUsageBinding; - выдает object для
files.from(...); - при необходимости выдает
BindingKey, если такой contribution должен схлопываться по logical identity.
Связь slot-а с остальной моделью:
variantsзадает topology variant/role/layer;variantSourcesпревращает topology в concreteSourceSetUsageBinding;variantArtifacts.slot(...)описывает, какие bindings надо включить в slot;VariantArtifactsResolverпревращает contributions slot-а вFileCollection;VariantArtifactsPluginрегистрирует для slot-а отдельныйArtifactAssembly;OutgoingVariantPublicationиOutgoingArtifactSlotPublicationпубликуют уже собранные slot artifacts наружу.
Каждый slot materialize-ится в отдельный ArtifactAssembly:
- task:
process<Variant><Slot>; - output dir:
build/variant-artifacts/<variant>/<slot>.
primary slot
Primary slot задает artifact, который публикуется как основной artifact
configuration <variant>Elements.
Формы DSL:
variant('browser') { primarySlot('typesPackage') slot('typesPackage') { fromVariant { output('types') } } }
или sugar:
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.
Пример:
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.
def jsFiles = configurations.compileView.incoming.artifactView { attributes { attribute(slotAttr, 'js') } }.files
Здесь graph variant уже выбран, а artifactView выбирает нужный secondary
artifact representation.
VALIDATION
Проверяется:
- variant существует в topology model;
- slot contributions не ссылаются на неизвестные 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
from(Object);fromVariant(...);fromRole(String, ...);fromLayer(String, ...).
Внутренняя модель:
- slot хранит contributions, а не строковые rules;
fromVariant/fromRole/fromLayerсоздают topology-aware contributions;from(Object)создает direct contribution, который materialize-ится даже если у variant-а нет ни одногоSourceSetUsageBinding;- slot отдельно хранит topology references для validation:
referencedRoleNames()иreferencedLayerNames().
OutputSelectionSpec
output(name);output(name, extra...).
OutputSelectionSpec это внутренний DSL-buffer для одного блока
fromVariant/fromRole/fromLayer. Он локально накапливает contributions и
передает их в slot только после успешного завершения configure-блока.
KEY CLASSES
VariantsArtifactsPlugin— plugin adapter и materialization outgoing variants.VariantArtifactsExtension— root DSL и lifecycle.VariantArtifact— outgoing build variant model.VariantArtifactSlot— artifact representation slot.VariantArtifactsResolver— adapter междуvariantSourcesbindings и contribution model 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 -> ...).
