debhelper(7)

๐Ÿฆ‹ debhelper(7) โ€” the whole debhelper tool suite ๐Ÿฆ‹

๐Ÿ’– NAME

So bestie, this is the manual page for debhelper โ€” manual, section 7 โ€” and it's literally the debhelper tool suite. That's the whole vibe. The tools, the squad, the girlies. ๐Ÿ‘‘


โœจ SYNOPSIS โœจ

dh_* [-v] [-a] [-i] [--no-act] [-ppackage] [-Npackage] [-Ptmpdir]

๐ŸŒธ DESCRIPTION ๐ŸŒธ

Okay so here's the tea: debhelper exists to help you build a Debian package. That's it, that's the job. ๐Ÿ’…

The philosophy behind debhelper โ€” and honestly this is lowkey the most iconic part โ€” is that it gives you a collection of small, simple, and easily understood tools. You use them inside debian/rules to automate all the common little chores that come up when you're building a package. Which means: less work for you, the packager. ๐Ÿซถ

And there's a glow-up side effect too: because of this, these tools can get changed if Debian policy shifts, and any package using them just needs a rebuild to be in compliance with the new policy. No cap. Slay.

A typical debian/rules file that uses debhelper will call several debhelper commands one after another, or it'll use dh(1) to automate the whole process for you. And if you wanna see what these rules files actually look like, examples of rules files that use debhelper live in:

/usr/share/doc/debhelper/examples/

So to make a brand new Debian package with debhelper, you can literally just copy one of the sample rules files and edit it by hand. โœจ Or, if you want a lil assist, you can try the dh-make package โ€” it contains a dh_make command that partially automates the process for you. ๐ŸŽ€

And if you want a more gentle, hold-your-hand introduction, the maint-guide Debian package has a tutorial about making your first package using debhelper. Perfect for baby's first package fr fr. ๐Ÿผ

One big rule, and I need you to internalise this: except where a tool explicitly says otherwise, all the debhelper tools assume they're being run from the root directory of an unpacked source package. That's how they can find files like debian/control when they need them. Run them from the wrong place and they're just gonna be confused, bestie. ๐Ÿ™ˆ


๐Ÿ‘‘ DEBHELPER COMMANDS ๐Ÿ‘‘

Here's the full list of debhelper commands you can use. Go read their man pages if you want the full scoop on each one. ๐Ÿ’–

๐Ÿ’€ Deprecated Commands

A few debhelper commands are deprecated and should not be used. Like, don't. I'm begging. ๐Ÿ™

๐Ÿงš Other Commands

Here's the vibe check: if a program's name starts with dh_, and that program is not on the lists above, then it is not part of the debhelper package โ€” but it should still work like the other programs described on this page. ๐Ÿ’œ


โœจ DEBHELPER CONFIG FILES โœจ

bestie okay so here's the tea ๐Ÿซ– โ€” tons of debhelper commands read files out of debian/ to decide what they do. Yeah yeah, debian/changelog and debian/control live in every package, not just the debhelper girlies, but there's a whole extra little stash of files that tweak how specific commands behave. They're usually named debian/package.foo โ€” and obviously package gets swapped for whichever package is being acted on ๐Ÿ’…

Like, dh_installdocs reads debian/package.docs to get the list of doc files it's gonna install. Every command's own man page tells you the names and formats it wants. Generally these files are just "one file per line, go off", though some tools use pairs of file+destination or slightly more extra formats ๐Ÿ™ˆ

Heads up: if debian/control only lists one binary package, debhelper will happily fall back to debian/foo when there's no debian/package.foo. In compat levels before compat 15, that fallback also kicked in for the first binary package listed when you've got multiple binaries. But honestly? Keep the package. prefix โ€” it's more explicit and it's straight up required when you upgrade to compat 15. no cap ๐Ÿ”ฅ

There's also some special-case files where debhelper always falls back to the prefixless version โ€” things like debian/copyright and debian/changelog, because those are used and needed for all binary packages anyway. iconic behaviour honestly ๐Ÿ‘‘

And in rare cases you might want different versions of a file per arch or OS. If debian/package.foo.ARCH or debian/package.foo.OS exist โ€” where ARCH and OS match the output of dpkg-architecture -qDEB_HOST_ARCH / dpkg-architecture -qDEB_HOST_ARCH_OS โ€” then those win over the more general files ๐Ÿฆ‹

