I've been attempting to build OpenCV 3.3.1 for the Raspberry PI Zero (Raspbian stretch) on a Ubuntu 16.04 machine. I've been following the steps documented here https://github.com/HesselM/rpicross_notes with the major exception that instead of using the tools from https://github.com/raspberrypi/tools which have GCC 4.9.3 I used crosstool-ng to build a GCC 6.3.0 toolchain to match the GCC version shipped with Raspbian stretch. I've installed all the OpenCV prerequistites on a RaspberryPi Zero and then taken a copy of the filesystem to use as the system root for cross-compilation on the host.
CMake confliguration appears to work but the make build fails when linking libopencv_freetype.so
as the harfbuzz and freetype libraries are not found.
/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/../lib/gcc/arm-unknown-linux-gnueabihf/6.3.0/../../../../arm-unknown-linux-gnueabihf/bin/ld: cannot find -lfreetype
/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/../lib/gcc/arm-unknown-linux-gnueabihf/6.3.0/../../../../arm-unknown-linux-gnueabihf/bin/ld: cannot find -lharfbuzz
collect2: error: ld returned 1 exit status
modules/freetype/CMakeFiles/opencv_freetype.dir/build.make:97: recipe for target 'lib/libopencv_freetype.so.3.3.1' failed
These libraries are present in my RaspberryPI system in usr/lib/arm-linux-gnueabihf
-rw-r--r-- 1 dsnowdon dsnowdon 717178 Apr 27 2017 libfreetype.a
-rw-r--r-- 1 dsnowdon dsnowdon 909 Apr 27 2017 libfreetype.la
lrwxrwxrwx 1 dsnowdon dsnowdon 21 Apr 27 2017 libfreetype.so -> libfreetype.so.6.12.3
lrwxrwxrwx 1 dsnowdon dsnowdon 21 Apr 27 2017 libfreetype.so.6 -> libfreetype.so.6.12.3
-rw-r--r-- 1 dsnowdon dsnowdon 567844 Apr 27 2017 libfreetype.so.6.12.3
-rw-r--r-- 1 dsnowdon dsnowdon 739360 Jan 24 2017 libharfbuzz.a
lrwxrwxrwx 1 dsnowdon dsnowdon 24 Jan 24 2017 libharfbuzz.so -> libharfbuzz.so.0.10400.2
lrwxrwxrwx 1 dsnowdon dsnowdon 24 Jan 24 2017 libharfbuzz.so.0 -> libharfbuzz.so.0.10400.2
-rw-r--r-- 1 dsnowdon dsnowdon 551256 Jan 24 2017 libharfbuzz.so.0.10400.2
Building with make VERBOSE=1 shows that the failure occurs when this command is run:
/usr/bin/cmake -E cmake_link_script CMakeFiles/opencv_freetype.dir/link.txt --verbose=1
Which generates this:
/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++ -fPIC -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include/arm-linux-gnueabihf -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/local/include -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -mfp16-format=ieee -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -DNDEBUG -shared -Wl,-soname,libopencv_freetype.so.3.3 -o ../../lib/libopencv_freetype.so.3.3.1 CMakeFiles/opencv_freetype.dir/src/freetype.cpp.o ../../lib/libopencv_imgproc.so.3.3.1 -ldl -lm -lpthread -lrt ../../3rdparty/lib/libtegra_hal.a -lfreetype -lharfbuzz ../../lib/libopencv_core.so.3.3.1
However if I manually add -L${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf
to give the following then the link works:
/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++ -L/opt/software/raspberrypi/rpi-cross/rootfs/usr/lib/arm-linux-gnueabihf -fPIC -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include/arm-linux-gnueabihf -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/include -isystem /opt/software/raspberrypi/rpi-cross/rootfs/usr/local/include -fsigned-char -W -Wall -Werror=return-type -Werror=non-virtual-dtor -Werror=address -Werror=sequence-point -Wformat -Werror=format-security -Wmissing-declarations -Wundef -Winit-self -Wpointer-arith -Wshadow -Wsign-promo -Wuninitialized -Winit-self -Wno-narrowing -Wno-delete-non-virtual-dtor -Wno-comment -fdiagnostics-show-option -pthread -fomit-frame-pointer -ffunction-sections -mfp16-format=ieee -fvisibility=hidden -fvisibility-inlines-hidden -O3 -DNDEBUG -DNDEBUG -shared -Wl,-soname,libopencv_freetype.so.3.3 -o ../../lib/libopencv_freetype.so.3.3.1 CMakeFiles/opencv_freetype.dir/src/freetype.cpp.o ../../lib/libopencv_imgproc.so.3.3.1 -ldl -lm -lpthread -lrt ../../3rdparty/lib/libtegra_hal.a -lfreetype -lharfbuzz ../../lib/libopencv_core.so.3.3.1
This led me to think that maybe pkg-config was being called in such a way it was not emitting the correct libdir so I wrote a wrapper script for pkg-config to dump all the invocations by CMake and the output of running the command.
Wrapper script:
#! /bin/bash
LOGFILE=/tmp/pkg-config-wrapper.log
basename="`basename "$0"`"
timestamp=$(date)
printf "${timestamp} ${basename} $@\nPKG_CONFIG_DIR=${PKG_CONFIG_DIR}\nPKG_CONFIG_LIBDIR=${PKG_CONFIG_LIBDIR}\nPKG_CONFIG_SYSROOT_DIR=${PKG_CONFIG_SYSROOT_DIR}\n" >> $LOGFILE
result=$(/usr/bin/pkg-config "$@")
printf "pkf-config = ${result}\n" >> $LOGFILE
echo $result
The output of the wrapper script is here: https://gist.github.com/davesnowdon/548c87d735433d73c587210a27995b6c
As far as I can see pkg-config is being invoked correctly for cross-compilation so now I'm at a loss and am not sure what to try next.
I'm invoking CMake as follows:
cmake \
-D RPI_ROOTFS=${RPI_ROOTFS} \
-D BUILD_TESTS=NO \
-D BUILD_PERF_TESTS=NO \
-D BUILD_PYTHON_SUPPORT=ON \
-D OPENCV_EXTRA_MODULES_PATH=${RPI_CROSS_HOME}/src/opencv_contrib-3.3.1/modules \
-D CMAKE_TOOLCHAIN_FILE=${RPI_CROSS_HOME}/src/opencv-3.3.1/platforms/linux/arm.toolchain.cmake \
${RPI_CROSS_HOME}/src/opencv-3.3.1
Most of the changes to the OpenCV Cmake files are in opencv-3.3.1/cmake/OpenCVMinDepVersions.cmake
as described in https://github.com/HesselM/rpicross_notes
set(MIN_VER_CMAKE 2.8.12.2)
set(MIN_VER_CUDA 6.5)
set(MIN_VER_PYTHON2 2.6)
set(MIN_VER_PYTHON3 3.2)
set(MIN_VER_ZLIB 1.2.3)
set(MIN_VER_GTK 2.18.0)
# install dir
set( CMAKE_INSTALL_PREFIX ${RPI_ROOTFS}/usr/local CACHE STRING "")
set( CMAKE_FIND_ROOT_PATH "${RPI_ROOTFS}" CACHE FILEPATH "")
set( CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER )
# compilers
set( CMAKE_C_COMPILER "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-gcc" CACHE FILEPATH "")
set( CMAKE_CXX_COMPILER "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-g++" CACHE FILEPATH "")
set( CMAKE_AR "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-ar" CACHE FILEPATH "")
set( CMAKE_RANLIB "/opt/cross/x-tools/arm-unknown-linux-gnueabihf/bin/arm-unknown-linux-gnueabihf-ranlib" CACHE FILEPATH "")
#Pkg-config settings
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/pkgconfig" )
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/usr/share/pkgconfig" )
set( RPI_PKGCONFIG_LIBDIR "${RPI_PKGCONFIG_LIBDIR}:${RPI_ROOTFS}/opt/vc/lib/pkgconfig" )
#set( PKG_CONFIG_EXECUTABLE "/usr/bin/pkg-config" CACHE FILEPATH "")
set( PKG_CONFIG_EXECUTABLE "/opt/cross/pkg-config-wrapper.sh" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_DIR} "" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_LIBDIR} "${RPI_PKGCONFIG_LIBDIR}" CACHE FILEPATH "")
set( ENV{PKG_CONFIG_SYSROOT_DIR} "${RPI_ROOTFS}" CACHE FILEPATH "")
# setup rpi (target) directories for compiler
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/include/arm-linux-gnueabihf")
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/include")
set( RPI_INCLUDE_DIR "${RPI_INCLUDE_DIR} -isystem ${RPI_ROOTFS}/usr/local/include")
set( RPI_LIBRARY_DIR "${RPI_LIBRARY_DIR} -Wl,-rpath ${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf")
set( RPI_LIBRARY_DIR "${RPI_LIBRARY_DIR} -Wl,-rpath ${RPI_ROOTFS}/lib/arm-linux-gnueabihf")
# Setup C/CXX flags.
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${RPI_INCLUDE_DIR}" CACHE STRING "" FORCE)
set( CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} ${RPI_INCLUDE_DIR}" CACHE STRING "" FORCE)
set( CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${RPI_LIBRARY_DIR}" CACHE STRING "" FORCE)
#Python2.7
set( PYTHON_EXECUTABLE /usr/bin/python2.7 CACHE STRING "")
set( PYTHON_LIBRARY_DEBUG "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_LIBRARY_RELEASE "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_LIBRARY "${RPI_ROOTFS}/usr/lib/arm-linux-gnueabihf/libpython2.7.so" CACHE STRING "")
set( PYTHON_INCLUDE_DIR "${RPI_ROOTFS}/usr/include/python2.7" CACHE STRING "")
set( PYTHON2_NUMPY_INCLUDE_DIRS "${RPI_ROOTFS}/usr/lib/python2.7/dist-packages/numpy/core/include" CACHE STRING "")
set( PYTHON2_PACKAGES_PATH "${RPI_ROOTFS}/usr/local/lib/python2.7/site-packages" CACHE STRING "")
I also changed opencv-3.3.1/platforms/linux/arm.toolchain.cmake
according to https://github.com/HesselM/rpicross_notes replacing "-mthumb" with "-marm" to give this
if(COMMAND toolchain_save_config)
return() # prevent recursive call
endif()
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR)
set(CMAKE_SYSTEM_PROCESSOR arm)
else()
#message("CMAKE_SYSTEM_PROCESSOR=${CMAKE_SYSTEM_PROCESSOR}")
endif()
include("${CMAKE_CURRENT_LIST_DIR}/gnu.toolchain.cmake")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm AND NOT ARM_IGNORE_FP)
set(FLOAT_ABI_SUFFIX "")
if(NOT SOFTFP)
set(FLOAT_ABI_SUFFIX "hf")
endif()
endif()
if(NOT "x${GCC_COMPILER_VERSION}" STREQUAL "x")
set(__GCC_VER_SUFFIX "-${GCC_COMPILER_VERSION}")
endif()
if(NOT DEFINED CMAKE_C_COMPILER)
find_program(CMAKE_C_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-gcc${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_C_COMPILER=${CMAKE_C_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_CXX_COMPILER)
find_program(CMAKE_CXX_COMPILER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-g++${__GCC_VER_SUFFIX})
else()
#message(WARNING "CMAKE_CXX_COMPILER=${CMAKE_CXX_COMPILER} is defined")
endif()
if(NOT DEFINED CMAKE_LINKER)
find_program(CMAKE_LINKER NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ld)
else()
#message(WARNING "CMAKE_LINKER=${CMAKE_LINKER} is defined")
endif()
if(NOT DEFINED CMAKE_AR)
find_program(CMAKE_AR NAMES ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar${__GCC_VER_SUFFIX} ${GNU_MACHINE}${FLOAT_ABI_SUFFIX}-ar)
else()
#message(WARNING "CMAKE_AR=${CMAKE_AR} is defined")
endif()
if(NOT DEFINED ARM_LINUX_SYSROOT AND DEFINED GNU_MACHINE)
set(ARM_LINUX_SYSROOT /usr/${GNU_MACHINE}${FLOAT_ABI_SUFFIX})
endif()
if(NOT DEFINED CMAKE_CXX_FLAGS)
set(CMAKE_CXX_FLAGS "" CACHE INTERAL "")
set(CMAKE_C_FLAGS "" CACHE INTERAL "")
set(CMAKE_SHARED_LINKER_FLAGS "" CACHE INTERAL "")
set(CMAKE_MODULE_LINKER_FLAGS "" CACHE INTERAL "")
set(CMAKE_EXE_LINKER_FLAGS "" CACHE INTERAL "")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fdata-sections -Wa,--noexecstack -fsigned-char -Wno-psabi")
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(CMAKE_CXX_FLAGS "-marm ${CMAKE_CXX_FLAGS}")
set(CMAKE_C_FLAGS "-marm ${CMAKE_C_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -Wl,-z,nocopyreloc")
endif()
if(CMAKE_SYSTEM_PROCESSOR STREQUAL arm)
set(ARM_LINKER_FLAGS "-Wl,--fix-cortex-a8 -Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL aarch64)
set(ARM_LINKER_FLAGS "-Wl,--no-undefined -Wl,--gc-sections -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
endif()
set(CMAKE_SHARED_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_SHARED_LINKER_FLAGS}")
set(CMAKE_MODULE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_MODULE_LINKER_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${ARM_LINKER_FLAGS} ${CMAKE_EXE_LINKER_FLAGS}")
else()
#message(WARNING "CMAKE_CXX_FLAGS='${CMAKE_CXX_FLAGS}' is defined")
endif()
if(USE_NEON)
message(WARNING "You use obsolete variable USE_NEON to enable NEON instruction set. Use -DENABLE_NEON=ON instead." )
set(ENABLE_NEON TRUE)
elseif(USE_VFPV3)
message(WARNING "You use obsolete variable USE_VFPV3 to enable VFPV3 instruction set. Use -DENABLE_VFPV3=ON instead." )
set(ENABLE_VFPV3 TRUE)
endif()
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${ARM_LINUX_SYSROOT})
if(EXISTS ${CUDA_TOOLKIT_ROOT_DIR})
set(CMAKE_FIND_ROOT_PATH ${CMAKE_FIND_ROOT_PATH} ${CUDA_TOOLKIT_ROOT_DIR})
endif()
set(TOOLCHAIN_CONFIG_VARS ${TOOLCHAIN_CONFIG_VARS}
ARM_LINUX_SYSROOT
ENABLE_NEON
ENABLE_VFPV3
CUDA_TOOLKIT_ROOT_DIR
)
toolchain_save_config()
The output from CMake is here: https://gist.github.com/davesnowdon/c9904c315c84863ea8840f2752145126
At this point I'm at a loss and don't know what to try next. What could be the problem?