Translation(s): none


What is sbuild

What is sbuild for:

sbuild is fundamentally a tool for making and testing binary packages (.deb) from a debian source package (.dsc). Its isolation features help you test your packages before uploading. To maintain and upload a package you will also want to use a package builder like git-buildpackage).

An alternative to sbuild is pbuilder combined with cowbuilder.

This main part of this page is intended as a short guide to sbuild. It documents how to set up sbuild and build packages with it. The later parts document optional enhancements to the simple setup described in the first section.

Setup

There are many ways to use sbuild. This section documents a modern configuration, using mmdebstrap and unshare, which allows you to build and test packages without being root. In this configuration packages are built and tested in a clean and ephemeral chroot that is extracted from a tarball. It is recommend to use the sbuild version from bookworm-backports.

1. Install necessary packages:

   1 sudo apt install sbuild mmdebstrap uidmap

2. Create a directory for the chroot tarball

   1 mkdir -p ~/.cache/sbuild

3. Create/Update the tarball

   1 mmdebstrap --include=ca-certificates --skip=output/dev --variant=buildd unstable ~/.cache/sbuild/unstable-amd64.tar.zst https://deb.debian.org/debian

Change deb.debian.org to any mirror if you need. You can use any mmdebstrap arguments such as --include=gnupg,debhelper to customise the chroot. The chroot is extracted into a temporary directory whenever it is used, ensuring builds and tests happen in a clean environment.

If you are using apt-cacher-ng, specify http:// instead of https:// and enable the apt-cacher-ng proxy using --aptopt='Acquire::http { Proxy "http://127.0.0.1:3142"; }'. You can use any tarball compression extension you prefer.

   1 mmdebstrap --include=ca-certificates --skip=output/dev --variant=buildd unstable ~/.cache/sbuild/unstable-amd64.tar.xz http://deb.debian.org/debian --aptopt='Acquire::http { Proxy "http://127.0.0.1:3142"; }'

You can use choose the compression algorithm for the tarball by specifying the extension (.tar.xz, .tar.gz or plain .tar, etc). As of May 2024, ZST seems to provide the best size/time ratio. It certainly is the fastest on a Dell Precision 3800M, 16GB RAM, on an SSD drive (a computer from early 2015):

Format

Tarball size

Time

.xz

~100MB

179,60s user 7,09s system 75% cpu 4:07,49 total

.gz

~150MB

38,51s user 6,13s system 83% cpu 53,423 total

.zst

~139MB

22,68s user 6,28s system 74% cpu 38,868 total

4. Configure ~/.sbuildrc

The command below will create a new ~/.sbuildrc with suitable options. It will overwrite any existing file, so make sure you do not already have an ~/.sbuildrc or create a backup copy.

NOTE: Starting with version 0.87.1 you may see the information to prefer ~/.config/sbuild/config.pl over ~/.sbuildrc, see #1087378 and 0.87.1 changelog.

   1 cat << "EOF" > ~/.sbuildrc
   2 # Set the chroot mode to be unshare.
   3 $chroot_mode = 'unshare';
   4 
   5 $external_commands = { "build-failed-commands" => [ [ '%SBUILD_SHELL' ] ] };
   6 
   7 # Uncomment below to specify the distribution; this is the same as passing `-d unstable` to sbuild.
   8 # Specifying the distribution is currently required for piuparts when the changelog targets UNRELEASED.  See #1088928.
   9 #$distribution = 'unstable';
  10 
  11 ## Run lintian after every build (in the same chroot as the build); use --no-run-lintian to override.
  12 $run_lintian = 1;
  13 # Display info tags.
  14 $lintian_opts=['--display-info', '--verbose', '--fail-on','error,warning', '--info'];
  15 # Display info and pedantic tags, as well as overrides.
  16 #$lintian_opts=['--display-info','--verbose', '--fail-on','error,warning', '--info', '--pedantic', '--show-overrides'];
  17 
  18 ## Run autopkgtest after every build (in a new, clean, chroot); use --no-run-autopkgtest to override.
  19 $run_autopkgtest = 1;
  20 # Specify autopkgtest options.  The commented example below is the default since trixie.
  21 #$autopkgtest_opts = ['--apt-upgrade', '--', 'unshare', '--release', '%r', '--arch', '%a' ];
  22 
  23 ## Run piuparts after every build (in a new, temporary, chroot); use --no-run-piuparts to override.
  24 # this does not work in bookworm
  25 $run_piuparts = 1;
  26 # Build a temporary chroot.
  27 $piuparts_opts = ['--no-eatmydata', '--distribution=%r', '--fake-essential-packages=systemd-sysv'];
  28 # Build a temporary chroot that uses apt-cacher-ng as a proxy to save bandwidth and time.
  29 #$piuparts_opts = ['--no-eatmydata', '--distribution=%r', '--bootstrapcmd=mmdebstrap --skip=check/empty --variant=minbase --aptopt="Acquire::http { Proxy \"http://127.0.0.1:3142\"; }"'];
  30 
  31 EOF

