Packaging binaries for Linux

I had the pleasure to prepare a binary (including dependencies) for Linux a few months ago. It’s been a while since I had to do that, and I forgot how difficult it is. Here’s some advice:

Compile libraries yourself

This is probably the most important point. You can either link statically or link dynamically and ship your shared objects, the latter usually being a lot less problematic. If you ship the shared objects prepared by your distribution, you will introduce lots of unnecessary direct and transitive dependencies, forcing you to add tons of additional libraries. For instance, here goes an ldd of the core SDL library on Ubuntu 10.10: =>  (0x00007fff64bce000) => /lib/ (0x00007f6888cfe000) => /lib/ (0x00007f6888afa000) => /usr/lib/ (0x00007f68888f5000) => /usr/lib/ (0x00007f68886b4000) => /lib/ (0x00007f6888497000) => /lib/ (0x00007f6888113000)
/lib64/ (0x00007f6889236000) => /usr/lib/ (0x00007f6887ec7000) => /lib/ (0x00007f6887cbf000) => /usr/lib/ (0x00007f6887abc000) => /usr/lib/ (0x00007f6887786000) => /usr/lib/ (0x00007f688756b000) => /usr/lib/ (0x00007f6887361000) => /usr/lib/ (0x00007f688715a000) => /usr/lib/ (0x00007f6886f55000) => /usr/lib/ (0x00007f6886d37000) => /lib/ (0x00007f6886b2c000) => /usr/lib/ (0x00007f68868c7000) => /lib/ (0x00007f6886684000) => /lib/ (0x00007f688647f000) => /usr/lib/ (0x00007f688626c000) => /usr/lib/ (0x00007f688605c000) => /usr/lib/ (0x00007f6885e59000) => /usr/lib/ (0x00007f6885c52000) => /lib/ (0x00007f6885a38000) => /usr/lib/ (0x00007f68857ee000) => /usr/lib/ (0x00007f688531e000) => /usr/lib/ (0x00007f68850f2000) => /usr/lib/ (0x00007f6884eeb000)

That’s quite a lot of libraries, some of which you can’t even reasonably ship (I’m looking at you, pulseaudio). The solution? Build your required libraries yourself, and do it on the oldest and humblest distribution you want your software to work on. I’m building our current C++ game project and its dependencies on Debian Lenny, this makes it work on all reasonably current popular distributions. Furthermore, if you build the libraries yourself, you can usually configure it to compile only the features you need, reducing size and dependencies even further. This is an ldd of the core SDL library I built: =>  (0xf77a7000) => /lib32/ (0xf76d3000) => /lib32/ (0xf76cf000) => /lib32/ (0xf76b5000) => /lib32/ (0xf755a000)
/lib/ (0xf77a8000)

See the difference? Also make sure to ship a 32 bit version, preferably both 32 and 64 bit.

Check distribution compatibility

There are lots of Linux distributions out there, and you’ll probably want to support as many as possible. If you compile libraries yourself, you’re definitely on the right track, but you should also check for compatibility issues and see what you can do about them. You can either get dozens of distributions and check if it works, or you can use the excellent Linux Application Checker tool:

Reduce dependencies

Reducing your dependencies is good, but do you know what’s even better? Get rid of them. You really shouldn’t overdo this, but look at each of your dependencies and think about whether you really need them. Should you include a large library if you need just one function? Is there a more coherent alternative? Would it be possible and perhaps faster or easier if you just wrote the required functionality yourself?

4 Responses

  1. Cool post, I always wondered how to package a binary that is not part of the distribution’s package management. Thanks for the info!

Leave a Reply