Coordinates
A Build Variant is build metadata whose identifiers name target axes, ordered coarse→fine:
<version>+<os>[.<arch>[.<abi>]][.<feature>]*- The fixed axes appear in order (os, then arch, then abi) and each MAY be omitted only if every later fixed axis is too. A variant is therefore always a prefix of the axis sequence:
linux,linux.x64,linux.x64.muslare valid;x64alone (arch without os) is not. - Feature tags (
avx2,cuda, …) are optional, lowercase and free-form; they MUST follow the fixed axes and SHOULD be sorted for a canonical form. - Each axis value is its own dot-separated build identifier, so a variant slots straight into the existing
<build>grammar.
Registered vocabularies (extensible; aligned with runtime values like Node's process.platform / process.arch):
| Axis | Values |
|---|---|
os | linux · darwin · win32 · freebsd · openbsd · android · sunos · aix |
arch | x64 · arm64 · arm · ia32 · ppc64 · s390x · riscv64 · loong64 · wasm32 |
abi | glibc · musl · msvc |
Consumers MUST ignore axis or feature identifiers they don't recognise and MUST NOT treat that as a match failure on its own; an unknown coordinate simply doesn't narrow selection.
Selection
Selection is just the core Build Channels rule applied to
coordinates: a +-channel is orthogonal to the numeric comparison and admits only
versions whose build coordinates begin with the channel's. Because coordinates are
ordered coarse→fine, a shorter channel is a broader subset.
^1.0.0+linux: every linux variant in the 1.x line.^1.0.0+linux.x64: narrows to linux/x64, both glibc and musl.^1.0.0+linux.x64.musl: just the musl build.>=1.1.0+darwin.arm64: the Apple-silicon build at or above 1.1.0.1.2.0+win32.x64: one exact artifact.^1.0.0: no channel, so every variant and the plain release.
Within a subset the fixed axes are equal, so ordering falls back to numeric precedence, and “the latest linux/x64 build of 1.x” is well-defined.
Prefix matching means a coarse channel also admits deeper refinements:+linux.x64matcheslinux.x64.musl. To distinguish siblings that share a prefix, make the distinguishing axis explicit: model libc aslinux.x64.glibcvslinux.x64.muslrather than leaving glibc implicit.
Relationship to other schemes
- npm
os/cpu/libc+ optional-dependency fanout. The same target matrix, expressed in one version instead of across many packages, and now rangeable. A resolver can still map a selected variant onto whatever artifact layout a registry uses. - OCI / Docker multi-arch manifests. A manifest list selects by
os/architecture/variant; Build Variants lifts that tuple into the version grammar, so the same selection works in a specifier or lockfile, not only at pull time. - Package-URL qualifiers. PURL carries target facts in
?arch=…&os=…qualifiers, outside the version and outside precedence. Build Variants keeps them in the version, where they order and where ranges select them, while staying round-trippable to PURL qualifiers for interchange.
Composability
Variant coordinates are ordinary build identifiers, so they compose with other build-metadata
extensions. A variant can precede an integrity digest
(1.0.0+linux.x64.musl.sha512.… pins the exact bytes of one target), and a
Dependency Lock can pin the resolved variant
chosen for each node of a graph. See Package Integrity
for the digest half.
Grammar (BNF)
Building on VERSION.bnf's <build>:
<variant build> ::= <os> ( "." <arch> ( "." <abi> )? )? ( "." <feature> )*
<os> ::= "linux" | "darwin" | "win32" | "freebsd" | "openbsd"
| "android" | "sunos" | "aix"
<arch> ::= "x64" | "arm64" | "arm" | "ia32" | "ppc64" | "s390x"
| "riscv64" | "loong64" | "wasm32"
<abi> ::= "glibc" | "musl" | "msvc"
<feature> ::= <build identifier>