Guidelines for making complex Debian packages easier to cross-compile. For basic information about cross-compiling, see CategoryMultiarch. For information about creating packages, see CategoryPackaging.
Salsa CI
The default Salsa-CI pipeline includes a test-crossbuild-arm64 job that cross-compiles your package on amd64 but for arm64. The job has allow_failure: true by default, so the pipeline will succeed even if the job fails. Check the status manually or override the property in your pipeline definition:
test-crossbuild-arm64:
allow_failure: false
Or to disable the job entirely (e.g. because your package cannot be made cross-compilable yet):
variables:
SALSA_CI_DISABLE_CROSSBUILD_ARM64: 1
PTS Multi-arch hints
Your package's page on tracker.debian.org should have an "action needed" heading. That heading may include a report from the Multiarch hinter.
For a list of messages the hinter can report, see MultiArch/Hints. For a list of packages that currently have hints on their tracker page, see multiarch-hints.yaml.
Hints
clear the dependency_libs field of any .la files you ship - they can break when packages change multiarch support
make sure you added Multi-Arch: fields
if your -dev package contains architecture-specific headers, prefer Multi-Arch: foreign
at the time of writing, no decision about architecture-dependant headers has been made, and the toolchain needs to be updated
if your debian/rules file uses d-shlibmove, add the --multiarch command-line argument and add Build-Depends: d-shlibs (>= 0.48~) in your debian/control
if your library has support files that need to go in the same package as the library, see Debian Policy: Shared library support files
if your packages is built with Qbs, see Qbs multiarch paths
when creating udeb packages (for the Debian installer), install libraries in /usr/lib unless there's a specific benefit to using the multiarch directory
if your package depends on different packages for different architectures, see BuildProfileSpec
for example, a program might use libfoo-assembly where available, and fall back to libfoo-legacy for architectures it hasn't been ported to yet
packages without a Multi-Arch field are usually treated as Multi-Arch: no, but Ubuntu treats Architecture: all packages as Multi-Arch: foreign for purposes of calculating build-dependencies
in some cases, a package that would otherwise be Architecture: all will need to be made Architecture: any for the sake of a dependency's Multi-Arch setting
for example, python3 is a dependency package which depends on an actual Python version, so it needs to be Architecture: any even though it has no architecture-specific files
use dh_auto_build instead of make
use dh_auto_configure instead of ./configure, meson, cmake, qmake etc.
- check you're using the terms "build" and "host" correctly
see nomenclature
install pkg-config .pc files to /usr/lib/<multiarch-name>/pkgconfig/, not to /usr/lib/pkgconfig
annotate your test-dependencies with <!nocheck>
avoid help2man
see help2man
change commands like e.g. gcc to e.g. $(CC)
Perl extensions
If you're building a Perl extension ("xs") module, add Build-Depends: perl-xs-dev.
Python extensions
If you're building a Python extension, replace all Build-Depends: python3{,-all,-$VERSION}-dev with Build-Depends: python{,-all,-$VERSION}-dev:native, libpython{,-all,-$VERSION}-dev:
Replace... |
... with |
python-dev |
python-dev:native, libpython-dev |
python3-all-dev |
python3-all-dev:native, libpython3-all-dev |
python3-dev |
python3-dev:native, libpython3-dev |
python3.11-dev |
python3.11-dev:native, libpython3.11-dev |
libtool-bin
Replace Build-Depends: libtool-bin with Build-Depends: libtool, then make your package run libtoolize and configure to create a libtool that knows about your host architecture.
pkg-config
If you need to run programs directly within debian/rules (outside of dh), use the cross-building variants of those programs:
-include /usr/share/dpkg/buildtools.mk
PKG_CONFIG?=pkg-config
export DEB_CFLAGS_MAINT_APPEND = $(shell $(PKG_CONFIG) --cflags foobar)
For a list of supported variables, do less /usr/share/dpkg/buildtools.mk.
Autoconf
autoconf won't run tests that need run a program during cross-compilation. Instead, we can get pre-seeded answers with config.cache. These can be architecture-specific answers or generic Debian answers. In rare cases, you may even need to supply your own answers.
These autoconf cache files are managed by dpkg-cross, which contains a set of /etc/dpkg-cross/cross-config.<arch> files and one /etc/dpkg-cross/cross-config.cache file.
To enable this mechanism and use the default settings, add this to debian/rules:
CONFIG_SITE=/etc/dpkg-cross/cross-config.$(DEB_HOST_ARCH)
If you need a special package-specific variable, set PACKAGE=<packagename> to match up with a stanza in one of the config files. This is to avoid clashes if two different packages need a variable set in a different way.
help2man
Source packages that build-depend on help2man are hard to cross-compile, because a system that can cross-compile myprogram.c can't necessarily run the generated myprogram --help. Solutions include:
- work with upstream to write a proper man page
this is recommended, because --help commands don't usually have everything we'd like
- pre-generate a man page and store it in the source package
can be combined with generating the man page via help2man during a native build and comparing it to the pre-generated version, making sure they do not get out-of-date. Example: https://sources.debian.org/src/vcmi/1.6.5%2Bdfsg-2/debian/rules#L50
build the native version (don't forget to add :native B-D for that where needed), run help2man on it, delete it, then build the cross-compiled version
add a build-dependency on the generated binary package with <cross> and then run the installed version to generate the man page
- process the source code with regular expressions to generate the man page from the data in the source without compiling and running it
maybe work with upstream to make the information from which the --help output is generated machine-readable
add the man page to a new Architecture:all package which does not need to be cross-compiled
- use the nodoc profile and give up on reproducible cross-builds because your cross-built package will not include the man page
- decide that the convenience of having a manual page is more important than spending effort on cross building
A previous plan involved building the native package, then installing it and extracting its man page during cross-compilation. This was rejected in 751437.
gobject-introspection
Source packages using gobject-introspection cannot be cross-compiled at this point. Technical challenges include:
gobject-introspection is arch:any and depends on python3-mako, which is arch:all and cannot just be marked Multi-Arch: foreign due to its dependency on python3-markupsafe (this is immediately observable).
gobject-introspection is arch:any and depends on python3 (without :any), so it'll cross-grade your python interpreter, which will fail postinst as it runs said interpreter to byte-compile objects.
The python3 dependency cannot be just annotated with :any, because gir is related to a foreign function interface. g-ir-scanner, which is run during build, will load the built shared libraries into its own process memory and use them to derive information. Thus g-ir-scanner effectively forces the host architecture to equal the build architecture. Every distribution that has a working cross-build for gir packages (e.g. PtxDist, Yocto, Void) employs qemu.
The generated .gir files are architecture-dependent xml files that are shipped in /usr/share. As such, we generally cannot mark packages containing .gir files Multi-Arch: same. The differences amount to introspected argument types (e.g. int vs long). Arguably, this is a FHS violation.
Reporting Bugs
"FTCBFS" means "Failed to cross-build from source", and is adapted from FTBFS. Please tag FTCBFS bug reports like this:
User: debian-cross@lists.debian.org
Usertags: ftcbfs
If your report specifically concerns cross-build dependency resolution, replace ftcbfs with cross-satisfiability.
See also the list of cross-build bugs.
Building
Use dh instead of this!
dh_auto_build and other dh tools usually do the right thing without any special logic. Only use the following if you've unsuccessfully tried using debhelper.
Build environment
Below is a list of values set by various tools, but the interface to package-building is still defined as the debian/rules targets, which should not rely on anything else to set the environment.
The recommended way to set the architecture variables provided by dpkg-architecture is to include this snippet provided by dpkg-dev:
DPKG_EXPORT_BUILDFLAGS := 1
include /usr/share/dpkg/architecture.mk
The include makes the variables available to debian/rules, and the DPKG_EXPORT_BUILDFLAGS exports them to the commands invoked by debian/rules. This is available from dpkg-dev 1.16.1 onward, and will not overwrite any previously-supplied values.
Environment set by build tools
dpkg-buildpackage
DEB_BUILD_ARCH
DEB_BUILD_ARCH_ABI
DEB_BUILD_ARCH_BITS
DEB_BUILD_ARCH_CPU
DEB_BUILD_ARCH_ENDIAN
DEB_BUILD_ARCH_LIBC
DEB_BUILD_ARCH_OS
DEB_BUILD_GNU_CPU
DEB_BUILD_GNU_SYSTEM
DEB_BUILD_GNU_TYPE
DEB_BUILD_MULTIARCH
DEB_HOST_ARCH
DEB_HOST_ARCH_ABI
DEB_HOST_ARCH_BITS
DEB_HOST_ARCH_CPU
DEB_HOST_ARCH_ENDIAN
DEB_HOST_ARCH_LIBC
DEB_HOST_ARCH_OS
DEB_HOST_GNU_CPU
DEB_HOST_GNU_SYSTEM
DEB_HOST_GNU_TYPE
DEB_HOST_MULTIARCH
DEB_TARGET_ARCH
DEB_TARGET_ARCH_ABI
DEB_TARGET_ARCH_BITS
DEB_TARGET_ARCH_CPU
DEB_TARGET_ARCH_ENDIAN
DEB_TARGET_ARCH_LIBC
DEB_TARGET_ARCH_OS
DEB_TARGET_GNU_CPU
DEB_TARGET_GNU_SYSTEM
DEB_TARGET_GNU_TYPE
DEB_TARGET_MULTIARCH
DEB_RULES_REQUIRES_ROOT and DEB_GAIN_ROOT_CMD
depending on the package's Rules-Requres-Root value
if the value implies the package is built via fakeroot, it will also some variables
SOURCE_DATE_EPOCH will be set if not previously set
MAKEFLAGS might be set depending on dpkg-buildpackage options used
- some locale variables will get sanitized depending on the vendor
sbuild
CONFIG_SITE=/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH
for autoconf cache settings provided by dpkg-cross
DEB_BUILD_OPTS+=nocheck
- anything else configured to be set in build environment
pbuilder
DEB_BUILD_OPTIONS+=nocheck
unless the user passes --no-auto-cross
DEB_BUILD_PROFILES+=nocheck
unless the user passes --no-auto-cross
xdeb
CONFIG_SITE=/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH
for autoconf cache settings provided by dpkg-cross
DEB_BUILD_OPTS+=nocheck
GTEST_INCLUDEDIR=/usr/$DEB_HOST_GNU_TYPE/include
GTEST_LIBDIR=/usr/$DEB_HOST_GNU_TYPE/lib
make
make sets some things itself too. Some of them not very helpfully, like the implicit $(CC)=cc.
Setting the correct compiler
Without dh, you will need to set $(CC) yourself. The easiest way to re-initialize common tool variables in debian/rules is with:
include /usr/share/dpkg/buildtools.mk
Or in a Makefile:
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
ifeq ($(origin CC),default)
CC := $(DEB_HOST_GNU_TYPE)-gcc
endif
override_dh_auto_build:
$(MAKE) CC=$(CC) build
Or in a shell script:
# Set CC to gcc, unless it has already been set:
CC="${CC:=gcc}"
TODO
The easy cross problems are solved, only the hard ones are left. Consider the following list if you have a lot of free time on your hands:
- cross-compiling perl
src:perl is now built from source in Debian, so patching the upstream configure generator to not need try_run checks for e.g. sizeof is possible
- gobject-introspection
Yocto and PtxDist gave up and use qemu
- non-glibc ports
- musl-linux-any
- rebootstrap
failures detected by rebootstrap (see also HelmutGrohne/rebootstrap)
Questions
will the nocheck stuff ever be part of the build system, I think it should be, (maybe setting an additional variable if it does so), this way it would be smarter, for example if we use cross-build to orchestrate multilib, or building with qemu--and still run tests.
--scientes
