Ubuntu下编译跨CUDA/非CUDA环境的Server程序遇libcudart.so依赖问题
搞定OpenCV/CUDA依赖与静态链接问题
嘿,这个问题我之前也碰到过!咱们先拆解下根源:你用带CUDA支持编译的OpenCV,哪怕代码里没直接碰CUDA,OpenCV的动态库本身就依赖libcudart.so.8.0,所以到了没装CUDA的机器上就会报错。下面给你两个靠谱的解决方案,还有检查静态链接的方法:
方案1:静态链接关键库(首推)
直接把OpenCV、Boost和CUDA的cudart库都静态链接进可执行文件,这样就不会有动态依赖的问题了。我帮你修改了CMakeLists.txt,每一步都标了注释:
修改后的完整CMakeLists.txt
cmake_minimum_required( VERSION 3.10.0 FATAL_ERROR ) project( Testproject ) # 设置C++11标准,同时静态链接gcc的标准库避免版本问题 if( UNIX ) SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11 -static-libgcc -static-libstdc++" ) # 要是想强制全静态链接(注意可能和部分系统库冲突),可以换成下面这行: # SET( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -std=c++11 -static" ) endif() # 告诉CMake优先用OpenCV的静态库(.a文件) set(OpenCV_STATIC ON) # 查找OpenCV set ( OpenCV_DIR /opt/opencv/opencv-3.0/share/OpenCV ) set( OpenCV_FIND_QUIETLY FALSE ) find_package( OpenCV 3.0 REQUIRED ) include_directories( ${OpenCV_INCLUDE_DIRS} ) # 强制使用Boost的静态库版本 set(Boost_USE_STATIC_LIBS ON) set(Boost_USE_MULTITHREADED ON) set(Boost_USE_STATIC_RUNTIME ON) # 查找Boost find_package( Boost COMPONENTS log log_setup thread system program_options REQUIRED ) include_directories( ${Boost_INCLUDE_DIRS} ) # 线程库 find_package( Threads ) add_executable( Server src/Server.cpp ) target_include_directories(Server PUBLIC /usr/include/spinnaker PRIVATE ./src/ PRIVATE ../build/ ) # 手动链接CUDA的静态cudart库,替代动态版 find_package(CUDA 8.0 REQUIRED) target_link_libraries( Server ${OpenCV_LIBS} ${Boost_LIBRARIES} ${CUDA_cudart_static_LIBRARY} Spinnaker ${CMAKE_THREAD_LIBS_INIT} )
关键修改说明
- C++编译选项:
-static-libgcc -static-libstdc++能帮你静态链接gcc的标准库,避免目标机器因为libstdc++版本不同出问题;如果想彻底静态化所有库,可以用-static,但要注意有些系统库(比如libpthread)可能只能动态链接,会有冲突。 - OpenCV静态链接:
set(OpenCV_STATIC ON)让CMake找静态库而不是动态库,这样OpenCV的所有依赖(包括CUDA相关)都会被打包进可执行文件。 - Boost静态链接:那三个Boost的开关是强制用静态库的标配,确保Boost的组件不会依赖动态库。
- CUDA静态链接:通过
find_package(CUDA)找到静态版的libcudart_static.a,直接链接它就不会再依赖动态的libcudart.so了。
方案2:动态库自动打包(备选)
要是静态链接遇到麻烦(比如某些库只有动态版本),可以让CMake自动收集依赖的动态库,打包到可执行文件同目录。不过这种方式需要用户在目标机器上设置LD_LIBRARY_PATH,不如静态链接省心,所以只当备选。
你可以在CMake里加个自定义脚本,用ldd扫描可执行文件的依赖,把需要的.so文件复制到输出目录:
add_custom_command(TARGET Server POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:Server> ${CMAKE_BINARY_DIR}/deploy/ COMMAND bash -c "ldd $<TARGET_FILE:Server> | grep -o '/.*\.so\.[0-9]*' | xargs cp -t ${CMAKE_BINARY_DIR}/deploy/" )
怎么检查静态链接成功了?
生成可执行文件后,用这两个命令验证:
- 查看动态依赖:
ldd ./Server
如果输出里找不到libcudart.so.8.0和libboost_*.so,说明这些库已经被静态链接进去了。
2. 检查符号表:
nm -D ./Server | grep cudart
要是没输出,就说明cudart的符号已经嵌入到可执行文件里了,不会再找动态库。
小提醒
- 静态链接会让可执行文件变大,这是正常的,换来了环境兼容性。
- 要是你的OpenCV当初编译时没生成静态库,那得重新编译OpenCV并开启
BUILD_STATIC_LIBS选项(不过你说没法分发源码,所以只能用现有环境的静态库啦)。 - 目标机器还是得装Spinnaker的动态库哦,除非你也能静态链接它。
内容的提问来源于stack exchange,提问作者Martin Herrmann
相关产品推荐
相关产品推荐