Mostly these config files are all about lists: docs to install, examples to install, files to move, that whole vibe. When it makes sense, you can use standard shell wildcards (? and * and [..] character classes) inside them. You can also drop comments โ€” any line starting with # gets ignored ๐Ÿ’–

The syntax is intentionally kept super simple so it's easy to read, understand, and edit. it's giving "low effort, high reward" ๐ŸŒธ

๐Ÿ’ซ Substitutions in debhelper config files

So in compatibility level 13 and later, you can do little substitutions inside debhelper config files โ€” but only for these specific tools:

  • dh_clean
  • dh_install
  • dh_installcatalogs
  • dh_installdeb
  • dh_installdirs
  • dh_installdocs
  • dh_installexamples
  • dh_installinfo
  • dh_installman
  • dh_installwm
  • dh_link
  • dh_missing
  • dh_ucf

Every substitution variable looks like ${foo} and yes the braces are mandatory, no skipping them bestie. Variable names are case-sensitive and can contain alphanumerics (a-zA-Z0-9), hyphens (-), underscores (_), and colons (:). The first character has to be alphanumeric, periodt.

If you need a literal dollar sign that doesn't trigger a substitution, you can either use the ${Dollar} substitution or the sequence ${} ๐Ÿซถ

Here's what you can expand:

  • DEB_HOST_*, DEB_BUILD_*, DEB_TARGET_* โ€” expands to the relevant dpkg-architecture(1) value (basically like running dpkg-architecture -qVARIABLE_HERE). When in doubt, the DEB_HOST_* variant is the one that works for both native and cross builds. And for performance reasons, debhelper tries to resolve these names from the environment first before asking dpkg-architecture(1) โ€” mostly mentioning it for completeness, it won't matter in most cases.
  • Dollar โ€” expands to a single literal $-symbol. This symbol will never be considered part of a substitution variable. So like:
# Triggers an error
${NO_SUCH_TOKEN}
# Expands to the literal value "${NO_SUCH_TOKEN}"
${Dollar}{NO_SUCH_TOKEN}

This variable is equivalent to the sequence ${} and the two can be used interchangeably โœจ

  • Newline, Space, Tab โ€” expands to a single ASCII newline, space and tab respectively. Super useful when you need a literal whitespace character (like a space) somewhere it'd otherwise get stripped or treated as a separator.
  • env:NAME โ€” expands to the environment variable NAME. That env var must be set (it's fine if it's set to the empty string).
Real talk: every variable has to expand to a defined value. So if debhelper sees ${env:FOO}, it will insist that the environment variable FOO is set โ€” empty string is allowed, unset is not ๐Ÿ˜ญ

Substitution limits

To dodge infinite loops and resource exhaustion, debhelper bails out with an error if a token contains too many substitution variables (50) or if the total size of the expanded value blows past a certain size (4096 characters or 3x the length of the original input โ€” whichever is bigger).

And note: the limit is per token. Every helper gets to define its own idea of what a token is. In the common case of files like debian/package.install, each token is a file/path name ๐ŸŽ€

Substitution limitations: filtering

The built-in substitution cannot be used to "filter" content out. If you try to create "comments" or "empty lines" via substitution, those variables just end up being considered a token in their own right with the content you gave. ๐Ÿ’€

If you actually want filtering, go use an executable debhelper config file with dh-exec as interpreter โ€” dh-exec supports several features right out of the box. Just keep in mind dh-exec has its own substitution logic that can interact with debhelper's, so don't get it twisted ๐Ÿ™ˆ

๐Ÿงš Executable debhelper config files

Need more flexibility? Many debhelper tools (like dh_install(1)) support just executing a config file as a script. slay ๐ŸŽ€

To use it: mark the config file as executable (e.g. chmod +x debian/package.install) and the tool will try to execute it and use the script's output. In lots of cases you can set dh-exec(1) as the interpreter of the config file to keep most of the original syntax while getting the extra flexibility you need ๐Ÿฆ‹

When you're using executable debhelper config files, please be aware of this:

  • The executable config file must exit with success (i.e. its return code should indicate success).
  • In compatibility level 13+, the output will be subject to substitutions (see "Substitutions in debhelper config files") where the tool supports them. Be careful if your generator also provides substitutions, because that's a one-way ticket to unnecessary confusion ๐Ÿ˜ตโ€๐Ÿ’ซ Notably, the commonly used dh-exec tool has its own substitution support.
  • Otherwise, the output is used exactly as-is. And notably, debhelper will not expand wildcards or strip comments or strip whitespace in the output it reads. (dh-exec has an output filter on by default that prunes those things out for you. ๐Ÿ’…)

