-
Notifications
You must be signed in to change notification settings - Fork 4
llar Build Matrix
One version of a library can be built in several ways. For example, the same source may produce these variants:
linux + amd64 + shared=OFF
linux + arm64 + shared=OFF
linux + amd64 + shared=ON
LLAR uses a build matrix to describe these differences. The selected matrix values tell a formula which dependencies, flags, commands, and output it should use. A different accepted selection may produce a different reusable artifact.
This idea is similar to Go build constraints. Go constraints choose source files; an LLAR matrix chooses the result of the whole build.
LLAR separates matrix values into two groups:
| Group | Meaning | Examples |
|---|---|---|
Require |
Build environment that dependencies may also need to know. |
os, arch, libc, ABI, toolchain |
Options |
Build choices for the current package. |
shared, debug, zlib, tests
|
The Go representation is:
type Matrix struct {
Require map[string][]string
Options map[string][]string
DefaultOptions map[string][]string
}A build request is flat. The caller does not need to decide whether a value is
Require or Options. A formula makes that decision by reading the value through
target.require or target.options.
Formula callbacks read the selected values 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") {
...
}
}In this example:
-
osis required build environment and is passed through dependency resolution. -
zlibandsharedare choices used only by formulas that support them.
Only values read by a formula affect that module's build variant. An unrelated request value does not create another artifact for the module.
defaults supplies option values when the request does not provide them:
defaults {
"debug": "OFF",
"shared": "OFF",
}An explicitly requested value replaces the default for the same key. Defaults
apply only to Options; they do not set os, arch, or other Require values.
Suppose the request is:
os=linux, arch=arm64, zlib=ON
pnggroup/libpng reads the zlib option and depends on madler/zlib. Their
effective matrices are:
pnggroup/libpng: os=linux, arch=arm64 | zlib=ON
madler/zlib: os=linux, arch=arm64
The environment values reach the dependency. The zlib option stays with
libpng because zlib's own formula does not use that option.
When a matrix has several values, each choice can produce another build variant:
os: [linux, darwin]
arch: [amd64, arm64]
shared: [ON, OFF]
This gives up to eight combinations:
2 operating systems x 2 architectures x 2 shared modes = 8
The matrix describes what can be selected. LLAR may prebuild some combinations and build others only when requested.
Not every combination is supported by every project. A formula can reject an
unsupported selection with filter:
filter => {
if slices.contains(target.require["os"], "darwin") &&
slices.contains(target.require["arch"], "mips") {
return false
}
return true
}LLAR sets target before running the filter. When the filter returns false,
LLAR stops before dependency discovery and building for that selection.
Use Filter for real limits of the platform or upstream project. For example, an upstream library may simply not support one operating-system and architecture pair.
Matrix keys should be understandable on their own. Choosing one key should not make another key appear, disappear, or change its meaning.
Dependent keys quickly create confusing combinations. For example:
tls: [native, openssl]
openssl-linkage: [static, shared]
What does this mean?
tls=native, openssl-linkage=static
The linkage value has no effect because OpenSSL is not selected. It still makes the matrix look larger and may create different cache names for builds that are actually the same.
Represent the real choices as one option instead:
tls: [native, openssl-static, openssl-shared]
Independent options make formulas easier to write because:
- every combination has a clear meaning;
- defaults cannot produce half-valid selections;
- cache and artifact names do not contain irrelevant values;
- dependencies can receive Require values without learning option rules from another package;
- LLAR can discover and test each matrix key without first satisfying another key.
Use Filter for genuine platform limitations. When two options describe one combined choice, merge them into one option instead of connecting them with Filter rules.