Package Format
Packages are defined using JSON manifests. This document describes the format.
Basic Structure
{
"name": "example-package",
"version": "1.0.0",
"description": "An example package",
"source": {
"type": "git",
"url": "https://github.com/user/repo.git",
"branch": "main"
},
"dependencies": ["libfoo@1.0.0", "libbar"],
"build_dependencies": ["cmake@3.20.0", "pkg-config"],
"build_system": "cmake",
"cmake_args": ["-DBUILD_SHARED_LIBS=ON"],
"env": {
"CXXFLAGS": "-O2"
}
}
Multi-Version Format
Packages can have multiple versions:
{
"name": "example-package",
"versions": [
{
"version": "1.2.0",
"description": "Latest version",
"source": {...},
"dependencies": [...],
...
},
{
"version": "1.1.0",
"description": "Previous version",
"source": {...},
"dependencies": [...],
...
}
]
}
Required Fields
name: Package name (must match filename)version: Package version (orversionsarray for multi-version)description: Brief descriptionsource: Source information (see below)
Source Types
Tarball
Git Repository
Or with a specific tag or commit:
Zip Archive
Local Directory
Build Systems
Autotools
CMake
{
"build_system": "cmake",
"cmake_args": [
"-DBUILD_SHARED_LIBS=ON",
"-DCMAKE_BUILD_TYPE=Release"
]
}
Meson
Make
Custom
Meta
A package that installs nothing of its own and exists only to pull in others. It declares no source — TSI records it and its dependencies and skips fetch, build and link entirely.
{
"name": "autotools",
"version": "2.72",
"description": "Meta-package for GNU Autoconf, Automake, and Libtool",
"dependencies": ["autoconf", "automake", "libtool"],
"build_system": "meta"
}
Before this existed, autotools had to name some source to satisfy the schema
and downloaded GNU hello on every install. A meta package that declares a
source, or that declares no dependencies, is rejected by
scripts/validate-packages.py.
Dependencies
Runtime Dependencies
Build Dependencies
Dependencies can be:
- Unversioned: "zlib"
- Versioned: "curl@8.7.1"
Environment Variables
Patches
Optional Fields
configure_args: Arguments for./configure(autotools)cmake_args: Arguments forcmakemeson_args: Arguments formesonmake_args: Arguments formakeenv: Environment variablespatches: Array of patch file URLs or pathsbuild_commands: Custom build commands (for custom build system)source_dir: Subdirectory of the fetched tree that holds the build rootplatforms: Platforms this version can build on, e.g.["linux"]or["linux-aarch64", "darwin"]. Omit it (the default) for portable packages; see OS-specific configuration.
$TSI_INSTALL_DIR
Package definitions can write $TSI_INSTALL_DIR, and TSI expands it before the
build tool sees it. It expands to two different directories depending on where
you write it, because two different directories are what you want in each
place:
| Where | Expands to | Use it for |
|---|---|---|
make_args (build_system make), build_commands |
this package's own versioned install dir, install/<name>-<version>/ |
telling the build where to install: PREFIX=$TSI_INSTALL_DIR |
configure_args, cmake_args, meson_args, make_args (build_system autotools), env values |
the shared prefix, install/ |
pointing at what dependencies already installed: -I$TSI_INSTALL_DIR/include |
The split is by what you need in each place, not by whim. Packages install into
their own versioned directory and are symlinked into the shared prefix
afterwards, so a value naming the package's own directory points at something
still empty while the package is being built -- useless for finding a
dependency. Conversely make and build_commands have no --prefix to
receive, so they have to be told where to install.
autotools is the case worth pausing on: it is the one build system where
make_args mean the shared prefix, because TSI passes --prefix to configure
itself, so an autotools package never has to name its own install directory.
Use make_args there for variables configure assigns in the Makefile and does
not let you override any other way -- readline needs
SHLIB_LIBS=-L$TSI_INSTALL_DIR/lib -ltinfow, without which its shared library
links no termcap library and everything using it dies on an undefined UP.
Overriding CPPFLAGS
TSI already puts the shared prefix on the include path, but on macOS it uses
-idirafter, which loses to /usr/include. That is deliberate -- it keeps the
prefix from shadowing system headers -- but it is wrong when a dependency in the
prefix replaces a system library. gawk is the case: macOS ships a
readline/readline.h that is really a libedit shim without history_list(), so
gawk has to be told to prefer the prefix's GNU readline:
A package's own CPPFLAGS replaces TSI's default rather than adding to it, so
-I here wins over /usr/include. Reach for this only when a prefix header
must beat a system one; otherwise leave the default alone.
Example Package Definitions
See the packages/ directory for complete examples.
Version Constraints
When specifying dependencies, you can use version constraints:
"package"- Any version"package@1.0.0"- Exact version- Version matching is done by string comparison
Best Practices
- Use semantic versioning for versions
- Include version in source URL when possible
- Specify build dependencies explicitly
- Use consistent URL patterns across versions
- Document any special build requirements in description