If you need the package to build on a file system where you can't disable the executable bit, then reach for dh-exec(1) and its strip-output script ๐ŸŒˆ


๐Ÿ”ฅ SHARED DEBHELPER OPTIONS ๐Ÿ”ฅ

These command line options are supported by all debhelper programs. every single one. periodt ๐Ÿ‘‘

  • -v, --verbose โ€” Verbose mode: shows the commands that modify the package build directory. Note that verbose mode may also spit out other "internal" commands that don't directly affect the package build directory.
  • --no-act โ€” Do not really do anything. If you use it with -v, the result is that the command outputs what it would have done. ghost mode ๐Ÿ‘ป
  • -a, --arch โ€” Act on architecture dependent packages that should be built for the DEB_HOST_ARCH architecture.
  • -i, --indep โ€” Act on all architecture independent packages.
  • -ppackage, --package=package โ€” Act on the package named package. You can specify this option multiple times to make debhelper operate on a given set of packages.
  • -s, --same-arch โ€” Deprecated alias of -a. This option is removed in compat 12. ๐Ÿ’€
  • -Npackage, --no-package=package โ€” Do not act on the specified package, even if an -a, -i, or -p option lists that package as one that should be acted on.
  • --remaining-packages โ€” Do not act on the packages that have already been acted on by this debhelper command earlier (i.e. if the command is present in the package debhelper log). Like, if you need to call a command with special options for just a couple of binary packages, pass this option to the last call of the command so the rest of the packages get processed with default settings โœจ
  • -Ptmpdir, --tmpdir=tmpdir โ€” Use tmpdir for the package build directory. The default is debian/package.
  • --mainpackage=package โ€” Deprecated: this option has no practical use in compat 15 or later, since the behaviour it affects is removed in compat 15. This little-used option changes which package debhelper considers the "main package" โ€” that is, the first one listed in debian/control, and the one for which debian/foo files can be used instead of the usual debian/package.foo files.
  • -O=option|bundle โ€” This is used by dh(1) when passing user-specified options to all the commands it runs. If the command supports the specified option or option bundle, it takes effect. If the command doesn't support the option (or any part of an option bundle), it just gets ignored. shrug ๐Ÿ’…

๐ŸŒธ COMMON DEBHELPER OPTIONS ๐ŸŒธ

These command line options are supported by some debhelper programs โ€” check each program's man page for a full explanation of what each one does, bestie ๐Ÿ’–

  • -n โ€” Do not modify postinst, postrm, etc. scripts.
  • -Xitem, --exclude=item โ€” Exclude an item from processing. You can use this option multiple times to exclude more than one thing. The item is typically part of a filename, and any file containing the specified text will be excluded ๐Ÿšซ
  • -A, --all โ€” Makes files or other items specified on the command line take effect in ALL packages acted on, not just the first one. glow up for everyone โœจ

๐ŸŒธ BUILD SYSTEM OPTIONS ๐ŸŒธ

okay so every single one of the dh_auto_* debhelper programs takes these command line options. These programs vibe with a whole buffet of build systems, and normally they just heuristically figure out which one to use and how to use it (iconic behavior tbh). But you can totally override the default behavior with these options. Normally you hand them to dh(1), which then passes them along to all the dh_auto_* programs. Chain reaction, bestie. ๐Ÿ’ซ


  • -Sbuildsystem, --buildsystem=buildsystem โ€” say it with your chest: force the use of the specified buildsystem, instead of letting it try to auto-select one that might work for the package. And if you pass none as buildsystem, that disables auto-selection entirely. bye bestie, no build system for you ๐Ÿ’…
  • -Ddirectory, --sourcedir=directory, --sourcedirectory=directory โ€” assume the original package source tree lives at the specified directory, rather than the top level directory of the Debian source package tree.
โš ๏ธ warning bestie โš ๏ธ The --sourcedir variant matches a similarly named option in dh_install and dh_missing (etc.) for historical reasons. Like, they have a similar name but they have very distinct purposes, and in some cases it can cause errors when this variant gets passed to dh (when dh then passes it on to all the tools). iykyk, it's messy. ๐Ÿ˜ญ
  • -B[directory], --builddir[=directory], --builddirectory[=directory] โ€” turns on out of source building and uses the specified directory as the build directory. If you omit the directory parameter, a default build directory gets chosen for you. โœจ

