Home / Alt manpages / deb-substvars(5)

  • deb-substvars(5)
  • File format
  • linux

Set Reusable Package Values in debian/substvars

Stop hard-coding the same string into five control fields: debian/substvars lets you define it once and reuse it. By the end you will have reusable values for package metadata, a one-off override for a test build, and a way to inspect the generated control fields before packaging. The examples match dpkg 1.22.6, provided here by dpkg-dev version 1.22.6ubuntu6.6.

Allow about fifteen minutes. You need an unpacked Debian source tree with a usable debian/control and changelog, plus the dpkg-dev package. The commands generate control text or read files. They do not install, remove or upgrade a package, and no elevated privileges are required.

1. Check the installed toolchain

Start in the root of the source tree. Confirm the package version and the command that will consume your variables:

$ dpkg-query -W -f='${Package} ${Version}\n' dpkg-dev
dpkg-dev 1.22.6ubuntu6.6
$ command -v dpkg-gencontrol
/usr/bin/dpkg-gencontrol

Your version and path may differ. dpkg-source, dpkg-gencontrol and dpkg-genchanges all substitute variables while writing their control information. This guide uses dpkg-gencontrol -O because it prints the result without replacing a control file in the package build directory.

Checkpoint

Run pwd and confirm the current directory contains debian/control. If it does not, stop and move to the source-tree root before creating a variables file.

2. Add a value to debian/substvars

Create or edit debian/substvars with one assignment per line. A normal assignment uses =. Comments, blank lines and trailing whitespace are ignored:

# Values used by the package metadata
BuildNote=assembled by the local build
DescriptionText=This description came from debian/substvars.

Reference those names in substituted fields with ${NAME}. For example, a binary stanza can contain:

Package: example-package
Architecture: all
Description: ${BuildNote}
 ${DescriptionText}

Control fields are parsed before substitution. The continuation line still needs its normal leading space, while the value of ${DescriptionText} can itself contain ${Newline} when you deliberately want more generated lines.

Checkpoint

Run sed -n '1,120p' debian/substvars and check the spelling and case of every name. Variable names are case-sensitive and may contain letters, digits, hyphens and colons, but must start with an alphanumeric character.

3. Generate control output without changing the package

Use dpkg-gencontrol -O to print the binary control stanza. It reads debian/substvars by default:

$ dpkg-gencontrol -O
Package: example-package
Version: 0.1-1
Architecture: all
Description: assembled by the local build
 This description came from debian/substvars.

The complete output also includes fields such as maintainer, section and the calculated installed size. The two description lines are the checkpoint that matters: the placeholders have gone, and the continuation line is formatted as control data. If your tree has no valid changelog, fix that first, because dpkg-gencontrol needs package version information to produce a normal stanza.

To use another variables file, pass -T. It suits a temporary build profile without replacing the project file:

$ dpkg-gencontrol -T /tmp/example-substvars -O
Package: example-package
Description: assembled for the test profile

For that exact result the temporary file contains BuildNote=assembled for the test profile. Create it before running the check:

Example

printf '%s\n' 'BuildNote=assembled for the test profile' > /tmp/example-substvars

-T can be used more than once. Keep temporary files outside the source tree and remove them after the check. That cleanup is safe because it does not alter package metadata.

4. Override a value for one command

The common -Vname=value option supplies a substitution variable on the command line. Use it for a one-off value, or a build-system name you do not want committed:

$ dpkg-gencontrol -O -VBuildNote='assembled by CI'
Package: example-package
Description: assembled by CI

This example assumes BuildNote is not also defined in a variables file. Quote values containing spaces, dollar signs or shell punctuation. The option sets the variable for this invocation only. It does not edit debian/substvars.

Warning

If the same name is defined in a file read during the command, do not assume the command-line value wins. Use a distinct name, or make the input order explicit and verify the rendered result.

Substitution repeats until no variable references remain. That allows a controlled chain such as ReleaseLabel=${BuildNote} (${Arch}), but a circular definition can stop expansion from ever finishing. Keep references short and acyclic.

5. Handle missing values and literal dollar signs

An undefined variable produces a warning and expands to nothing. Treat that warning as a packaging defect, not noise:

$ dpkg-gencontrol -O
dpkg-gencontrol: warning: package example-package: substitution variable ${MissingText} used, but is not defined

The exact warning context depends on the field and dpkg version. If a variable is intentionally optional, use the question-mark assignment introduced in dpkg 1.21.8:

OptionalNote?=

An optional variable does not warn when it is unused. It still expands to its value when you reference it. Do not use ?= to hide a required release or dependency value.

To emit a literal ${NAME}, write ${}{NAME} in the input. The empty substitution produces the dollar sign, and the following braces stay ordinary text. That is handy when generating documentation or another template, where you do not want dpkg to expand the inner name.

6. Respect the fields that cannot use variables

Do not put substitutions in Package, Source or Architecture. The build metadata needs these fields while it is being parsed, before ordinary substitution happens. Keep their values literal and stable.

Built-in values cover the common cases:

  • Arch is the host architecture.
  • source:Version and binary:Version expose package versions.
  • Newline, Space and Tab contain those characters.
  • Field values are available through canonical names such as S:Homepage and F:Description in binary control generation. Use the exact case the field name requires.

Warning

Avoid the obsolete Source-Version variable. The installed man page says it emits an error because its meaning differs from its old function. Pick source:Version or binary:Version according to what the package metadata needs.

7. Verify the final change

Before a real build, render the relevant fields and search for unresolved substitutions:

$ dpkg-gencontrol -O > /tmp/example-control
$ rg '\$\{[^}]+\}' /tmp/example-control
$ printf 'control status: %s\n' "$?"
control status: 1

Status 1 from rg means it found no remaining placeholder. If it prints a match, inspect the variable spelling, the file selected by -T, and whether the field is one of the protected ones. Keep the temporary output if you need to compare builds, then remove that specific file when you finish.

Tip

Do not reach for sudo to fix a substitution warning. These are source metadata and syntax problems, and elevated privileges only make it easier to write files with the wrong owner.

Done means

  • Tools identified. dpkg-dev and dpkg-gencontrol were found on the machine.
  • Values defined. Required names are set with the correct case in debian/substvars or an intentional -T file.
  • Output expanded. dpkg-gencontrol -O produced control text with the expected fields filled in.
  • No leftovers. No unresolved ${...} references remain in the rendered output.
  • Protected fields literal. Package, Source and Architecture hold plain values.
  • Temp files handled. Validation output was removed, or kept deliberately outside the source tree.