Make sure to setup aliases if building for UNRELEASED or experimental unless you want to manually specify the distribution.

When using this example ~/.sbuildrc, Piuparts generates a temporary, minimal chroot with every run, because doing so gives more complete results than using the one built with --variant=buildd. There are many options for sbuild: see the man pages for sbuild(1) and sbuild.conf(5) for details.

Updating chroot manually (with unshare)

To keep the chroot updated it is recommended to simply delete it and recreate it by re-running the mmdebstrap command (see above): this is fast and makes sure the build environment remains clean.

If you really want to update an old chroot you can call:

   1 sbuild-update --chroot-mode=unshare --update --upgrade --dist-upgrade --autoremove unstable

Building packages

Standalone

With properly configured ~/.sbuildrc as above, you can build a package by running the following from the source directory of the package. This will build a source package (.dsc) and then build all the binary packages in it.

   1 sbuild

If you have a .dsc (generated by dpkg-buildpackage, git-buildpackage, etc) you can use that:

   1 sbuild package_*.dsc

If you have deb-src lines configured for apt you can build any package without downloading the source code first:

   1 sbuild -d unstable hello

Integration with git-buildpackage (gbp-buildpackage)

To build a package from a git repository managed with git-buildpackage run

   1 gbp buildpackage --git-builder=sbuild

or put the following in ~/.gbp.conf:

[buildpackage]
builder = sbuild

This creates a source package from the git repository (on the host machine), and then runs sbuild to build and test the binary packages (in a chroot) There is more information about packaging with git at PackagingWithGit.

Speeding up build process

Use apt-cacher-ng

   1 sudo apt install apt-cacher-ng

Add this to the mmdebstrap options when creating the chroot tarball:

--aptopt='Acquire::http { Proxy "http://127.0.0.1:3142"; }'

Hash sum mismatch errors

apt-cacher-ng clients may (regularly) found errors about hash sum mismatches that yield to non-valid key signatures. One way to solve this is to clean the expired cache files, as documented at the apt-cacher-ng wiki page.

Building on tmpfs [pre-trixie]

Since trixie this is no longer needed because /tmp is now a tmpfs by default.

   1 echo "\$unshare_tmpdir_template = '/dev/shm/tmp.sbuild.XXXXXXXXXX';" >> ~/.sbuildrc

This requires /dev/shm to be large enough to unpack the chroot and run the build process: to temporarily enlarge it you can do (as root)

   1 mount -o remount,size=2G /dev/shm

Using ccache with sbuild

ccache is a compiler wrapper that will cache compilation results (object files) from gcc and g++. If you repeatedly compile the same source code (or parts of it), ccache will shorten recompilation times by avoiding recompilation of files that have not changed between runs.

This is usually a significant saving during package development because only a few files are changed between runs.

It is also effective with packages that are frequently updated, because often only a few files actually change during updates to a software.

