Skip to content

llar Build Matrix

Rick Guo edited this page Jul 11, 2026 · 7 revisions

An LLAR build matrix describes the build variants of one module version. Build systems commonly vary by operating system, architecture, ABI, toolchain, linkage, and package features. LLAR represents these inputs through one matrix model and includes the effective selection in the artifact identity.

The design is inspired by Go build constraints. Go constraints select source files for one compilation; an LLAR matrix selects a complete build result. Two accepted selections may use different dependencies, flags, commands, or installed output, and therefore produce different reusable artifacts.

Matrix Model

type Matrix struct {
    Require        map[string][]string
    Options        map[string][]string
    DefaultOptions map[string][]string
}
Part Purpose Examples
Require Build-environment dimensions that propagate through dependency resolution. Dependencies using the same dimension must select compatible values. os, arch, ABI, libc, toolchain
Options Package-specific build choices. An option affects only formulae that read it. shared, debug, zlib, tests
DefaultOptions Option values used when the request omits those keys. debug=OFF, shared=OFF

A build request supplies a flat set of matrix values. Formula usage determines whether a key is consumed through target.require or target.options; callers do not classify request values themselves.

Formula Usage

Formula callbacks read the active matrix selection through target:

import "slices"

onRequire (proj, deps) => {
    if slices.contains(target.options["zlib"], "ON") {
        deps.require "madler/zlib", "v1.3.1"
    }
}

onBuild (ctx, proj, out) => {
    if slices.contains(target.require["os"], "linux") {
        ...
    }
    if slices.contains(target.options["shared"], "ON") {
        ...
    }
}

Only keys read by the formula participate in that module's matrix semantics. Unused request keys do not change its artifact identity.

defaults supplies omitted option values:

defaults {
    "debug": "OFF",
    "shared": "OFF",
}

Requested values override defaults with the same key. Defaults apply only to options; they do not set required dimensions or declare every legal value.

Dependency Propagation

Given this request:

os=linux, arch=arm64, zlib=ON

if pnggroup/libpng reads zlib and depends on madler/zlib, their effective matrices are:

pnggroup/libpng: os=linux, arch=arm64 | zlib=ON
madler/zlib:     os=linux, arch=arm64

Required dimensions propagate across the dependency edge. Package options stay only where they affect formula behavior.

Combination Space

Each dimension contributes possible values. Their Cartesian product describes the possible artifact variants before unsupported selections are filtered:

os:     [linux, darwin]
arch:   [amd64, arm64]
shared: [ON, OFF]

2 x 2 x 2 = 8 possible variants

The matrix describes variant possibility. Build policy separately decides which accepted variants are prebuilt and which are built on demand.

Filter

filter rejects a selected combination that the formula or upstream project cannot build:

filter => {
    if slices.contains(target.require["os"], "darwin") &&
        slices.contains(target.require["arch"], "mips") {
        return false
    }
    return true
}

LLAR injects the selected target before running filter. A false result means the module version does not support that selection, so dependency discovery and building do not continue for it.

Filter is appropriate for external compatibility limits, such as an upstream project not supporting a platform combination. It should not be the primary way to model options whose meanings depend on each other.

Best Practices

Keep Dimensions Orthogonal

A matrix dimension should remain meaningful and independently selectable regardless of the values chosen for other dimensions. Orthogonal dimensions keep the matrix close to a Cartesian product instead of turning it into a graph of conditional keys and values.

This matters because orthogonality provides:

  • Predictable combinations: LLAR and formula authors can enumerate variants without a separate cross-key constraint solver.
  • Stable artifact identity: every selected value contributes meaningfully to the result, avoiding duplicate artifacts that differ only by irrelevant keys.
  • Independent propagation: required dimensions can move across dependency edges without carrying package-specific conditional rules.
  • Local formula logic: matrix discovery, defaults, overrides, tests, and maintenance can treat each key independently instead of reconstructing option prerequisites.

If one option exists only under a particular value of another option, merge the valid combinations into one dimension. Do not model an OpenSSL-only linkage option separately:

tls:             [native, openssl]
openssl-linkage: [static, shared]

openssl-linkage has no meaning when tls=native, and the Cartesian product creates meaningless selections. Represent the valid choices directly:

tls: [native, openssl-static, openssl-shared]

Use filter for genuine upstream compatibility restrictions, not to preserve a dependent option model that can be expressed as one orthogonal dimension.

Clone this wiki locally