Как скомпилировать приложение c ++, используя статические библиотеки opencv в Docker - PullRequest
0 голосов
/ 09 октября 2018

Я создаю свое первое приложение на основе OpenCV на C ++.Моя цель - создать промежуточный образ докера, который может статически скомпилировать приложение, чтобы оно могло работать автономно в полученном меньшем образе.Я готов использовать любой образ докера для этого шага, но для того, чтобы вы могли точно видеть, что у меня есть, вот файл докера для воспроизведения всей среды:

FROM ubuntu:18.04 as compiler

ENV OPENCV_VERSION='3.4.2' DEBIAN_FRONTEND=noninteractive

RUN apt-get -y update && \
    apt-get -y upgrade && \
    apt-get -y dist-upgrade && \
    apt-get -y autoremove && \
    apt-get install -y build-essential cmake 
RUN apt-get install -y qt5-default libvtk6-dev
RUN apt-get install -y zlib1g-dev libjpeg-dev libwebp-dev libpng-dev libtiff5-dev libopenexr-dev libgdal-dev
RUN apt-get install -y libdc1394-22-dev libavcodec-dev libavformat-dev libswscale-dev libtheora-dev libvorbis-dev libxvidcore-dev libx264-dev yasm libopencore-amrnb-dev libopencore-amrwb-dev libv4l-dev libxine2-dev
RUN apt-get install -y unzip wget
RUN wget --progress=dot:giga https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \
    unzip -q ${OPENCV_VERSION}.zip && \
    rm ${OPENCV_VERSION}.zip && \
    mv opencv-${OPENCV_VERSION} OpenCV && \
    cd OpenCV && \
    mkdir build && \
    cd build && \
    cmake  \
        -D BUILD_SHARED_LIBS=OFF \ 
        -D WITH_QT=ON \
        -D WITH_OPENGL=ON \ 
        -D FORCE_VTK=ON \
        -D WITH_TBB=ON \ 
        -D WITH_GDAL=ON \ 
        -D WITH_XINE=ON \ 
        -D BUILD_EXAMPLES=OFF \
        -D ENABLE_PRECOMPILED_HEADERS=OFF \
        -D BUILD_DOCS=OFF \
        -D BUILD_PERF_TESTS=OFF \
        -D BUILD_TESTS=OFF \
        -D BUILD_opencv_apps=OFF \
        .. && \
    make -j4 && \
    make install && \
    ldconfig

COPY compile-test.cpp compile-test.cpp

RUN g++ -std=c++11 -static compile-test.cpp -o /app $(pkg-config --cflags --libs opencv)

В настоящее время я могу скомпилировать свои приложения на С ++без проблем с использованием dyanmic libs, но это создает огромный образ докера, и я действительно хочу иметь возможность создавать автономные двоичные файлы для распространения с минимальным размером.

Как вы можете видеть, я компилирую OpenCV из исходного кодавключая флаг BUILD_SHARED_LIBS=OFF, чтобы убедиться, что я получаю статические библиотеки .a вместо динамических библиотек .so.Я взял подсказку из крайне рекомендуемого сценария сборки и изменил его для использования с докером, пропустив несколько вещей на Python, так как я использую c ++.

, потому что у меня было так много проблем смое настоящее приложение, я пошел вперед и создал гораздо более простое приложение, которое также взрывается во время компиляции.Я считаю, что это как-то связано с включенными cflags и libs.Проблема в настоящее время находится за пределами моего понимания.Я получаю кучу ошибок, которые, кажется, меняются, когда я настраиваю одно включение в моей команде компиляции.Вот самое простое приложение, которое я пытаюсь скомпилировать.Это действительно ничего не делает, но включает библиотеку.

#include "opencv2/imgcodecs.hpp"
using namespace cv; 
Mat img;

int main( int argc, char** argv ) {
  img = cv::imread( argv[1], IMREAD_COLOR );
}

Затем я пытаюсь скомпилировать это так:

g++ -std=c++11 -static compile-test.cpp -o /app $(pkg-config --cflags --libs opencv)

И это заканчивается кучейошибки слишком длинные, чтобы их можно было вставить полностью.

