tartarus's bolg tartarus's bolg
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • Linux and Unix Guide
  • CMake
  • gcc
  • gdb
  • bash
  • GNU Make
  • DDCA-ETH
  • CS106L
  • CS144
  • NJU PA
  • NJU OS(jyy)
  • C
  • C++
  • Python
  • reveal-md
  • LaTex
  • Paper Reading
  • TBD
  • pdb

  • make

  • cmake

    • Introduction
    • Basic Intro
    • Basic 01-hello-cmake
    • Basic 02-hello-headers
    • Basic 03-static-library
    • Basic 04-shared-library
    • Basic 05-installing
    • Basic 06-build-type
    • Basic 07-complie-flags
    • Basic 08-third-party-library
    • Basic 09-compiling-with-clang
    • Basic 10-building-with-ninjia
    • Basic 11-cpp-standard
    • Intermediate sub-projects
      • 介绍
      • 概念理解
        • 添加一个子文件夹(Adding a Sub-Directories)
        • 引用一个子项目文件夹(Referencing Sub-Project Directories)
        • 只包含头文件的库(Header only Libraries)
        • 从子项目中引用库(Referencing LIbraries from Sub-Projects)
        • 包含子项目的头文件(Include Directories from Sub-projects)
      • 构建本例
    • Intermediate static-analysis
    • packge-management 04-conan
    • packge-management 05-vcpkg
    • Offical Tutorial(未完成)
  • Linux and Unix

  • Basic_Software
  • cmake
tartarus
2023-04-24
目录

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
1
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)
1
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
)
1
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
)
1
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
)
1
2
3
4
5
6
7
8
9
10

在这个例子中,把每个项目的头文件移到了包含目录的一个子文件夹中,同时将目标包含 ( target_include_directories ) 设置为各源文件目录下的 include 文件夹。这是一个很好的做法,可以防止文件名冲突。因为我们所有源文件中包含一个文件的路径都会使用:

#include "sublib1/sublib1.h"
1

这也意味着,如果源文件被安装到了别人的电脑上,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中的库构建自己
1
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}
1
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
)
1
2
3
4

或者,可以创建一个别名目标,使您可以在只读的地方中引用该目标。

创建目标别名的方法 (我们在 Basic 04-shared-library 也有具体讲过):

add_library(sublibrary2)
add_library(sub::lib2 ALIAS sublibrary2)
1
2

引用这个目标的方法:

target_link_libraries(subbinary
    sub::lib2
)
1
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
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
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 选项中。大家可以仔细看一下。

上次更新: 12/27/2023, 8:55:47 AM
Basic 11-cpp-standard
Intermediate static-analysis

← Basic 11-cpp-standard Intermediate static-analysis→

Theme by Vdoing | Copyright © 2023-2023 tartarus | CC BY-NC-SA 4.0
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式