1

I am currently using CMake to create a static library which utilizes a few of the static libraries from OpenCV 4 ( core imgcodecs video highgui imgproc ). My intention is to be able to bundle all of the required OpenCV static libraries into my own library so that I can distribute it as one library. Additionally, I want for the user of my library to not have to install OpenCV 4 on their system (but do not mind if the user has to do simple installs using apt-get install). I know there are tools for bundling static libraries (such as using ar for linux). However, where I really am having the issue is with all the dependencies of OpenCV (such as libjpeg, libpng, etc). I don't necessarily mind if these libraries are bundled with mine or linked dynamically as they are relatively easy to install (can be installed with sudo apt-get install, whereas opencv4 needs to be built from source).

What is the best way to go about doing this? This is my current CMakeLists.txt It is currently working, but that is because I am using find_package(OpenCV REQUIRED) (which defeats the purpose of what I am trying to do). When I remove that line, the linker complains about not being able to find the OpenCV dependencies.

cmake_minimum_required(VERSION 2.8)
project(myproject)

set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)

find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)

set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)

list(APPEND LINKER_LIBS opencv_core opencv_highgui opencv_video opencv_imgcodecs libmxnet.so libncnn.a nlohmann_json::nlohmann_json)
file(GLOB SRC${CMAKE_CURRENT_LIST_DIR}/src/*.cpp${CMAKE_CURRENT_LIST_DIR}/main.cpp)

add_library(myproject ${SRC})
target_link_libraries(myproject ${LINKER_LIBS} ${OpenMP_CXX_FLAGS})

To elaborate on my question. I build my project which generates libmyproject.a. I then take this library and will eventually extract the symbols from the OpenCV libs (libopencv_core.a libopencv_highgui.a libopencv_imgcodecs.a libopencv_video.a) and add them to my lib (for the time being, I have not yet done this step, which is why in the below example I am linking libopencv_*). I then use my library in a new project, for which the CMakeLists.txt is shown below:

cmake_minimum_required(VERSION 2.8)
project(myproject-driver)

set(CMAKE_CXX_STANDARD 14)
include_directories(${CMAKE_CURRENT_LIST_DIR}/include)
link_directories(${CMAKE_CURRENT_LIST_DIR}/lib)
find_package(OpenMP REQUIRED)

add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so libopencv_core.a  libopencv_highgui.a  libopencv_imgcodecs.a  libopencv_video.a)

Building this generates the following errors:

Linking CXX executable myproject-driver
/usr/bin/ld: /home/nchafni/Cyrus/myproject/lib/libopencv_imgcodecs.a(grfmt_jpeg.cpp.o): undefined reference to symbol 'jpeg_default_qtables@@LIBJPEG_8.0'
//usr/lib/x86_64-linux-gnu/libjpeg.so.8: error adding symbols: DSO missing from command line
collect2: error: ld returned 1 exit status

How can I fix this. Is there some CMake command which will link all these dependencies for me? Do I need to manually track down each dependency of those libopencv_* libs and link those manually? Once again, this is assuming that the person using libmyproject.a can't use find_package(OpenCV REQUIRED) as it won't be defined as they have not installed OpenCV on their machine.

cyrusbehr
  • 1,100
  • 1
  • 12
  • 32

1 Answers1

1

First of all, don't use the super old and outdated version 2.8 of CMake.
CMake 3.x is so much more powerful and pretty straightforward to use.
Some tips for modern CMake.

  • Don't use file(GLOB), see here why that is.
  • Don't use directory wide instructions, rather use target instructions, e.g. target_include_directories vs. include_directories.
  • Don't use string variables like ${<PACKAGE_NAME>_LIBRARIES}, rather use targets, e.g. <Package_NAME>::lib
  • When using targets instead of string variables, all the properties (including LINK_INTERFACE) of that target will be populated to the library/executable when calling target_link_libraries, so no more include_directories,link_directories, etc.

myproject

cmake_minimum_required(VERSION 3.14)

project(myproject)

set(CMAKE_CXX_STANDARD 14)

find_package(OpenMP REQUIRED)
find_package(OpenCV REQUIRED)

set(JSON_BuildTests OFF CACHE INTERNAL "")
add_subdirectory(nlohmann_json)

set(SOURCES ...) # list all the source files here

add_library(myproject ${SOURCES})
target_include_directories(myproject PUBLIC # give it a scope
                           ${CMAKE_CURRENT_LIST_DIR}/include
)
target_link_libraries(myproject PUBLIC # give it a scope
                      opencv_core # using the target, you will get all LINK_LIBRARIES
                      opencv_highgui 
                      opencv_video 
                      opencv_imgcodecs 
                      libmxnet.so  # where is this coming from?
                      libncnn.a # where is this coming from?
                      nlohmann_json::nlohmann_json 
                      OpenMP::OpenMP_CXX ## linking against a target, CXX_FLAGS will be populated automatically
)

myprojec-driver

cmake_minimum_required(VERSION 3.14)
project(myproject-driver)

set(CMAKE_CXX_STANDARD 14)

add_executable(myproject-driver main.cpp)
target_link_libraries(myproject-driver PUBLIC # give it a scope
                      myproject # gets all dependencies through the LINK_INTERFACE
)
serkan.tuerker
  • 1,681
  • 10
  • 20
  • I appreciate all the good advice. So to confirm, would I be able to run the myproject-driver on a machine that does not have OpenCV installed? Because that is my ultimate goal. – cyrusbehr Jun 29 '19 at 06:29
  • @AppD3veloper Probably not. Be default you are building a STATIC library; that will require the static libraries to be present for people who use your library. If you built a SHARED library that actual linked in the OpenCV archive libraries then you may be good to go. If your library is supposed to provide all of the OpenCV symbols are not makes this even harder. – fdk1342 Jun 29 '19 at 11:19
  • @AppD3veloper Have you tried it out yourself? I am confident that it will work. When I do `ldd` on the executable, I am getting an ouput similar to this: ```linux-vdso.so.1 (0x00007ffd1c5f9000) libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f3d5da25000) libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f3d5d80d000) libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3d5d41c000) libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f3d5d218000) libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f3d5cff9000)```, so no *OpenCV* libs. – serkan.tuerker Jul 01 '19 at 19:51
  • Hi @kanstar, so my ultimate goal is to create a library which I can ship to the client which bundles OpenCV (so that they can create an application on their own device, which may not have OpenCV installed). I appreciate your answer, but here the driver is created on a machine which has OpenCV, so it is not necessarily what I am trying to do. – cyrusbehr Jul 01 '19 at 21:31
  • @AppD3veloper I understand your ultimate goal. But you clearly don't understand fundamental concepts when it comes to linking libraries etc. On top, you clearly did not try out my approach. I don't know what else to tell you. – serkan.tuerker Jul 01 '19 at 22:19
  • I tried what you suggested but get undefined reference errors in the `myproject-driver` program. ```undefined reference to `cv::String::deallocate()' /home/nchafni/Cyrus/myproject-driver/lib/libamyproject.a(ageDetect.cpp.o): In function `cv::String::operator=(cv::String const&)' ``` – cyrusbehr Jul 01 '19 at 23:31
  • Well, of course you cannot develop OpenCV based application if you don't have OpenCV installed. You have to do all the OpenCV based programming in `myproject` and then use that library in `myproject-driver`. – serkan.tuerker Jul 02 '19 at 00:12