• 6.如何使用外部共享库和头文件


    抱歉,本节仍然继续折腾 Hello World
    上一节我们已经完成了 libhello 动态库的构建以及安装,本节我们的任务很简单:编写一个程序使用我们上一节构建的共享库

    1.准备工作

    建立 t4 目录,本节所有资源将存储在 t4 目录

    mkdir t4
    
    • 1

    2.建立目录

    重复以前的步骤,建立 src 目录,编写源文件 main.c,内容如下:

    #include 
    int main()
    {
        HelloFunc();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用cat命令查看main.c里面内容,结果如下:

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/src# cat main.c
    #include 
    int main()
    {
            HelloFunc();
            return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    编写工程主文件 CMakeLists.txt

    project(newHello)
    add_subdirectory(src)
    
    • 1
    • 2

    编写 src/CMakeLists.txt

    add_executable(main main.c)
    
    • 1

    上述工作已经严格按照我们前面几节提到的内容完成了,如下:

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4# tree
    .
    ├── CMakeLists.txt
    └── src
        ├── CMakeLists.txt
        └── main.c
    
    1 directory, 3 files
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3.外部构建

    按照习惯,仍然建立 build 目录,使用 cmake …方式构建

    cmake ..
    make
    
    • 1
    • 2

    编译过程:
    构建失败: main.c:1:10: fatal error: hello.h: No such file or directory

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build# ls
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build# cmake ..
    -- The C compiler identification is GNU 9.4.0
    -- The CXX compiler identification is GNU 9.4.0
    -- Check for working C compiler: /usr/bin/cc
    -- Check for working C compiler: /usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /usr/bin/c++
    -- Check for working CXX compiler: /usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    CMake Warning (dev) in CMakeLists.txt:
      No cmake_minimum_required command is present.  A line of code such as
    
        cmake_minimum_required(VERSION 3.16)
    
      should be added at the top of the file.  The version specified may be lower
      if you wish to support older CMake versions for this project.  For more
      information run "cmake --help-policy CMP0000".
    This warning is for project developers.  Use -Wno-dev to suppress it.
    
    -- Configuring done
    -- Generating done
    -- Build files have been written to: /mnt/d/study/lwstudy/cmake/t4/build
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build# make
    Scanning dependencies of target main
    [ 50%] Building C object src/CMakeFiles/main.dir/main.o
    /mnt/d/study/lwstudy/cmake/t4/src/main.c:1:10: fatal error: hello.h: No such file or directory
        1 | #include 
          |          ^~~~~~~~~
    compilation terminated.
    make[2]: *** [src/CMakeFiles/main.dir/build.make:63: src/CMakeFiles/main.dir/main.o] Error 1
    make[1]: *** [CMakeFiles/Makefile2:94: src/CMakeFiles/main.dir/all] Error 2
    make: *** [Makefile:84: all] Error 2
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    4.引入头文件搜索路径

    hello.h 位于/usr/include/hello 目录中,并没有位于系统标准的头文件路径
    为了让我们的工程能够找到 hello.h 头文件,我们需要引入一个新的指令
    INCLUDE_DIRECTORIES,其完整语法为:
    INCLUDE_DIRECTORIES([AFTER|BEFORE] [SYSTEM] dir1 dir2 …)

    这条指令可以用来向工程添加多个特定的头文件搜索路径,路径之间用空格分割,如果路径
    中包含了空格,可以使用双引号将它括起来,默认的行为是追加到当前的头文件搜索路径的
    后面,你可以通过两种方式来进行控制搜索路径添加的方式
    1,CMAKE_INCLUDE_DIRECTORIES_BEFORE,通过 SET 这个 cmake 变量为 on,可以
    将添加的头文件搜索路径放在已有路径的前面。
    2,通过 AFTER 或者 BEFORE 参数,也可以控制是追加还是置前。

    现在我们在 src/CMakeLists.txt 中添加一个头文件搜索路径,方式很简单,加入:
    INCLUDE_DIRECTORIES(/usr/include/hello)

    src/CMakeLists.txt代码如下:

    include_directories(/usr/include/hello)
    add_executable(main main.c)
    
    • 1
    • 2

    重新编译过程如下:
    这是找不到 hello.h 的错误已经消失,但是出现了一个
    新的错误: main.c:(.text+0xe): undefined reference to `HelloFunc
    因为我们并没有 link 到共享库 libhello 上

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build# make
    [ 50%] Linking C executable main
    /usr/bin/ld: CMakeFiles/main.dir/main.o: in function `main':
    main.c:(.text+0xe): undefined reference to `HelloFunc'
    collect2: error: ld returned 1 exit status
    make[2]: *** [src/CMakeFiles/main.dir/build.make:84: src/main] Error 1
    make[1]: *** [CMakeFiles/Makefile2:94: src/CMakeFiles/main.dir/all] Error 2
    make: *** [Makefile:84: all] Error 2
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    5.为 target 添加共享库

    我们现在需要完成的任务是将目标文件链接到 libhello,这里我们需要引入两个新的指令
    LINK_DIRECTORIES 和 TARGET_LINK_LIBRARIES
    LINK_DIRECTORIES 的全部语法是:
    LINK_DIRECTORIES(directory1 directory2 …)

    这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可
    执行二进制,在编译时就需要指定一下这些共享库的路径。这个例子中我们没有用到这个指
    令。todo liuwang

    TARGET_LINK_LIBRARIES 的全部语法是:
    TARGET_LINK_LIBRARIES(target library1 library2 …)
    这个指令可以用来为 target 添加需要链接的共享库,本例中是一个可执行文件,但是同样可以用于为自己编写的共享库添加共享库链接。
    为了解决我们前面遇到的 HelloFunc 未定义错误,我们需要作的是向src/CMakeLists.txt 中添加如下指令:
    TARGET_LINK_LIBRARIES(main hello)
    也可以写成
    TARGET_LINK_LIBRARIES(main libhello.so)
    这里的 hello 指的是我们上一节构建的共享库 libhello

    进入 build 目录重新进行构建。

    cmake ..
    make
    
    • 1
    • 2

    这是我们就得到了一个连接到 libhello 的可执行程序 main,位于 build/src 目录,运
    行 main 的结果是输出:

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build# cd src/
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src# ls
    CMakeFiles  Makefile  cmake_install.cmake  main
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src# ./main
    Hello World
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src#
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    让我们来检查一下 main 的链接情况:

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src# ldd  main
            linux-vdso.so.1 (0x00007fffe4d8d000)
            libhello.so.1 => /lib/libhello.so.1 (0x00007f11515c0000)
            libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f11513c0000)
            /lib64/ld-linux-x86-64.so.2 (0x00007f11515ef000)
    
    • 1
    • 2
    • 3
    • 4
    • 5

    可以清楚的看到 main 确实链接了共享库 libhello,而且链接的是动态库libhello.so.1

    那如何链接到静态库呢?
    方法很简单:
    将 TARGET_LINK_LIBRRARIES 指令修改为:
    TARGET_LINK_LIBRARIES(main libhello.a)

    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src# ldd main
            linux-vdso.so.1 (0x00007fffd9167000)
            libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f7f91370000)
            /lib64/ld-linux-x86-64.so.2 (0x00007f7f91585000)
    root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/build/src#
    
    • 1
    • 2
    • 3
    • 4
    • 5

    说明,main 确实链接到了静态库 libhello.a

    6.特殊的环境变量 CMAKE_INCLUDE_PATH 和 CMAKE_LIBRARY_PATH

    务必注意,这两个是环境变量而不是 cmake 变量。
    如果头文件没有存放在常规路径(/usr/include, /usr/local/include 等),则可以通过这些变量就行弥补。
    我们以本例中的 hello.h 为例,它存放在/usr/include/hello 目录,所以直接查找肯定是找不到的。
    前面我们直接使用了绝对路径 INCLUDE_DIRECTORIES(/usr/include/hello)告诉工程这个头文件目录。
    为了将程序更智能一点,我们可以使用 CMAKE_INCLUDE_PATH 来进行,使用 bash 的方法
    如下:
    export CMAKE_INCLUDE_PATH=/usr/include/hello
    然后在头文件中将 INCLUDE_DIRECTORIES(/usr/include/hello)替换为:

    FIND_PATH(myHeader hello.h)
    IF(myHeader)
    INCLUDE_DIRECTORIES(${myHeader})
    ENDIF(myHeader)
    
    • 1
    • 2
    • 3
    • 4

    这里简单说明一下,FIND_PATH 用来在指定路径中搜索文件名,比如:
    FIND_PATH(myHeader NAMES hello.h PATHS /usr/include /usr/include/hello)

    这里我们没有指定路径,但是,cmake 仍然可以帮我们找到 hello.h 存放的路径,就是因
    为我们设置了环境变量 CMAKE_INCLUDE_PATH。
    如果你不使用 FIND_PATH,CMAKE_INCLUDE_PATH 变量的设置是没有作用的,你不能指望它会直接为编译器命令添加参数-I

    以此为例,CMAKE_LIBRARY_PATH 可以用在 FIND_LIBRARY 中。同样,因为这些变量直接为 FIND_指令所使用,所以所有使用 FIND_指令的 cmake 模块都会受益。

    7.小节

    本节我们探讨了:
    如何通过 INCLUDE_DIRECTORIES 指令加入非标准的头文件搜索路径。
    如何通过 LINK_DIRECTORIES 指令加入非标准的库文件搜索路径。
    如果通过 TARGET_LINK_LIBRARIES 为库或可执行二进制加入库链接。
    并解释了如果链接到静态库。

    到这里为止,您应该基本可以使用 cmake工作了,但是还有很多高级的话题没有探讨,比如编译条件检查、编译器定义、平台判断、如何跟 pkgconfig配合使用等等。

    到这里,或许你可以理解前面讲到的“cmake的使用过程其实就是学习 cmake 语言并编写cmake 程序的过程”,既然是“cmake 语言”,自然涉及到变量、语法等。

    下一节,我们将抛开程序的话题,看看常用的 CMAKE 变量以及一些基本的控制语法规则。

  • 相关阅读:
    认识网络安全
    Spring框架(八):基于xml方式Bean的配置
    CAS登录认证
    lsblk 硬盘属性查看
    C/C++数据结构——QQ帐户的申请与登陆(散列表)
    SpringCloud复习:(6)feign整合hystrix
    Docker 用centos 编译安装apache
    微信小程序反编译
    【Linux】常用指令
    盘点35个Python书籍Python爱好者不容错过
  • 原文地址:https://blog.csdn.net/qq_29407397/article/details/127684938