If you don't specify this option, the build happens in source by default โ€” unless the build system requires or prefers out of source tree building. In that case, the default build directory gets used even if --builddirectory is not specified. Noted. ๐Ÿ“

And if the build system prefers out of source tree building but still allows in source building, you can re-enable the in source one by passing a build directory path that is the same as the source directory path. Sneaky but it works. ๐Ÿซถ

  • --parallel, --no-parallel โ€” controls whether parallel builds happen, if the underlying build system supports them. The number of parallel jobs is controlled by the DEB_BUILD_OPTIONS environment variable ("Debian Policy, section 4.9.1") at build time. It might also be subject to a build system specific limit, so don't come for me if it caps itself. ๐Ÿ™ˆ

If neither option is specified, debhelper currently defaults to --parallel in compat 10 (or later) and --no-parallel otherwise. the way that it just picks for you ๐Ÿ’œ

As an optimization, dh will try to avoid passing these options to subprocesses, if they are unnecessary and the only options passed. Notably this happens when DEB_BUILD_OPTIONS does not have a parallel parameter (or its value is 1). Efficient queen. ๐Ÿ‘‘

  • --max-parallel=maximum โ€” this option implies --parallel and lets you further limit the number of jobs that can be used in a parallel build. If the package build is known to only work with certain levels of concurrency, you can set this to the maximum level that is known to work, or that you wish to support. ๐Ÿ”ฅ

Notably, setting the maximum to 1 is effectively the same as using --no-parallel. Same energy fr fr.

  • --reload-all-buildenv-variables โ€” by default, dh(1) will compute several environment variables (e.g. by using dpkg-buildflags(1)) and cache them, so it doesn't make all the dh_auto_* tools recompute them. Caching era. ๐Ÿ’–

When you pass this option, the concrete dh_auto_* tool will ignore the cache from dh(1) and retrigger a rebuild of these variables. This is useful in the very rare case where the package needs to do multiple builds but with different ...FLAGS options. A concrete example would be needing to change the -O parameter in CFLAGS in the second build:

export DEB_CFLAGS_MAINT_APPEND=-O3

%:
    dh $@

override_dh_auto_configure:
    dh_auto_configure -Bbuild-deb ...
    DEB_CFLAGS_MAINT_APPEND=-Os dh_auto_configure \
       --reload-all-buildenv-variables -Bbuild-udeb ...

Without --reload-all-buildenv-variables in the second call to dh_auto_configure(1), the change in DEB_CFLAGS_MAINT_APPEND would be ignored, because dh_auto_configure(1) would use the cached value of CFLAGS set by dh(1). caught in 4k using stale flags ๐Ÿ’€

This option is only available with debhelper (>= 12.7~) when the package uses compatibility level 9 or later. So like, check your compat before you try it bestie.

  • --list, -l โ€” list all build systems supported by debhelper on this system. The list includes both default and third party build systems (marked as such). It also shows which build system would be automatically selected, or which one is manually specified with the --buildsystem option. It's giving receipts. ๐Ÿงพโœจ

โœจ COMPATIBILITY LEVELS โœจ

so from time to time, major non-backwards-compatible changes need to be made to debhelper, to keep it clean and well-designed as needs change and its author gains more experience. Which, honestly? Respect. ๐Ÿ‘‘

But to prevent those major changes from breaking existing packages, the concept of debhelper compatibility levels was introduced. You must tell debhelper which compatibility level it should use, and it modifies its behavior in various ways. No cap, it's mandatory. ๐Ÿšจ

In current debhelper, you can specify the compatibility level in debian/control by adding a Build-Depends on the debhelper-compat package. For example, to use v14 mode, ensure debian/control has:

Build-Depends: debhelper-compat (= 14)

This also serves as an appropriate versioned build dependency on a sufficient version of the debhelper package, so you do not need to specify a separate versioned build dependency on the debhelper package โ€” unless you need a specific point release of debhelper (such as for the introduction of a new feature or bugfix within a compatibility level). two birds, one stone, periodt ๐ŸŽ€

Note that debhelper does not provide debhelper-compat for experimental or beta compatibility levels. Packages experimenting with those compatibility levels should put the compat level in the X-DH-Compat field of the Source stanza of the debian/control file (or, if only for selected commands, the DH_COMPAT environment variable). Living on the edge, we love that for you ๐Ÿฆ‹

