|
|
|
---
|
|
|
|
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 nclude 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`.
|