How to compile a c++ application using static opencv libraries within docker
After a lot of experimentation I finally got something working! There were a few issues that are all fixed in this Dockerfile
. In order to reproduce this, create a Dockerfile with the following contents, and create another file called app.cpp
with the simple code from my question above, in the same folder.
I will explain what the issues were below:
FROM alpine:3.8 as compilerRUN echo -e '@edgunity http://nl.alpinelinux.org/alpine/edge/community \ @edge http://nl.alpinelinux.org/alpine/edge/main \ @testing http://nl.alpinelinux.org/alpine/edge/testing \ @community http://dl-cdn.alpinelinux.org/alpine/edge/community' \ >> /etc/apk/repositoriesRUN apk add --update --no-cache \ build-base \ openblas-dev \ unzip \ wget \ cmake \ g++ \ libjpeg \ libjpeg-turbo-dev \ libpng-dev \ jasper-dev \ tiff-dev \ libwebp-dev \ clang-dev \ linux-headers ENV CC /usr/bin/clangENV CXX /usr/bin/g++ENV OPENCV_VERSION='3.4.2' DEBIAN_FRONTEND=noninteractiveRUN mkdir /opt && cd /opt && \ wget https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \ unzip ${OPENCV_VERSION}.zip && \ rm -rf ${OPENCV_VERSION}.zipRUN mkdir -p /opt/opencv-${OPENCV_VERSION}/build && \ cd /opt/opencv-${OPENCV_VERSION}/build && \ cmake \ -D BUILD_DOCS=OFF \ -D BUILD_EXAMPLES=OFF \ -D BUILD_opencv_apps=OFF \ -D BUILD_opencv_python2=OFF \ -D BUILD_opencv_python3=OFF \ -D BUILD_PERF_TESTS=OFF \ -D BUILD_SHARED_LIBS=OFF \ -D BUILD_TESTS=OFF \ -D CMAKE_BUILD_TYPE=RELEASE \ -D ENABLE_PRECOMPILED_HEADERS=OFF \ -D FORCE_VTK=OFF \ -D WITH_FFMPEG=OFF \ -D WITH_GDAL=OFF \ -D WITH_IPP=OFF \ -D WITH_OPENEXR=OFF \ -D WITH_OPENGL=OFF \ -D WITH_QT=OFF \ -D WITH_TBB=OFF \ -D WITH_XINE=OFF \ -D BUILD_JPEG=ON \ -D BUILD_TIFF=ON \ -D BUILD_PNG=ON \ .. && \ make -j$(nproc) && \ make install && \ rm -rf /opt/opencv-${OPENCV_VERSION}RUN wget --progress=dot:giga https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.0-linux-x86-64.tar.gz && \ pwd && \ tar -xzf libwebp-1.0.0-linux-x86-64.tar.gz && \ mv /libwebp-1.0.0-linux-x86-64/lib/libwebp.a /usr/lib && \ rm -rf /libwebp*RUN wget --progress=dot:giga http://www.ece.uvic.ca/~frodo/jasper/software/jasper-2.0.10.tar.gz && \ tar -xzf jasper-2.0.10.tar.gz && \ cd jasper-2.0.10 && \ mkdir BUILD && \ cd BUILD && \ cmake -DCMAKE_INSTALL_PREFIX=/usr \ -DCMAKE_BUILD_TYPE=Release \ -DCMAKE_SKIP_INSTALL_RPATH=YES \ -DCMAKE_INSTALL_DOCDIR=/usr/share/doc/jasper-2.0.10 \ -DJAS_ENABLE_SHARED=FALSE \ .. && \ make install && \ rm -rf /jasper-2.0.10*ENV PKG_CONFIG_PATH=/usr/local/lib64/pkgconfig:/usr/lib/pkgconfigCOPY app.cpp app.cppRUN g++ -Wl,-Bstatic -static-libgcc -std=c++11 \ app.cpp \ -o /app \ $(pkg-config --cflags --libs -static opencv) \ -lgfortran -lquadmathFROM alpine COPY --from=compiler /app /bin/app
Problems
Linker
There were indeed files that needed linking that weren't present, there were two reasons for this:
- The
pkg-config
command is supposed to emit all of the necessary flags for compilation, but in my earlier attempt I hadn't included the-static
flag topkg-config
. When you add the-static
flag it makes sure to link the extra required packages. I saw a few people run into this problem with the solution of adding the extra flags like-pthread
, but I found that the-static
flag did this for me and so was preferable. ld: cannot find -lgcc_s
error. This appeared to be fixed by adding the-static-libgcc
flag tog++
. Some of this is still a mystery to me.
Missing Static Libraries
There were two libraries that I wanted to be included as static which needed to be acquired from sources other than apk
. These were libjasper
and libwebp
. There are build steps above that acquire and build these as necessary and copy the resources into the required place.
More missing links
For reasons I can't yet explain pkg-config
didn't provide the last two necessary flags. Those were -lgfortran
and -lquadmath
.
Notes about this Solution
I switched to alpine linux, just because I had read that some people had success with that, I'm sure the same could be done with Ubuntu. It did result in a much smaller image, so I do like that. This is about 900mb for the intermediate image, which, while huge, is much smaller than the 1.9GB Ubuntu image.
The actual resulting image is about 44mb including all of the statically linked OpenCV libs. This seems like a good solution for those that need a small docker image to run a single C++ bin.