Add this to the mmdebstrap options when creating the chroot tarball:

   1 --include=ccache --customize-hook='chroot "$1" update-ccache-symlinks'

(The update-ccache-symlinks is needed due to 632779)

Add this to your .sbuildrc:

   1 cat << "EOF" >> ~/.sbuildrc
   2 $build_environment = { "CCACHE_DIR" => "/build/ccache" };
   3 $path = "/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games";
   4 $build_path = "/build/package/";
   5 $dsc_dir = "package";
   6 $unshare_bind_mounts = [ { directory => "$HOME/.cache/ccache", mountpoint => "/build/ccache" } ];
   7 $autopkgtest_opts = [ '--apt-upgrade', '--env=CCACHE_DIR=/build/ccache', '--env=PATH=/usr/lib/ccache:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games', '--', 'unshare', '--release', '%r', '--arch', '%a', '--prefix=/dev/shm/tmp.autopkgtest.', '--bind', "$HOME/.cache/ccache", '/build/ccache' ];
   8 EOF

The sbuild user in the chroot needs to have access to $HOME/.cache/ccache which is on the host. You can either do chmod a+X "$HOME" "$HOME/.cache" and chmod -R a+rwX "$HOME/.cache/ccache" on the host, or use another cache location (note that files in /tmp are at risk of being wiped on reboot or by systemd's periodic cleanup).

use eatmydata

It is unclear how much this helps if the chroot is extracted to a tmpfs, but running eatmydata may provide a speed boost by preventing data being written to the disk as often -- this is, obviously, with a risk of data corruption. It only affects the package build, not the rest of your system. To use it:

1. Include eatmydata in the chroot: add --include=eatmydata to the mmdebootstrap options when creating the chroot tarball.

2. Use eatmydata in the build: add the following to .sbuildrc: this chroot is amd64: you can adapt the path on other architectures.

   1 # use it in the chroot
   2 $chroot-setup-commands => [ "export LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libeatmydata.so" ];
   3 
   4 # use it in the build
   5 $build_environment ={                                                                                                          
   6   "LD_PRELOAD" => "/usr/lib/x86_64-linux-gnu/libeatmydata.so"                                                                
   7 };                
   8 
   9 # add LD_PRELOAD to variables retained                                                                                                             
  10 $environment_filter=[Dpkg::BuildInfo::get_build_env_allowed(), '^LD_PRELOAD$']];

3. (You may want to add --no-eatmydata to $piuparts_opts since piuparts also enables eatmydata by default).

4. You can then call sbuild as usual: you may want to also install eatmydata on the host and call sbuild as

   1 LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libeatmydata.so sbuild

which may help speed up the unpacking of the chroot tarball.

Use schroot instead of unshare

sbuild can use chroots created by schroot, these use overlayfs which can be faster than unshare. However, this requires greater use of root than unshare: with schroot you can run the build, lintian and autopkgtests as a user (if root has granted access), but piuparts and creating a new chroot requires root (or permission to elevate privileges with sudo or similar).

Advanced Tips

Cross-compiling packages

sbuild also supports cross-compiling a package to build it for a different processor architecture. For example, you can build arm64 or ppc64el packages on an amd64 machine.

To build a package for arm64 in an amd64 chroot (as created above), you can use:

   1 sbuild --host=arm64

NB: There's no need to cross-compile packages marked as arch:all as they are independent of architecture.

External Commands

sbuild supports running external commands at various stages of the build process. For example you can run a script inside the chroot after it has been setup.

To run /usr/local/bin/myscript, add it to the chroot tarball and include the following in ~/.sbuildrc:

   1 $external_commands = {
   2                         'chroot-setup-commands' => ['/usr/local/bin/myscript']
   3                       };

sbuild supports some percent-escaped keywords here. For example, %SBUILD_CHANGES is changed to the path of the '.changes' file for a successfully built package.

