|
|
|
---
|
|
|
|
short-description: Setting up cross-compilation
|
|
|
|
...
|
|
|
|
|
|
|
|
# Cross compilation
|
|
|
|
|
|
|
|
Meson has full support for cross compilation. Since cross compiling is
|
|
|
|
more complicated than native building, let's first go over some
|
|
|
|
nomenclature. The three most important definitions are traditionally
|
|
|
|
called *build*, *host* and *target*. This is confusing because those
|
|
|
|
terms are used for quite many different things. To simplify the issue,
|
|
|
|
we are going to call these the *build machine*, *host machine* and
|
|
|
|
*target machine*. Their definitions are the following:
|
|
|
|
|
|
|
|
* *build machine* is the computer that is doing the actual compiling.
|
|
|
|
* *host machine* is the machine on which the compiled binary will run.
|
|
|
|
* *target machine* is the machine on which the compiled binary's
|
|
|
|
output will run, *only meaningful* if the program produces
|
|
|
|
machine-specific output.
|
|
|
|
|
|
|
|
The `tl/dr` summary is the following: if you are doing regular cross
|
|
|
|
compilation, you only care about `build_machine` and
|
|
|
|
`host_machine`. Just ignore `target_machine` altogether and you will
|
|
|
|
be correct 99% of the time. Only compilers and similar tools care
|
|
|
|
about the target machine. In fact, for so-called "multi-target" tools
|
|
|
|
the target machine need not be fixed at build-time like the others but
|
|
|
|
chosen at runtime, so `target_machine` *still* doesn't matter. If your
|
|
|
|
needs are more complex or you are interested in the actual details, do
|
|
|
|
read on.
|
|
|
|
|
|
|
|
This might be easier to understand through examples. Let's start with
|
|
|
|
the regular, not cross-compiling case. In these cases all of these
|
|
|
|
three machines are the same. Simple so far.
|
|
|
|
|
|
|
|
Let's next look at the most common cross-compilation setup. Let's
|
|
|
|
suppose you are on a 64 bit OSX machine and you are cross compiling a
|
|
|
|
binary that will run on a 32 bit ARM Linux board. In this case your
|
|
|
|
*build machine* is 64 bit OSX, your *host machine* is 32 bit ARM Linux
|
|
|
|
and your *target machine* is irrelevant (but defaults to the same value
|
|
|
|
as the *host machine*). This should be quite understandable as well.
|
|
|
|
|
|
|
|
The usual mistake in this case is to call the OSX system the *host* and
|
|
|
|
the ARM Linux board the *target*. That's because these were their actual
|
|
|
|
names when the cross-compiler itself was compiled! Let's assume the
|
|
|
|
cross-compiler was created on OSX too. When that happened the *build*
|
|
|
|
and *host machines* were the same OSX and different from the ARM Linux
|
|
|
|
*target machine*.
|
|
|
|
|
|
|
|
In a nutshell, the typical mistake assumes that the terms *build*,
|
|
|
|
*host* and *target* refer to some fixed positions whereas they're
|
|
|
|
actually relative to where the current compiler is running. Think of
|
|
|
|
*host* as a *child* of the current compiler and *target* as an optional
|
|
|
|
*grand-child*. Compilers don't change their terminology when they're
|
|
|
|
creating another compiler, that would at the very least make their user
|
|
|
|
interface much more complex.
|
|
|
|
|
|
|
|
The most complicated case is when you cross-compile a cross
|
|
|
|
compiler. As an example you can, on a Linux machine, generate a cross
|
|
|
|
compiler that runs on Windows but produces binaries on MIPS Linux. In
|
|
|
|
this case *build machine* is x86 Linux, *host machine* is x86 Windows
|
|
|
|
and *target machine* is MIPS Linux. This setup is known as the
|
|
|
|
[Canadian
|
|
|
|
Cross](https://en.wikipedia.org/wiki/Cross_compiler#Canadian_Cross). As
|
|
|
|
a side note, be careful when reading cross compilation articles on
|
|
|
|
Wikipedia or the net in general. It is very common for them to get
|
|
|
|
build, host and target mixed up, even in consecutive sentences, which
|
|
|
|
can leave you puzzled until you figure it out.
|
|
|
|
|
|
|
|
Again note that when you cross-compile something,
|
|
|
|
the 3 systems (*build*, *host*, and *target*) used when
|
|
|
|
building the cross compiler don't align with the ones used when
|
|
|
|
building something with that newly-built cross compiler. To take our
|
|
|
|
Canadian Cross scenario from above (for full generality), since its
|
|
|
|
*host machine* is x86 Windows, the *build machine* of anything we
|
|
|
|
build with it is *x86 Windows*. And since its *target machine* is MIPS
|
|
|
|
Linux, the *host machine* of anything we build with it is *MIPS
|
|
|
|
Linux*. Only the *target machine* of whatever we build with it can be
|
|
|
|
freely chosen by us, say if we want to build another cross compiler
|
|
|
|
that runs on MIPS Linux and targets Aarch64 iOS. As this example
|
|
|
|
hopefully makes clear to you, the machine names are relative and
|
|
|
|
shifted over to the left by one position.
|
|
|
|
|
|
|
|
If you did not understand all of the details, don't worry. For most
|
|
|
|
people it takes a while to wrap their head around these
|
|
|
|
concepts. Don't panic, it might take a while to click, but you will
|
|
|
|
get the hang of it eventually.
|
|
|
|
|
|
|
|
## Defining the environment
|
|
|
|
|
|
|
|
Meson requires you to write a cross build definition file. It defines
|
|
|
|
various properties of the cross build environment. The cross file
|
|
|
|
consists of different sections. The first one is the list of
|
|
|
|
executables that we are going to use. A sample snippet might look like
|
|
|
|
this:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[binaries]
|
|
|
|
c = '/usr/bin/i586-mingw32msvc-gcc'
|
|
|
|
cpp = '/usr/bin/i586-mingw32msvc-g++'
|
|
|
|
ld = 'gold'
|
|
|
|
ar = '/usr/i586-mingw32msvc/bin/ar'
|
|
|
|
strip = '/usr/i586-mingw32msvc/bin/strip'
|
|
|
|
pkgconfig = '/usr/bin/i586-mingw32msvc-pkg-config'
|
|
|
|
exe_wrapper = 'wine' # A command used to run generated executables.
|
|
|
|
```
|
|
|
|
|
|
|
|
The entries are pretty self explanatory but the last line is
|
|
|
|
special. It defines a *wrapper command* that can be used to run
|
|
|
|
executables for this host. In this case we can use Wine, which runs
|
|
|
|
Windows applications on Linux. Other choices include running the
|
|
|
|
application with qemu or a hardware simulator. If you have this kind
|
|
|
|
of a wrapper, these lines are all you need to write. Meson will
|
|
|
|
automatically use the given wrapper when it needs to run host
|
|
|
|
binaries. This happens e.g. when running the project's test suite.
|
|
|
|
|
|
|
|
ld is special because it is compiler specific. For compilers like gcc and
|
|
|
|
clang which are used to invoke the linker this is a value to pass to their
|
|
|
|
"choose the linker" argument (-fuse-ld= in this case). For compilers like
|
|
|
|
MSVC and Clang-Cl, this is the path to a linker for meson to invoke, such as
|
|
|
|
`link.exe` or `lld-link.exe`. Support for ls is *new in 0.53.0*
|
|
|
|
|
|
|
|
The next section lists properties of the cross compiler and its target
|
|
|
|
system, and thus properties of host system of what we're building. It
|
|
|
|
looks like this:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[properties]
|
|
|
|
sizeof_int = 4
|
|
|
|
sizeof_wchar_t = 4
|
|
|
|
sizeof_void* = 4
|
|
|
|
|
|
|
|
alignment_char = 1
|
|
|
|
alignment_void* = 4
|
|
|
|
alignment_double = 4
|
|
|
|
|
|
|
|
has_function_printf = true
|
|
|
|
|
|
|
|
c_args = ['-DCROSS=1', '-DSOMETHING=3']
|
|
|
|
c_link_args = ['-some_link_arg']
|
|
|
|
sys_root = '/some/path'
|
|
|
|
pkg_config_libdir = '/some/path/lib/pkgconfig'
|
|
|
|
```
|
|
|
|
|
|
|
|
In most cases you don't need the size and alignment settings, Meson
|
|
|
|
will detect all these by compiling and running some sample
|
|
|
|
programs. If your build requires some piece of data that is not listed
|
|
|
|
here, Meson will stop and write an error message describing how to fix
|
|
|
|
the issue. If you need extra compiler arguments to be used during
|
|
|
|
cross compilation you can set them with `[langname]_args =
|
|
|
|
[args]`. Just remember to specify the args as an array and not as a
|
|
|
|
single string (i.e. not as `'-DCROSS=1 -DSOMETHING=3'`).
|
|
|
|
|
|
|
|
*Since 0.52.0* The `sys_root` property may point to the root of the host
|
|
|
|
system path (the system that will run the compiled binaries). This is used
|
|
|
|
internally by Meson to set the PKG_CONFIG_SYSROOT_DIR environment variable
|
|
|
|
for pkg-config. If this is unset the host system is assumed to share a root
|
|
|
|
with the build system.
|
|
|
|
|
|
|
|
*Since 0.54.0* The pkg_config_libdir property may point to a list of path used
|
|
|
|
internally by Meson to set the PKG_CONFIG_LIBDIR environment variable for pkg-config.
|
|
|
|
This prevents pkg-config from searching cross dependencies in system directories.
|
|
|
|
|
|
|
|
One important thing to note, if you did not define an `exe_wrapper` in
|
|
|
|
the previous section, is that Meson will make a best-effort guess at
|
|
|
|
whether it can run the generated binaries on the build machine. It
|
|
|
|
determines whether this is possible by looking at the `system` and
|
|
|
|
`cpu_family` of build vs host. There will however be cases where they
|
|
|
|
do match up, but the build machine is actually not compatible with the
|
|
|
|
host machine. Typically this will happen if the libc used by the build
|
|
|
|
and host machines are incompatible, or the code relies on kernel
|
|
|
|
features not available on the build machine. One concrete example is a
|
|
|
|
macOS build machine producing binaries for an iOS Simulator x86-64
|
|
|
|
host. They're both `darwin` and the same architecture, but their
|
|
|
|
binaries are not actually compatible. In such cases you may use the
|
|
|
|
`needs_exe_wrapper` property to override the auto-detection:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[properties]
|
|
|
|
needs_exe_wrapper = true
|
|
|
|
```
|
|
|
|
|
|
|
|
The next bit is the definition of host and target machines. Every
|
|
|
|
cross build definition must have one or both of them. If it had
|
|
|
|
neither, the build would not be a cross build but a native build. You
|
|
|
|
do not need to define the build machine, as all necessary information
|
|
|
|
about it is extracted automatically. The definitions for host and
|
|
|
|
target machines look the same. Here is a sample for host machine.
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[host_machine]
|
|
|
|
system = 'windows'
|
|
|
|
cpu_family = 'x86'
|
|
|
|
cpu = 'i686'
|
|
|
|
endian = 'little'
|
|
|
|
```
|
|
|
|
|
|
|
|
These values define the machines sufficiently for cross compilation
|
|
|
|
purposes. The corresponding target definition would look the same but
|
|
|
|
have `target_machine` in the header. These values are available in
|
|
|
|
your Meson scripts. There are three predefined variables called,
|
|
|
|
surprisingly, `build_machine`, `host_machine` and
|
|
|
|
`target_machine`. Determining the operating system of your host
|
|
|
|
machine is simply a matter of calling `host_machine.system()`.
|
|
|
|
|
|
|
|
There are two different values for the CPU. The first one is
|
|
|
|
`cpu_family`. It is a general type of the CPU. Common values might
|
|
|
|
include `x86`, `arm` or `x86_64`. The second value is `cpu` which is a
|
|
|
|
more specific subtype for the CPU. Typical values for a `x86` CPU
|
|
|
|
family might include `i386` or `i586` and for `arm` family `armv5` or
|
|
|
|
`armv7hl`. Note that CPU type strings are very system dependent. You
|
|
|
|
might get a different value if you check its value on the same machine
|
|
|
|
but with different operating systems.
|
|
|
|
|
|
|
|
If you do not define your host machine, it is assumed to be the build
|
|
|
|
machine. Similarly if you do not specify target machine, it is assumed
|
|
|
|
to be the host machine.
|
|
|
|
|
|
|
|
Additionally, you can define the paths that you want to install to in your
|
|
|
|
cross file. This may be especially useful when cross compiling an entire
|
|
|
|
operating system, or for operating systems to use internally for consistency.
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[paths]
|
|
|
|
prefix = '/my/prefix'
|
|
|
|
libdir = 'lib/i386-linux-gnu'
|
|
|
|
bindir = 'bin'
|
|
|
|
```
|
|
|
|
|
|
|
|
This will be overwritten by any options passed on the command line.
|
|
|
|
|
|
|
|
Since meson 0.52.0 it is possible to layer cross files together. This
|
|
|
|
works like native file layering: the purpose is to compose cross files
|
|
|
|
together, and values from the second cross file will replace those
|
|
|
|
from the first.
|
|
|
|
|
|
|
|
|
|
|
|
## Starting a cross build
|
|
|
|
|
|
|
|
|
|
|
|
Once you have the cross file, starting a build is simple
|
|
|
|
|
|
|
|
```console
|
|
|
|
$ meson srcdir builddir --cross-file cross_file.txt
|
|
|
|
```
|
|
|
|
|
|
|
|
Once configuration is done, compilation is started by invoking Ninja
|
|
|
|
in the usual way.
|
|
|
|
|
|
|
|
## Introspection and system checks
|
|
|
|
|
|
|
|
The main *meson* object provides two functions to determine cross
|
|
|
|
compilation status.
|
|
|
|
|
|
|
|
```meson
|
|
|
|
meson.is_cross_build() # returns true when cross compiling
|
|
|
|
meson.has_exe_wrapper() # returns true if an exe wrapper has been defined
|
|
|
|
```
|
|
|
|
|
|
|
|
Note that the latter gives undefined return value when doing a native
|
|
|
|
build.
|
|
|
|
|
|
|
|
You can run system checks on both the system compiler or the cross
|
|
|
|
compiler. You just have to specify which one to use.
|
|
|
|
|
|
|
|
```meson
|
|
|
|
build_compiler = meson.get_compiler('c', native : true)
|
|
|
|
host_compiler = meson.get_compiler('c', native : false)
|
|
|
|
|
|
|
|
build_int_size = build_compiler.sizeof('int')
|
|
|
|
host_int_size = host_compiler.sizeof('int')
|
|
|
|
```
|
|
|
|
|
|
|
|
## Mixing host and build targets
|
|
|
|
|
|
|
|
Sometimes you need to build a tool which is used to generate source
|
|
|
|
files. These are then compiled for the actual target. For this you
|
|
|
|
would want to build some targets with the system's native
|
|
|
|
compiler. This requires only one extra keyword argument.
|
|
|
|
|
|
|
|
```meson
|
|
|
|
native_exe = executable('mygen', 'mygen.c', native : true)
|
|
|
|
```
|
|
|
|
|
|
|
|
You can then take `native_exe` and use it as part of a generator rule or anything else you might want.
|
|
|
|
|
|
|
|
## Using a custom standard library
|
|
|
|
|
|
|
|
Sometimes in cross compilation you need to build your own standard
|
|
|
|
library instead of using the one provided by the compiler. Meson has
|
|
|
|
built-in support for switching standard libraries transparently. The
|
|
|
|
invocation to use in your cross file is the following:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[properties]
|
|
|
|
c_stdlib = ['mylibc', 'mylibc_dep'] # Subproject name, dependency name
|
|
|
|
```
|
|
|
|
|
|
|
|
This specifies that C standard library is provided in the Meson
|
|
|
|
subproject `mylibc` in internal dependency variable `mylibc_dep`. It
|
|
|
|
is used on every cross built C target in the entire source tree
|
|
|
|
(including subprojects) and the standard library is disabled. The
|
|
|
|
build definitions of these targets do not need any modification.
|
|
|
|
|
|
|
|
## Changing cross file settings
|
|
|
|
|
|
|
|
Cross file settings are only read when the build directory is set up
|
|
|
|
the first time. Any changes to them after the fact will be
|
|
|
|
ignored. This is the same as regular compiles where you can't change
|
|
|
|
the compiler once a build tree has been set up. If you need to edit
|
|
|
|
your cross file, then you need to wipe your build tree and recreate it
|
|
|
|
from scratch.
|
|
|
|
|
|
|
|
## Custom data
|
|
|
|
|
|
|
|
You can store arbitrary data in `properties` and access them from your
|
|
|
|
Meson files. As an example if you cross file has this:
|
|
|
|
|
|
|
|
```ini
|
|
|
|
[properties]
|
|
|
|
somekey = 'somevalue'
|
|
|
|
```
|
|
|
|
|
|
|
|
then you can access that using the `meson` object like this:
|
|
|
|
|
|
|
|
```meson
|
|
|
|
myvar = meson.get_cross_property('somekey')
|
|
|
|
# myvar now has the value 'somevalue'
|
|
|
|
```
|
cross: Implement support for loading cross files from system paths
One thing that makes cross compiling with meson a pain is the need for
cross files. The problem is not with cross files themselves (they're
actually rather brilliant in that they allow for a much greater deal of
flexibility than autotools hardcoded paths approach) but that each user
needs to reimplement them themselves, when for most people what they
really want is a cross file that could be provided by their distro, all
they really want is the correct toolchain.
This patch is the first stop to making it easier for distros to ship
their own cross files (and for users to put their's somewhere safe so
they don't get `git clean`ed. It allows the cross files (on Linux and
*BSD) to be stored in home and system paths (~/.config/meson/cross,
/usr/share/meson/cross, and /usr/local/share/meson/cross), and to be
loaded by simply by specificying --cross-file.
With this patch meson will check the locations its always checked first,
(is cross file absolute, or is it relative to $PWD), then will check
~/.config/meson/cross, /usr/local/share/meson/cross,
/usr/share/meson/cross, (or $XDG_CONFIG_PATH and $XDG_DATA_DIRS) for the
files, raising an exception if it cannot find the specified cross file.
Fixes #2283
7 years ago
|
|
|
|
|
|
|
## Cross file locations
|
|
|
|
|
|
|
|
As of version 0.44.0 meson supports loading cross files from system locations
|
|
|
|
(except on Windows). This will be $XDG_DATA_DIRS/meson/cross, or if
|
cross: Implement support for loading cross files from system paths
One thing that makes cross compiling with meson a pain is the need for
cross files. The problem is not with cross files themselves (they're
actually rather brilliant in that they allow for a much greater deal of
flexibility than autotools hardcoded paths approach) but that each user
needs to reimplement them themselves, when for most people what they
really want is a cross file that could be provided by their distro, all
they really want is the correct toolchain.
This patch is the first stop to making it easier for distros to ship
their own cross files (and for users to put their's somewhere safe so
they don't get `git clean`ed. It allows the cross files (on Linux and
*BSD) to be stored in home and system paths (~/.config/meson/cross,
/usr/share/meson/cross, and /usr/local/share/meson/cross), and to be
loaded by simply by specificying --cross-file.
With this patch meson will check the locations its always checked first,
(is cross file absolute, or is it relative to $PWD), then will check
~/.config/meson/cross, /usr/local/share/meson/cross,
/usr/share/meson/cross, (or $XDG_CONFIG_PATH and $XDG_DATA_DIRS) for the
files, raising an exception if it cannot find the specified cross file.
Fixes #2283
7 years ago
|
|
|
XDG_DATA_DIRS is undefined, then /usr/local/share/meson/cross and
|
|
|
|
/usr/share/meson/cross will be tried in that order, for system wide cross
|
|
|
|
files. User local files can be put in $XDG_DATA_HOME/meson/cross, or
|
|
|
|
~/.local/share/meson/cross if that is undefined.
|
|
|
|
|
|
|
|
The order of locations tried is as follows:
|
|
|
|
- A file relative to the local dir
|
|
|
|
- The user local location
|
|
|
|
- The system wide locations in order
|
|
|
|
|
|
|
|
Distributions are encouraged to ship cross files either with
|
cross: Implement support for loading cross files from system paths
One thing that makes cross compiling with meson a pain is the need for
cross files. The problem is not with cross files themselves (they're
actually rather brilliant in that they allow for a much greater deal of
flexibility than autotools hardcoded paths approach) but that each user
needs to reimplement them themselves, when for most people what they
really want is a cross file that could be provided by their distro, all
they really want is the correct toolchain.
This patch is the first stop to making it easier for distros to ship
their own cross files (and for users to put their's somewhere safe so
they don't get `git clean`ed. It allows the cross files (on Linux and
*BSD) to be stored in home and system paths (~/.config/meson/cross,
/usr/share/meson/cross, and /usr/local/share/meson/cross), and to be
loaded by simply by specificying --cross-file.
With this patch meson will check the locations its always checked first,
(is cross file absolute, or is it relative to $PWD), then will check
~/.config/meson/cross, /usr/local/share/meson/cross,
/usr/share/meson/cross, (or $XDG_CONFIG_PATH and $XDG_DATA_DIRS) for the
files, raising an exception if it cannot find the specified cross file.
Fixes #2283
7 years ago
|
|
|
their cross compiler toolchain packages or as a standalone package, and put
|
|
|
|
them in one of the system paths referenced above.
|
|
|
|
|
|
|
|
These files can be loaded automatically without adding a path to the cross
|
|
|
|
file. For example, if a ~/.local/share/meson/cross contains a file called x86-linux,
|
|
|
|
then the following command would start a cross build using that cross files:
|
|
|
|
|
|
|
|
```sh
|
|
|
|
meson builddir/ --cross-file x86-linux
|
|
|
|
```
|