This page discusses how to add multiarch support to packages. For other multiarch topics, see the multiarch category.
Multiarch support is useful for more than just cross-compiling. It lets users install i386 programs on their amd64 PC, and even on more exotic architectures using QEMU user emulation.
The term "architecture" can refer to either Debian architecture names or multiarch names. This page uses examples like "i386" or "amd64" to discuss Debian architecture names, and "i386-linux-gnu" or "x86_64-linux-gnu" to discuss multiarch names.
Overview of the problem
Multiarch packages can be installed either for your computer's architecture or for some other architecture. This section provides a high-level overview of the problems and solutions associated with that requirement.
Co-installing the same package for two architectures at once
To have the same package co-installed on the same machine at the same time for two (or more) architectures, the package manager needs to deal with files that would normally conflict with each other. For example, most C programs depend on an architecture-specific version of libc6, so if you want to run i386 and amd64 programs on the same system, they can't both be called /usr/lib/libc.so.6. Multiarch provides the following techniques to solve that problem:
multiarch names let you (mostly) unambiguously specify individual architectures
files in /usr/lib/, /usr/include/ etc. can be moved to .../<multiarch-name>/ so multiple versions can be co-installed at once
packages should include Multi-Arch fields in their debian/control files to describe their co-installation behaviour
conflicts between files in /usr/share/ and /etc/ are ignored between architectures so long as the files are byte-for-byte identical
Multiarch names (sometimes called triplets or multiarch tuples) refer to architectures by their ISA, OS and ABI. Unlike normal Debian architecture names, these are unambiguous within Debian and between distributions with compatible naming schemes.
Debian Policy section 9.1.1 allows files that would normally go in e.g. /usr/lib/ to go in e.g. /usr/lib/<multiarch-name>/ instead. But paths like e.g. /usr/bin/<multiarch-name>/ are not supported, so although you can co-install multiple versions of a library, you can only install one version of a program at once.
The Multi-Arch field specifies whether a package is co-installable with other copies of the same package for different architectures. For example, most libraries can be made co-installable (because their files can be moved to .../<multiarch-name>/), but most programs can't be made co-installable (because there's no /usr/bin/<multiarch-name>/ directory).
Byte-for-byte identical files let packages install e.g. /usr/share/<package>/copyright without conflicting with their twin. This can sometimes be tricky - for example, gzip stores timestamps by default, so your build system will need to use gzip -n for co-installable files in /usr/share/ and /etc/.
Negotiating dependency architectures
When installing a package for another architecture, the package manager needs to know which architecture to use for each of the package's dependencies. For example, consider a C program that links against libc6 and runs git commands. It would need to link against the libc6 for its own architecture, but would happily use any version of git that accepted its command-line arguments. The package manager decides the architecture for each dependency by looking at:
whether the package demands the native version of a build-dependency
- the preference specified by the dependency
- the preference specified by the package
- the architecture of the package itself
Build-dependencies of the form Build-Depends: <dependency>:native force the use of your computer's architecture, overriding everything else. This was useful when multiarch support was first being rolled out, because packages would often have dependencies that didn't yet support multiarch. Modern packages should prefer <dependency>:any, described below.
If a dependency has a Multi-Arch field in its debian/control file, the packaging framework can work out which architecture to install. In the case of Multi-Arch: allowed, this preference is just a default that can be overridden by the package itself.
If a dependency specifies Multi-Arch: allowed, the package can specify Depends: <dependency>:any to use your computer's architecture instead of the package's architecture.
Multi-Arch: foreign vs. Architecture: all
Consider the following shell script:
case "$(uname -m)" in
i386) echo "(do i386 stuff)" ;;
x86_64) echo "(do x86_64 stuff)" ;;
*) echo "(fallback)" ;;
esac
This script is byte-for-byte identical on all architectures, which means it should be marked Architecture: all. But it behaves differently depending on the host architecture, so it can't be Multi-Arch: foreign.
For more information, see the multiarch rationale.
Specific solutions
Creating a proper multiarch package involves solving all the problems discussed above. This section discusses specific solutions you can use in your packages.
Split your package
Most packages fall into one of the following categories:
program packages that go in /usr/bin, /usr/sbin and so on
library packages that go in /usr/lib, /usr/lib32 and so on
development packages that go in /usr/include
support packages that go in /usr/share
Each category has distinct issues in a multiarch environment, so you might want to split your source package into multiple binary packages. It's not uncommon for a single source package foo to create binary packages called foo-bin (programs), libfoo (library), libfoo-dev (development) and foo-data (support). These packages can then depend on each other in the normal way.
Change file paths
If you want to support co-installing your package for two architectures at once, architecture-specific paths need to be renamed to avoid conflicting with each other. That usually means choosing paths based on the multiarch name for the architecture you're currently building on. The most important changes are:
move architecture-specific libraries from {/,/usr/}{lib,lib32,lib64}/ to {/,/usr/}lib/<multiarch-name>/
this only applies to /usr/lib/pkgconfig/your-library.pc if its text differs across architectures
move architecture-specific headers from {/,/usr/}include/ to {/,/usr/}include/<multiarch-name>/
- headers are only architecture-specific if the header text itself depends on the architecture
- fix symlinks that point to files mentioned above
update references in installed files (e.g. in /usr/lib/pkgconfig/your-library.pc)
update references in debian/*.install etc.
these should support wildcard sources, so e.g. /usr/lib/foo.so becomes /usr/lib/*/foo.so
for architecture-specific targets, see dh-exec
make any appropriate changes to debian/rules
if you include /usr/share/dpkg/buildtools.mk, you can use e.g. /usr/lib/$(DEB_HOST_MULTIARCH)
add support for new directories in the program itself
for example, a program that checked /usr/lib/myprogram-plugins/*.plugin should still support that directory for old plugins, but also add /usr/lib/myprogram-plugins/<multiarch-name>/*.plugin
if you have to get rid of the old path, add a line like like Breaks: plugin-package (< first-multiarch-version) to your debian/control
Use Multi-Arch fields
Each binary package in your debian/control file should have a Multi-Arch field specifying its multiarch behaviour. These fields tell the package manager two things: whether it can co-install the same package for two architectures at once, and which architecture to use when the package is installed as a dependency.
Multi-Arch options are explained in the multiarch specification, but most packages can use the following simplified rules:
if your package can be co-installed, use Multi-Arch: same
packages with Architecture: all cannot be co-installed, but see the edge case above
else use Multi-Arch: foreign
Multi-Arch: allowed is also available, but only needed by packages that are used differently by different dependencies. For example, ananta and gimp both depend on python3; but ananta is a Python script that needs an interpreter, while gimp is a C program that links against Python. So ananta uses Depends: python3:any to indicate a foreign architecture is fine, gimp uses Suggests: python3 to indicate it needs the same architecture, and python3 uses Multi-Arch: allowed to indicate it supports both use cases. If in doubt, ignore this option.
If you don't add a Multi-Arch field, the default (Multi-Arch: no) disallows both co-installation and multi-arch dependencies.
Multi-Arch must be specified for each binary package in your debian/control file. By convention, it goes after the various Depends etc. and before Description.
Specify proper dependencies
Your debian/control file specifies which other packages your package depends on, and multiarch packages need to specify whether they want dependencies with the same architecture as the package, or will accept any architecture.
Consider the following control file fragment:
Source: mypackage
Build-Depends: package-with-unspecified-architecture, package-with-host-architecture:any
...
Package: mypackage-bin
Depends: package-with-unspecified-architecture, package-with-host-architecture:any
That means, when you build mypackage or install mypackage-bin...
the packaging system can decide which architecture of package-with-unspecified-architecture to install
the packaging system should prefer the architecture of package-with-host-architecture that matches the host system, and the dependency's control file must have Multi-Arch: allowed
Explicit :any qualifiers are most useful for dependencies on language interpreters, like Depends: python3:any or Depends: perl:any. The package manager usually makes the right choice the rest of the time.
You can use :native instead of :any in Build-Depends. This overrides the Multi-Arch behaviour, and is only recommended for dependencies with a missing or broken Multi-Arch field. Modern Debian packages should prefer :any wherever possible.
The rules for Build-Depends also apply to other fields relevant at build-time, and the rules for Depends apply to other fields relevant at install-time.
Architecture-independent dependencies
When building several packages from the same source, some dependencies may only be needed for packages that are not architecture-specific. For example, even if mypackage is architecture-specific, mypackage-doc probably isn't. If you only need pandoc when building the documentation, you can do:
Source: mypackage
Build-Depends-Indep: pandoc
Build-Depends-Indep lets the build system skip packages when cross-compiling, which is faster and avoids the need for multiarch qualifiers.
If you use Build-Depends-Indep, you may need debian/rules to behave differently when cross-compiling. Adapt the following to your requirements:
# Only enable a target when building architecture-independently:
execute_after_dh_auto_build-indep:
dh_auto_build -- optional-target
# Arbitrary actions depending on which packages you're building:
PACKAGE_LIST := $(shell dh_listpackages)
binary-arch: build
ifneq (,$(filter mypackage1,$(PACKAGE_LIST)))
# create artifacts for mypackage1
endif
ifneq (,$(filter mypackage2,$(PACKAGE_LIST)))
# create artifacts for mypackage2
endif
See also Cross-compilation-specific rules in debian/rules.
dh-sequence-* dependencies
Some guides recommend editing debian/rules like this:
%:
dh $@ --with $ADDON
Such guides usually also recommend adding some dependencies to debian/control:
Build-Depends: $ADDON, ...
This can be a problem if the instructions aren't multiarch-aware. Instead, remove the --with option and replace the Build-Depends with:
Build-Depends: dh-sequence-$ADDON
# Or for architecture-independent dependencies:
Build-Depends-Indep: dh-sequence-$ADDON
This will automatically include the right dependencies and add the --with option. It also has some unrelated benefits, like keeping the dependencies up-to-date and avoiding the risk that the two files go out-of-sync.
See also
CrossBuildPackagingGuidelines has guidance for complex cases
MultiArch/Hints discusses common multi-arch issues