Here is an example of adding a post build command to run /usr/local/bin/postbuildscript with %SBUILD_CHANGES as an argument.

   1 $external_commands = {
   2                         'post-build-commands' => ['/usr/local/bin/postbuildscript', '%SBUILD_CHANGES'],
   3                         'chroot-setup-commands' => ['/usr/local/bin/myscript'],
   4                         'chroot-cleanup-commands' => [],
   5                         'pre-build-commands' => []
   6                      };

Here is how to drop into a shell inside the ephemeral chroot if the build fails

   1 $external_commands = {                                                                                                                 
   2                         "chroot-update-failed-commands" => [ [ '%SBUILD_SHELL' ] ],                                                                        
   3                         "build-deps-failed-commands" => [ [ '%SBUILD_SHELL' ] ],                                                                           
   4                         "build-failed-commands" => [ [ '%SBUILD_SHELL' ] ],                                                                                
   5 };   

See the 'EXTERNAL COMMANDS' section of the sbuild man page for more information on external commands.

Enabling experimental

To allow the build to use packages from the Debian experimental repository you can use the --extra-repository argument. You may also want to use the aspcud resolver:

   1 sbuild --extra-repository='deb https://deb.debian.org/debian experimental main' --build-dep-resolver=aspcud mypkg.dsc

To exactly mimic the resolver used on the official buildds you need to add aspcud preferences that minimise the number of packages from experimental. See the DSA puppet repository at https://salsa.debian.org/dsa-team/mirror/dsa-puppet/-/blob/production/modules/buildd/templates/sbuild.conf.erb

   1 sbuild --extra-repository='deb https://deb.debian.org/debian experimental main' --build-dep-resolver=aspcud --aspcud-criteria '-count(solution,APT-Release:=/a=experimental/),-removed,-changed,-new' mypkg.dsc

You can also use the aptitude resolver (used by *-backports), which for most packages should have the same effect.

   1 sbuild --extra-repository='deb http://deb.debian.org/debian experimental main' --build-dep-resolver=aptitude mypkg.dsc

Another possibility is to have a dedicated ~/.sbuildrc.experimental configuration file and run sbuild via

   1 SBUILD_CONFIG=~/.sbuildrc.experimental sbuild mypkg.dsc

with ~/.sbuildrc.experimental containing for example:

   1 $extra_repositories = [ 'deb http://deb.debian.org/debian experimental main' ];
   2 $build_dep_resolver = 'aptitude'; 

If you need to test a build against a versioned Build-Depends from experimental, you can use --add-depends:

   1 sbuild --extra-repository='deb http://deb.debian.org/debian experimental main' --build-dep-resolver=aspcud --add-depends='foo-dev (>= 1.2.3-4)' mypkg.dsc

Build for experimental

If you want to build for unstable but upload to experimental, use, for example,

   1 sbuild -d experimental -c unstable-amd64-sbuild mypkg.dsc

Alternatively, if you know that you always want to build your packages for experimental in a unstable chroot, just add experimental as an alias for your unstable chroot:

   1 cd ~/.cache/sbuild
   2 ln -s {unstable,experimental}-*.tar*

Enabling incoming.debian.org

If you want to build before the next dinstall you can add the incoming queue as an extra repositiory:

   1 sbuild --extra-repository='deb http://incoming.debian.org/debian-buildd/ buildd-unstable main' mypkg.dsc

Using aliases

sbuild chooses the chroot based on the distribution in debian/changelog. You can change this with the -d option or you can use symlinks to set up aliases so that source package with UNRELEASED or experimental in debian/changelog are built in an unstable chroot (or so that -d sid can be used to build in unstable, etc):

   1 cd ~/.cache/sbuild
   2 ln -s {unstable,UNRELEASED}-*.tar*
   3 ln -s {unstable,sid}-*.tar*

Adding extra packages

It is often necessary to add extra binary packages as build dependencies for the package you are building. For example, you might want to provide a build dependency that is waiting in the NEW queue and so isn't available from the mirrors ; or you're precisely trying to assess if your new package breaks anything before uploading. To do this, use the --extra-package=/path/to/foo.deb option to sbuild ; and if there are several such packages, --extra-package=/path/to/ will also scan this directory. The path must be an absolute path on the host, and ~ is not automatically expanded to $HOME!

