UVL feature model


Table of contents

  1. Introduction
  2. Writing a model
  3. Pinning the model in your product
  4. Where the model is found
  5. Working with a model
    1. Fetching it
    2. Creating and publishing one
    3. Changing constraints
    4. Validating a product
  6. Who reads the model
  7. See also

Introduction

The UVL (Universal Variability Language) file is the formal model of an SPL. It says which features exist, which are mandatory or optional, and which combinations are valid. Your product does not store it. It pins it by DOI.

features
    my_spl
        mandatory
            notes {org 'splent-io', package 'splent_feature_notes'}
        optional
            reports {org 'splent-io', package 'splent_feature_reports'}

constraints
    reports => notes

pyproject.toml says which features your product runs, at which versions. The UVL says whether that combination is valid. The model has no version numbers.


Writing a model

A fuller model of the invented line my_spl looks like this. The sample line the tutorials use, sample_splent_spl, has the same shape.

features
    my_spl
        mandatory
            notes {org 'splent-io', package 'splent_feature_notes'}
        optional
            notes_tags {org 'splent-io', package 'splent_feature_notes_tags'}
        optional
            reports {org 'splent-io', package 'splent_feature_reports'}
        optional
            cache {org 'splent-io', package 'splent_feature_cache'}
        mandatory
            session_type
                alternative
                    sessions_in_cache {org 'splent-io', package 'splent_feature_sessions_in_cache'}
                    sessions_on_disk {org 'splent-io', package 'splent_feature_sessions_on_disk'}

constraints
    notes_tags => notes
    reports => notes
    sessions_in_cache => cache

Each feature sits in a mandatory, optional or alternative group, and {org, package} links it to its SPLENT package. session_type has no package. Its alternative group means a product picks exactly one session backend.

The constraints are dependencies across the tree. reports => notes means selecting reports requires notes.

A node with a param attribute and no package stands for a value of a feature’s parameter, such as sells_subscriptions {param 'SHOP_SELLS', value 'subscriptions'}. See Letting a value select features.


Pinning the model in your product

[tool.splent]
spl = "sample_splent_spl"

[tool.splent.spl_model]
mirror = "uvlhub.io"
doi = "10.5281/zenodo.23076870"
concept_doi = "10.5281/zenodo.18873371"
version = "v4"
file = "sample_splent_spl.uvl"

doi pins the exact bytes, the way @v1.5.8 pins a feature. concept_doi identifies the line across versions, so spl:outdated can find a newer model. file is the filename on UVLHub, <spl>.uvl when left out.

spl:pin writes this block. With it, deriving needs only your product’s repository and UVLHub. Several products can follow one SPL, each pinning its own version.


Where the model is found

Where a product's UVL model is looked for, in order pyproject.toml spl names the line, spl_model its DOI 1 Working copy splent_spl_<core>/ yours, never overwritten 2 Cache .splent_cache/spls/ <spl>@<version>/ derived data 3 UVLHub by DOI, no account CLI only what comes down is cached The running app looks on disk only The CLI also downloads
The first place that has the model wins. After one download, every later resolution works offline.
<workspace>/
├── splent_spl_<core>/                  <- 1. the working copy, yours
│   ├── metadata.toml                   <-    name, description, DOI
│   └── <spl_name>.uvl
├── .splent_cache/spls/                 <- 2. the cache
│   └── <spl_name>@<version>/
└── <product>/pyproject.toml            <- spl + DOI

The working copy is splent_spl_<name without _spl>/ or splent_spl_<name>/, and a fetch never overwrites it. The cache is keyed on the pin’s version, or on the DOI when there is none. A cached copy recording another DOI is skipped, so your product never derives from another product’s version. UVLHub is asked by DOI, and what comes down is cached.

The running app never downloads. It looks in the working copy, the cache, and <product>/uvl/<file> when you set [tool.splent.uvl].file. With no model on disk it still starts, and loads features in pyproject.toml order.

An SPL name must be a single directory name, without a path separator, .., a leading . or a control character. Nothing is ever written outside .splent_cache/spls/.


Working with a model

Fetching it

splent spl:fetch <spl_name>

spl:fetch downloads a published model into the cache, with no account, and runs with no product selected. Commands that need a model fetch it themselves, so you rarely run it.

Creating and publishing one

spl:create scaffolds splent_spl_<core>/ with no product selected. Edit the .uvl, then publish it and pin it.

splent login
splent spl:publish <spl_name>
splent product:select <product>
splent spl:pin <spl_name>

spl:publish goes through the marketplace, which holds the only UVLHub key, and writes the DOI it gets back into metadata.toml. spl:pin records it in the selected product. A model without a DOI works locally, but nobody without your working copy can resolve it.

Changing constraints

spl:add-constraints writes to the model, with no product selected. A new constraint reaches your products once the model is published and each product is pinned again.

Validating a product

splent product:validate
  ✅ Product is valid.

Run product:validate after adding or removing a feature. It checks the selection against every constraint with Flamapy. A product that fails cannot be derived.


Who reads the model

Reader Why
product:validate Checks the selection against the constraints
feature:order Shows the load order, dependencies first
db:seed Runs seeders in that order
The framework (FeatureLoadOrderResolver) Orders feature registration at runtime

See also


Back to top

splent. Distributed by an LGPL license v3. Contact us: drorganvidez@us.es