Use Docker to compile C++ 20

Last time we created a simple Docker image that allowed building any OpenCV based application. Today we will go one step further and allow our image to compile spaceships.

By spaceship I mean the three-way comparison operator (<=>) – a new feature inside the C++ language (still not official released version 20). To use it we need a gcc 10 so lets add it to our image. We need to add same steps to our recipe that you would normally execute on your dev machine: get dependencies, clone, build and install. I also included the latest cmake version because the default one used by Ubuntu 18 (3.16.0) was not supporting the C++ 20 yet.

FROM ubuntu:18.04

ARG DEBIAN_FRONTEND=noninteractive

# GCC dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libgmp-dev \ 
    libmpfr-dev \
    libmpc-dev \ 
    bash \
    git \
    flex \
    gcc-multilib \
    && rm -rf /var/lib/apt/lists/*

# CMAKE dependencies
RUN apt-get update && apt-get install -y \
    libssl-dev \
    && rm -rf /var/lib/apt/lists/*

# OpenCV Library
RUN apt-get update && apt-get install -y --no-install-recommends\
    libopencv-dev \
    && rm -rf /var/lib/apt/lists/*

# Install CMAKE
RUN git clone https://github.com/Kitware/CMake.git cmake_repo && \
    cd cmake_repo && \
    git checkout v3.17.3 && \
    ./bootstrap && \
    make && \
    make install && \
    cd .. && \
    rm cmake_repo -r

# Install GCC
RUN git clone git://gcc.gnu.org/git/gcc.git gcc_repo && \
    cd gcc_repo && \
    git checkout releases/gcc-10.1.0 && \
    ./configure --enable-languages=c,c++ --disable-multilib && \
    make && \
    make install && \
    cd .. && \
    rm gcc_repo -r

# Set environment variables
ENV CC=/usr/local/bin/gcc
ENV export CXX=/usr/local/bin/g++

CMD cd /home/out && cmake /home/source/. && make    

After building the image we can run it and compile a special version of our demo application that makes use of the magic operator (branch gcc20).

Nice but we can do better than that. When creating an image we added some dependencies needed to build required libraries. We need them only during the build process and Docker provides nice mechanism to deal with such situations: multi-stage builds.

Using this Docker inside a Docker philosophy we can protect our final image from all unwanted dependencies. All we need to do is split our file into two parts – one that will produce needed artifacts and second that makes use of them.

FROM ubuntu:18.04 AS builder

ARG DEBIAN_FRONTEND=noninteractive

# Build image dependencies
RUN apt-get update && apt-get install -y \
    build-essential \
    libgmp-dev \ 
    libmpfr-dev \
    libmpc-dev \ 
    bash \
    git \
    flex \
    gcc-multilib \
    libssl-dev \
    checkinstall \
    && rm -rf /var/lib/apt/lists/*

# Build CMAKE
RUN git clone https://github.com/Kitware/CMake.git cmake_repo && \
    cd cmake_repo && \
    git checkout v3.17.3 && \
    mkdir out && \
    ./bootstrap && \
    make && \
    checkinstall -D --pkgname=cmake --pkgversion=3.17.3


# Build GCC
RUN git clone git://gcc.gnu.org/git/gcc.git gcc_repo && \
    cd gcc_repo && \
    git checkout releases/gcc-10.1.0 && \
    mkdir out && \
    ./configure --enable-languages=c,c++ --disable-multilib && \
    make && \
    checkinstall -D --pkgname=gcc --pkgversion=10.1.0


# Target image
FROM ubuntu:18.04

ARG DEBIAN_FRONTEND=noninteractive

# OpenCV Library
RUN apt-get update && apt-get install -y --no-install-recommends\
    libopencv-dev \
    libmpc-dev \
    && rm -rf /var/lib/apt/lists/*

# Copy packages from builder image
COPY --from=builder /cmake_repo/*.deb .
COPY --from=builder /gcc_repo/*.deb .

# Install CMAKE and GCC
RUN apt install ./*.deb && rm *.deb

# Set environment variables
ENV CC=/usr/local/bin/gcc
ENV export CXX=/usr/local/bin/g++

CMD cd /home/out && cmake /home/source/. && make

Here we created 2 packages containing compiled versions of cmake and gcc. We copy and install them when creating production image.

Leave a comment