如何用CMake构建捆绑OpenCV静态依赖的自定义静态库?
你遇到的问题本质是:当你尝试把OpenCV静态库整合进自定义静态库libmyproject.a后,OpenCV静态库依赖的系统级动态库(比如libjpeg、libpng这类)并没有被自动关联,导致链接器找不到对应符号。下面是具体的解决思路和实操步骤:
一、先明确核心目标的实现逻辑
你的需求是让库的使用者无需手动编译安装OpenCV,但可以通过apt-get轻松安装系统级依赖库。要达成这个目标,我们需要做两件事:
- 将OpenCV的静态库代码合并到你的自定义静态库中,避免使用者接触OpenCV的库文件
- 明确并处理OpenCV静态库依赖的系统库,确保链接器能找到这些库的符号
二、解决当前链接错误的快速方案
你现在碰到的jpeg_default_qtables未定义错误,是因为libopencv_imgcodecs.a依赖libjpeg.so,但你在新项目的CMake配置里没有显式链接这个库。针对你用到的OpenCV模块(core、imgcodecs、video、highgui、imgproc),它们常见的系统依赖包括:
- 图像编码类:
libjpeg.so、libpng.so、libtiff.so(可选,取决于OpenCV编译配置) - 压缩类:
libz.so - GUI后端类:
libglib-2.0.so、libgtk-3.so(如果highgui用GTK) - 视频处理类:
libavcodec.so、libavformat.so、libswscale.so(video模块依赖FFmpeg)
你可以直接修改新项目的CMakeLists.txt,把这些依赖加进去:
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) # 列出OpenCV静态库依赖的系统库 set(OPENCV_SYSTEM_DEPS jpeg png z glib-2.0 gtk-3 avcodec avformat swscale) add_executable(myproject-driver main.cpp) target_link_libraries(myproject-driver myproject libncnn.a ${OpenMP_CXX_FLAGS} libmxnet.so ${OPENCV_SYSTEM_DEPS} )
这样就能解决当前的符号未定义问题,但手动列依赖容易遗漏,更优雅的方式是用CMake自动处理。
三、更省心的CMake自动化方案
我们可以在构建自定义库时,提前提取OpenCV的依赖信息,生成CMake配置文件让使用者一键导入,无需手动处理依赖。
步骤1:修改原项目的CMakeLists.txt,提取并保存依赖
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的完整依赖链 find_package(OpenCV REQUIRED COMPONENTS core imgcodecs video highgui imgproc) set(JSON_BuildTests OFF CACHE INTERNAL "") add_subdirectory(nlohmann_json) file(GLOB SRC ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp ${CMAKE_CURRENT_LIST_DIR}/main.cpp) add_library(myproject STATIC ${SRC}) # 链接所有依赖(包括OpenCV和系统库) target_link_libraries(myproject ${OpenCV_LIBS} libmxnet.so libncnn.a nlohmann_json::nlohmann_json ${OpenMP_CXX_FLAGS} ) # 生成CMake配置文件,方便使用者导入 include(CMakePackageConfigHelpers) configure_package_config_file( ${CMAKE_CURRENT_SOURCE_DIR}/MyProjectConfig.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake INSTALL_DESTINATION lib/cmake/MyProject ) # 安装自定义库和配置文件 install(TARGETS myproject ARCHIVE DESTINATION lib ) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/MyProjectConfig.cmake DESTINATION lib/cmake/MyProject )
步骤2:创建MyProjectConfig.cmake.in模板文件
@PACKAGE_INIT@ include(CMakeFindDependencyMacro) find_dependency(OpenMP REQUIRED) # 传递所有依赖信息 set(MYPROJECT_LIBS @OpenCV_LIBS@ libmxnet.so libncnn.a nlohmann_json::nlohmann_json) # 声明自定义库的导入属性 add_library(myproject STATIC IMPORTED) set_target_properties(myproject PROPERTIES IMPORTED_LOCATION ${PACKAGE_PREFIX_DIR}/lib/libmyproject.a INTERFACE_INCLUDE_DIRECTORIES ${PACKAGE_PREFIX_DIR}/include INTERFACE_LINK_LIBRARIES "${MYPROJECT_LIBS}" )
步骤3:使用者的极简配置
使用者只需要在自己的CMakeLists.txt中写:
cmake_minimum_required(VERSION 2.8) project(myproject-driver) set(CMAKE_CXX_STANDARD 14) find_package(MyProject REQUIRED) add_executable(myproject-driver main.cpp) target_link_libraries(myproject-driver myproject)
CMake会自动处理所有依赖,包括OpenCV的系统库,使用者完全不需要关心OpenCV的存在。
四、可选:将OpenCV静态库合并到自定义库中
如果你想让分发的文件更简洁,完全隐藏OpenCV的库文件,可以用ar命令在CMake中添加自定义合并步骤:
# 获取OpenCV静态库的路径 get_target_property(OPENCV_CORE_PATH opencv_core LOCATION) get_target_property(OPENCV_IMGCODECS_PATH opencv_imgcodecs LOCATION) get_target_property(OPENCV_VIDEO_PATH opencv_video LOCATION) get_target_property(OPENCV_HIGHGUI_PATH opencv_highgui LOCATION) get_target_property(OPENCV_IMGPROC_PATH opencv_imgproc LOCATION) # 自定义命令:合并静态库 add_custom_command(TARGET myproject POST_BUILD COMMAND ar -x ${OPENCV_CORE_PATH} COMMAND ar -x ${OPENCV_IMGCODECS_PATH} COMMAND ar -x ${OPENCV_VIDEO_PATH} COMMAND ar -x ${OPENCV_HIGHGUI_PATH} COMMAND ar -x ${OPENCV_IMGPROC_PATH} COMMAND ar -r ${CMAKE_CURRENT_BINARY_DIR}/libmyproject.a *.o COMMAND rm -f *.o WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} )
这样最终生成的libmyproject.a就包含了你自己的代码和所有OpenCV静态库的代码,使用者只需要链接这个库和系统依赖即可。
关键注意事项
- 确保OpenCV是静态编译的:必须用
BUILD_SHARED_LIBS=OFF编译OpenCV,否则无法合并到你的静态库中 - 系统依赖版本兼容:你编译OpenCV时用的系统库版本(比如
libjpeg8)要和使用者系统的版本一致,主流发行版(如Ubuntu)通常能保证兼容 - 避免重复链接:如果已经合并了OpenCV静态库,使用者不要再手动链接
libopencv_*.a,否则会出现重复定义错误
内容的提问来源于stack exchange,提问作者cyrusbehr

