C, docker

C build environment in a docker container

This container is my base build environment for compiling C programs. It produces colored output and it caches compilation artifacts so that subsequent re-compilations are greately accelerated.

It is layered on top of my minidebian container, and I usually expand it further depending on the project. See this example.

Features

  • GCC 6
  • ccache for fast recompilation
  • colorgcc for colored output
  • Support for cross-compiling with external toolchain
  • Very small: 240 MB including the base ownyourbits/minidebian ( ~50 MB )

CCache is a compiler cache that greatly improves recompilation times. I sometimes spend hours and hours waiting for builds to complete, so this is a must in my toolbox.

colorGCC is also a nice addition. It helps to make sense out of compilation output, and it’s really nice to catch warnings in long builds ( “eh! what was that colored output?” ).

Did I mention that I love color already?

Initially, I created this container because I was tired of setting up my C environment with ccache and colorgcc over and over again.

I used to have to go to the Arch wiki every single time, in order to remember the exact setup and the location of the symlinks needed for this rather hacky setup to work. Now I have it scripted and packaged… much better!

Usage

Compilation

It is recommended to use this alias

alias mmake='docker run --rm -v "$(pwd):/src" -t ownyourbits/mmake'

Then, use it just as you would use make

cd test
mmake

You can pass Makefile targets and variables the usual way

mmake alltargets CFLAGS=-g -j5

A .ccache directory will be generated in the directory of execution to speed up subsequent compilations.

Note that the container only includes the generic libraries, so if you need to use some external libraries for your project, you will need to extend the container.

For instance, if you need to link against the ncurses library it will naturally fail

$ mmake
cc main.c -lncurses -o main
main.c:5:1: warning: return type defaults to `int' [-Wimplicit-int]
main()
^~~~
/usr/bin/ld: cannot find -lncurses
collect2: error: ld returned 1 exit status
Makefile:3: recipe for target 'world' failed
make: *** [world] Error 1

You need to install it first. Just create another layer on top of mmake

FROM ownyourbits/mmake

RUN sudo apt-get update; sudo apt-get install -y libncurses5-dev
Cross-Compilation

This method supports specifying an external toolchain, such as this ARM cross-compiler toolchain.

In order to cross-compile to a different architecture, you can use the following alias

alias xmake='docker run --rm -v "$(pwd):/src" -v "/toolchain/path:/toolchain" -t ownyourbits/xmake'

Then again, use it just as you would use make

cd test
xmake

If we now inspect the file, we can see that we are crosscompiling the same C code, in the same folder just by invoking xmake instead of mmake. Nice!

$ file main
main: ELF 32-bit MSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), dynamically linked, interpreter /lib/ld-musl-mips-sf.so.1, with debug_info, not stripped

We now have a MIPS version of main.

The output will still be colored, but if you want to use ccache, you have to include it in the toolchain it and set it up for your particular case.

Check out this collection of ready to use toolchains from free-electrons.

Advanced usage

In order to avoid the delay in the creation and deletion of the container, you can leave the container running for faster execution.

Use these aliases

alias runmake='docker run --rm -d -v "$(pwd):/src" --entrypoint /bg.sh -t --name mmake ownyourbits/mmake'
alias mmake='docker exec -t mmake /run.sh'

I do this whenever I am developing a single project and I am in the stage of frequent recompiling and curating the code.

Uses

Even though initially I was using this as a simple make wrapper, I have found many uses for it:

  • Having a stable build environment is very important in order to achieve reproducible builds that do not depend on what system each member of the development team runs. It is common that Arch is ahead of Debian in gcc version, for example.
  • Pulling the image from docker makes it really easy to share the development environment with your team.
  • It is ideal to be linked it to a continuos integration system that supports docker, such as Gitlab CI.
  • You can docker run –entrypoint bash into the container and work inside the development environment, in the fashion of good old chroot build environments.
  • It is a nice base for creating derivative containers taylored to different projects, reusing a common base and saving disk space. For instance, you might want to build a container with a bunch of libraries plus CUnit for one project, and Doxygen for another.
Code

As usual, you can find the build code on Github.

# Make wrapper with GCC 6, colorgcc and ccache
#
# Copyleft 2017 by Ignacio Nunez Hernanz <nacho _a_t_ ownyourbits _d_o_t_ com>
# GPL licensed (see end of file) * Use at your own risk!
#
# Usage:
#
#   It is recommended to use this alias
#
#     alias mmake='docker run --rm -v "$(pwd):/src" -t ownyourbits/mmake'
#
#   Then, use it just as you would use 'make'
#
# Note: a '.ccache' directory will be generated in the directory of execution
#
# Note: you can leave the container running for faster execution. Use these aliases
#
#     alias runmmake='docker run --rm -d -v "$(pwd):/src" --name mmake -t ownyourbits/mmake /bg'
#     alias mmake='docker exec -t mmake /run.sh'
#
# Details at ownyourbits.com

FROM ownyourbits/minidebian

LABEL description="Make wrapper with GCC 6, colorgcc and ccache"
MAINTAINER Ignacio Núñez Hernanz <nacho@ownyourbits.com>

# Install toolchain 
RUN   apt-get update;\
      DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends gcc make libc6-dev;\
      DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends ccache colorgcc; \
      dpkg -L binutils | grep -v  "^/usr/bin\|^/usr/lib" | while read f; do test -f $f && rm $f; done; \
      dpkg -L gcc-6    | grep -v  "^/usr/bin\|^/usr/lib" | while read f; do test -f $f && rm $f; done; \
      apt-get autoremove -y; apt-get clean; rm /var/lib/apt/lists/* -r; rm -rf /usr/share/man/*

# Set colorgcc and ccache
COPY colorgccrc /etc/colorgcc/colorgccrc

RUN mkdir  /usr/lib/colorgcc; \
    ln -s /usr/bin/colorgcc /usr/lib/colorgcc/c++; \
    ln -s /usr/bin/colorgcc /usr/lib/colorgcc/cc ; \
    ln -s /usr/bin/colorgcc /usr/lib/colorgcc/gcc; \
    ln -s /usr/bin/colorgcc /usr/lib/colorgcc/g++; 

# Builder user
RUN apt-get update; \
    DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends adduser; \
    adduser builder --disabled-password --gecos ""; \
    echo "builder ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers; \
    apt-get purge -y adduser passwd; \
    apt-get autoremove -y; apt-get clean; rm /var/lib/apt/lists/* -r; rm -rf /usr/share/man/*

RUN echo 'export PATH="/usr/lib/colorgcc/:$PATH"' >> /home/builder/.bashrc; \
    echo 'export CCACHE_DIR=/src/.ccache'         >> /home/builder/.bashrc; \
    echo 'export TERM="xterm"'                    >> /home/builder/.bashrc; 

USER builder

# Run
CMD ["/run.sh"]

COPY run.sh /
COPY bg.sh /bg

# License
#
# This script is free software; you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This script is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this script; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330,
# Boston, MA  02111-1307  USA

Author: nachoparker

Humbly sharing things that I find useful [ github dockerhub ]

Leave a Reply

Your email address will not be published. Required fields are marked *