Building Portable Linux binaries using Gitlab CI

  • I host my code in Gitlab, and use Gitlab’s CI to run a linux build and test for each commit, which is handy when I dev from a Windows box
  • Switching to build on a different distro is as simple as providing a different Image: line in the CI config yaml file to tell the CI runner to use a different Docker image
  • Ubuntu 12 is old. Dates back to 2012, and even the LTS branch stopped being updated in 2017
  • Ubuntu 12 is old. The official docker images for it are deprecated, but you can find alternative Docker images
  • Ubuntu 12 is old. The version of gcc it uses doesn’t support c++11 features, and there isn’t a version of SDL2 in its repositories
  • So scratch that. I found someone had built Docker images based on the Steam Runtimes here:
  • The jimbly/steamrt-amd64-gcc image is based on Ubuntu 12 again, and includes all the dependencies I need (from the Steam Runtimes) and a version of GCC from 2015, so it at least recognises most of c++11
  • It doesn’t seem to support std::regex and some datetime functions, so I need to replace those
  • Stripped out the unsupported bits of c++11 code I was using (thankfully nothing major) and it all compiles within the Steam Runtime docker image environment
  • I’ve already heavily cut down external dependencies, so the only non-system ones I actually need are SDL2, GLEW and GL itself - I highly recommend the stb libraries for header-only things like image reading (it’s a dead simple replacement to SDL_Image, and gives you one less library to worry about) -
  • Advice is you really don’t want to ship GL libraries, as they’re pretty hw and distro dependent
  • So, just GLEW and SDL2 then.
  • Using ldd and grep, I can pull out the local paths of those two libraries on the build machine, and copy them to an output folder as part of the build

* Test runners just need to set the lib path to that output folder when running the binary

* I also install a version of GL as part of the runners launch script - usually some version of Mesa, as that works happily in software with no GPU * Voila, tests running and succeeding on a variety of Linux distros:

CI Runners Success * So I’ve got it running on Arch Linux, Debian Jessie (the oldest supported LTS version), Debian Latest, Fedora Latest, Ubuntu 14 (LTS again) and Ubuntu Latest - the biggest hurdle to testing it on other platforms is figuring out how to install the libGL package or equivalent on each system in a non-interactive fashion. * My tests basically run the app in a headless mode, and launch a special test script that checks an assortment of logic is functioning as expected - it’ll exit with an OK or failure error code depending on the test results

Lastly I just need a final Deploy stage that’ll output a zip, or push to for general consumption (I’m yet to figure that bit out yet!)

More notes so I don’t forget when documenting this properly * The standard way of checking whether a platform is 32 or 64 bit is to use “uname -m” which returns “i686” or “x86_64” depending on the architecture * But if you’re running a 32 bit docker image, you’ll still (likely) be running on a 64 bit kernel so uname will always tell you it’s 64 bit * One hackaround is to examine a system executable, and figure out the bit-ness from that * od -An -t x1 -j 4 -N 1 /bin/bash will return “ 01” for a 32 bit system or “ 02” for a 64 bit one (it’s looking at the 4th byte of the /bin/bash elf header, if you’re curious) * You can do the same to your built game binary to be sure it’s using the right platform. Likewise ldd should show i686 system libs in the 32 bit build (hopefully!)