Basic 02-hello-headers
# 介绍
构建一个最简单的 Hello world 项目,但是源文件和包含的文件不在同一个文件中。
本例的目录🌲:
B-hello-headers$ tree
.
├── CMakeLists.txt # 包含了本项目运行所需要的CMake命令
├── include
│ └── Hello.h # main.cpp, Hello.cpp包含的头文件
└── src
├── Hello.cpp # Hello.h头文件对应的cpp文件
└── main.cpp # main不需要解释了吧😁
2
3
4
5
6
7
8
关于源文件和头文件各自的作用和区别见为什么我们需要头文件
# 概念解释
# 文件夹路径变量
CMake 的语法规定了很多的变量 (opens new window)其中下面是一些有用的指定一些文件夹的目录:
| Variable | Info |
|---|---|
| CMAKE_SOURCE_DIR | The root source directory |
| CMAKE_CURRENT_SOURCE_DIR | The current source directory if using sub-projects and directories. |
| PROJECT_SOURCE_DIR | The source directory of the current cmake project. |
| CMAKE_BINARY_DIR | The root binary / build directory. This is the directory where you ran the cmake command. |
| CMAKE_CURRENT_BINARY_DIR | The build directory you are currently in. |
| PROJECT_BINARY_DIR | The build directory for the current project. |
# 源文件变量
创建一个包含源文件的变量可以更清晰地了解这些文件,并且可以轻松地将它们添加到多个命令中 (这也是我们在 GNU Make 中经常做的)。比如添加到 add_executable 中
# Create a sources variable with a link to all cpp files to compile
set(SOURCES
src/Hello.cpp
src/main.cpp
)
add_executable(${PROJECT_NAME} ${SOURCES})
2
3
4
5
6
注意
一个替代在 SOURCES 变量中设置所有源文件名为变量的方法是使用 GLOB 命令使用通配符 (wildcard pattern) 匹配查找文件。
file(GLOB SOURCES "src/*.cpp")
使用建议
对于现代的 CMake,不建议使用变量来存储源文件 (sources),而是直接在 add_xxx 函数中声明源文件。
这对于使用 GLOB 命令查找源文件尤其重要,因为如果添加了新的源文件,则可能无法正确显示结果。
因为 file (GLOB) 命令只在执行一次时进行评估,并且在目录内容发生变化时不会自动更新
举例说明:
比如你有一个目录 src ,其中包含了 foo.cpp 、 bar.cpp 和 baz.cpp 三个源文件。你可以使用以下方式来定义这些源文件:
file(GLOB SOURCES "src/*.cpp")
这会搜索 src 目录中所有以 .cpp 结尾的文件,并将它们的路径保存到 SOURCES 变量中。然后,你可以在 add_executable 调用中使用 SOURCES 变量来指定源文件列表:
add_executable(my_executable ${SOURCES})
这样, my_executable 目标就会编译 src 目录中的所有 .cpp 文件,包括 foo.cpp 、 bar.cpp 和 baz.cpp 。
但是,如果你在之后向 src 目录中添加了一个新的 .cpp 文件 qux.cpp ,那么它不会被自动包含在 SOURCES 变量中,因为 file(GLOB) 命令已经在之前执行过了,它不会自动更新。所以如果你在 add_executable 调用中仍然使用 SOURCES 变量来指定源文件列表,那么 qux.cpp 就不会被编译进 my_executable 目标中。这就是为什么建议直接在 add_executable 调用中列出源文件,而不是使用 file(GLOB) 命令来搜索源文件的原因。
# 使用 target_include_directories() 包含编译目录
当您有不同的包含文件夹时,可以使用 target_include_directories() 函数使编译器意识到它们。在编译此目标时,将使用 -I 标志将这些目录添加到编译器中,例如 -I/directory/path (注意 - I 和后面的文件夹路径不能用空格分开)。
target_include_directories(target
PRIVATE
${PROJECT_SOURCE_DIR}/include
)
2
3
4
PRIVATE 标志符指定了 include 的搜索范围,具体请看 TBD
# 构建本例
$ mkdir build
$ cd 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/lib/ccache/cc
-- Check for working C compiler: /usr/lib/ccache/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/lib/ccache/c++
-- Check for working CXX compiler: /usr/lib/ccache/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build
$ make
Scanning dependencies of target hello_headers
[ 33%] Building CXX object CMakeFiles/hello_headers.dir/src/Hello.cpp.o
[ 66%] Building CXX object CMakeFiles/hello_headers.dir/src/main.cpp.o
[100%] Linking CXX executable hello_headers
[100%] Built target hello_headers
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
在之前的例子中,当运行 make 命令时,输出只显示构建的状态。为了查看完整的输出以进行调试,可以在运行 make 命令时添加 VERBOSE=1 标志。
$ make clean
$ make VERBOSE=1
/usr/bin/cmake -S/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers -B/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build/CMakeFiles /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/depend
make[2]: Entering directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
cd /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build/CMakeFiles/hello_headers.dir/DependInfo.cmake --color=
make[2]: Leaving directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
make -f CMakeFiles/hello_headers.dir/build.make CMakeFiles/hello_headers.dir/build
make[2]: Entering directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
[ 33%] Building CXX object CMakeFiles/hello_headers.dir/src/Hello.cpp.o
/usr/lib/ccache/c++ -I/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/include -o CMakeFiles/hello_headers.dir/src/Hello.cpp.o -c /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/src/Hello.cpp
[ 66%] Building CXX object CMakeFiles/hello_headers.dir/src/main.cpp.o
/usr/lib/ccache/c++ -I/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/include -o CMakeFiles/hello_headers.dir/src/main.cpp.o -c /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/src/main.cpp
[100%] Linking CXX executable hello_headers
/usr/bin/cmake -E cmake_link_script CMakeFiles/hello_headers.dir/link.txt --verbose=1
/usr/lib/ccache/c++ CMakeFiles/hello_headers.dir/src/Hello.cpp.o CMakeFiles/hello_headers.dir/src/main.cpp.o -o hello_headers
make[2]: Leaving directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
[100%] Built target hello_headers
make[1]: Leaving directory '/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build'
/usr/bin/cmake -E cmake_progress_start /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/build/CMakeFiles 0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
下面是从中摘取的一些关键信息:
- 输出中的绿色部分就可以看到包含目录被添加到了 C++ 编译器命令中,这就是
target_include_directories的作用。 - 输出中的蓝色部分就可以看到链接过程,这就是源文件变量小节说的
add_excutable的作用。
/usr/lib/ccache/c++ -I/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/include -o CMakeFiles/hello_headers.dir/src/Hello.cpp.o -c /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/src/Hello.cpp
/usr/lib/ccache/c++ -I/home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/include -o CMakeFiles/hello_headers.dir/src/main.cpp.o -c /home/tartarus/cmake/cmake-examples/01-basic/B-hello-headers/src/main.cpp
/usr/lib/ccache/c++ CMakeFiles/hello_headers.dir/src/Hello.cpp.o CMakeFiles/hello_headers.dir/src/main.cpp.o -o hello_headers
( /usr/lib/ccache/c++ 是因为我使用了 ccache 加快编译的速度,它会保存目标文件,下次编译过程中如果没有发生改变就直接进行替换。)
提示
这里也可以使用 cmake --build . 命令来替换 make ,两者的区别在于:前者更加通用,无论你使用的构建工具是 make 还是 ninja 还是...,都可以运行该命令进行构建编译。后者只是在使用 make 作为构建工具的平台 (比如 Linux) 适用,但是看起来更加直观。