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

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

1.准备工作

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

mkdir t4

2.建立目录

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

#include <hello.h>
int main()
{
    HelloFunc();
    return 0;
}

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

root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4/src# cat main.c
#include <hello.h>
int main()
{
        HelloFunc();
        return 0;
}

编写工程主文件 CMakeLists.txt

project(newHello)
add_subdirectory(src)

编写 src/CMakeLists.txt

add_executable(main main.c)

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

root@BIH-L-55661:/mnt/d/study/lwstudy/cmake/t4# tree
.
├── CMakeLists.txt
└── src
    ├── CMakeLists.txt
    └── main.c

1 directory, 3 files

3.外部构建

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

cmake ..
make

编译过程:
构建失败: 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 <hello.h>
      |          ^~~~~~~~~
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#

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)

重新编译过程如下:
这是找不到 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#

5.为 target 添加共享库

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

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

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

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

cmake ..
make

这是我们就得到了一个连接到 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#

让我们来检查一下 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)

可以清楚的看到 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#

说明,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)

这里简单说明一下,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_INCLUDE_PATH>。

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

7.小节

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

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘大望

谢谢你请的咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值