Historically, debhelper required specifying the compatibility level in the file debian/compat, and debhelper up to version 14 supports this for backward compatibility. To use this method, the debian/compat file should contain the compatibility level as a single number, and no other content. Like literally nothing else. If you specify the compatibility level by this method, your package will also need a versioned build dependency on a version of the debhelper package equal to (or greater than) the compatibility level your package uses. So, if you specify compatibility level 14 in debian/compat, ensure debian/control has:

Build-Depends: debhelper (>= 14~)

Note that you must use exactly one method for specifying the default debhelper compat level of the package. Whenever possible, the debhelper-compat build-dependency is recommended. do the recommended thing bestie, it's the moment ๐ŸŒˆ

If needed be, the DH_COMPAT environment variable can be used to override the compat level for a given command. The feature is mostly useful for either temporarily upgrading a few commands to a new compat level, or keeping a few commands on a lower compat level. The feature is best used sparingly, as it effectively introduces special-cases into the debian/rules file that may be surprising to maintainers or reviewers (or, in the long term, to yourself). ๐Ÿ˜ญ Setting a global DH_COMPAT environment variable is not recommended. Don't do it. Please. ๐Ÿ™

Unless otherwise indicated, all debhelper documentation assumes that you are using the most recent compatibility level, and in most cases does not indicate if the behavior is different in an earlier compatibility level. So if you are not using the most recent compatibility level, you're advised to read below for notes about what is different in earlier compatibility levels. read the fine print, bestie ๐Ÿ“–๐Ÿ’œ

Supported compatibility levels

The list of supported compatibility levels and the related upgrade check list has moved to debhelper-compat-upgrade-checklist(7). Go go go, it's over there โœจ๐Ÿ’…๐Ÿซถ

๐ŸŒธ NOTES ๐ŸŒธ

okay bestie listen up, these are the lore drops you need before anything else makes sense ๐Ÿ’–

๐Ÿซถ Multiple binary package support ๐Ÿซถ

so if ur source package pops out more than one binary package, all the debhelper programs are just gonna default to acting on every single one of them when u run them. which is normally whatever, exceptโ€ฆ if ur source builds one arch-dependent package AND one arch-independent package, that default is lowkey wrong โœ‹๐Ÿ˜ญ because the arch-dependent ones need to be built in the binary-arch target of debian/rules, and the arch-independent ones go in the binary-indep target.

so to help u out (and to give u way more control over which packages get touched by debhelper programs), all debhelper programs accept the -a, -i, -p, and -s parameters. and here's the thing โ€” these parameters are cumulative. if u give none of them, debhelper programs default to acting on all packages listed in the control file, with the exceptions below ๐Ÿ‘‡

first exception: any package whose Architecture field in debian/control doesn't match the DEB_HOST_ARCH architecture gets excluded ("Debian Policy, section 5.6.8").

also, some extra packages might get excluded based on what's in the DEB_BUILD_PROFILES environment variable and the Build-Profiles fields in the binary package stanzas of debian/control, per the draft policy at https://wiki.debian.org/BuildProfileSpec.

๐Ÿ’ซ Interaction between package selections and Build-Profiles ๐Ÿ’ซ

Build-Profiles affect which packages get included by the package selection mechanisms in debhelper. generally speaking, the package selections are described assuming all packages are enabled. this section here tells u how the selections react when a package gets disabled because of the active Build-Profiles (or because there are no active Build-Profiles at all).

  • -a/--arch, -i/--indep, OR no selection options at all (like a raw dh_X call) โ€” the package that's disabled by Build-Profiles is just silently excluded from the selection. heads up tho: u will get a warning if all the packages related to these selections end up disabled. and honestly in that case it generally doesn't even make sense to do the build in the first place ๐Ÿ’€
  • -N package / --no-package package โ€” the option is accepted and effectively does nothing. iconic, honestly.
  • -p package / --package package โ€” the option is accepted, but debhelper will not act on the package.

and ngl, it does not matter whether a package is enabled or disabled by default. periodt.


๐ŸŽ€ Automatic generation of Debian install scripts ๐ŸŽ€

some debhelper commands will literally auto-generate parts of Debian maintainer scripts for u. if u want those auto-generated bits to land inside ur existing Debian maintainer scripts, then u gotta add #DEBHELPER# to ur scripts, right in the spot where the code should go. #DEBHELPER# gets replaced by whatever auto-generated code when u run dh_installdeb โœจ

and if a script doesn't exist at all and debhelper needs to add something to it, then debhelper will just create the complete script. slay.

