这两个问题是比较典型的。

cmake 编译无法找到库

这个看《【cmake开发(6)】Cmakelists 使用 gcc/g++指定版本,find_package 基本原理》中find_package 的原理。

如果不使用find

确保三个路径配置好了:
1、#设置包括的头文件

include_directories(
        ${CMAKE_CURRENT_SOURCE_DIR}
        ${CMAKE_CURRENT_BINARY_DIR}
        ${CMAKE_CURRENT_SOURCE_DIR}/include
 )

2、设置搜索的库目录

link_directories(
    ${CMAKE_CURRENT_SOURCE_DIR}/libs
)

3、目标生成

target_link_libraries( demo
                       # 目标库需要链接的库
                       ${log-lib} )

如果使用find

在CMakeList.txt我们经常看见find_package这个命令,如
find_package(yaml-cpp REQUIRED)
当找不到包时,则会报错,停止编译。
1.2.1 让 find_package 到指定路径找包,有三种方法
1 设置 DIR

   	  set(yaml-cpp_DIR ~/yaml-cpp)
	  find_package(yaml-cpp REQUIRED)

2 设置PATHS

	  set(yaml-cpp required PATHS ~/yaml-cpp)

3 指定 DCMAKE_PREFIX_PATH

	  cmake -DCMAKE_PREFIX_PATH="~/yaml-cpp" ..

另外《【cmake开发(11)】CMake中的CMAKE_PREFIX_PATH使用技巧与示例》会记录DCMAKE_PREFIX_PATH的用法,可以参考

编译通过后运行时无法找到库

因为运行和编译是不一样的,编译时 cmake 会组织“材料” 进行加工。运行时,需要使用 系统的寻库程序进行。因此需要依靠系统的工具实现

查找问题

在之前的文章《【编译原理】linux动态链接库的路径及依赖的查询objdump 和ldd》提到了工具 ldd 查看。

1、一个 二进制文件(动态库、可执行程序)还可能依赖别的动态库,可用以下命令去查询[7]:

$ objdump -x libxxxxx.so | grep NEEDED

2、 还可以直接使用ldd命令查看动态库的依赖关系:

ldd libXXX.so

3、通过 file 命令来查看库的编译平台:

file xxx.so

4、readelf命令,一般用于查看ELF格式的文件信息,常见的文件如在Linux上的可执行文件,动态库(.so)或者静态库(.a) 等包含ELF格式的文件。
使用方式(usage)

      readelf -d test   #test为可执行文件
      readelf -d libc.so.6  #libc.so.6为动态库

解决问题

方式 1、/etc/ld.so.conf ,我们帮助系统找到库

/etc/ld.so.conf 此文件记录了编译时使用的动态库的路径,也就是加载so库的路径

查到/etc/ld.so.conf这个文件,此文件记录了编译时使用的动态库的路径,也就是加载so库的路径。将/usr/lib加入到文件/etc/ld.so.conf中,执行ldconfig命令,刷新库文件缓存。

具体分析
1、默认情况下,编译器只会使用/lib和/usr/lib这两个目录下的库文件,而通常通过源码包进行安装时,如果不指定–prefix会将库安装在/usr/local目录下,而又没有在文件/etc/ld.so.conf中添加/usr/local/lib这个目录。这样虽然安装了源码包,但是使用时仍然找不到相关的.so库,就会报错。也就是说系统不知道安装了源码包。对于此种情况有2种解决办法:

(1) 在用源码安装时,用–prefix指定安装路径为/usr/lib。
(2) 直接将路径/usr/local/lib路径加入到文件/etc/ld.so.conf文件的中。在文件/etc/ld.so.conf中末尾直接添加:/usr/local/lib(这个方法给力!)

ldconfig
再来看看ldconfig这个程序,位于/sbin下,它的作用是将文件/etc/ld.so.conf列出的路径下的库文件缓存到/etc/ld.so.cache以供使用,因此当安装完一些库文件,或者修改/etc/ld.so.conf增加了库的新的搜索路径,需要运行一下ldconfig,使所有的库文件都被缓存到文件/etc/ld.so.cache中,如果没做,可能会找不到刚安装的库。

方式 2、修改二进制文件里面的 rpath

方法1.chrpath 修改rpath

安装chrpath,sudo apt install chrpath

    #显示rpath路径,比如
      chrpath -l xxx.so  或者 chrpath -l test
     
    #修改rpath路径,比如
      chrpath -r ./lib xxx.so    #其中./lib是需要修改的路径,xxx.so为源路径库
      chrpath -r ./lib test    #其中./lib是需要修改的路径,test可执行文件
方法2.patchelf设置rpath

安装patchelf sudo apt install patchelf

  patchelf --set-rpath  ./lib  xxx.so   #其中./lib是需要修改的路径,xxx.so为源路径库
方法3.gcc 指定rpath 编译选项(推荐)

GCC详解的-Wl选项说明与测试

gcc -o test test.c -I. -L. -lc -Wl,-rpath=.   
其中

-I. 是指定头文件路径为当前目录下;

-L. 是指定库文件路径为当前目录下;

-lc 是从-L指定库文件路径下去找libc.so.6这个库,也就是库名砍头去尾(类似使用过-lpthread);

-Wl,-rpath=. 为告诉编译器将后面的参数传递给链接器。
方法 4.cmake中指定rpath选项(推荐)
    IF(UNIX)
    	set(CMAKE_SKIP_BUILD_RPATH TRUE)
		set(CMAKE_CXX_FLAGS   "-Wl,-z,origin,-rpath,$ORIGIN")
    ENDIF()

或者

    set(CMAKE_SKIP_BUILD_RPATH FALSE)                 # 编译时加上RPATH  
    set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)         # 编译时RPATH不使用安装的RPATH  
    set(CMAKE_INSTALL_RPATH "./")                     # 安装RPATH为当前目录  
    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)      # 安装的执行文件不加上RPATH

方式 3 修改环境变量

设置LD_LIBRARY_PATH

  export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/yimning/lib  

冒号:为设置多个路径下的分割符号。设置之后再去编译即可。

        export -p //列出当前的环境变量值
        export MYENV //定义环境变量
        export MYENV=7 //定义环境变量并赋值

如果直接使用export声明变量,则export 的效力仅限于该次登陆操作。如果想要持久化生效,则需要将export命令写入 /etc/profile 文件或者/root/.bashrc文件中。

参考

https://blog.csdn.net/Castlehe/article/details/107630544
https://cloud.tencent.com/developer/article/1342894
https://www.runoob.com/linux/linux-comm-export.html

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