You might find that the output from the resolver is not helpful in determining which extra package you need to make available. In this case, it can be useful to pass --build-dep-resolver=aptitude which tends to provide more useful output (though you should remove it once you've figured out the problem).

Remote build servers

One advantage of cowbuilder over sbuild is that it supports offloading builds to a remote server with cowpoke. One way to do this with sbuild is to create a source package locally and transfer it to the remote machine for building. The latter can be done with dcmd. Example:

   1 dpkg-buildpackage -S
   2 dcmd scp ../foo-1.0.dsc example.net:build-area
   3 ssh example.net sbuild build-area/foo-1.0.dsc

Validate package cleanup

Packages will fail to build twice in a row if the clean target of debian/rules does not restore the source directory to its initial state. Adding the following to your ~/.sbuildrc will help you to detect modifications:

$external_commands = {
    "starting-build-commands" => [
        'bash -c \'find %SBUILD_PKGBUILD_DIR -print0 |
                  sort -z |
                  while read -d $\'\\\'\'\0\'\\\'\' file; do
                      echo -n "$(stat -c "%n %F %%s" "${file}") "
                      if [ -f "${file}" ]; then
                          sha256sum "${file}" |
                              cut -d " " -f 1
                      else
                              echo
                      fi
                  done > /tmp/file-list.pre-build\''
    ],
    "chroot-cleanup-commands" => [
        'cd %SBUILD_PKGBUILD_DIR && ./debian/rules clean',
        'bash -c \'find %SBUILD_PKGBUILD_DIR -print0 |
                  sort -z |
                  while read -d $\'\\\'\'\0\'\\\'\' file; do
                      echo -n "$(stat -c "%n %F %%s" "${file}") "
                      if [ -f "${file}" ]; then
                          sha256sum "${file}" |
                              cut -d " " -f 1
                      else
                              echo
                      fi
                  done > /tmp/file-list.post-build\'',
        'diff /tmp/file-list.pre-build /tmp/file-list.post-build'
    ]
};

Using podman for autopkgtests

   1 sudo apt install podman passt
   2 mmdebstrap --include=libpam-systemd,systemd-resolved --customize-hook='sed "s/^deb /deb-src /" "$1/etc/apt/sources.list" > "$1/etc/apt/sources.list.d/src.list"' unstable | podman import - debian-unstable
   3 cat << "EOF" >> ~/.sbuildrc
   4 $autopkgtest_root_args = 'PATH=/usr/sbin:/usr/bin:/sbin:/bin';
   5 $autopkgtest_opts = ['--shell-fail', '--apt-upgrade', '--', 'podman', '--init', 'debian-%r'];
   6 EOF

Using qemu for autopkgtests

   1 sudo apt install qemu-system
   2 mmdebstrap-autopkgtest-build-qemu --boot=efi unstable $HOME/.cache/sbuild/unstable-amd64.img
   3 cat << "EOF" >> ~/.sbuildrc
   4 $autopkgtest_opts = ['--shell-fail', '--apt-upgrade', '--', 'qemu', '--cpus', '4', '--ram-size', '8096', '--efi', "$HOME/.cache/sbuild/%r-%a.img"];
   5 EOF

Using /var/cache/apt/archives/ as package cache

   1 sudo apt -o Dir::State::status=lock build-dep -d <pkg>
   2 sudo apt -o Dir::State::status=lock install -d lintian
   3 cd $(mktemp -d)
   4 ln -s /var/cache/apt/archives/
   5 apt-ftparchive packages . > Packages
   6 apt-ftparchive release . > Release
   7 python3 -m http.server 5678 --bind 127.0.0.1
   8 sbuild --extra-repository="deb [trusted=yes] http://127.0.0.1:5678 ./" --chroot-setup-commands "rm /etc/apt/sources.list"


CategoryPackaging CategoryPackaging