Skip to content

OS-Specific Package Configuration

TSI supports OS-specific configurations in package definitions to handle differences between operating systems (e.g. macOS vs Linux vs Windows).

Format

Package JSON files use the pattern <field>_<os> for the supported OS names below.

Supported OS names (in JSON)

These keys are implemented for the three primary families:

  • darwin — macOS
  • linux — Linux distributions
  • windows — Microsoft Windows

src/platform/mod.rs can also report freebsd, openbsd, netbsd, or unknown at runtime, but there are no env_freebsd / configure_args_freebsd-style fields yet. On those hosts, only base env, configure_args, and cmake_args apply.

Supported OS-specific fields

Base field OS-specific keys Merge behavior
env env_darwin, env_linux, env_windows Merge keys: start from base env, then apply the current OS map (overwriting duplicate keys), then apply arch maps env_x86_64 / env_aarch64 on top.
configure_args configure_args_darwin, configure_args_linux, configure_args_windows Replace list: if the OS-specific list is present, it replaces base configure_args entirely; otherwise base is used. Arch extras configure_args_x86_64 / configure_args_aarch64 are appended when set.
cmake_args cmake_args_darwin, cmake_args_linux, cmake_args_windows Replace list: if the OS-specific list is present, it replaces base cmake_args entirely; otherwise base cmake_args is used. No arch-specific cmake keys (yet).
make_args make_args_darwin, make_args_linux, make_args_windows Replace list: same as cmake_args. Note these are the whole list, so repeat PREFIX=$TSI_INSTALL_DIR in the OS-specific one if the package needs it. No arch-specific make keys.

make_args is the only way to reach a variable a Makefile assigns itself, since a command-line argument beats a file assignment and the environment does not. libgif needs both halves: its Makefile sets CFLAGS, and on macOS its link line records no -install_name, so the dylib comes out with a bare relative id that nothing can load. The flag that fixes it is an error on Linux, which is what make_args_darwin is for.

Not implemented in JSON today: build_system_*, or OS-specific dependencies.

What TSI puts in the build environment

Every build starts from these, before any package env is applied:

Variable Value Why
CPPFLAGS -I<prefix>/include, or -idirafter <prefix>/include on macOS Prefix headers must not shadow SDK ones on macOS (git's archive.h, prefix uuid/uuid.h vs Apple's uuid_string_t)
LDFLAGS -L<prefix>/lib -Wl,-rpath,<prefix>/lib -L alone resolves at link time only; TSI installs where no loader searches, so without the RPATH the result links and then fails to load
PKG_CONFIG_PATH, CMAKE_PREFIX_PATH, PATH prefix-first So configure and CMake find what TSI already built

A package that sets one of these in env replaces it outright — that is the documented merge behaviour, and it means overriding LDFLAGS drops the RPATH and overriding CPPFLAGS opts out of the macOS header demotion. Both are sometimes exactly what you want: postgresql sets CPPFLAGS precisely to get prefix headers ahead of the SDK, because -idirafter beats -I for the same directory in either order and psql needs GNU readline's append_history.

Restricting a package to some platforms

Some packages cannot build everywhere at all — libcap, libseccomp, and liburing wrap Linux kernel APIs with no macOS equivalent. Declare that with platforms:

{
  "name": "liburing",
  "version": "2.13",
  "platforms": ["linux"]
}
  • Entries are either a bare OS name (linux, darwin, windows) or an os-arch pair (linux-aarch64, darwin-x86_64).
  • An absent or empty platforms means "every platform" — the default, and what almost every package should use.
  • tsi install refuses before fetching anything when the requested package or any of its dependencies is unsupported here, so an unbuildable package never burns a source build first.
  • CI and scripts/build-all-packages.sh read this field to decide whether a package is legitimately skipped (— in the status table) or genuinely broken (❌).

Use it only for packages that are impossible on a platform, not for ones that are merely untested or currently failing — a platforms entry silences the validation matrix, which is exactly what you do not want for a fixable build.

Examples

Environment (key merge)

{
  "env": { "CFLAGS": "-O2 -g" },
  "env_darwin": {
    "CFLAGS": "-O2 -g -Wno-error=format-nonliteral"
  }
}

On macOS, CFLAGS ends up as the darwin value; on Linux/Windows, the base value unless env_linux / env_windows overrides.

Autotools / configure (replace list when OS block exists)

{
  "configure_args": ["--disable-nls"],
  "configure_args_linux": ["--disable-nls", "--disable-werror"]
}

On Linux the full configure argument list is exactly configure_args_linux. On macOS, if configure_args_darwin is omitted, the list is ["--disable-nls"].

CMake (replace list when OS block exists)

{
  "cmake_args": ["-DCMAKE_BUILD_TYPE=Release"],
  "cmake_args_darwin": [
    "-DCMAKE_BUILD_TYPE=Release",
    "-DCMAKE_OSX_DEPLOYMENT_TARGET=11.0"
  ],
  "cmake_args_windows": ["-G", "Ninja", "-DCMAKE_BUILD_TYPE=Release"]
}

On each OS, if the corresponding cmake_args_<os> is set, that list is used instead of cmake_args.

OS detection

  • macOS → darwin
  • Linux → linux
  • Windows → windows
  • BSD / other → no _* overrides apply unless future fields are added; base fields only.