Extension · Build Variants

One version, every target.

Build Variants encodes deployment-target coordinates (os, arch, abi and features) as build metadata, so a single release addresses a whole family of artifacts. Because Semantic Versions restores build precedence, each variant is a real, orderable version, and a build channel can select a coherent subset that no numeric range can express.

variant specifier
napi:sharp@0.33.0+linux.x64.musl

napi:sharp@0.33.0 is the release. +linux.x64.musl is the target, an os·arch·abi coordinate, ordered coarse→fine so ranges can drill down.

Why build metadata

A target isn't a pre-release, or a new package.

A platform build is a finished sibling of a release, not a step toward it, so it ranks at or above the plain release, never below it like a pre-release. And it needn't fork the identity: instead of scattering software across pkg-linux-x64, pkg-darwin-arm64, … wired together with optionalDependencies and os/cpu/libc filters, Build Variants keeps one name, one version, and moves target selection back into the range grammar.

Live

Range over a variant subset.

Every cell is a concrete artifact: a release built for one target. Type a build channel and watch the selected subset light up. Shorter channels are broader subsets; the plain release column has no build, so a channel excludes it. This runs entirely in your browser on the reference semantic-versions parser.

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.musl are valid; x64 alone (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):

AxisValues
oslinux · darwin · win32 · freebsd · openbsd · android · sunos · aix
archx64 · arm64 · arm · ia32 · ppc64 · s390x · riscv64 · loong64 · wasm32
abiglibc · 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.x64 matches linux.x64.musl. To distinguish siblings that share a prefix, make the distinguishing axis explicit: model libc as linux.x64.glibc vs linux.x64.musl rather 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>