Compile code with Docker

Every time I need to compile some code which I downloaded on the Internet, I have to spend a lot of time installing tools and libraries. Sometimes I need to cross-compile code or patch a library, and I need to configure some local environment which occupies space on my disk, but I can't throw away because I might need it again later.

What if I could easily and reliabily create the environment when I need?

Docker represents a versatile tool which can help to simplify and eliminate tedious operations such as preparing an environment for compiling code. This principle can be extended to any build process, and actually there are already companies such as CircleCI, which have adopted Docker for their CI/CD solution.

Personally I find Docker extremely helpful for creating a build environment. I don't need to install tools and libraries on my local computer or on a virtual machine. I can build an environment for each library or application I want to build, I can throw it away and then recreate it everytime I need. I can have multiple versions of the same tools, but in different containers, so I don't need to worry about conflicts. I can commit my Dockerfile into some git repository, I can change computer, checkout my code and I am still able to build, because I just need Docker and the Dockerfile. No more "it works for me" excuses :)

Pull base image

There are many images which we can use to create our build environment. We can choose among Ubuntu, Debian, Fedora, Centos, and others which are available on Docker Hub. Let's assume we choose Ubuntu, we can pull any version we want just passing a tag:

docker pull ubuntu:latest
docker pull ubuntu:16.04

Create Dockerfile

We want to create our build environment using Ubuntu as operating system. We can do that creating a Dockerfile where we declare the commands required to install all the tools we need, for example the GNU C compiler:

mkdir build-c-image

cat <<EOF >build-c-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get install -y gcc

Build custom image

We can now create a new image using our Dockerfile:

docker build -t build-c build-c-image

Compile and execute code

We can easily compile and execute a simple C program:

mkdir -p project/src project/bin

cat <<EOF >project/src/main.c
#include <stdio.h>
int main(int argv, char ** argc) {
    printf("Hello Docker!\n");

docker run -it -v $(pwd)/project:/project build-c gcc -o /project/bin/main /project/src/main.c

docker run -it -v $(pwd)/project:/project build-c /project/bin/main

Cross-compile code

We can install cross compilation tools like Mingw-w64 and compile code for Windows:

mkdir build-mingw-w64-image

cat <<EOF >build-mingw-w64-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get -y install mingw-w64

docker build -t build-mingw-w64 build-mingw-w64-image

mkdir -p project/src project/bin

cat <<EOF >project/src/main.c
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    MessageBox(NULL, "Hello World!", "My app", MB_OK);

docker run -it -v $(pwd)/project:/project build-mingw-w64 /usr/bin/i686-w64-mingw32-gcc -o /project/bin/main.exe -luser32 /project/src/main.c

Customize a library

We can compile a customized version of a library like ffmpeg:

mkdir build-autotools-image

cat <<EOF >build-autotools-image/Dockerfile
FROM ubuntu:16.04
RUN apt-get update -y && apt-get -y install git make gcc autoconf nasm yasm pkg-config

docker build -t build-autotools build-autotools-image

mkdir output

docker run -it --rm -v $(pwd)/output:/output build-autotools git clone /output/ffmpeg
docker run -it --rm -v $(pwd)/output:/output build-autotools bash -c "cd /output/ffmpeg && ./configure --enable-gpl --disable-ffserver && make"

Execute isolated builds

One common issue with CI servers is managing multiple versions of the tools required for building code. Managing multiple versions of different tools means that we need to install and configure several plugins in our CI server and we need to configure our builds to use the correct version of those tools.

A simplier approach, which is available in Jenkins and other CI/CD solutions such as CircleCI, would be to use Docker to create the environment with the required tools for executing our builds. Using this approach we relay on Docker for creating isolated build processes which can easily run in parallel.