//usr/local/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): In function `cv::JpegEncoder::write(cv::Mat const&, std::vector<int, std::allocator<int> > const&)':
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0xf8): undefined reference to `jpeg_CreateCompress'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x105): undefined reference to `jpeg_std_error'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2b5): undefined reference to `jpeg_set_defaults'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2d0): undefined reference to `jpeg_set_quality'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x2fe): undefined reference to `jpeg_quality_scaling'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x30d): undefined reference to `jpeg_quality_scaling'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x367): undefined reference to `jpeg_default_qtables'
grfmt_jpeg.cpp:(.text._ZN2cv11JpegEncoder5writeERKNS_3MatERKSt6vectorIiSaIiEE+0x379): undefined reference to `jpeg_start_compress'
grfmt_jpeg.cpp:
...
collect2: error: ld returned 1 exit status

Некоторые вещи, которые я уже пробовал

  • Начинаем гуглить каждую из, казалось бы, уникальных ошибок компиляции и добавляем соответствующие флаги вконец моего кода компиляции.
  • Переупорядочение некоторых флагов включения, но их слишком много, чтобы сделать это эффективно
  • Использование пакета opencv-dev вместо его компиляции, но кажетсявы не можете этого сделать и ожидаете использовать статические библиотеки.

1 Ответ

0 голосов
/ 12 октября 2018

После долгих экспериментов я наконец-то заработал!Было несколько проблем, которые все исправлены в этом Dockerfile.Чтобы воспроизвести это, создайте файл Docker со следующим содержимым и создайте другой файл с именем app.cpp с простым кодом из моего вопроса выше, в той же папке.

Я объясню, какие проблемы были ниже:

FROM alpine:3.8 as compiler

RUN 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/repositories

RUN 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/clang
ENV CXX /usr/bin/g++
ENV OPENCV_VERSION='3.4.2' DEBIAN_FRONTEND=noninteractive

RUN mkdir /opt && cd /opt && \
  wget https://github.com/opencv/opencv/archive/${OPENCV_VERSION}.zip && \
  unzip ${OPENCV_VERSION}.zip && \
  rm -rf ${OPENCV_VERSION}.zip

RUN 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/pkgconfig

COPY app.cpp app.cpp

RUN g++ -Wl,-Bstatic -static-libgcc -std=c++11 \
    app.cpp \ 
    -o /app \
    $(pkg-config --cflags --libs -static opencv) \
    -lgfortran -lquadmath

FROM alpine    
COPY --from=compiler /app /bin/app

Проблемы

Линкер

Действительно, были файлы, для которых требовалось связывание, которых не было, для этого было две причины:

  • Команда pkg-config должна выдавать все необходимые флаги для компиляции, но в моей более ранней попытке я не включил флаг -static в pkg-config.Когда вы добавляете флаг -static, он обязательно связывает дополнительные обязательные пакеты.Я видел, как несколько человек сталкивались с этой проблемой при решении проблемы добавления дополнительных флагов, таких как -pthread, но я обнаружил, что флаг -static сделал это для меня и поэтому был предпочтительным.
  • ld: cannot find -lgcc_s ошибка,Похоже, это было исправлено добавлением флага -static-libgcc к g++.Некоторые из них до сих пор остаются для меня загадкой.

Отсутствуют статические библиотеки

Было две библиотеки, которые я хотел включить как статические, которые нужно было получить из источников, отличных от apk.Это были libjasper и libwebp.Выше приведены шаги сборки, которые приобретают и собирают их по мере необходимости и копируют ресурсы в нужное место.

Больше недостающих ссылок

По причинам, которые я пока не могу объяснить, pkg-config не сделалукажите последние два необходимых флага.Это были -lgfortran и -lquadmath.

Заметки об этом решении

Я перешел на Alpine Linux, потому что я читал, что некоторые люди добились успеха в этом, я уверен, чтоТо же самое можно сделать с Ubuntu.В результате получилось намного меньшее изображение, поэтому мне это нравится.Это около 900 МБ для промежуточного изображения, которое, хотя и огромно, намного меньше, чем образ Ubuntu 1,9 ГБ.

Фактическое результирующее изображение составляет около 44 МБ, включая все статически связанные библиотеки OpenCV.Это кажется хорошим решением для тех, кому нужен небольшой образ докера для запуска одного бина C ++.

...