抱歉,本节仍然继续折腾 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 …)
这个指令非常简单,添加非标准的共享库搜索路径,比如,在工程内部同时存在共享库和可
执行二进制,在编译时就需要指定一下这些共享库的路径。这个例子中我们没有用到这个指
令。todo liuwang
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 变量以及一些基本的控制语法规则。