Differences between revisions 1 and 33 (spanning 32 versions)
Revision 1 as of 2012-03-30 10:28:46
Size: 4558
Editor: wookey
Comment:
Revision 33 as of 2022-08-11 15:18:56
Size: 21180
Editor: ?DanieleForsi
Comment: fix grammar
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
This page describes good (and bad) practice in packaging (and upstream code) with respect to cross-building Debian-based packages. Little of this is yet mnandated by policy, but some is and more will be in the future.

There are often lots of ways of 'fixing' a cross-build problem, but unless you are expert it he area it will not be clear what the implications of using one method over another are. This page endeavours to document standard guidelines so that packagers can use it as a reference, and Debian and derivatives can develop policy in this area.
This page describes good (and bad) practice in packaging (and upstream code) with respect to cross-building Debian-based packages. Little of this is yet mandated by policy, but some is and more will be in the future.

There are often lots of ways of 'fixing' a cross-build problem, but unless you are an expert in the area it will not be clear what the implications of using one method over another are. This page endeavours to document standard guidelines so that packagers can use it as a reference, and Debian and derivatives can develop policy in this area.

<<TableOfContents>>
Line 9: Line 11:
 * HOST is the machine we are building for
 * BUILD is the machine we are building on

This somewhat confusing terminology is GNU's fault. 
 * '''HOST''' is the machine we are building ''for''
 * '''BUILD''' is the machine we are building ''on''
 * (TARGET is only relevant for compilers and is the architecture that a compiler outputs code for. Unless packaging ```binutils```, ```gcc``` or ```hurd```, the target architecture is irrelevant.)


This somewhat confusing terminology is GNU's fault.
Line 16: Line 19:
dpkg-architecture is responsible for translating between triplets, Debian architecture names and multiarch paths. This provides a consistent interface and namespace.

cross-building is defined as BUILD != HOST. more explicitly in debian.rules:
```dpkg-architecture``` is responsible for translating between triplets, Debian architecture names and multiarch paths. This provides a consistent interface and namespace.

cross-building is defined as BUILD != HOST. more explicitly in `debian/rules`:
{{{#!highlight make
Line 24: Line 28:
}}}
Line 27: Line 32:
Classic cross-compiling assumes never running any host architecture binaries, and this remains good policy, but we often have QEMU or similar emulation available these days so it makes sense to control this separately where we can. In practice this means running tests, which should be controlled with DEB_BUILD_OPTS=nocheck rather than 'are we cross-compiling or not'. Cross-build tools will set this by default, but it can be enabled when approapriate.

When cross-building the build must select the correct tools who's output varies with architecture. This is usually done with an explicit GNU triplet prefix (arm-linux-gnueabi- ia64-linux-gnu-). This applies to the following tools: gcc, g++, binutils (ld, as, strip, ar, readelf etc), pkg-config
When cross-building the build must select the correct tools whose output varies with architecture. This is usually done with an explicit GNU triplet prefix (```arm-linux-gnueabi-``` ```ia64-linux-gnu-```). This applies to the following tools: ```gcc```, ```g++```, ```binutils``` (```ld```, ```as```, ```strip```, ```ar```, ```readelf``` etc), ```pkg-config```.

== Salsa CI ==