when u drop the #DEBHELPER# token into a script, pls pause and think about whether any of ur hand-written code has a compelling reason to show up before the #DEBHELPER# token. #DEBHELPER# will generally contain the code that starts (postinst) or stops (prerm/preinst) any services the package is providing. and often (but not always!) it's better to let the code from #DEBHELPER# run first. the common exceptions are stuff like set -e, initializing debconf (when needed), and special cases like generation or hand-written management of config files. those things should generally happen before the #DEBHELPER# token.

also: all debhelper commands that automatically generate code in this way let u disable it with the -n parameter (see above ๐Ÿ‘†).

โš ๏ธ important bestie: the inserted code is shell code, so u cannot directly use it in a Perl script. if u wanna embed it into a Perl script, here's one way to do it (and yes, they made sure $1, $2, etc are set with the set command):
my $temp="set -e\nset -- @ARGV\n" . << 'EOF';
#DEBHELPER#
EOF
if (system($temp)) {
   my $exit_code = ($? >> 8) & 0xff;
   my $signal = $? & 0x7f;
   if ($exit_code) {
       die("The debhelper script failed with error code: ${exit_code}");
   } else {
       die("The debhelper script was killed by signal: ${signal}");
   }
}

๐Ÿ’œ Automatic injection of substvar dependencies ๐Ÿ’œ

starting with compat 14, dh_gencontrol will automatically apply relationship substvars to the relevant fields ๐Ÿ˜Œ which means a bunch of substvars like ${misc:Depends} and ${shlibs:Depends} no longer need to be explicitly mentioned in debian/control. this applies to any substvar named after a field that the installed version of dpkg considers a relation or dependency-like field. for example: ${*:Pre-Depends}, ${*:Depends}, ${*:Recommends}, ${*:Conflicts}, ${*:Breaks}, ${*:Replaces}, ${*:Provides}, ${*:Built-Using}, ${*:Static-Built-Using}, etc.

๐Ÿงš Automatic generation of miscellaneous dependencies ๐Ÿงš

some debhelper commands may make the generated package need to depend on some other packages. like, if u use dh_installdebconf(1), ur package will generally need to depend on debconf. or if u use dh_installxfonts(1), ur package will generally need to depend on a particular version of xutils. keeping track of all these miscellaneous dependencies is genuinely so annoying bc they depend on how debhelper does things, so debhelper offers u a way to automate it ๐Ÿซถ

