Linux Packaging
This directory contains all files needed to build RPM and Debian packages for xrpld.
Directory layout
package/
build_pkg.sh Staging and build script (called by the CMake `package` target and CI)
rpm/
xrpld.spec RPM spec
debian/ Debian control files (control, rules, copyright, xrpld.docs, xrpld.links, source/format)
shared/
xrpld.service systemd unit file (used by both RPM and DEB)
xrpld.sysusers sysusers.d config (used by both RPM and DEB)
xrpld.tmpfiles tmpfiles.d config (used by both RPM and DEB)
xrpld.logrotate logrotate config (installed to /etc/logrotate.d/xrpld)
50-xrpld.preset systemd preset (RPM only; DEB enables the unit via dh_installsystemd)
Prerequisites
Packaging targets and their container images are declared in
.github/scripts/strategy-matrix/linux.json
under package_configs, one entry per distro. Today only linux/amd64 is
emitted. Each entry pins its full container image in an image field; to move
to a new image, edit that field and both CI and local builds pick it up. The
package format (deb or rpm) is inferred at build time from the container's
package manager (apt-get -> deb, dnf/yum -> rpm).
| Package type | Image (package_configs.<distro>[].image in linux.json) |
Tools required |
|---|---|---|
| RPM | ghcr.io/xrplf/xrpld/packaging-rhel:sha-<sha> |
rpmbuild |
| DEB | ghcr.io/xrplf/xrpld/packaging-debian:sha-<sha> |
dpkg-buildpackage, debhelper-compat (= 13) |
To print the full packaging matrix (artifact names and images) for the current
linux.json:
./.github/scripts/strategy-matrix/generate.py --packaging
Building packages
Via CI
Caller workflows (on-pr.yml, on-tag.yml, on-trigger.yml) call
reusable-package.yml. That workflow generates its own packaging matrix from
package_configs in linux.json (via generate.py --packaging) and fans out
one job per distro. Each job downloads the pre-built xrpld binary artifact,
runs in that distro's container (so the package format follows from the
container's package manager), and calls build_pkg.sh with XRPLD_VERSION and
PKG_RELEASE supplied via env — no CMake configure or build step is needed
inside the packaging job.
Locally (mirrors CI)
With an xrpld binary already built at build/xrpld, run the packaging step
inside the same container CI uses. The image tag is derived from linux.json
so you don't need to hardcode a SHA.
# From the repo root. Each distro's container image is the `image` field of its
# package_configs entry in linux.json; the package format is inferred from the
# container's package manager. Example for the rpm-producing image (use
# .package_configs.debian[0].image for the deb image):
IMAGE=$(jq -r '.package_configs.rhel[0].image' .github/scripts/strategy-matrix/linux.json)
XRPLD_VERSION=2.4.0-local
PKG_RELEASE=1
docker run --rm \
-v "$(pwd):/src" \
-w /src \
"$IMAGE" \
./package/build_pkg.sh --xrpld-version "$XRPLD_VERSION" --pkg-release "$PKG_RELEASE"
# Output:
# build/debbuild/*.deb (DEB + dbgsym .ddeb)
# build/rpmbuild/RPMS/x86_64/*.rpm
Via CMake (host-side target)
If you run CMake configure on a host that has rpmbuild or dpkg-buildpackage
installed natively, you can use the CMake target directly — no container
needed, but the host toolchain replaces the pinned CI image:
cmake \
-Dxrpld=ON \
-Dpkg_release=1 \
-Dtests=OFF \
..
cmake --build . --target package # deb on Debian/Ubuntu, rpm on RHEL
The cmake/XrplPackaging.cmake module defines the package target only if at
least one of rpmbuild / dpkg-buildpackage is present; build_pkg.sh then
infers the package format from the host's package manager. The packaging script
installs to FHS-standard paths (/usr/bin, /etc/xrpld, etc.) regardless of
CMAKE_INSTALL_PREFIX.
The version is not a CMake input on this path: cmake/XrplVersion.cmake reads
it from src/libxrpl/protocol/BuildInfo.cpp into xrpld_version, and
XrplPackaging.cmake exports that to build_pkg.sh as XRPLD_VERSION (a
-Dxrpld_version=... on the command line is overwritten and has no effect). The
release defaults to 1 and is overridable with -Dpkg_release=N. To package a
custom version string, use the script or container path above with
--xrpld-version.
How build_pkg.sh works
build_pkg.sh accepts long-form flags, each of which can also be set via an
environment variable. Flags override env vars; env vars override the built-in
defaults. Run ./package/build_pkg.sh --help for the same table:
| Flag | Env var | Default/source | Purpose |
|---|---|---|---|
--src-dir DIR |
SRC_DIR |
$PWD |
repo root |
--build-dir DIR |
BUILD_DIR |
$PWD/build |
directory holding pre-built xrpld |
--xrpld-version STR |
XRPLD_VERSION |
set by CMake/CI; fallback: parsed from xrpld --version |
xrpld version, e.g. 3.2.0-b1 |
--pkg-release N |
PKG_RELEASE |
1 |
package release iteration |
--source-date-epoch SECS |
SOURCE_DATE_EPOCH |
latest git commit ctime; fallback: current time | reproducibility timestamp |
XRPLD_VERSION is the build_pkg.sh input for the xrpld software version in
both package formats. CMake derives it as xrpld_version and exports it as
XRPLD_VERSION; direct script invocations may pass it explicitly or let the
script fall back to parsing xrpld --version.
build_pkg.sh converts pre-release versions such as 3.2.0-b1 or
3.2.0-rc1 from - to ~ for package metadata so pre-releases sort before
the final release. Versions with more than one - are rejected so the
pre-release suffix remains a single token.
PKG_RELEASE is a different value: the package release iteration for that
xrpld version. RPM receives the normalized version and PKG_RELEASE as
separate pkg_version and pkg_release macros for its Version and Release
values; DEB writes them as ${pkg_version}-${PKG_RELEASE} in
debian/changelog.
With PKG_RELEASE=1, the package metadata becomes:
| Input version | RPM version/release | Debian version |
|---|---|---|
3.2.0 |
3.2.0-1%{?dist} |
3.2.0-1 |
3.2.0-b0+abc1234 |
3.2.0~b0+abc1234-1%{?dist} |
3.2.0~b0+abc1234-1 |
3.2.0-b1 |
3.2.0~b1-1%{?dist} |
3.2.0~b1-1 |
3.2.0-rc1 |
3.2.0~rc1-1%{?dist} |
3.2.0~rc1-1 |
The Debian changelog's distribution field carries our apt repo component (the
suite/codename is assigned by the publishing infra at ingest, not here): final
releases use stable, b0 builds, including b0+metadata, use develop, and
other pre-releases use unstable.
The RPM path intentionally uses ~ in Version, matching the Debian
pre-release ordering convention, so RPM filenames/NVRs begin with forms like
xrpld-3.2.0~b1-... and xrpld-3.2.0~rc1-... instead of encoding
pre-releases with an older 0.<release>.<suffix> RPM Release value.
The package format (deb or rpm) is inferred from the host's package
manager (apt-get -> deb, dnf/yum -> rpm). Hosts without one of those
fail early.
Flags are for explicit invocation; environment variables are intended for
CMake/CI integration. The CI workflow and the CMake package target both invoke
build_pkg.sh with no flags; CMake supplies SRC_DIR, BUILD_DIR,
XRPLD_VERSION, and PKG_RELEASE via env, while CI supplies BUILD_DIR,
XRPLD_VERSION, and PKG_RELEASE via env and lets the script use defaults for
the rest.
It resolves SRC_DIR and BUILD_DIR to absolute paths, then calls
stage_common() to copy the binary, config files, and shared support files
into the staging area, and invokes the platform build tool.
RPM
- Creates the standard
rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS}tree inside the build directory. - Copies
xrpld.specand all source files (binary, configs, service files) intoSOURCES/. - Runs
rpmbuild -bb, passing the normalized version andPKG_RELEASEas thepkg_versionandpkg_releaseRPM macros. The spec uses manualinstallcommands to place files, disablesdwz, and writes uncompressed RPM payloads while generating debuginfo packages. - Output:
rpmbuild/RPMS/x86_64/xrpld-*.rpm
DEB
- Creates a staging source tree at
debbuild/source/inside the build directory. - Stages the binary, configs,
README.md, andLICENSE.md. - Copies
package/debian/control files intodebbuild/source/debian/. - Copies shared service/sysusers/tmpfiles into
debian/wheredh_installsystemd,dh_installsysusers, anddh_installtmpfilespick them up automatically. - Generates a minimal
debian/changelogusing${pkg_version}-${PKG_RELEASE}. - Runs
dpkg-buildpackage -b --no-sign -d(-dskips the build-dependency check, since the binary is already built).debian/rulesuses manualinstallcommands. - Output:
debbuild/*.debanddebbuild/*.ddeb(dbgsym package)
Post-build verification
# DEB
dpkg-deb -c debbuild/*.deb | grep -E 'systemd|sysusers|tmpfiles'
lintian -I debbuild/*.deb
# RPM
rpm -qlp rpmbuild/RPMS/x86_64/*.rpm
Reproducibility
build_pkg.sh already defaults SOURCE_DATE_EPOCH to the latest git commit
time, or the current time outside a git tree, and exports it (override with
--source-date-epoch / SOURCE_DATE_EPOCH); the RPM spec clamps file
modification times to it via %build_mtime_policy. The remaining variables
below further improve reproducibility but are not set by the script — export
them yourself if needed:
export TZ=UTC
export LC_ALL=C.UTF-8
export GZIP=-n
export DEB_BUILD_OPTIONS="noautodbgsym reproducible=+fixfilepath"