| --- |
| short-description: Creating universal Linux binaries |
| ... |
| |
| # Creating Linux binaries |
| |
| Creating Linux binaries that can be downloaded and run on any distro |
| (like .dmg packages for OSX or .exe installers for Windows) has |
| traditionally been difficult. This is even more tricky if you want to |
| use modern compilers and features, which is especially desired in game |
| development. There is still no simple turn-key solution for this |
| problem but with a bit of setup it can be relatively straightforward. |
| |
| ## Installing system and GCC |
| |
| First you need to do a fresh operating system install. You can use |
| spare hardware, VirtualBox, cloud or whatever you want. Note that the |
| distro you install must be *at least as old* as the oldest release you |
| wish to support. Debian stable is usually a good choice, though |
| immediately after its release you might want to use Debian oldstable |
| or the previous Ubuntu LTS. The oldest supported version of CentOS is |
| also a good choice. |
| |
| Once you have installed the system, you need to install |
| build-dependencies for GCC. In Debian-based distros this can be done |
| with the following commands: |
| |
| ```console |
| $ apt-get build-dep g++ |
| $ apt-get install pkg-config libgmp-dev libmpfr-dev libmpc-dev |
| ``` |
| |
| Then create a `src` subdirectory in your home directory. Copy-paste |
| the following into `install_gcc.sh` and execute it. |
| |
| ```bash |
| #!/bin/sh |
| |
| wget ftp://ftp.fu-berlin.de/unix/languages/gcc/releases/gcc-4.9.2/gcc-4.9.2.tar.bz2 |
| tar xf gcc-4.9.2.tar.bz2 |
| |
| mkdir objdir |
| cd objdir |
| ../gcc-4.9.2/configure --disable-bootstrap --prefix=${HOME}/devroot \ |
| --disable-multilib --enable-languages=c,c++ |
| make -j 4 |
| make install-strip |
| ln -s gcc ${HOME}/devroot/bin/cc |
| ``` |
| |
| Then finally add the following lines to your `.bashrc`. |
| |
| ```console |
| $ export LD_LIBRARY_PATH=${HOME}/devroot/lib |
| $ export PATH=${HOME}/devroot/bin:$PATH |
| $ export PKG_CONFIG_PATH=${HOME}/devroot/lib/pkgconfig |
| ``` |
| |
| Log out and back in and now your build environment is ready to use. |
| |
| ## Adding other tools |
| |
| Old distros might have too old versions of some tools. For Meson this |
| could include Python 3 and Ninja. If this is the case you need to |
| download, build and install new versions into `~/devroot` in the usual |
| way. |
| |
| ## Adding dependencies |
| |
| You want to embed and statically link every dependency you can |
| (especially C++ dependencies). Meson's [Wrap package |
| manager](Wrap-dependency-system-manual.md) might be of use here. This |
| is equivalent to what you would do on Windows, OSX, Android etc. |
| Sometimes static linking is not possible. In these cases you need to |
| copy the .so files inside your package. Let's use SDL2 as an example. |
| First we download and install it as usual giving it our custom install |
| prefix (that is, `./configure --prefix=${HOME}/devroot`). This makes |
| Meson's dependency detector pick it up automatically. |
| |
| ## Building and installing |
| |
| Building happens in much the same way as normally. There are just two |
| things to note. First, you must tell GCC to link the C++ standard |
| library statically. If you don't then your app is guaranteed to break |
| as different distros have binary-incompatible C++ libraries. The |
| second thing is that you need to point your install prefix to some |
| empty staging area. Here's the Meson command to do that: |
| |
| ```console |
| $ LDFLAGS=-static-libstdc++ meson --prefix=/tmp/myapp <other args> |
| ``` |
| |
| The aim is to put the executable in `/tmp/myapp/bin` and shared |
| libraries to `/tmp/myapp/lib`. The next thing you need is the |
| embedder. It takes your dependencies (in this case only |
| `libSDL2-2.0.so.0`) and copies them in the lib directory. Depending on |
| your use case you can either copy the files by hand or write a script |
| that parses the output of `ldd binary_file`. Be sure not to copy |
| system libraries (`libc`, `libpthread`, `libm` etc). For an example, |
| see the [sample |
| project](https://github.com/jpakkane/meson/tree/master/manual%20tests/4%20standalone%20binaries). |
| |
| Make the script run during install with this: |
| |
| ```meson |
| [[#meson.add_install_script]]('linux_bundler.sh') |
| ``` |
| |
| ## Final steps |
| |
| If you try to run the program now it will most likely fail to start or |
| crashes. The reason for this is that the system does not know that the |
| executable needs libraries from the `lib` directory. The solution for |
| this is a simple wrapper script. Create a script called `myapp.sh` |
| with the following content: |
| |
| ```bash |
| #!/bin/bash |
| |
| cd "${0%/*}" |
| export LD_LIBRARY_PATH="$(pwd)/lib" |
| bin/myapp |
| ``` |
| |
| Install it with this Meson snippet: |
| |
| ```meson |
| [[#install_data]]('myapp.sh', install_dir : '.') |
| ``` |
| |
| And now you are done. Zip up your `/tmp/myapp` directory and you have |
| a working binary ready for deployment. To run the program, just unzip |
| the file and run `myapp.sh`. |