as of compat 14, debhelper will automatically inject the relevant dependencies for u, since the dependency is done via a "relationship substvar" (peep https://lists.debian.org/debian-devel/2024/02/msg00230.html and https://lists.debian.org/debian-devel/2024/03/msg00030.html for the deets).

in earlier compat levels tho, u have to manually specify the relevant substvars in the relevant fields. typically that's ${misc:Depends} for the Depends field and ${misc:Pre-Depends} for the Pre-Depends fields.

the variables listed here are related to miscellaneous dependencies (${misc:*}) and are entirely independent of the standard ${shlibs:Depends} generated by dh_makeshlibs(1), and the ${perl:Depends} generated by dh_perl(1). for compat 13 and earlier, u will also have to add these to the respective fields when relevant ๐Ÿ‘‘๐Ÿ’…


๐Ÿฆ‹ Package build directories ๐Ÿฆ‹

by default, all debhelper programs assume the temporary directory used for assembling the tree of files in a package is debian/package ๐Ÿ“

sometimes tho, u might wanna use some other temporary directory. this is supported by the -P flag! for example, "dh_installdocs -Pdebian/tmp" will use debian/tmp as the temporary directory. just know that if u use -P, the debhelper programs can only be acting on a single package at a time. so if u have a package that builds a bunch of binary packages, u will need to also use the -p flag to specify which binary package the debhelper program will act on.

๐ŸŒˆ udebs ๐ŸŒˆ

debhelper includes support for udebs! to create a udeb with debhelper, add "Package-Type: udeb" to the package's stanza in debian/control. debhelper will try to create udebs that comply with debian-installer policy, by:

  • making the generated package files end in .udeb
  • not installing any documentation into a udeb
  • skipping over preinst, postrm, prerm, and config scripts
  • etc. โœจ

๐Ÿ’– ENVIRONMENT ๐Ÿ’–

okay bestie, this bit is all about the env vars โ€” the little gremlins in your shell that change how debhelper acts, or that debhelper just kinda rubs shoulders with. ๐Ÿซถ

SUPER IMPORTANT: these have to be actual environment variables to do anything to debhelper. Not Makefile variables. Not vibes. Actual env vars. So if you're setting them in debian/rules, you gotta export them properly, like export DH_VERBOSE. ๐Ÿ’…

๐ŸŒธ the env vars themselves ๐ŸŒธ

  • DH_VERBOSE โ€” set it to literally anything non-empty and boom, verbose mode is on. details live in the -v / --verbose option, go peek at those.
  • DH_QUIET โ€” set to a non-empty value to turn on quiet mode. debhelper won't print out the commands it's calling on your upstream build system, and dh won't announce which subcommands it's running โ€” and depending on the upstream build system it might go even quieter too. it's giving "easier to spot the important stuff" โœจ but also it makes the output genuinely useless as a buildd log. ๐Ÿ˜ญ
    • Ignored if DH_VERBOSE is also set, or if -v / --verbose got passed.
  • DH_COMPAT โ€” temporarily says which compatibility level debhelper should run at, overriding the source package's default compat level. (temporary!! don't get attached ๐Ÿ’€)
  • DH_NO_ACT โ€” set it to 1 to enable no-act mode.
  • DH_OPTIONS โ€” every debhelper tool will slurp up the command line arguments listed in this variable before any command option โ€” basically like they were prepended to the command line args. sadly though, some third-party tools might not support this variable at all and will just ignore those args. ๐Ÿ™ˆ
    • When you're using dh(1), you can pass it options that get handed down to each debhelper command, and honestly that's generally better than using DH_OPTIONS.
  • DH_ALWAYS_EXCLUDE โ€” if you set it, its value gets added to the -X options of all commands that support -X. and on top of that, dh_builddeb will rm -rf anything matching that value inside your package build tree. ๐Ÿ˜ณ
    • this is lowkey a lifesaver if you're building from a CVS source tree โ€” setting DH_ALWAYS_EXCLUDE=CVS stops any CVS directories from sneaking into the package you build. or, if a package has a source tarball that (unwisely!!) ships CVS directories, you might want to export DH_ALWAYS_EXCLUDE=CVS in debian/rules so it takes effect wherever your package gets built.
    • you can exclude multiple things by separating them with colons, like DH_ALWAYS_EXCLUDE=CVS:.svn
  • DH_EXTRA_ADDONS โ€” if set, this adds the dh addons you specify so they run in the appropriate places in the command sequence. same energy as specifying the addon with the --with flag in the debian/rules file. and heads up: any --without calls naming an addon that's in this env var will not run. ๐Ÿ‘‘
    • it's meant for downstreams or specific local configs that need a debhelper addon running across multiple builds without having to patch a whole pile of rules files. but fr fr, if it's at all possible, avoid this and just use a --with flag in the rules file instead.
  • DH_COLORS, DPKG_COLORS โ€” these control whether debhelper commands use colors in their text output. can be set to "always", "auto" (the default!), or "never". ๐ŸŒˆ
    • note that DPKG_COLOR also affects a number of dpkg related tools, and debhelper uses it assuming you want the same color setting for dpkg and debhelper. in the off-hand chance you want a different color setting for debhelper, use DH_COLORS instead โ€” or in addition to DPKG_COLORS.
  • NO_COLOR โ€” if no explicit request for color was given (like, DH_COLORS and DPKG_COLORS are both unset), then the mere presence of this environment variable makes the default color setting become "never".
    • this variable is defined per <https://no-color.org/>. in this project, the environment variables (such as DH_COLORS) count as an explicit request for color.
  • CFLAGS, CPPFLAGS, CXXFLAGS, OBJCFLAGS, OBJCXXFLAGS, GCJFLAGS, FFLAGS, FCFLAGS, LDFLAGS โ€” by default (in any non-deprecated compat level), debhelper automatically sets these flags using dpkg-buildflags(1) when they're unset. if you need to change the default flags, please use the features from dpkg-buildflags(1) to do that (like DEB_BUILD_MAINT_OPTIONS=hardening=all or DEB_CPPFLAGS_MAINT_APPEND=-DCUSTOM_MACRO=true) rather than setting the concrete variable directly. ๐ŸŽ€
  • HOME, XDG_* โ€” in compat 13 and later, these get reset before the upstream build system is invoked via the dh_auto_* helpers. HOME (for all dh_auto_* helpers) and XDG_RUNTIME_DIR (dh_auto_test only) get set to a writable directory. every remaining variable, plus XDG_RUNTIME_DIR (except during dh_auto_test), gets cleared. ๐Ÿงน
    • the HOME directory gets created as an empty directory but it's reused between calls to dh_auto_*. any content persists until it's explicitly deleted or dh_clean happens.
  • DEB_BUILD_OPTIONS โ€” go see "Supported flags in DEB_BUILD_OPTIONS" for this one. ๐Ÿ‘€
    • also please note: this variable should not be altered by package maintainers inside debian/rules to change debhelper's behavior. instead, when the maintainer needs those features, they should look at disabling the relevant feature directly (e.g. by overriding the concrete tools).
  • DEB_BUILD_MAINT_OPTIONS โ€” this is a dpkg specific environment variable (see e.g. dpkg-buildflags(1)). the debhelper tool suite silently ignores it. ๐Ÿ’€
    • it's documented here purely because it has a similar name to DEB_BUILD_OPTIONS, which makes some people mistakenly assume debhelper will also react to it. it will not. not even a little.

