- Dockerfile 67.5%
- Shell 32.5%
| .gitignore | ||
| build.sh | ||
| docker-compose.yml | ||
| Dockerfile | ||
| README.caddy | ||
| README.md | ||
Docker for building SynoCommunity packages
Dockerized build environment for producing Synology .spk packages with
spksrc.
This wraps SynoCommunity's official prebuilt image
(ghcr.io/synocommunity/spksrc) rather than reimplementing its ~50-package
build environment - see their
Docker setup docs.
The local Dockerfile only adds a host-UID-matched user so build output
isn't root-owned on the host.
Requirements for the build machine:
- Docker Desktop or Docker Engine + the
docker composeplugin, withlinux/amd64support (on Apple Silicon this runs under emulation - fine for building, just slower).
Set up to create the Docker container:
You need Docker installed on the build machine first. If that's not this
machine, copy the wrapper files (Dockerfile, docker-compose.yml,
build.sh, .gitignore, README.md) to the target machine, then set
up spksrc there.
A plain clone of upstream is enough just to build for local use:
git clone https://github.com/SynoCommunity/spksrc.git spksrc
If you intend to submit a package upstream, fork
SynoCommunity/spksrc on
GitHub first and clone your fork instead, adding upstream for staying in
sync:
git clone https://github.com/<you>/spksrc.git spksrc
cd spksrc
git remote add upstream https://github.com/SynoCommunity/spksrc.git
cd ..
Layout
spksrc/- a clone (or fork) of spksrc (which is gitignored here; it's its own git repo).Dockerfile,docker-compose.yml,build.sh- the container wrapper.
First-time setup
chmod +x build.sh # if the executable bit didn't survive the copy
./build.sh make setup
This generates spksrc/local.mk with default toolchain configuration.
Find your TCVERSION
TCVERSION has to exactly match one of the toolchain directories spksrc
ships - not just "your NAS's DSM version" loosely. Get the real value from
the toolchains present for your arch, inside the container:
ls toolchain/ | grep -E '^syno-x64-'
DSM's own version string can be more precise than the toolchain name -
/etc.defaults/VERSION's productversion may read e.g. 7.3.2, but the
toolchain directory (and thus TCVERSION) is just syno-x64-7.3. Match
against the ls output above rather than pasting the NAS's version
verbatim.
Using TCVERSION=7.3.2 will fail with No such file or directory for
toolchain/syno-x64-7.3.2 since that directory doesn't exist. (Older DSM
releases are the exception: 6.2.4 really is the full toolchain name
there - again, let the ls output be the source of truth.)
Test build
./build.sh make -C spk/transmission ARCH=x64 TCVERSION=<your version, e.g. 7.2>
If it completes, the environment works. The output .spk files are in
spksrc/packages/, which is bind-mounted back from Docker to the host.
ARCH selection: x64 vs denverton
Most packages (anything that isn't a kernel module and doesn't need
kernel headers) build as generic ARCH=x64 - spksrc only builds a
platform-specific variant like ARCH=denverton when a package actually
depends on kernel sources. Default to x64; only reach for denverton
if a package's Makefile requires it (you'll see it fail asking for
kernel headers otherwise).
Building a new package
This container setup doesn't decide how you do this. See spksrc's
Your First Package
and Package Anatomy
guides for creating a new spk/<name>/Makefile. Once one exists:
./build.sh make -C spk/<name> ARCH=x64 TCVERSION=<your version>
Creating a new package
There's no scaffolding generator in spksrc - new packages are created by hand, starting from an existing one that resembles what you're building. Two directories are involved, and you may only need one of them:
cross/<name>/- a source package: fetches and cross-compiles something (a C/C++ project, a library, anything built from a tarball). Only needed if you're compiling your own or upstream source code.spk/<name>/- the installable package: assembles one or morecross/outputs (or, for a pure script/self-contained binary, nothing external at all) into the final.spk, plus the service script, icon, and install wizard.
Some real examples already in spksrc/ are worth copying from:
| Example | What it shows |
|---|---|
spksrc/cross/tree/ |
Minimal cross/ source package (plain Makefile build, no autotools) |
spksrc/spk/demoservice/ |
Minimal spk/ package with no upstream source at all - a hand-written src/start.sh script wired up as a background service. Best starting point if you're writing your own program from scratch rather than packaging existing software. |
spksrc/spk/transmission/ |
Full real-world example: cross/transmission dependency, service lifecycle hooks, install wizard, settings file |
Option A - packaging your own script/program (no cross/ needed)
mkdir -p spksrc/spk/<name>/src- Put your program in
spksrc/spk/<name>/src/(e.g.start.sh, a Python script, a prebuilt static binary - whatever runs on the NAS). - Copy
spksrc/spk/demoservice/Makefiletospksrc/spk/<name>/Makefileand edit:SPK_NAME,SPK_VERS,SPK_REV,MAINTAINER,DESCRIPTION,DISPLAY_NAME,LICENSE,SERVICE_PORT/SERVICE_PORT_TITLEif it listens on a port. Replace the_pre_copy/_extra_installtargets at the bottom withinstallcommands that copy your files fromsrc/into$(STAGING_DIR). - Write
spksrc/spk/<name>/PLIST- one line per installed file, e.g.bin/start.sh:bin/start.sh(<install path>:<source in $(STAGING_DIR)>). - Copy
spksrc/spk/demoservice/src/service-setup.shas a starting point if your program needs to run as a background service (definesSERVICE_COMMAND, and hooks likeservice_postinst()). - Add a package icon at
src/<name>.pngand reference it viaSPK_ICON.
Option B - packaging/compiling source code (needs cross/)
mkdir -p spksrc/cross/<name>- Copy
spksrc/cross/tree/Makefileas a starting point and setPKG_NAME,PKG_VERS,PKG_DIST_SITE/PKG_DIST_FILE(where the source tarball comes from),DEPENDS(othercross/packages this one needs), and theinclude ../../mk/spksrc.cross-*.mkline matching its build system -spksrc.cross-cc.mk(plain Makefile),spksrc.cross-configure-make.mk(autotools),spksrc.cross-cmake.mk,spksrc.cross-python3.mk, etc. (see othercross/*/Makefiles using the same upstream build system for the right one). - Generate the checksum file once the Makefile can fetch the source:
./build.sh make -C cross/<name> digests - Add a
cross/<name>/PLISTif the build's default install layout needs remapping, and anypatches/*.patchthe source needs. - Build it standalone to iterate:
./build.sh make -C cross/<name> ARCH=x64 TCVERSION=<your version> - Then create the
spk/<name>/wrapper as in Option A, but setDEPENDS = cross/<name>in its Makefile instead of writing install commands by hand - the SPK build pulls the compiled output straight from thecross/package's staging output.
Submitting a package to SynoCommunity
Publishing itself is automatic - SynoCommunity's CI builds and publishes a package once its PR merges, and a repo admin activates it. What's on you is getting a working, tested PR in front of them:
-
Keep your fork current before starting:
cd spksrc git checkout master && git fetch upstream && git merge upstream/master && git push origin master cd .. -
Branch:
add-<name>for a new package,<name>-<version>for a version bump of an existing one. -
Test it - build for every architecture/DSM combination you can reasonably reach, install the resulting
.spkon a real NAS via Package Center → ⚙️ (Manual Install), and work through their checklist:- Service starts and stops from Package Center
- Log/data files land under
/volume1/@appdata/<name>/(the DSM 7 path for${SYNOPKG_PKGVAR}) - Any bundled commands are on
PATHvia/usr/local/binand their--version/--helpwork - Uninstall removes
/var/packages/<name>/cleanly
-
Ensure that
all-supportedbuilds cleanly
The PR rules require all-supported to run correctly. This target builds
the package for every architecture/DSM-toolchain combination spksrc
knows about, not just the ones your package actually supports, so "it
finished" isn't the same as "it passed". See below for more detail:
./build.sh make -C spk/<name> all-supported
(Don't use ARCH=/TCVERSION= - it iterates those internally.
You must make setup first, the same as any other build.)
The takes quite a while and download several toolchains the first time.
all-latest (latest DSM toolchain per arch only) is a faster sanity
check beforehand, but the PR rule specifically asks for all-supported.
-
Commit messages and the PR title are both prefixed with the package's
DISPLAY_NAME:<Display Name>: <what and why>. The PR description should explain why, not just restate the diff, and should include what you tested it on (NAS model, architecture, DSM version). -
Push the branch to your fork and open the PR against
SynoCommunity/spksrc:master.
See SynoCommunity's own Contributing guide for the authoritative version of this process.
Checking all-supported was ok
Two things make the exit code of all-supported unreliable here:
- Its target list is spksrc's global
SUPPORTED_ARCHS- every combo it has a toolchain for, including any your package deliberately excludes viaUNSUPPORTED_ARCHSorREQUIRED_MIN_DSM. - Each combo is invoked with a
-@prefix (Make's "ignore this recipe's failure and keep going"), so an excluded combo hitting your package's own precheck rejection ("is not a supported architecture"/"is lower than <version>") is silently swallowed -all-supportedreports overall success regardless of how many combos failed underneath.
So check what it actually did, not just whether it finished. Logs land in
the package directory itself (LOG_DIR defaults to $(CURDIR)):
spk/<name>/status-build.log- one BEGIN/END line pair per arch-version attempted; scan this first (grep for FAILED)spk/<name>/build-<arch>-<tcversion>.log- the full output for one specific combo, for when a status line looks wrong.packages/<name>_*.spk- the actual proof: list these afterward and confirm they match exactly the combos you expect to succeed.
A quick first pass:
grep -vE "is not a supported architecture|is lower than [0-9.]+" spk/<name>/status-build.log
Anything left over that isn't a clean BEGIN/END pair is a real failure
to chase down via its build-<arch>-<tcversion>.log.
Reference
- Package Anatomy and Makefile Variables for the full variable list.
- PLIST Files and Service Scripts for the two files you'll spend the most time on.
- Contributing and Update Policy for the fork/branch/PR conventions and testing checklist a submission needs to follow.
Get an interactive shell inside Docker:
./build.sh