##// END OF EJS Templates
Refactor variant artifact slots into contribution-based inputs
Refactor variant artifact slots into contribution-based inputs

File last commit:

r35:389e9d6c7860 default
r35:389e9d6c7860 default
Show More
variant-artifacts-plugin.md
354 lines | 10.5 KiB | text/x-minidsrc | MarkdownLexer
/ common / variant-artifacts-plugin.md

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, не зависящий от variantSources bindings;
  • итоговый contribution при materialization:
  • проверяет, подходит ли текущий SourceSetUsageBinding;
  • выдает object для files.from(...);
  • при необходимости выдает BindingKey, если такой contribution должен схлопываться по logical identity.

Связь slot-а с остальной моделью:

  • variants задает topology variant/role/layer;
  • variantSources превращает topology в concrete SourceSetUsageBinding;
  • 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 между variantSources bindings и 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 -> ...).