| --- |
| 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. |
| |
| There are a number of options shared by cross and native files, |
| [here](Machine-files.md). It is assumed that you have read that section already, |
| as this documentation will only call out options specific to cross files. |
| |
| ### Binaries |
| |
| ```ini |
| [binaries] |
| exe_wrapper = 'wine' # A command used to run generated executables. |
| ``` |
| |
| The `exe_wrapper` option 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. |
| |
| ### Properties |
| |
| In addition to the properites allowed in [all machine |
| files](Machine-files.md#properties), the cross file may contain specific |
| information about the cross compiler or the host machine. 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 |
| |
| 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 |
| ``` |
| |
| ### Machine Entries |
| |
| 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. This should have a value from [the CPU Family |
| table](Reference-tables.md#cpu-families). *Note* that meson does not add |
| `el` to end cpu_family value for little endian systems. Big endian and little |
| endian mips are both just `mips`, with the `endian` field set approriately. |
| |
| 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. |
| |
| |
| ## 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 `meson compile` |
| 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.can_run_host_binaries() # returns true if the host binaries can be run, either with a wrapper or natively |
| ``` |
| |
| 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, variable 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. |
| |
| Note that it is supported for any language, not only `c`, using `<lang>_stdlib` |
| property. |
| |
| Since *0.56.0* the variable name parameter is no longer required as long as the |
| subproject calls `meson.override_dependency('c_stdlib', mylibc_dep)`. |
| The above example becomes: |
| |
| ```ini |
| [properties] |
| c_stdlib = 'mylibc' |
| ``` |
| |
| ## 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 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 |
| 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 |
| 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 |
| ``` |