Build Architecture¶
This document describes the internal architecture of the spksrc build system, including the build pipeline, stage interactions, and how packages are assembled.
Build Pipeline Overview¶
The spksrc build system uses a pipeline-based architecture where each build stage depends on the previous one. The pipeline is implemented through GNU Make with cookie files tracking completion.
block
columns 7
download space checksum space extract space patch
space space space space space space space
plist space install space compile space configure
download --> checksum
checksum --> extract
extract --> patch
patch --> configure
configure --> compile
compile --> install
install --> plist
Cross-Compilation Stages¶
Cross-compilation in spksrc runs in three stages. Stage 0 happens at parse time; stages 1 and 2 run as recipes.
Stage 0: Toolchain Pre-bootstrap¶
At parse time — before a package's DEPENDS are evaluated — stage 0 (mk/spksrc.common/stage0.mk) bootstraps the toolchain in its own work directory and loads its tc_vars.mk, so TC_GCC (and TC_VERS, TC_KERNEL, ...) are known. This lets version_ge($(TC_GCC),...)-gated dependencies (shaderc, vulkan, numpy, ...) evaluate against a real compiler version on a cold tree. It records .stage0-bootstrap_done and deliberately does not generate the package's own tc_vars*, which embed INSTALL_PREFIX-derived paths only available at recipe time.
Stage 1: Toolchain Bootstrap¶
Stage 1 ensures the cross-compilation toolchain is ready:
- Toolchain Download - Fetches the Synology toolchain for the target architecture
- Toolchain Extraction - Unpacks the toolchain to the working directory
- tc_vars Generation - Creates environment files that configure the build:
tc_vars.mk- Core toolchain identity and pathstc_vars.autotools.mk- Autotools adapter variablestc_vars.flags.mk- C/C++ compiler flagstc_vars.rust.mk- Rust environment (if applicable)tc_vars.cmake- CMake toolchain filetc_vars.meson-*- Meson cross/native configuration files
Stage 1 runs as a recipe and is the sole generator of the package's tc_vars* (those embed INSTALL_PREFIX-derived paths from the build environment). For spk/ packages it also builds the meta sources (spk-meta-source). It is idempotent—if the .stage1-tcvars_done cookie exists, it's skipped.
Stage 2: Package Build¶
Stage 2 builds the actual package using the cross-compilation environment:
- depend - Resolves and builds package dependencies
- download - Fetches source archives
- checksum - Verifies archive integrity
- extract - Unpacks source code
- patch - Applies patches from the
patches/directory - configure - Runs configure scripts (autotools, CMake, meson, etc.)
- compile - Compiles the source code
- install - Installs to the staging area
- plist - Generates the package list
Stage Pipeline¶
The same three-stage model drives both cross/ and spk/ builds (mirrors the diagrams in mk/spksrc.cross-cc.mk and mk/spksrc.spk.mk):
┌─ stage 0 ── parse time (mk/spksrc.common/stage0.mk) ───────────────┐
│ bootstrap the toolchain in its own work dir, load its tc_vars.mk │
│ → TC_GCC known, so version_ge($(TC_GCC),...) DEPENDS parse right │
│ cookie: .stage0-bootstrap_done (does NOT write package tc_vars) │
└────────────────────────────────────────────────────────────────────┘
│ (recipes run after parse)
┌─ stage 1 ── recipe time (cross-cc.mk / spk.mk) ────────────────────┐
│ make -C toolchain/<TC> toolchain (MANDATORY) │
│ make -C toolchain/<TC> toolkit (OPTIONAL, REQUIRE_TOOLKIT) │
│ generate the package work-dir tc_vars* (needs INSTALL_PREFIX) │
│ [spk only] spk-meta-source: build the meta sources │
│ cookies: .stage1-tcvars_done / .stage1-tkvars_done (idempotent) │
└────────────────────────────────────────────────────────────────────┘
│
┌─ stage 2 ── recipe time ───────────────────────────────────────────┐
│ cross: download → extract → patch → configure → compile → │
│ install → plist │
│ spk: depend → copy → strip → icon → wizards → package │
└────────────────────────────────────────────────────────────────────┘
SPK Package Assembly¶
For spk/ packages, additional stages create the final SPK:
block
columns 7
depend space copy space strip space iconinfo["icon/info"]
space space space space space space space
spkfile["spk file"] space package space wizards space scripts
depend --> copy
copy --> strip
strip --> iconinfo
iconinfo --> scripts
scripts --> wizards
wizards --> package
package --> spkfile
SPK Assembly Steps¶
- depend - Builds all dependencies listed in
DEPENDS - copy - Copies dependency outputs to staging
- strip - Strips debug symbols from binaries
- icon/info - Processes icons and generates INFO file
- scripts - Generates install/upgrade scripts from templates
- wizards - Processes wizard templates (mustache format)
- package - Creates
package.tgzfrom staging area - spk - Assembles final
.spkfile
Work Directory Structure¶
Each package build creates a work directory with consistent structure:
work-<arch>-<tcversion>/
├── <package>-<version>/ # Extracted source code
├── install/ # Staging area for installation
│ └── var/packages/<pkg>/
│ └── target/ # Final package files
├── staging/ # SPK assembly area
│ ├── package.tgz
│ ├── INFO
│ ├── scripts/
│ └── conf/
├── tc_vars.mk # Toolchain variables
├── tc_vars.*.mk # Build system adapters
└── .<stage>_done # Cookie files
Dependency Resolution¶
spksrc handles three types of dependencies:
Build Dependencies (DEPENDS)¶
Listed in the Makefile, these are built before the current package:
The framework recursively builds each dependency, installs it to the staging area, and makes it available to subsequent builds.
Native Dependencies (NATIVE_DEPENDS)¶
Tools needed on the build host (not cross-compiled):
Python Dependencies¶
Python packages use the wheel system for cross-compilation:
Meta Package Dependencies¶
Large shared libraries are provided by meta packages — ffmpeg7, python312,
and synocli-videodriver — that a consumer reuses instead of rebuilding. The
meta to use is selected by a single variable:
FFMPEG_PACKAGE = ffmpeg7 # e.g. spk/tvheadend
PYTHON_PACKAGE = python312 # any wheel consumer
VIDEODRV_PACKAGE = synocli-videodriver
include ../../mk/spksrc.spk-meta.mk
A consumer includes mk/spksrc.spk-meta.mk (not the per-meta files directly); based on which *_PACKAGE variables are set, spk-meta.mk pulls in the matching spksrc.spk-meta/{python,ffmpeg,videodriver}.mk (then spksrc.spk.mk). That builds the meta source in stage 1 and, during SPK assembly (stage 2), wires its staging area into the consumer's build through SPK_BASE_TEMPLATE:
PKG_CONFIG_LIBDIR(ordered) — the consumer's own staging first, then each meta'slib/pkgconfig(META_PKG_CONFIG_LIBDIR). pkg-config takes the first match, so a local copy always wins over the meta's. The same ordered path is shared by autotools, meson, and the cmake toolchain file.OPENSSL_STAGING_INSTALL_PREFIX/OPENSSL_ROOT_DIR— a single OpenSSL prefix derived from that ordered path (cmake'sfind_package(OpenSSL)ignores pkg-config, so the prefix is baked into the toolchain file).CMAKE_FIND_ROOT_PATH— includes the meta staging roots so cmake'sfind_package/find_path/find_librarylocate meta-provided headers and libraries (e.g. FFmpeg, EXPAT) without per-package hints.
Because the meta's lib/pkgconfig is on PKG_CONFIG_LIBDIR directly, a consumer
needs neither per-.pc symlinks nor per-package -DOPENSSL_* / -DEXPAT_* /
-DFFMPEG_* hints: the meta's libraries are resolved centrally.
Environment Configuration¶
The build environment is configured through layered includes:
- spksrc.common.mk - Base settings and utilities
- spksrc.cross/env-default.mk - Cross-compilation environment
- tc_vars.mk - Toolchain-specific variables
- Build system specific - CMake, meson, autotools adapters
Environment Variables¶
The framework exports numerous variables to configure cross-compilation:
| Variable | Purpose |
|---|---|
CC, CXX |
Cross-compiler paths |
CFLAGS, CXXFLAGS |
Compiler flags |
LDFLAGS |
Linker flags |
PKG_CONFIG_LIBDIR |
Ordered pkg-config search path (local staging, then meta staging) |
META_PKG_CONFIG_LIBDIR |
Meta packages' lib/pkgconfig dirs, exported to cross/ sub-makes |
OPENSSL_STAGING_INSTALL_PREFIX |
Single OpenSSL prefix shared by autotools and cmake |
STAGING_INSTALL_PREFIX |
Installation prefix |
Toolchain Variable Files (tc_vars*)¶
The toolchain bootstrap (stage 1; the toolchain-dir tc_vars.mk also at stage 0) generates a set of tc_vars* files. cross-env.mk loads a subset of them into the make environment, selected by DEFAULT_ENV (default autotools flags rust):
| File | Contents | Loaded into the make env |
|---|---|---|
tc_vars.mk |
Toolchain identity and paths (TC, TC_PATH, CC, CXX, TC_GCC, ...) |
always |
tc_vars.flags.mk |
C / C++ / CPP / LD compiler & linker flags | autotools |
tc_vars.autotools.mk |
Autotools (configure) environment |
autotools |
tc_vars.rust.mk |
Rust / Cargo environment | autotools |
tc_vars.cmake |
CMake toolchain file — read by cmake, not a .mk |
— |
tc_vars.meson-cross, tc_vars.meson-native |
Meson cross/native files — read by meson |
— |
Clean environment for CMake and Meson. CMake (DEFAULT_ENV = cmake) and Meson (DEFAULT_ENV = meson) deliberately load only tc_vars.mk into the make environment — there is no tc_vars.cmake.mk or tc_vars.meson.mk to pick up. All compiler/linker/rust flags are instead baked into the CMake toolchain file (tc_vars.cmake) and the Meson cross file (tc_vars.meson-cross), each generated by a sub-make run with DEFAULT_ENV="flags rust". CMake and Meson then read those files directly, so the build runs in a pristine, fully-controlled environment that does not inherit stray flags from the shell. Autotools, by contrast, receives the same flags through the environment (tc_vars.flags.mk + tc_vars.autotools.mk + tc_vars.rust.mk).
Parallel Build Support¶
spksrc supports parallel builds with three modes:
| Mode | Description |
|---|---|
nop |
No parallel build (single job) |
max |
Use all available CPUs |
N |
Use exactly N parallel jobs |
Configure in local.mk:
Or disable per-package:
Build Logging¶
Build output is logged to:
build-<arch>-<tcversion>.log- Main build logstatus-<arch>-<tcversion>.log- Status messages
The MSG macro provides consistent message formatting:
Related Documentation¶
- Makefile System - Detailed mk/*.mk documentation
- Toolchains - Toolchain management details
- Developer Guide: Build Workflow - Using make targets