# Tutorial 5: the debhelper pipeline and customizing debhelper steps Let's say you have an application that must be compiled in manner that debhelper doesn't support out-of-the-box. It could be a C application with a custom build system, or it could be an application written in an new programming language. What do you do? Do you fall back to not using debhelper as much, like you did in tutorial 3? But if you do that then you have to figure out which individual `dh_*` tools to call, which is a pain. There is a better way: you can override parts of debhelper while still allowing it to everything else. **Table of contents** * [The debhelper pipeline system](#the-debhelper-pipeline-system) - [The `clean` pipeline](#the-clean-pipeline) - [The `build` pipeline](#the-build-pipeline) - [The `binary` pipeline](#the-binary-pipeline) - [Notes](#notes) * [Our application and its custom build system](#our-application-and-its-custom-build-system) * [Overriding debhelper steps](#overriding-debhelper-steps) * [Verifying that it works](#verifying-that-it-works) * [Extending debhelper steps](#extending-debhelper-steps) * [Conclusion](#conclusion) --- ## The debhelper pipeline system Think of debhelper as a pipeline system. There are three pipelines: `clean`, `build` and `binary`, which correspond to the three `rules` makefile targets. Each pipeline calls a number of steps serially, with each step usually being a command invocation. You can see the steps in the `dh` output. But for your convenience, here is a listing. ### The `clean` pipeline The `clean` pipeline consists of these steps: | Step | Goal | Behavior | |------|------|----------| | [dh_testdir](https://manpages.debian.org/stretch/debhelper/dh_testdir.1.en.html) | Sanity checks the current working directory | Aborts if debian/control does not exist | | [**dh_auto_clean**](https://manpages.debian.org/stretch/debhelper/dh_auto_clean.1.en.html) | Remove compilation products generated by the app's build system | Runs `make clean` or a similar command if the build system is supported | | [dh_clean](https://manpages.debian.org/stretch/debhelper/dh_clean.1.en.html) | Removes build products generated by a previous debhelper run | Removes the package root directory, amongst others | ### The `build` pipeline The `build` pipeline consists of these steps: | Step | Goal | Behavior | |------|------|----------| | [dh_testdir](https://manpages.debian.org/stretch/debhelper/dh_testdir.1.en.html) | Sanity checks the current working directory | Aborts if debian/control does not exist | | [dh_auto_configure](https://manpages.debian.org/stretch/debhelper/dh_auto_configure.1.en.html) | Configures the source code | Runs `./configure`, `cmake` or a similar command if the build system is supported | | [**dh_auto_build**](https://manpages.debian.org/stretch/debhelper/dh_auto_build.1.en.html) | Compiles the source code | Runs `make`, `setup.py` or a similar build command if the build system is supported | | [dh_auto_test](https://manpages.debian.org/stretch/debhelper/dh_auto_test.1.en.html) | Runs the source code's test suite | Runs `make test`, `make check` or a similar command if the build system is supported | ### The `binary` pipeline The `binary` pipeline consists of these steps: | Step | Goal | Behavior | |------|------|----------| | [dh_testroot](https://manpages.debian.org/stretch/debhelper/dh_testroot.1.en.html) | Checks whether the `rules` makefile is run as root[1] | Aborts if it isn't | | [dh_prep](https://manpages.debian.org/stretch/debhelper/dh_prep.1.en.html) | Performs some preparation cleanups | Removes the package root directory, amongst others | | [**dh_auto_install**](https://manpages.debian.org/stretch/debhelper/dh_auto_install.1.en.html) | Installs the compiled application into the package root directory | Runs `make install DESTDIR=...` or a similar command if the build system is detected | | [dh_installdocs](https://manpages.debian.org/stretch/debhelper/dh_installdocs.1.en.html) | Installs documentation such as man pages into the package root directory | | | [dh_installchangelogs](https://manpages.debian.org/stretch/debhelper/dh_installchangelogs.1.en.html) | Installs the debian/changelog file into the package root directory | | | [dh_perl](https://manpages.debian.org/stretch/debhelper/dh_perl.1.en.html) | Do Perl stuff | Calculates Perl dependencies and cleans up after MakeMaker | | [dh_link](https://manpages.debian.org/stretch/debhelper/dh_link.1.en.html) | Creating necessary symlinks in the package root directory | | [dh_compress](https://manpages.debian.org/stretch/debhelper/dh_compress.1.en.html) | Compresses files and fix symlinks in package root directory | | | [dh_fixperms](https://manpages.debian.org/stretch/debhelper/dh_fixperms.1.en.html) | Fix various file permissions in the package root directory | | | [dh_strip](https://manpages.debian.org/stretch/debhelper/dh_strip.1.en.html) | Strips binaries | Automatically finds binaries in the package root and invokes `strip` on them | | [dh_makeshlibs](https://manpages.debian.org/stretch/debhelper/dh_makeshlibs.1.en.html) | Creates shlibs files using dpkg-gensymbols | | | [dh_shlibdeps](https://manpages.debian.org/stretch/debhelper/dh_shlibdeps.1.en.html) | Finds out which binaries depend on which shared libraries | Invokes dpkg-shlibdeps | | [dh_installdeb](https://manpages.debian.org/stretch/debhelper/dh_installdeb.1.en.html) | Installs key files in the debian/ directory into `/DEBIAN` | | | [dh_gencontrol](https://manpages.debian.org/stretch/debhelper/dh_gencontrol.1.en.html) | Creates `/DEBIAN/control` | Invokes `dpkg-gencontrol` | | [dh_md5sums](https://manpages.debian.org/stretch/debhelper/dh_md5sums.1.en.html) | Creates `/DEBIAN/md5sums` | Automatically scans all files in the package root | | [dh_builddeb](https://manpages.debian.org/stretch/debhelper/dh_builddeb.1.en.html) | Creates the .deb file(s) | Invokes dpkg-deb | ### Notes **Footnote 1:** Debian packages are supposed to be built as root, for reasons that I also do not totally comprehend. But wait, we ran tutorials 1-4 without root, so why didn't it abort? It turns out that dpkg-buildpackage automatically runs the `rules` makefile through [fakeroot](https://linux.die.net/man/1/fakechroot). So I think that the `dh_testroot` step is unnecessary nowadays. **Note:** the exact steps differ between different versions of debhelper. For example debhelper 10 (introduced in Debian 9) has more steps. When targeting a specific distribution version, be sure to look at the. **Tip:** [the debhelper man page](https://manpages.debian.org/stretch/debhelper/debhelper.7.en.html) has documentation on each of the step described above. **Tip 2**: I've highlighted the especially interesting steps: `dh_auto_clean`, `dh_auto_build` and `dh_auto_install`. We are going to override these steps in the rest of this tutorial. ## Our application and its custom build system The application that we use in this tutorial is the same hello world application from tutorial 4, but with a custom build system and with the version number bumped 5.0.0. The commands `make`, `make clean` and `make install DESTDIR=...` are replaced by `./build.sh`, `./clean.sh` and `env DESTDIR=... ./install.sh`. Let's prepare our application and its files: ~~~bash mkdir tutorial-5 cd tutorial-5 editor hello.c editor build.sh editor clean.sh editor install.sh chmod +x build.sh chmod +x clean.sh chmod +x install.sh mkdir debian editor debian/control echo 9 > debian/compat editor debian/changelog ~~~ ### `hello.c` hello.c must contain: ~~~c #include int main() { printf("hello 5.0.0\n"); return 0; } ~~~ ### `build.sh` build.sh must contain: ~~~bash #!/bin/sh set -ex gcc -Wall -g hello.c -o hello ~~~ ### `clean.sh` clean.sh must contain: ~~~bash #!/bin/sh set -ex rm -f hello ~~~ ### `install.sh` install.sh must contain: ~~~bash #!/bin/bash set -ex mkdir -p "$DESTDIR/usr/bin" cp hello "$DESTDIR/usr/bin/hello" ~~~ ### `debian/control` The control file remains the same compared to tutorial 4: ~~~ Source: hello Section: devel Priority: optional Maintainer: John Doe Build-Depends: build-essential, debhelper (>= 9) Package: hello Architecture: any Depends: ${shlibs:Depends}, ${misc:Depends} Description: John's hello package John's package is written in C and prints a greeting. . It is awesome. ~~~ ### `debian/compat` The compatibility level remains the same compared to tutorial 3: ~~~bash echo 9 > debian/compat ~~~ ### `debian/changelog` We add a new changelog entry to the beginning of the file. The beginning of the file looks like this: ~~~ hello (5.0.0-1) stretch; urgency=medium * Support custom build system. -- John Doe Tue, 11 Jul 2017 16:34:17 +0000 ~~~ ## Overriding debhelper steps From the above pipeline description, it becomes clear that if we want to support our custom build system, we need to override the `dh_auto_clean`, `dh_auto_build` and `dh_auto_install` steps. We can do that by defining targets in the rules file according to this naming scheme: override_ Inside this target you can then do whatever you want. This target will be invoked by debhelper in place of the corresponding `dh_*` command invocation. So here is what our rules file should look like: ~~~Makefile #!/usr/bin/make -f %: dh $@ override_dh_auto_clean: ./clean.sh override_dh_auto_build: ./build.sh override_dh_auto_install: env DESTDIR=debian/hello ./install.sh ~~~ ## Verifying that it works Build the package: ~~~bash dpkg-buildpackage -b ~~~ Then: ~~~ $ sudo gdebi -n ../hello_5.0.0-1_.deb $ hello 5.0.0 ~~~ ## Extending debhelper steps What if you don't want to completely override a step? What if you want to extend a step, e.g. by running custom commands right before or right after that step? For example what if the `dh_strip` step fails to find your binary (because your binary is gzipped for example), and you want to run your own commands to strip that file. Inside an override target you can call the original command. Here is how you can override the above hypothetical gzip-strip situation: ~~~Makefile override_dh_strip: dh_strip gunzip debian/hello/usr/lib/helper-binary.gz strip --strip-all debian/hello/usr/lib/helper-binary gzip debian/hello/usr/lib/helper-binary ~~~ ## Conclusion Congratulations, this concludes the beginner tutorials! You have learned that debhelper is a pipeline system and how you can override or extend specific pipeline steps.