The [[https://salsa.debian.org/salsa-ci-team/pipeline|default Salsa-CI pipeline]] now includes a `test-crossbuild-arm64` job, that will attempt to cross-compile your package on the default '''amd64''' build architecture for the '''arm64''' host architecture.

So use this default pipeline to get automatic feedback whether your package is cross-compilable (or not).

NOTE: Currently the `test-crossbuild-arm64` job has the {{{allow_failure: true}}} property set, so even if the cross-build job fails, the entire pipeline will still be reported as '''success'''.
You can either manually check the status of each job of the pipeline, or override the property to `false` in your pipeline definition. This will make sure, that your pipeline fails in case of a future cross-compilation regression:

{{{#!highlight yaml
test-crossbuild-arm64:
  allow_failure: false
}}}

If your package cannot be cross compiled, for example because it belongs to a package class that cannot be made cross-compilable yet, you can disable this step in salsaci like this:

{{{#!highlight yaml
variables:
  SALSA_CI_DISABLE_CROSSBUILD_ARM64: 1
}}}

== Common problems ==

Apart from language-specific cross-problems, the most common problems are:

 * Running `make` instead of `dh_auto_build`
 * Running `./configure` instead of `dh_auto_configure`
 * Running `meson` instead of `dh_auto_configure`
 * Running `cmake` instead of `dh_auto_configure`
 * Running `qmake` instead of `dh_auto_configure`
 * Confusing the terms build and host (see above or the `dpkg-architecture` man page for the definition)
 * Installing `pkg-config` .pc files to `/usr/lib/pkgconfig`
 * Missing ```Multi-Arch: foreign``` (but make sure that the package is really exposing an architecture independent interface before annotating)
 * Test dependencies not annotated ```<!nocheck>```
 * Using `help2man` which will attempt running foreign architecture binaries which usually fails without qemu
 * Hard coding build tools such as gcc

See also the section [[#Common_Problems|Common Problems]]
Line 33: Line 75:
Various things need to be set in the environment for cross-builds to work correctly. These are the things that are set by various tools: Various things need to be set in the environment for cross-builds to work correctly.

Below is a list of what is set by various tools, however the interface to package building is still defined as the `debian/rules` targets and your rules file should not rely on any outside environment setting.

The recommended way to set the architecture variables provided by dpkg-architecture is to include this snippet provided by dpkg-dev:
{{{#!highlight make
include /usr/share/dpkg/architecture.mk
}}}

This is available from dpkg-dev 1.16.1 onward, and will not overwrite any previously-supplied values.

=== Environment set by build tools ===
Line 59: Line 112:
(it also sets some FAKEROOT stuff that we don't care about here, and sanitises a load of stuff out of your normal environment) (It also sets some FAKEROOT stuff that we don't care about here, and sanitises a load of stuff out of your normal environment.)
Line 63: Line 116:
sbuild duses dpkg-buildpackage so sets the same things as that, and:
{{{
CONFIG_SITE/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH (for autoconf cache settings provided by dpkg-cross)
sbuild uses dpkg-buildpackage so sets the same things as that, and:
{{{
CONFIG_SITE=/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH (for autoconf cache settings provided by dpkg-cross)
Line 68: Line 121:
anything else configured to be set in build environment anything else configured to be set in build environment.

'''pbuilder'''

pbuilder automatically adds ```nocheck``` to ```DEB_BUILD_OPTIONS``` and ```DEB_BUILD_PROFILES``` unless passing ```--no-auto-cross```.
Line 72: Line 129:
xdeb duses dpkg-buildpackage so sets the same things as that, and: xdeb uses dpkg-buildpackage so sets the same things as that, and:
Line 82: Line 139:
Make sets some things itself too. Some of them not very helpfully, like the implicit $(CC)=cc


=== Setting correct compiler ===

Simply hardcoding 'gcc' will not allow cross-building, and is arguably wrong for other reasons.

Normally you want to set gcc for native builds and $(DEB_HOST_GNU_TYPE)-gcc for cross-builds. Sadly we can't just set $(DEB_HOST_GNU_TYPE)-gcc always because that doesn't work natively

So normally people do this:
{{{
ifeq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
       CC=gcc
else
       CC=$(DEB_HOST_GNU_TYPE)-gcc
Make sets some things itself too. Some of them not very helpfully, like the implicit ```$(CC)=cc```.

== Build-Dependencies ==

Many Build-Dependencies must use the architecture of the HOST architecture (e.g. C-libraries you want to link against).

Other Build-Dependencies must be ''executed'' on the BUILD machine. These are obviously compilers, but also interpreters, documentation generators,...

To help the cross-building environment, you can (and probably should, if things go wrong) annotate Build-Dependencies, so that the correct architecture is picked.

 <<Anchor(Build-Depends-Indep)>>
=== Declare "Indep" Build-Dependencies ===

Typically, cross-compilation is mostly interesting as a way to produce `arch:any` packages, as the host architecture can use `arch:all` packages produced on any (non-crossbuilding) build machine.

To allow the dependency resolver to not worry about any `Build-Depends` that it won't need anyhow, make sure to declare all build-dependencies that are only needed for building `arch:all` packages using the `Build-Depends-Indep` stanza.

Example:

{{{#!highlight debcontrol
Build-Depends-Indep:
 doxygen,
}}}

=== Architecture qualifiers ===

What is required in package dependencies is for the depending source package to distinguish build-dependencies which are satisfiable by any architecture ('tools') from build-dependencies which can only be satisfied by packages of the same architecture ('libraries' generally). This is very similar to the Multi-Arch: field options 'foreign' (for tools) and 'same' (for libraries), respectively. However it's not exactly the same because the architecture relationship is defined by the depending package, not the depended-on package, because only the depending-on package knows what it needs the build-dependency for. This is recognised in the multiarch spec with the Multi-Arch: option 'allowed' and the `Depends: package:any` syntax.

Despite the relationship being 'from the wrong end', in practice it is almost always right to use the Multi-arch field to decide if the build or host version (or both) of a package should be installed. By marking the exceptions to this rule in a packages' build-dependencies we minimise the package metadata changes needed (most packages will need no changes to their Build-Depends for this reason).

Exceptions to the normal case are specified using the build-dependency qualifiers `:native` and `:any`.

 * `:native` is appended to a build-dep to signify that it should be installed for the build (i.e 'native') architecture rather than the host architecture. It can be used on `Multi-Arch: same`, `Multi-Arch: allowed` or ''None'' packages.
 * The `:any` qualifier signifies that a `Multi-Arch: allowed` build-dep should be treated as 'foreign'. i.e. it allows the dependency to be met by any package with an architecture that can be executed on the builder, regardless of whether that is the same architecture as the current DEB_BUILD_ARCH. In order to maintain backwards-compatibility, the table of which arch a dependency resolves to is somewhat unintuitive.

Build Dependencies are resolved according to this table:

||''''''||'''Build-Depends: foo'''||'''Build-Depends: foo:any'''||'''Build-Depends: foo:native'''||
||'''no Multi-Arch field'''||DEB_HOST_ARCH||''disallowed''||DEB_BUILD_ARCH||
||'''Multi-Arch: same'''||DEB_HOST_ARCH||''disallowed''||DEB_BUILD_ARCH||
||'''Multi-Arch: foreign'''||any, preferably DEB_BUILD_ARCH||''disallowed''||''disallowed''||
||'''Multi-Arch: allowed'''||DEB_HOST_ARCH||any, preferably DEB_BUILD_ARCH||DEB_BUILD_ARCH||

See also [[https://wiki.ubuntu.com/MultiarchCrossCompilers|Ubuntu/MultiarchCrossCompilers]]


==== Use ':any' qualifier for dependencies ====

For some `Multi-Arch: allowed` packages a given dependency must have the HOST architecture in one case, and the BUILD architecture in another case.

If you require your `Multi-Arch: allowed` Build-Dependency to be ''executable'' on the HOST machine, annotate this by adding the `:any` architecture qualifier:

{{{#!highlight debcontrol
Build-Depends:
 python3:any,
}}}

As shown in the table above, the `:any` qualifier is only valid for packages that are marked as `Multi-Arch: allowed`.

 <<Anchor(dh-sequence)>>
=== Use `dh-sequence-...` meta build-dependencies if possible ===

If you are using `dh` addons,
it is often better to use a Build-Dependency on the relevant `dh-sequence-...` virtual package
instead of invoking `dh --with=...` and manually adding Build-Dependencies.

Adding a `dh-sequence-...` B-D will:
 * install all the ''correct'' packages required for using the `dh` addon (so you don't accidentally pick a dependency for the addon that only accidentally pulls in the real dependency - among other, unneeded stuff)
 * automatically enable the `dh` addon (so you only need to specify the sequence one (in d/control) rather than adjust both d/control and d/rules)

The fun part is, that this also works when using `Build-Depends-Indep`.
So the following will activate the ''sphinx'' sequence and install the relevant Build-Dependencies '''only''' when doing an `arch:all` build:

{{{#!highlight debcontrol
Build-Depends-Indep:
 dh-sequence-sphinxdoc,
}}}




=== Hints for specific groups of packages ===

==== Perl extensions ====

Whenever building a Perl extension module (often called "xs" module), ```perl-xs-dev``` should be added to ```Build-Depends```.

==== Python extensions ====

When building a Python extension, ```pythonSOMETHING-dev``` is often added to ```Build-Depends```. For working with cross compilation, that should be replaced with ```libpythonSOMETHING-dev, pythonSOMETHING-dev:any```. Where ```SOMETHING``` is either empty, a version or ```-all```.

==== libtool-bin ====

The libtool binary in your PATH makes cross-building impossible. Replace your build dependency on ```libtool-bin``` with ```libtool``` and change your package such that it runs libtoolize and configure. The latter will create a libtool that knows about your host architecture.

== Building ==

{{{#!wiki red/solid
`dh` automatically takes care of most cross-building issues. Convert your packaging to `dh` instead of calling the tools yourself.
}}}

=== Setting the correct compiler ===

Simply hardcoding 'gcc' will not allow cross-building, and is arguably wrong for other reasons (like using llvm/clang instead).

The easiest way to re-initialize common tool variables inside ```debian/rules``` is relying on ```dpkg```:
{{{#!highlight make
include /usr/share/dpkg/buildtools.mk
}}}

Formerly, you could set ```CC``` manually:
{{{
CC=$(DEB_HOST_GNU_TYPE)-gcc}}}

When `dh` deals with ```make``` build systems, using ```dh_auto_build``` will automatically pass cross tools as command variables.

When not using `dh`, things are a bit more complicated, as unfortunately ```make``` always sets $(CC) (to 'cc') so you cannot just test if it is already set, and set it if not.

This state can be detected with $(origin CC) see http://theory.uwinnipeg.ca/localfiles/infofiles/make/make_80.html.

In the end, this can be done this way for instance:
{{{#!highlight make
DEB_HOST_GNU_TYPE ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)

ifeq ($(origin CC),default)
CC := $(DEB_HOST_GNU_TYPE)-gcc
Line 98: Line 266:
}}}

But it should also be possible to override the compiler for the build from the environment so that for example LLVM test builds or strangly-named external toolchains can be used.

Unfortunately make always sets $(CC) (to 'cc') so you can't just test if it is already set, and set it if not. {{{CC ?= $(CC)}}}

This state can be detected with $(origin CC) see http://theory.uwinnipeg.ca/localfiles/infofiles/make/make_80.html
I wonder if we should be doing something clever with ifeq "$(origin CC)" "default" ?...

override_dh_auto_build:
    $(MAKE) CC=$(CC) build
}}}

Note: nowadays debhelper takes care of this, so these lines are usually not needed anymore.

This is a good way to set CC in a bash script:
{{{: "${CC:=gcc}" }}}
which means if $CC is not already set, set it to 'gcc', otherwise use the existing value. The ':' is the 'null command' and stops the value being executed.

=== Splitting build sequence into `arch:any` and `arch:all` ===
As mentioned in [[#Build-Depends-Indep]], it is often a good idea to separate the tasks of building `arch:any` packages (that need to be cross-compiled) and `arch:all` packages (where there is no need to do so).

Here's an example of a `dh`-based packaging build script, that uses `sphinx` only to generate an `arch:all` doc package:


{{{#!highlight make

%-arch:
       dh $@ --buildsystem=cmake

%-indep:
       dh $@ --with sphinxdoc --buildsystem=cmake

clean:
       dh $@ --buildsystem=cmake

execute_after_dh_auto_build-indep:
        dh_auto_build -- manual-html
}}}

See also [[#dh-sequence]]

=== Conditionalizing build sequences for build profiles ===

While using `-arch` and `-any` targets works well when separating the parts to build architecture independent and arch:any artifacts, the following method using `dh_listpackages` is capable of doing the same (decide yourself which style you prefer) while at the same time capable of respecting build profiles:

{{{#!highlight make
DOPACKAGES := $(shell dh_listpackages)

binary-arch: build
ifneq (,$(filter mypackage1,$(DOPACKAGES)))
    # create artifacts for mypackage1
endif
ifneq (,$(filter mypackage2,$(DOPACKAGES)))
    # create artifacts for mypackage2
endif
}}}

== Running pre-build tests/programs ==

=== pkg-config ===

Sometimes you need to run some programs directly within `debian/rules` (that is: 'outside' of `dh`), in which case you should make sure to pick the cross-building variants.
You can get cross-building variants of common build tools by including the `/usr/share/dpkg/buildtools.mk` snippet.

To make everything more optional, the following example does a ''weak'' include and defines a fallback:

{{{#!highlight make
-include /usr/share/dpkg/buildtools.mk
PKG_CONFIG?=pkg-config

export DEB_CFLAGS_MAINT_APPEND = $(shell $(PKG_CONFIG) --cflags foobar)
}}}

=== Autoconf configure tests ===

```autoconf``` will not do tests that need a program to be run when in 'cross-compiling' mode. To get the results that would be missing we can use the config.cache mechanism to supply pre-seeded answers. These can be architecture-specific answers, or generic 'Debian' answers. In rare cases you may need to change them for your own use-case.

These autoconf cache files are managed by the dpkg-cross package, which contains a set of files in {{{/etc/dpkg-cross/}}} to pre-seed the answers.
cross-config.<arch> contains the architecture-specific stuff (anything depending on size of variables, lengths of pointers, or peculiarities of this arch's glibc implementation).
cross-config.cache contains the architecture-independent stuff.

All you need to do in your package to enable this mechanism and use the default settings is to set the environment variable {{{CONFIG_SITE=/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH}}} e.g. {{{/etc/dpkg-cross/cross-config.armel}}}.

If you need a special package-specific variable then you should 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.

== Running run time tests (test suite) ==

Classic cross-compiling assumes never running any host architecture binaries, and this remains a good policy, but we often have QEMU or similar emulation available these days so it makes sense to control this separately where we can. In practice this means running tests, which should be controlled with DEB_BUILD_OPTS=nocheck rather than 'are we cross-compiling or not'. Cross-build tools will set 'nocheck' by default, but it can be enabled when appropriate.
Line 110: Line 351:

{{{
set DEB_BUILD_OPTIONS=nocheck
}}}

However there are packages where the tests do make sense, and they can be done using ```qemu``` so the responsibility of packagers is just to honor this variable.

Where build profiles are enabled (DebianBootstrap) having a profile for 'check' is useful to annotate build-dependencies which are only needed for checks/tests.

= Common Problems =

== help2man ==

Source packages that build depend on help2man cannot be cross compiled because they require a build architecture binary to be executed for generating the man page. There exist different solutions to the problem:

 * one could build twice, first native then cross and run help2man on the native version of the binary.
 * one could introduce substitution variable support in Build-Depends in dpkg-dev so that the source package could build depend on the exact same version of the binary package it builds to generate the man page from it. This is filed as DebianBug:751437.
 * one could use the nodoc profile.

== gobject-introspection ==

Source packages using `gobject-introspection` cannot be cross compiled at this point. There are multiple reasons, but the primary one is that nobody has done so thus far. Technical challenges:

     * 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 simply 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.

= TODO =

The easy cross problems are solved and what is left is only hard problems. Consider the following list if you have a lot of free time on your hand.

 * cross building 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.
 * gcc-for-host. WIP at DebianBug:666743, but we're stuck.
 * Reducing the toolchain bootstrap from 3 gcc stages to two. The glibc developers indicated that this is no longer necessary and build_many_glibcs.py seems to work with a two-stage bootstrap.
 * cross building perl extensions.
 * gobject-introspections (yocto and ptxdist gave up and use qemu).
 * non-glibc ports (e.g. musl-linux-any)
 * fixing failures from https://jenkins.debian.org/view/rebootstrap/ (is arm64 important to anyone?)

= 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

Guidelines for cross-build friendly packages

This page describes good (and bad) practice in packaging (and upstream code) with respect to cross-building Debian-based packages. Little of this is yet mandated by policy, but some is and more will be in the future.

There are often lots of ways of 'fixing' a cross-build problem, but unless you are an expert in the area it will not be clear what the implications of using one method over another are. This page endeavours to document standard guidelines so that packagers can use it as a reference, and Debian and derivatives can develop policy in this area.

Terminology

  • HOST is the machine we are building for

  • BUILD is the machine we are building on

  • (TARGET is only relevant for compilers and is the architecture that a compiler outputs code for. Unless packaging binutils, gcc or hurd, the target architecture is irrelevant.)

This somewhat confusing terminology is GNU's fault.

Principles

dpkg-architecture is responsible for translating between triplets, Debian architecture names and multiarch paths. This provides a consistent interface and namespace.

cross-building is defined as BUILD != HOST. more explicitly in debian/rules:

   1 ifeq ($(DEB_BUILD_GNU_TYPE),$(DEB_HOST_GNU_TYPE))
   2   native build
   3 else
   4   crossbuild
   5 endif
   6 

We cannot run tests to determine aspects of the host machine which will be different on the build machine.

When cross-building the build must select the correct tools whose output varies with architecture. This is usually done with an explicit GNU triplet prefix (arm-linux-gnueabi- ia64-linux-gnu-). This applies to the following tools: gcc, g++, binutils (ld, as, strip, ar, readelf etc), pkg-config.

Salsa CI

The default Salsa-CI pipeline now includes a test-crossbuild-arm64 job, that will attempt to cross-compile your package on the default amd64 build architecture for the arm64 host architecture.

So use this default pipeline to get automatic feedback whether your package is cross-compilable (or not).

NOTE: Currently the test-crossbuild-arm64 job has the allow_failure: true property set, so even if the cross-build job fails, the entire pipeline will still be reported as success. You can either manually check the status of each job of the pipeline, or override the property to false in your pipeline definition. This will make sure, that your pipeline fails in case of a future cross-compilation regression:

   1 test-crossbuild-arm64:
   2   allow_failure: false

If your package cannot be cross compiled, for example because it belongs to a package class that cannot be made cross-compilable yet, you can disable this step in salsaci like this:

   1 variables:
   2   SALSA_CI_DISABLE_CROSSBUILD_ARM64: 1

Common problems

Apart from language-specific cross-problems, the most common problems are:

  • Running make instead of dh_auto_build

  • Running ./configure instead of dh_auto_configure

  • Running meson instead of dh_auto_configure

  • Running cmake instead of dh_auto_configure

  • Running qmake instead of dh_auto_configure

  • Confusing the terms build and host (see above or the dpkg-architecture man page for the definition)

  • Installing pkg-config .pc files to /usr/lib/pkgconfig

  • Missing Multi-Arch: foreign (but make sure that the package is really exposing an architecture independent interface before annotating)

  • Test dependencies not annotated <!nocheck>

  • Using help2man which will attempt running foreign architecture binaries which usually fails without qemu

  • Hard coding build tools such as gcc

See also the section Common Problems

Build environment

Various things need to be set in the environment for cross-builds to work correctly.

Below is a list of what is set by various tools, however the interface to package building is still defined as the debian/rules targets and your rules file should not rely on any outside environment setting.

The recommended way to set the architecture variables provided by dpkg-architecture is to include this snippet provided by dpkg-dev:

   1 include /usr/share/dpkg/architecture.mk
   2 

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

all the dpkg-architecture variables:

DEB_BUILD_ARCH
DEB_BUILD_ARCH_OS
DEB_BUILD_ARCH_CPU
DEB_BUILD_ARCH_BITS
DEB_BUILD_ARCH_ENDIAN
DEB_BUILD_GNU_CPU
DEB_BUILD_GNU_SYSTEM
DEB_BUILD_GNU_TYPE
DEB_BUILD_MULTIARCH
DEB_HOST_ARCH
DEB_HOST_ARCH_OS
DEB_HOST_ARCH_CPU
DEB_HOST_ARCH_BITS
DEB_HOST_ARCH_ENDIAN
DEB_HOST_GNU_CPU
DEB_HOST_GNU_SYSTEM
DEB_HOST_GNU_TYPE
DEB_HOST_MULTIARCH

(It also sets some FAKEROOT stuff that we don't care about here, and sanitises a load of stuff out of your normal environment.)

sbuild

sbuild uses dpkg-buildpackage so sets the same things as that, and:

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

pbuilder automatically adds nocheck to DEB_BUILD_OPTIONS and DEB_BUILD_PROFILES unless passing --no-auto-cross.

xdeb

xdeb uses dpkg-buildpackage so sets the same things as that, and:

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.

Build-Dependencies

Many Build-Dependencies must use the architecture of the HOST architecture (e.g. C-libraries you want to link against).

Other Build-Dependencies must be executed on the BUILD machine. These are obviously compilers, but also interpreters, documentation generators,...

To help the cross-building environment, you can (and probably should, if things go wrong) annotate Build-Dependencies, so that the correct architecture is picked.

Declare "Indep" Build-Dependencies

Typically, cross-compilation is mostly interesting as a way to produce arch:any packages, as the host architecture can use arch:all packages produced on any (non-crossbuilding) build machine.

To allow the dependency resolver to not worry about any Build-Depends that it won't need anyhow, make sure to declare all build-dependencies that are only needed for building arch:all packages using the Build-Depends-Indep stanza.

Example:

   1 Build-Depends-Indep:
   2  doxygen,

Architecture qualifiers

What is required in package dependencies is for the depending source package to distinguish build-dependencies which are satisfiable by any architecture ('tools') from build-dependencies which can only be satisfied by packages of the same architecture ('libraries' generally). This is very similar to the Multi-Arch: field options 'foreign' (for tools) and 'same' (for libraries), respectively. However it's not exactly the same because the architecture relationship is defined by the depending package, not the depended-on package, because only the depending-on package knows what it needs the build-dependency for. This is recognised in the multiarch spec with the Multi-Arch: option 'allowed' and the Depends: package:any syntax.

Despite the relationship being 'from the wrong end', in practice it is almost always right to use the Multi-arch field to decide if the build or host version (or both) of a package should be installed. By marking the exceptions to this rule in a packages' build-dependencies we minimise the package metadata changes needed (most packages will need no changes to their Build-Depends for this reason).

Exceptions to the normal case are specified using the build-dependency qualifiers :native and :any.

  • :native is appended to a build-dep to signify that it should be installed for the build (i.e 'native') architecture rather than the host architecture. It can be used on Multi-Arch: same, Multi-Arch: allowed or None packages.

  • The :any qualifier signifies that a Multi-Arch: allowed build-dep should be treated as 'foreign'. i.e. it allows the dependency to be met by any package with an architecture that can be executed on the builder, regardless of whether that is the same architecture as the current DEB_BUILD_ARCH. In order to maintain backwards-compatibility, the table of which arch a dependency resolves to is somewhat unintuitive.

Build Dependencies are resolved according to this table:

Build-Depends: foo

Build-Depends: foo:any

Build-Depends: foo:native

no Multi-Arch field

DEB_HOST_ARCH

disallowed

DEB_BUILD_ARCH

Multi-Arch: same

DEB_HOST_ARCH

disallowed

DEB_BUILD_ARCH

Multi-Arch: foreign

any, preferably DEB_BUILD_ARCH

disallowed

disallowed

Multi-Arch: allowed

DEB_HOST_ARCH

any, preferably DEB_BUILD_ARCH

DEB_BUILD_ARCH

See also Ubuntu/MultiarchCrossCompilers

Use ':any' qualifier for dependencies

For some Multi-Arch: allowed packages a given dependency must have the HOST architecture in one case, and the BUILD architecture in another case.

If you require your Multi-Arch: allowed Build-Dependency to be executable on the HOST machine, annotate this by adding the :any architecture qualifier:

   1 Build-Depends:
   2  python3:any,

As shown in the table above, the :any qualifier is only valid for packages that are marked as Multi-Arch: allowed.

Use `dh-sequence-...` meta build-dependencies if possible

If you are using dh addons, it is often better to use a Build-Dependency on the relevant dh-sequence-... virtual package instead of invoking dh --with=... and manually adding Build-Dependencies.

Adding a dh-sequence-... B-D will:

  • install all the correct packages required for using the dh addon (so you don't accidentally pick a dependency for the addon that only accidentally pulls in the real dependency - among other, unneeded stuff)

  • automatically enable the dh addon (so you only need to specify the sequence one (in d/control) rather than adjust both d/control and d/rules)

The fun part is, that this also works when using Build-Depends-Indep. So the following will activate the sphinx sequence and install the relevant Build-Dependencies only when doing an arch:all build:

   1 Build-Depends-Indep:
   2  dh-sequence-sphinxdoc,

Hints for specific groups of packages

Perl extensions

Whenever building a Perl extension module (often called "xs" module), perl-xs-dev should be added to Build-Depends.

Python extensions

When building a Python extension, pythonSOMETHING-dev is often added to Build-Depends. For working with cross compilation, that should be replaced with libpythonSOMETHING-dev, pythonSOMETHING-dev:any. Where SOMETHING is either empty, a version or -all.

libtool-bin

The libtool binary in your PATH makes cross-building impossible. Replace your build dependency on libtool-bin with libtool and change your package such that it runs libtoolize and configure. The latter will create a libtool that knows about your host architecture.

Building

dh automatically takes care of most cross-building issues. Convert your packaging to dh instead of calling the tools yourself.

Setting the correct compiler

Simply hardcoding 'gcc' will not allow cross-building, and is arguably wrong for other reasons (like using llvm/clang instead).

The easiest way to re-initialize common tool variables inside debian/rules is relying on dpkg:

   1 include /usr/share/dpkg/buildtools.mk
   2 

Formerly, you could set CC manually:

CC=$(DEB_HOST_GNU_TYPE)-gcc

When dh deals with make build systems, using dh_auto_build will automatically pass cross tools as command variables.

When not using dh, things are a bit more complicated, as unfortunately make always sets $(CC) (to 'cc') so you cannot just test if it is already set, and set it if not.

This state can be detected with $(origin CC) see http://theory.uwinnipeg.ca/localfiles/infofiles/make/make_80.html.

In the end, this can be done this way for instance:

   1 DEB_HOST_GNU_TYPE   ?= $(shell dpkg-architecture -qDEB_HOST_GNU_TYPE)
   2 
   3 ifeq ($(origin CC),default)
   4 CC := $(DEB_HOST_GNU_TYPE)-gcc
   5 endif
   6 
   7 override_dh_auto_build:
   8     $(MAKE) CC=$(CC) build

Note: nowadays debhelper takes care of this, so these lines are usually not needed anymore.

This is a good way to set CC in a bash script: : "${CC:=gcc}"  which means if $CC is not already set, set it to 'gcc', otherwise use the existing value. The ':' is the 'null command' and stops the value being executed.

Splitting build sequence into `arch:any` and `arch:all`

As mentioned in #Build-Depends-Indep, it is often a good idea to separate the tasks of building arch:any packages (that need to be cross-compiled) and arch:all packages (where there is no need to do so).

Here's an example of a dh-based packaging build script, that uses sphinx only to generate an arch:all doc package:

   1 %-arch:
   2        dh $@ --buildsystem=cmake
   3 
   4 %-indep:
   5        dh $@ --with sphinxdoc --buildsystem=cmake
   6 
   7 clean:
   8        dh $@ --buildsystem=cmake
   9 
  10 execute_after_dh_auto_build-indep:
  11         dh_auto_build -- manual-html

See also #dh-sequence

Conditionalizing build sequences for build profiles

While using -arch and -any targets works well when separating the parts to build architecture independent and arch:any artifacts, the following method using dh_listpackages is capable of doing the same (decide yourself which style you prefer) while at the same time capable of respecting build profiles:

   1 DOPACKAGES := $(shell dh_listpackages)
   2 
   3 binary-arch: build
   4 ifneq (,$(filter mypackage1,$(DOPACKAGES)))
   5     # create artifacts for mypackage1
   6 endif
   7 ifneq (,$(filter mypackage2,$(DOPACKAGES)))
   8     # create artifacts for mypackage2
   9 endif
  10 

Running pre-build tests/programs

pkg-config

Sometimes you need to run some programs directly within debian/rules (that is: 'outside' of dh), in which case you should make sure to pick the cross-building variants. You can get cross-building variants of common build tools by including the /usr/share/dpkg/buildtools.mk snippet.

To make everything more optional, the following example does a weak include and defines a fallback:

   1 -include /usr/share/dpkg/buildtools.mk
   2 PKG_CONFIG?=pkg-config
   3 
   4 export DEB_CFLAGS_MAINT_APPEND = $(shell $(PKG_CONFIG) --cflags foobar)

Autoconf configure tests

autoconf will not do tests that need a program to be run when in 'cross-compiling' mode. To get the results that would be missing we can use the config.cache mechanism to supply pre-seeded answers. These can be architecture-specific answers, or generic 'Debian' answers. In rare cases you may need to change them for your own use-case.

These autoconf cache files are managed by the dpkg-cross package, which contains a set of files in /etc/dpkg-cross/ to pre-seed the answers. cross-config.<arch> contains the architecture-specific stuff (anything depending on size of variables, lengths of pointers, or peculiarities of this arch's glibc implementation). cross-config.cache contains the architecture-independent stuff.

All you need to do in your package to enable this mechanism and use the default settings is to set the environment variable CONFIG_SITE=/etc/dpkg-cross/cross-config.$DEB_HOST_ARCH e.g. /etc/dpkg-cross/cross-config.armel.

If you need a special package-specific variable then you should 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.

Running run time tests (test suite)

Classic cross-compiling assumes never running any host architecture binaries, and this remains a good policy, but we often have QEMU or similar emulation available these days so it makes sense to control this separately where we can. In practice this means running tests, which should be controlled with DEB_BUILD_OPTS=nocheck rather than 'are we cross-compiling or not'. Cross-build tools will set 'nocheck' by default, but it can be enabled when appropriate.

Running/skipping tests

Normally you don't want to run tests when cross-building.

set DEB_BUILD_OPTIONS=nocheck

However there are packages where the tests do make sense, and they can be done using qemu so the responsibility of packagers is just to honor this variable.

Where build profiles are enabled (DebianBootstrap) having a profile for 'check' is useful to annotate build-dependencies which are only needed for checks/tests.

Common Problems

help2man

Source packages that build depend on help2man cannot be cross compiled because they require a build architecture binary to be executed for generating the man page. There exist different solutions to the problem:

  • one could build twice, first native then cross and run help2man on the native version of the binary.
  • one could introduce substitution variable support in Build-Depends in dpkg-dev so that the source package could build depend on the exact same version of the binary package it builds to generate the man page from it. This is filed as 751437.

  • one could use the nodoc profile.

gobject-introspection

Source packages using gobject-introspection cannot be cross compiled at this point. There are multiple reasons, but the primary one is that nobody has done so thus far. Technical challenges:

  • 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 simply 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.

TODO

The easy cross problems are solved and what is left is only hard problems. Consider the following list if you have a lot of free time on your hand.

  • cross building 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.
  • gcc-for-host. WIP at 666743, but we're stuck.

  • Reducing the toolchain bootstrap from 3 gcc stages to two. The glibc developers indicated that this is no longer necessary and build_many_glibcs.py seems to work with a two-stage bootstrap.
  • cross building perl extensions.
  • gobject-introspections (yocto and ptxdist gave up and use qemu).
  • non-glibc ports (e.g. musl-linux-any)
  • fixing failures from https://jenkins.debian.org/view/rebootstrap/ (is arm64 important to anyone?)

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