Intermediate sub-projects
# 介绍
在大的工程项目中,经常会需要在主工程中调用不同的子工程模块。
这个例子展示了如何在 CMake 主工程中包含多个子工程模块,并且使用顶层的 CMakeLists.txt 调用子文件夹中的子工程:
- sublibrary1 - A static library
- sublibrary2 - A header only library
- subbinary - A executable
本例的目录🌲:
├── CMakeLists.txt # Top level CMakeLists.txt
├── subbinary
│ ├── CMakeLists.txt # To make the executable
│ └── main.cpp # Source for the executable
├── sublibrary1
│ ├── CMakeLists.txt # To make a static library
│ ├── include
│ │ └── sublib1
│ │ └── sublib1.h
│ └── src
│ └── sublib1.cpp # Source for the static library
└── sublibrary2
├── CMakeLists.txt # To setup header only library
└── include
└── sublib2
└── sublib2.h
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
./CMakeLists.txt:
cmake_minimum_required (VERSION 3.5)
project(subprojects)
# Add sub directories
add_subdirectory(sublibrary1)
add_subdirectory(sublibrary2)
add_subdirectory(subbinary)
2
3
4
5
6
7
8
./subbinary/CMakeLists.txt:
project(subbinary)
# Create the executable
add_executable(${PROJECT_NAME} main.cpp)
# Link the static library from subproject1 using its alias sub::lib1
# Link the header only library from subproject2 using its alias sub::lib2
# This will cause the include directories for that target to be added to this project
target_link_libraries(${PROJECT_NAME}
sub::lib1
sub::lib2
)
2
3
4
5
6
7
8
9
10
11
12
./subbinary1/CMakeLists.txt:
# Set the project name
project (sublibrary1)
# Add a library with the above sources
add_library(${PROJECT_NAME} src/sublib1.cpp)
add_library(sub::lib1 ALIAS ${PROJECT_NAME})
target_include_directories( ${PROJECT_NAME}
PUBLIC ${PROJECT_SOURCE_DIR}/include
)
2
3
4
5
6
7
8
9
10
./subbinary2/CMakeLists.txt:
# Set the project name
project (sublibrary2)
add_library(${PROJECT_NAME} INTERFACE)
add_library(sub::lib2 ALIAS ${PROJECT_NAME})
target_include_directories(${PROJECT_NAME}
INTERFACE
${PROJECT_SOURCE_DIR}/include
)
2
3
4
5
6
7
8
9
10
在这个例子中,把每个项目的头文件移到了包含目录的一个子文件夹中,同时将目标包含 ( target_include_directories ) 设置为各源文件目录下的 include 文件夹。这是一个很好的做法,可以防止文件名冲突。因为我们所有源文件中包含一个文件的路径都会使用:
#include "sublib1/sublib1.h"
这也意味着,如果源文件被安装到了别人的电脑上,GCC 会从 /usr/local/include/sublib1/sublib1.h 处去查找该文件。(可以在 gcc 编译时使用 --verbose 查看查找路径)
# 概念理解
# 添加一个子文件夹 (Adding a Sub-Directories)
添加一个子文件夹进行构建。一个 CMakeLists.txt 可以通过如下的方式包含并且调用子文件夹下的 CMakeLists.txt :
add_subdirectory(sublibrary1)
add_subdirectory(sublibrary2) # 可以使用sublibrary1中的库构建自己
add_subdirectory(subbinary) # 可以使用sublibrary1和sublibrary2中的库构建自己
2
3
# 引用一个子项目文件夹 (Referencing Sub-Project Directories)
当一个项目使用 project() 创建后,CMake 将会为该项目生成一系列的变量:
| Variable | Info |
|---|---|
| PROJECT_NAME | The name of the project set by the current project(). |
| CMAKE_PROJECT_NAME | the name of the first project set by the project() command, i.e. the top level project. |
| PROJECT_SOURCE_DIR | The source director of the current project. |
| PROJECT_BINARY_DIR | The build directory for the current project. |
| name_SOURCE_DIR | The source directory of the project called "name". In this example the source directories created would be sublibrary1_SOURCE_DIR , sublibrary2_SOURCE_DIR , and subbinary_SOURCE_DIR |
| name_BINARY_DIR | The binary directory of the project called "name". In this example the binary directories created would be sublibrary1_BINARY_DIR , sublibrary2_BINARY_DIR , and subbinary_BINARY_DIR |
比如在主项目中可以使用以下变量确定子项目所在的文件夹:
${sublibrary1_SOURCE_DIR}
${sublibrary2_SOURCE_DIR}
2
# 只包含头文件的库 (Header only Libraries)
如果您有一个使用头文件库创建的库,CMake 支持在 add_library(${PROJECT_NAME} INTERFACE) 中使用可见性 INTERFACE 来创建一个没有可执行目标输出的库。
add_library(${PROJECT_NAME} INTERFACE) 的作用是创建一个库目标,并且该目标没有产生任何库,但是它的 INTERFACE_INCLUDE_DIRECTORIES 属性里有包含的头文件会传递给链接该库的其他所有目标。
关于可见性的讲解,请看 Basic 03-static-library
# 从子项目中引用库 (Referencing LIbraries from Sub-Projects)
如果一个子项目创建了库,那么在其他项目中就可以使用该库,使用方法为:
target_link_libraries(subbinary
PUBLIC
sublibrary1
)
2
3
4
或者,可以创建一个别名目标,使您可以在只读的地方中引用该目标。
创建目标别名的方法 (我们在 Basic 04-shared-library 也有具体讲过):
add_library(sublibrary2)
add_library(sub::lib2 ALIAS sublibrary2)
2
引用这个目标的方法:
target_link_libraries(subbinary
sub::lib2
)
2
3
# 包含子项目的头文件 (Include Directories from Sub-projects)
当我们在源文件总调用一个库时,通常都会包含头文件到源文件中。比如这个例子的 main.cpp 就包含了 sublib2/sublib2.h 和 sublib1/sublib1.h 两个头文件。
所以在 CMake V3 之前,我们调用库时,都需要在 CMakeLists.txt 中添加库的头文件路径。但是 CMake V3 后引入了 target_include_directories 使得头文件可以跟随着目标一起传递。
比如在这个例子中,因为目标 subbinary 使用 target_link_libraries 函数链接了目标 sublibrary1 和 sublibrary2 ,使得头文件 ${sublibrary1_SOURCE_DIR}/include and ${sublibrary2_SOURCE_DIR}/include 自动倍导入到 subbinary 的包含路径中。
关于可见性的讲解,请看 Basic 03-static-library。看完保证你能理解!
# 构建本例
$ 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/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build
$ make VERBOSE=1
/usr/bin/cmake -S/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic -B/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build --check-build-system CMakeFiles/Makefile.cmake 0
/usr/bin/cmake -E cmake_progress_start /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/CMakeFiles /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/CMakeFiles/progress.marks
make -f CMakeFiles/Makefile2 all
make[1]: Entering directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
make -f sublibrary1/CMakeFiles/sublibrary1.dir/build.make sublibrary1/CMakeFiles/sublibrary1.dir/depend
make[2]: Entering directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/sublibrary1 /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1 /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1/CMakeFiles/sublibrary1.dir/DependInfo.cmake --color=
Dependee "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1/CMakeFiles/sublibrary1.dir/DependInfo.cmake" is newer than depender "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1/CMakeFiles/sublibrary1.dir/depend.internal".
Dependee "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1/CMakeFiles/sublibrary1.dir/depend.internal".
Scanning dependencies of target sublibrary1
make[2]: Leaving directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
make -f sublibrary1/CMakeFiles/sublibrary1.dir/build.make sublibrary1/CMakeFiles/sublibrary1.dir/build
make[2]: Entering directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
[ 25%] Building CXX object sublibrary1/CMakeFiles/sublibrary1.dir/src/sublib1.cpp.o
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1 && /usr/lib/ccache/c++ -I/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/sublibrary1/include -o CMakeFiles/sublibrary1.dir/src/sublib1.cpp.o -c /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/sublibrary1/src/sublib1.cpp
[ 50%] Linking CXX static library libsublibrary1.a
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1 && /usr/bin/cmake -P CMakeFiles/sublibrary1.dir/cmake_clean_target.cmake
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/sublibrary1 && /usr/bin/cmake -E cmake_link_script CMakeFiles/sublibrary1.dir/link.txt --verbose=1
/usr/bin/ar qc libsublibrary1.a CMakeFiles/sublibrary1.dir/src/sublib1.cpp.o
/usr/bin/ranlib libsublibrary1.a
make[2]: Leaving directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
[ 50%] Built target sublibrary1
make -f subbinary/CMakeFiles/subbinary.dir/build.make subbinary/CMakeFiles/subbinary.dir/depend
make[2]: Entering directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build && /usr/bin/cmake -E cmake_depends "Unix Makefiles" /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/subbinary /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary/CMakeFiles/subbinary.dir/DependInfo.cmake --color=
Dependee "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary/CMakeFiles/subbinary.dir/DependInfo.cmake" is newer than depender "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary/CMakeFiles/subbinary.dir/depend.internal".
Dependee "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary/CMakeFiles/CMakeDirectoryInformation.cmake" is newer than depender "/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary/CMakeFiles/subbinary.dir/depend.internal".
Scanning dependencies of target subbinary
make[2]: Leaving directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
make -f subbinary/CMakeFiles/subbinary.dir/build.make subbinary/CMakeFiles/subbinary.dir/build
make[2]: Entering directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
[ 75%] Building CXX object subbinary/CMakeFiles/subbinary.dir/main.cpp.o
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary && /usr/lib/ccache/c++ -I/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/sublibrary1/include -I/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/sublibrary2/include -o CMakeFiles/subbinary.dir/main.cpp.o -c /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/subbinary/main.cpp
[100%] Linking CXX executable subbinary
cd /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build/subbinary && /usr/bin/cmake -E cmake_link_script CMakeFiles/subbinary.dir/link.txt --verbose=1
/usr/lib/ccache/c++ CMakeFiles/subbinary.dir/main.cpp.o -o subbinary ../sublibrary1/libsublibrary1.a
make[2]: Leaving directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
[100%] Built target subbinary
make[1]: Leaving directory '/home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/build'
/usr/bin/cmake -E cmake_progress_start /home/tartarus/workspace/cmake/cmake-examples/02-sub-projects/A-basic/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
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
从 make VERBOSE=1 的输出中可以看出,确实我们包含文件夹都出现在了编译器的 -I 选项中。大家可以仔细看一下。