Makefile Variables¶
This page documents the Makefile variables used in spksrc packages.
Reference Documentation
For a complete reference of all variables and targets, see Makefile Reference.
Include Order Matters
Architecture variables (ARMv7_ARCHS, x64_ARCHS, etc.) and the helper macros are defined by spksrc.common.mk, which loads the architecture classification and macros early (see Macros). Any ifeq using them must therefore appear after include ../../mk/spksrc.common.mk (or the relevant spksrc.cross-*.mk, which includes it) — referenced before the include they are empty.
Package Identification¶
Cross Packages¶
| Variable | Required | Description |
|---|---|---|
PKG_NAME |
Yes | Package name (lowercase, hyphens) |
PKG_VERS |
Yes | Package version |
PKG_EXT |
Yes | Source file extension (tar.gz, tar.xz, zip) |
PKG_DIST_NAME |
Yes | Source filename to download |
PKG_DIST_SITE |
Yes | Base URL for download |
PKG_DIST_MIRRORS |
No | Extra base URLs to fall back to (see Source downloads and mirrors) |
PKG_DIR |
Yes | Directory name after extraction |
Example:
PKG_NAME = curl
PKG_VERS = 8.4.0
PKG_EXT = tar.xz
PKG_DIST_NAME = $(PKG_NAME)-$(PKG_VERS).$(PKG_EXT)
PKG_DIST_SITE = https://curl.se/download
PKG_DIR = $(PKG_NAME)-$(PKG_VERS)
Source downloads and mirrors¶
A download is never a single request. The framework builds a list of candidate
URLs, tries each in turn, and stops at the first success. The list is finite --
the primary URL, plus the mirrors described below, de-duplicated -- and each
candidate is retried DOWNLOAD_TRIES times (2 by default), so a download that
cannot succeed fails instead of looping.
Well-known project mirrors are automatic. If PKG_DIST_SITE points at one
of the big source hosts, the framework already knows its mirrors and will try
them without any declaration on your part:
| If the URL is hosted on | Fallbacks are taken from |
|---|---|
GNU (ftp.gnu.org, ftpmirror.gnu.org) |
GNU_MIRRORS |
SourceForge (downloads.sourceforge.net) |
SOURCEFORGE_MIRRORS |
GNOME (download.gnome.org) |
GNOME_MIRRORS |
X.Org (www.x.org) |
XORG_MIRRORS |
kernel.org (cdn.kernel.org) |
KERNEL_MIRRORS |
A candidate is built by replacing everything up to and including the family's
tree-root marker (/gnu/, /project/, /sources/, ...) with the mirror base,
so mirrors that host the tree under a different prefix still resolve correctly.
Any other URL -- GitHub, a project's own server -- simply has no family, and the
primary URL is used on its own.
PKG_DIST_MIRRORS covers everything else. It takes a space-separated list of
base URLs; PKG_DIST_NAME is appended to each. Use it when upstream is the
right place to fetch from but cannot be relied on:
PKG_DIST_SITE = https://znc.in/releases
# znc.in serves the release tarballs, but it has been flaky (its TLS
# certificate expired on 2026-07-14). Fall back to our own mirror.
PKG_DIST_MIRRORS = https://github.com/SynoCommunity/spksrc/releases/download/sources
Two things to keep in mind:
- The file name must match. The mirror has to serve the file under exactly
PKG_DIST_NAME. A distribution that repackages the tarball under its own name (Debian'sznc_1.10.2.orig.tar.gz, say) cannot be used as a mirror base. - The digests still apply. Every candidate is checked against
digests, so a mirror serving different bytes fails the build rather than poisoning it. This is what makes it safe to list a third-party mirror at all.
When no mirror serves the right file name, the durable answer is to upload the
tarball to the SynoCommunity
sources
release and point PKG_DIST_MIRRORS at it.
SPK Packages¶
| Variable | Required | Description |
|---|---|---|
SPK_NAME |
Yes | Package name shown in Package Center |
SPK_VERS |
Yes | Version displayed to users |
SPK_REV |
Yes | Revision number (increment for each release) |
SPK_ICON |
No | Path to package icon (256x256+ PNG, auto-resized) |
Metadata¶
| Variable | Required | Description |
|---|---|---|
HOMEPAGE |
No | Project website |
COMMENT |
Yes | Short description |
LICENSE |
Yes | License name (GPLv2, MIT, etc.) |
LICENSE_FILE |
No | Path to license agreement file |
MAINTAINER |
Yes | Package maintainer name |
DESCRIPTION |
SPK only | Full description for Package Center |
DISPLAY_NAME |
SPK only | Display name in Package Center |
CHANGELOG |
SPK only | Changes in this version |
Dependencies¶
Build Dependencies¶
| Variable | Description |
|---|---|
DEPENDS |
Cross packages to build/include |
BUILD_DEPENDS |
Packages needed only for building |
NATIVE_DEPENDS |
Native tools needed for building |
# Include these in the SPK
DEPENDS = cross/curl cross/openssl
# Only needed during build
BUILD_DEPENDS = native/cmake
SPK Dependencies¶
| Variable | Description |
|---|---|
SPK_DEPENDS |
Other SPK packages required at runtime |
SPK_CONFLICT |
Packages that conflict with this one |
# Requires WebStation to be installed
SPK_DEPENDS = "WebStation>=3.0"
# Cannot be installed alongside
SPK_CONFLICT = "transmission"
Build Configuration¶
These variables apply to cross/ package Makefiles.
Build System Selection¶
A cross package's build system is chosen by which mk/spksrc.cross-*.mk it includes; the arguments are then passed through the matching variable:
| Build system | Include | Arguments variable |
|---|---|---|
| autotools | spksrc.cross-cc.mk + GNU_CONFIGURE = 1 |
CONFIGURE_ARGS |
| CMake | spksrc.cross-cmake.mk |
CONFIGURE_ARGS |
| Meson | spksrc.cross-meson.mk |
CONFIGURE_ARGS (passed to meson setup) |
All three build systems also honour ADDITIONAL_CONFIGURE_ARGS, appended right
after CONFIGURE_ARGS on the configure/cmake/meson setup command line. It is
never set by the framework. Prefer CONFIGURE_ARGS +=; reach for
ADDITIONAL_CONFIGURE_ARGS only when a package reuses CONFIGURE_ARGS for its
own extra configure invocations and needs args that go to the framework's
invocation only (see cross/x265, whose 10/12-bit sub-builds share
CONFIGURE_ARGS but must not receive the final-build link flags).
# autotools
GNU_CONFIGURE = 1
CONFIGURE_ARGS = --enable-shared --disable-static
CONFIGURE_ARGS += --with-ssl=$(STAGING_INSTALL_PREFIX)
include ../../mk/spksrc.cross-cc.mk
# Meson (CONFIGURE_ARGS is forwarded to `meson setup`)
CONFIGURE_ARGS = -Dtests=disabled
include ../../mk/spksrc.cross-meson.mk
Compiler Flags¶
| Variable | Description |
|---|---|
ADDITIONAL_CFLAGS |
Extra C compiler flags |
ADDITIONAL_CXXFLAGS |
Extra C++ compiler flags |
ADDITIONAL_CPPFLAGS |
Extra preprocessor flags |
ADDITIONAL_LDFLAGS |
Extra linker flags |
These ADDITIONAL_* variables are package-scoped. The toolchain-wide
counterparts — the target ABI in TC_EXTRA_BUILD_FLAGS (folded into each
TC_EXTRA_<LANG>FLAGS) and the link flags in TC_EXTRA_LDFLAGS (-lrt /
-latomic) — are declared by the toolchain, not the package; see
Extra flags a toolchain can declare.
Compile and Install Arguments¶
COMPILE_ARGS and INSTALL_ARGS carry extra arguments for the compile and install steps across every build system. For autotools / plain GNU make each is the make command; for CMake and Meson they are appended as-is to cmake --build / cmake --install and ninja / ninja install respectively.
On the classic gnu-make build path only (not CMake or Meson) both variables have a sensible default when a package leaves them unset, so package-specific make routines can reference them directly:
COMPILE_ARGSdefaults to-j$(NCPUS)(parallel jobs).INSTALL_ARGSdefaults toinstall DESTDIR=$(INSTALL_DIR) prefix=$(INSTALL_PREFIX).
| Variable | Description |
|---|---|
COMPILE_ARGS |
Extra arguments for the compile step (make / cmake --build / ninja); defaults to -j$(NCPUS) on the make path |
INSTALL_ARGS |
Extra arguments for the install step (make / cmake --install / ninja install); defaults to install DESTDIR=$(INSTALL_DIR) prefix=$(INSTALL_PREFIX) on the make path |
INSTALL_TARGET |
Make target for installation (default: install) |
Service Configuration¶
These variables apply to spk/ package Makefiles.
| Variable | Description |
|---|---|
STARTABLE |
yes if package has a service to start |
SERVICE_USER |
auto to create sc-<packagename> user (required for DSM 7) |
SERVICE_SETUP |
Path to service-setup.sh |
SERVICE_PORT |
Port used by the service |
SERVICE_PORT_TITLE |
Label for the port |
SERVICE_WIZARD_SHARENAME |
Wizard share variable for shared folder |
FWPORTS |
Path to firewall port configuration file |
SPK_COMMANDS |
List of bin/command paths for /usr/local/bin symlinks |
STARTABLE = yes
SERVICE_USER = auto
SERVICE_SETUP = src/service-setup.sh
SERVICE_PORT = 8080
SERVICE_PORT_TITLE = Web Interface
# Firewall ports (creates resource entry)
FWPORTS = src/mypackage.sc
# Commands to link to /usr/local/bin
SPK_COMMANDS = bin/mycommand bin/myother
Architecture Support¶
Declare a capability floor (preferred)¶
When a package cannot build on some architectures because of what the
toolchain provides — its compiler, its C library, or the target being
32-bit — declare that floor instead of listing the architectures by hand. The
framework checks each floor against the toolchain's own TC_GCC / TC_GLIBC
and refuses exactly the architectures that cannot meet it, with a
human-readable reason.
| Variable | Description |
|---|---|
MIN_GCC_VERSION |
Needs at least this gcc (e.g. 8, 4.9) |
MIN_GLIBC_VERSION |
Needs at least this glibc — a runtime floor no toolchain can lift |
REQUIRE_64BIT |
Set to 1 when the package needs a 64-bit target |
# Needs C++17 → gcc 8 or newer
MIN_GCC_VERSION = 8
# Needs a 64-bit target (e.g. SVT-AV1)
REQUIRE_64BIT = 1
Why this over an arch list: a hardcoded list says where a package fails, not why. It has to be rechecked by hand every time a toolchain moves, and it cannot express "any architecture whose gcc is older than X". A declared floor can, and it stays correct on its own. Unmet floors accumulate, so an arch that misses more than one is told about all of them.
The toolchain values these check against — TC_GCC, TC_GLIBC and TC_KERNEL
— are declared in each toolchain's Makefile and read statically, so a package
can gate on them before anything is extracted.
Keep the floor consistent between spk/ and cross/. A floor on an spk/
package belongs on its matching cross/ package too, so a build is refused at its
own level instead of failing deep in a dependency. If you add a floor to spk/foo,
add it to cross/foo as well.
Let the essential dependencies set the floor. Every mandatory dependency must
build at the floor the package declares; if one of them needs a newer gcc or glibc,
raise the floor on the package and its cross/ to match. Do not push that
floor onto a shared cross/ library, though — a library keeps the minimum it
needs, so other packages built against it at a lower floor stay buildable.
Gate an optional dependency instead of raising the floor. When a dependency is
optional, do not lift the whole package's floor for it. Guard it with an ifeq (on
the arch, or a capability group) and keep the configure options that use it inside
the same block, so the dependency and its flags turn on together — the way optional
ffmpeg-backed features are wired.
Exclude architectures explicitly¶
For an exclusion that is not a capability floor — a package that is simply not wanted on a platform, or a specific arch/DSM-version pairing — use the arch lists directly.
| Variable | Description |
|---|---|
UNSUPPORTED_ARCHS |
Architectures that cannot build this package |
REQUIRED_MIN_DSM |
Minimum DSM version required |
OS_MIN_VER |
Minimum OS version (alternative to above) |
# Not supported on this whole family
UNSUPPORTED_ARCHS = $(PPC_ARCHS)
# Requires DSM 7.0+
REQUIRED_MIN_DSM = 7.0
Architecture Groups¶
spksrc provides groups such as x64_ARCHS, ARMv7_ARCHS, ARMv8_ARCHS, ARM_ARCHS, PPC_ARCHS, 32bit_ARCHS and 64bit_ARCHS. The complete, authoritative list (with the platform codenames each contains) is in Reference: Architectures.
Use them in ifeq to enable code per architecture. The groups are available after including a spksrc entry point (or spksrc.common.mk):
include ../../mk/spksrc.common.mk
# Only build a feature on 64-bit targets
ifeq ($(findstring $(ARCH),$(64bit_ARCHS)),$(ARCH))
CONFIGURE_ARGS += --enable-feature
endif
# x64-only dependency
ifneq ($(findstring $(ARCH),$(x64_ARCHS)),)
DEPENDS += cross/intel-media-driver
endif
# Exclude a whole family from the build
UNSUPPORTED_ARCHS = $(PPC_ARCHS) $(ARMv5_ARCHS)
Version Conditions¶
The version_* macros gate code on a toolchain (or any version) — they return 1 when true:
include ../../mk/spksrc.common.mk
# Newer toolchains only
ifeq ($(call version_ge,$(TC_GCC),12),1)
DEPENDS += cross/libplacebo
endif
# Workaround for old compilers
ifeq ($(call version_lt,$(TC_GCC),5.0),1)
ADDITIONAL_CFLAGS += -std=gnu99
endif
Path Variables (Available During Build)¶
| Variable | Description |
|---|---|
WORK_DIR |
Package work directory |
PKG_DIR |
Extracted source directory |
INSTALL_DIR |
Installation destination |
STAGING_INSTALL_PREFIX |
Path prefix for installed files |
INSTALL_PREFIX |
Runtime prefix on NAS |
SPK-Specific Variables¶
| Variable | Description |
|---|---|
ADMIN_PORT |
Port for admin interface |
ADMIN_PROTOCOL |
Protocol (http/https) for admin interface |
ADMIN_URL |
Custom admin URL path |
Web Interface Shortcuts¶
Packages with web interfaces can add a shortcut icon to the DSM main menu. For the complete list of variables, see the Makefile Reference.
Automatic Generation (Recommended)¶
Use SERVICE_PORT to automatically generate the shortcut:
DSM_UI_DIR = app
SERVICE_PORT = 8096
SERVICE_PORT_TITLE = My App (HTTP)
ADMIN_PORT = $(SERVICE_PORT)
Custom Configuration¶
For custom URL paths or descriptions, create src/app/config:
{
".url": {
"com.synocommunity.packages.<pkgname>": {
"title": "Package Name",
"desc": "Tooltip description",
"icon": "images/<pkgname>-{0}.png",
"type": "url",
"protocol": "http",
"port": "8080",
"url": "/admin",
"allUsers": true
}
}
}
Point DSM_UI_CONFIG at that file — the framework installs it as the package's app/config, overriding the auto-generated one: