02.10 - Root variants
How root variants are defined and resolved.
Root variants
Root variants let one root produce multiple concrete build outputs from variant dimensions such as os, arch, or project-specific traits.
Filesystem layout
Variant configuration lives under:
<root>/dyd/variants/<dimension>/<option>
Each option file must contain exactly true or false:
truemeans option is enabledfalsemeans option is disabled
Example:
dyd/variants/
os/
linux # true
darwin # true
none # true
arch/
amd64 # true
arm64 # false
_include/
arch=amd64+os=any # true|false
_exclude/
arch=amd64+os=darwin # true|false
In that example, enabled concrete variants include:
arch=amd64+os=linuxarch=amd64+os=darwinarch=amd64(becauseos=noneomits theostrait)
Name rules and reserved options
Variant dimension names and option names use:
[A-Za-z0-9._-]+
Reserved option names in the dimension catalog:
inheritanyhost
The none option is allowed and means “omit this trait dimension” when selected.
Descriptor forms
Dryad uses two descriptor encodings:
- Filesystem form:
arch=amd64+os=linux - URL form:
?arch=amd64&os=linux
Canonical order is ascending by dimension name, for example arch=... before os=....
Where selectors are used
Requirement filename (condition):
foo~arch=any+os=linux
Requirement file value (target selector):
root:../../../foo?arch=inherit&os=host
Condition and target selector are independent:
- condition controls when requirement is active
- target selector controls which variant(s) of the dependency are linked
Root content path selectors:
dyd/assets~<descriptor>dyd/commands~<descriptor>dyd/traits~<descriptor>dyd/secrets~<descriptor>dyd/docs~<descriptor>dyd/requirements~<descriptor>- example:
dyd/assets~arch=amd64,arm64+os=linux
Selector keywords
none
- Selects the omitted option for a dimension.
any
- Selects all enabled concrete options for that dimension.
- It does not include
none.
inherit
- For requirement target selectors, takes the option from the parent/root variant for the same dimension.
- If the parent omits that dimension, it resolves as
none.
host
- Resolves to current host values for
osandarch.
In requirement conditions:
anymatches when the parent has a concrete value for that dimension.inheritacts as a wildcard.nonematches when parent omits that dimension.hostmatches parent variant against hostos/arch.
In requirement target selectors:
anyselects all enabled concrete options for that dimension and does not includenone.noneselects the omitted variant for that dimension.inherituses the parent value for that dimension and resolves tononeif the parent omits it.hostresolves from the current host forosandarch.- omitted dimensions resolve to
nonewhen that target dimension enablesnone; otherwise the selector is under-specified and fails.
In root content path selectors (dyd/assets~..., dyd/commands~..., dyd/traits~..., dyd/secrets~..., dyd/docs~..., dyd/requirements~...):
- supported: concrete options,
none,any, and comma lists - not supported:
inheritandhost - omitted dimensions are wildcards over all enabled values, including
none; the plain path (for exampledyd/assets) is the empty selector and therefore matches all variants - selector descriptors must be canonical filesystem descriptors
- for each path kind, at most one selector may match:
- no matches are allowed (the path is omitted from the build input)
- multiple matches fail the build
Exclusions
Variant exclusions live under:
<root>/dyd/variants/_exclude/<descriptor>
Each exclusion file also contains true or false:
truemeans exclusion is activefalsemeans exclusion is ignored
Exclusion filenames must be canonical filesystem descriptors, for example:
arch=amd64+os=darwin
Exclusion selectors may omit dimensions; omitted dimensions act as wildcards over all enabled values, including none. Unlike requirement selectors, exclusions are root-local and do not support parent/host context:
- supported in
_exclude: concrete options,none,any, and comma lists (for exampleos=darwin,linux) - not supported in
_exclude:inheritandhost
Inclusions
Variant inclusions live under:
<root>/dyd/variants/_include/<descriptor>
Each inclusion file also contains true or false:
truemeans inclusion is activefalsemeans inclusion is ignored
Inclusion filenames must be canonical filesystem descriptors.
Inclusion selectors use the same selector rules as exclusions:
- supported in
_include: concrete options,none,any, and comma lists - not supported in
_include:inheritandhost
Resolution behavior:
- candidate variants are generated from enabled dimension options
- if
_excluderules are enabled, matching variants are removed - if
_includerules are enabled, only matching variants are kept - if the effective inclusion map is empty, all variants are treated as included
- final allow rule is: included and not excluded
Materialization
When dryad builds a concrete variant:
- variant configuration is read from
dyd/variants/ - selected variant options are materialized into concrete traits under
dyd/traits/ dyd/variants/itself is not materialized into stems or develop workspaces- if a dimension is selected as
none, that trait key is omitted in the concrete build
For quick inspection of resolved build variants for a root, use:
dryad root variants list <root_path>