๐Ÿฆ‹ Supported flags in DEB_BUILD_OPTIONS ๐Ÿฆ‹

the debhelper tool suite reacts to these flags in DEB_BUILD_OPTIONS. bestie, here's the whole list:

  • dherroron=obsolete-compat-levels โ€” this is a debhelper specific value. when dherroron is present and set to obsolete-compat-levels, debhelper tools promote deprecation warnings about old, soon-to-be-removed compat levels into full errors. ๐Ÿ”ฅ useful for automated checking of code relying on deprecated compat levels that are scheduled for removal. it's intended for testing purposes โ€” not production builds.
  • nostrip โ€” this value changes the content of the debs being built. the .deb packages built when it's set are therefore not bit-for-bit reproducible with a regular build in the general case. ๐Ÿ˜ญ this value makes the official debhelper tools skip actions and helpers that either remove, detach or deduplicate debugging symbols in ELF binaries. affects dh_dwz(1) and dh_strip(1).
  • nocheck โ€” this value makes the official debhelper build systems skip runs of upstream test suites. package maintainers looking to avoid running the upstream tests should not rely on this; instead they can add an empty override target to skip dh_auto_test. affects dh_auto_test(1).
  • nodoc โ€” this value changes the content of the debs being built. the .deb packages built when it's set are therefore not bit-for-bit reproducible with a regular build in the general case. this value makes several debhelper tools skip installation of documentation such as manpages or upstream provided documentation. additionally, the tools will ignore it if declared documentation is "missing", on the assumption that the documentation hasn't been built. this value effects tools like dh_installdocs(1), which knows it's working with documentation. ๐Ÿ“š
  • notrimdch โ€” this value changes the content of the debs being built. the .deb packages built when it's set are therefore not bit-for-bit reproducible with a regular build in the general case. this value makes dh_installchangelogs(1) act as if it had been passed the --no-trim option, forcing it to forgo removing older entries from changelogs. โœ‚๏ธ๐Ÿšซ
  • noautodbgsym, noddebs โ€” the official name is noautodbgsym. the noddebs variant is accepted for historical reasons. this value causes debhelper to skip generation of automatically generated debug symbol packages. affects dh_strip(1).
  • parallel=N โ€” this value enables debhelper to use up to N threads or processes (subject to parameters like --no-parallel and --max-parallel=M). not all debhelper tools work with parallel tasks and may silently ignore the request. this value affects many debhelper tools โ€” most notably dh_auto_*, which will attempt to run the underlying upstream build system with that number of threads. โšก
  • terse โ€” this value makes the official debhelper build systems configure upstream builds to be terse (i.e. reduce verbosity in their output). this is subject to the upstream and the debhelper build system supporting such features. this value affects most dh_auto_* tools directly. for commands provided by the debhelper package, it also makes the tools act like the DH_QUIET environment variable was non-empty. ๐Ÿคซ
Unknown flags are silently ignored.
Note: third-party debhelper-like tools or third-party provided build systems may or may not react to the above flags. this tends to depend on implementation details of the tool.

๐ŸŒŸ SEE ALSO ๐ŸŒŸ


๐Ÿ‘‘ AUTHOR ๐Ÿ‘‘

Joey Hess <joeyh@debian.org>


and that's a wrap on the whole page, bestie!! ๐ŸŽ€โœจ this was debhelper(7) from debhelper version 14.5ubuntu1, dated 2026-09-21. go forth and build packages, you absolute main character. ๐Ÿ’…๐Ÿ’–๐Ÿฆ‹

๐Ÿ“ฆ where this page comes from