UVL feature model
The UVL (Universal Variability Language) file is the formal model of an SPL’s variability. It defines which features exist, whether they are mandatory or optional, and what constraints govern their combination. The UVL lives in the shared splent_catalog/ repository at workspace root — not inside individual products.
Table of contents
- What it governs
- Location
- Format
- Who reads the UVL
- Lifecycle of the UVL
- UVL vs pyproject.toml
- See also
What it governs
pyproject.toml declares which features a product uses and at which version. The UVL declares which combinations are valid — structure, optionality, and cross-tree constraints.
Together they answer two different questions:
| Question | Answered by |
|---|---|
| What features does this product include? | pyproject.toml |
| Is this combination of features valid? | UVL |
The UVL does not contain version numbers. Versions are managed exclusively in pyproject.toml via feature:attach.
Location
The UVL lives in the SPL catalog — a shared splent_catalog/ git repository at workspace root. Each SPL has its own directory containing a metadata.toml and the .uvl file:
<workspace>/
├── splent_catalog/ <- shared catalog repo
│ └── <spl_name>/
│ ├── metadata.toml <- DOI, mirror, description
│ └── <spl_name>.uvl <- the feature model
├── <product_a>/
│ └── pyproject.toml <- spl = "<spl_name>"
└── <product_b>/
└── pyproject.toml <- spl = "<spl_name>" (same SPL)
Products reference an SPL by name in pyproject.toml:
[tool.splent]
spl = "sample_splent_app"
The catalog’s metadata.toml holds the UVLHub metadata:
mirror = "uvlhub.io"
doi = "10.5281/zenodo.19219696"
Multiple products can derive from the same SPL. Constraints are properties of the feature model (the SPL), not of individual products.
Legacy support: Products that still use [tool.splent.uvl] in their pyproject.toml (with the UVL file in product/uvl/) continue to work. All UVL commands resolve from the catalog first, then fall back to the legacy per-product location.
Format
UVL is a domain-specific language for variability modelling. A typical SPLENT product model looks like this:
features
sample_splent_app
mandatory
auth {org 'splent-io', package 'splent_feature_auth'}
mandatory
public {org 'splent-io', package 'splent_feature_public'}
optional
redis {org 'splent-io', package 'splent_feature_redis'}
optional
mail {org 'splent-io', package 'splent_feature_mail'}
optional
confirmemail {org 'splent-io', package 'splent_feature_confirmemail'}
optional
profile {org 'splent-io', package 'splent_feature_profile'}
optional
reset {org 'splent-io', package 'splent_feature_reset'}
constraints
profile => auth
confirmemail => mail
reset => mail
Features block
Each feature is declared as mandatory or optional under the product root. The {org, package} attributes link the UVL name to its SPLENT package identity.
Constraints block
Cross-tree constraints express dependencies between features:
profile => auth— selectingprofilerequiresauthconfirmemail => mail— selectingconfirmemailrequiresmail
These constraints are enforced by product:validate before derivation.
Who reads the UVL
| Consumer | What it reads | Why |
|---|---|---|
product:validate |
Full model + constraints | Validates that the product’s feature selection satisfies all constraints (uses Flamapy) |
feature:order |
Feature tree + constraints | Resolves the topological load order (dependencies load first) |
db:seed |
Feature tree + constraints | Runs seeders in topological order |
splent_framework (FeatureLoadOrderResolver) |
Feature tree + constraints | Determines runtime feature registration order |
product:validate |
Indirectly (via load order) | Checks inter-feature contract conflicts in dependency order |
Lifecycle of the UVL
Fetching from UVLHub
If the SPL’s UVL is already published on UVLHub, fetch it:
splent spl:fetch
This downloads the canonical model into splent_catalog/<spl_name>/ using the DOI declared in the catalog’s metadata.toml.
Creating from scratch
For new SPLs, use spl:create <name> (available in detached mode) to scaffold the catalog entry at splent_catalog/<name>/. Edit the .uvl file manually, then publish to UVLHub and add the DOI to metadata.toml.
Fixing constraints
spl:add-constraints writes changes to the catalog, not to the product. Constraints are properties of the SPL’s feature model — they are shared across all products that derive from it.
Validating
Run product:validate every time you add or remove a feature:
splent product:validate
OK: configuration is satisfiable.
If the check fails, the product cannot be derived.
UVL vs pyproject.toml
A common question: why two files?
- pyproject.toml is the operational truth — it tells the system exactly what to install and run.
- The UVL is the formal truth — it proves that the chosen configuration is valid according to the product line model.
Both are required. A product with a valid pyproject.toml but an invalid UVL configuration will be rejected at derivation time. A product with a valid UVL but missing feature entries in pyproject.toml will fail at sync time.
See also
spl:fetch— download the canonical UVL from UVLHubproduct:validate— validate the current feature selectionproduct:auto-require— synchronise the local UVL with the published versionfeature:order— display the resolved load order- UVLHub — public registry for UVL models