CMake learning (7): CMake nesting

Blog reference from: Da Cing who loves programming: https://subingwen.cn/cmake/CMake-primer/ , for learning and sharing only

If 项目很大, or there are many source code directories in the project, if only one is used when managing the project through CMake CMakeLists.txt, then this file will be relatively complicated. One 化繁为简way is to 每个源码目录add a CMakeLists.txt file (the header file directory is not required), 这样每个文件都不会太复杂,而且更灵活,更容易维护.

Let's take a look at the following directory structure:

$ tree
.
├── build
├── calc
│   ├── add.cpp
│   ├── CMakeLists.txt
│   ├── div.cpp
│   ├── mult.cpp
│   └── sub.cpp
├── CMakeLists.txt
├── include
│   ├── calc.h
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
├── test1
│   ├── calc.cpp
│   └── CMakeLists.txt
└── test2
    ├── CMakeLists.txt
    └── sort.cpp

6 directories, 15 files

  • include 目录: header file directory
  • calc 目录: The addition, subtraction, multiplication, and division algorithms corresponding to the four source files in the directory
    • The corresponding header file is calc.h include in
  • sort 目录 : The two source files in the directory correspond to the insertion sort and selection sort algorithms
    • The corresponding header file is includesort.h in
  • test1 目录加、减、乘、除: Test directory, to test the algorithm implemented in calc
  • test2 目录: Test directory, to test the sorting algorithm implemented in sort

As can be seen from the directory structure, two executable files will eventually be generated, one related to calculators and the other related to sorting. If all operations are written in the top level CMakeLists.txt, then CMakeLists.txtthe content of this will be more complicated. Sometimes, in order to make these script files easier to maintain, we can split the content of the file by breaking them one by one, and process each source file CMakeLists.txtseparately

There are several source files in the calc directory, we add one in this directory CMakeListsto manage these source files. There are two sorted source files in the sort directory, we can sortalso add one in the directory CMakeLists.txt; for a test file in testand respectively, we add one in andtest2test1test2CMakeLists.txt

After adding these files, you need to CMakeLists.txtdefine them through the top level 若干个CMakeLists.txt形成父子关系. How to let the parent node bind these child nodes? Use add_subdirectoryto add these subdirectories. This way, the top-level CMakeLists knows about its children.

In each CMakeLists.txt, we can set some 变量. When we define some variables in the parent node, the variables defined in the parent node can be used by the child nodes ( 父节点定义的这些变量是全局变量), in 子节点中定义的变量称为局部变量. Parent nodes cannot use variables of child nodes, but variables defined by parent nodes can be used in child nodes.

1 Preparations

1.1 Node relationship

As we all know, the Linux directory is a tree structure, so the nested CMake is also a tree structure, 最顶层and the CMakeLists.txt is 根节点, 其次both 子节点. Therefore, we need to know some information about the scope of variables in the CMakeLists.txt file:

  • 根节点Variables in CMakeLists.txt are globally available
  • 父节点Variables in CMakeLists.txt can be used in child nodes
  • 子节点Variables in CMakeLists.txt can only be used in the current node

1.2 Add a subdirectory

Next, we also need to know how the relationship between parent and child nodes is established in CMake. Here we need to use a CMake command

add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])

Note source_dirthat the specified is where the child node CMakeLists.txt is located目录

  • source_dir: Specifies the location of the CMakeLists.txt source file and code file, which is actually the specified subdirectory
  • binary_dir: Specify the path of the output file, generally do not need to specify, just ignore it.
  • EXCLUDE_FROM_ALL: The targets under the sub-path will not be included in the ALL target of the parent path by default, and will also be excluded from the IDE project file. Users must explicitly build targets under subpaths.

In this way between CMakeLists.txt files父子关系就被构建出来了

2 Examples

Project directory structure image

├── CMakeLists.txt
├── calc
│   ├── add.cpp
│   ├── CMakeLists.txt
│   ├── div.cpp
│   ├── mult.cpp
│   └── sub.cpp
├── include
│   ├── calc.h
│   └── sort.h
├── sort
│   ├── CMakeLists.txt
│   ├── insert.cpp
│   └── select.cpp
├── test1
│   ├── calc.cpp
│   └── CMakeLists.txt
└── test2
    ├── CMakeLists.txt
    └── sort.cpp
  • First there is a root node, there is one in the root node CMakeList.txt, and then there are 4child nodes, respectively calc, sort, test1, test2, and these child nodes have their ownCMakeLists.txt
  • 分析:The role of CMakeLists in test1and test2is actually to generate them separately 一个可执行程序, assuming they are app1 and app2 respectively; for calcthe four source files in the directory add.cpp, div.cpp, mult.cpp, sub.cpp, in fact, it provides an interface for calc.cpp in test1, so we need to put it Generate the corresponding 库文件(dynamic/static library). insert.cppThe same is true for the source files in the sort directory select.cpp, which provide an interface for sort.cpp in test2, and we also generate library files (dynamic/static libraries)
  • 库文件的本质其实还是代码,只不过是从文本格式变成了二进制格式

How to choose whether to generate a static library or a dynamic library?

  • In the end is to generate a dynamic library or a static library? In fact, both of these are possible, 推荐: in 源文件非常多,推荐生成动态库;在源文件比较少的情况,推荐生成静态库.
  • If generated 动态库, its corresponding source code will not be in the packaged executable file, if it is, 静态库it will 打包go inside the executable file. If we finally require that the generated executable file is very small, then we can generate a dynamic library from these source files, and then when releasing the application, we must provide the dynamic library to the program user; There is no requirement for the size of the executable file, and it is required to be relatively easy to use. We can generate these source files into a static library. After the static library is generated, these source files will be packaged into an executable file. Just execute the program, no need to publish additional library files.
  • The usage 静态库的缺点is finally generated 可执行文件会比较大, and if you start multiple executable programs, it takes up 物理内存more than the dynamic library . Because if the program is using 动态库, no matter how many executables are started, yes 内存中动态库有且只有一份, yes 共享的.

2.1 root directory

根目录The CMakeLists.txt in the file can define some 全局变量, and the defined variables can be used for sub-nodes, such as: definition 头文件的路径, generation 可执行文件的名字, and sub-node generation 库文件的存储路径, etc., which can CMakeLists.txtbe defined first in the root directory. Later, directly use the variables defined by the root node in the child nodes to find the corresponding path.

The content of the CMakeLists.txt file in the root directory is as follows:

cmake_minimum_required(VERSION 3.0)
project(test)
# 定义变量
# 静态库生成的路径
set(LIB_PATH ${
    
    CMAKE_CURRENT_SOURCE_DIR}/lib) #lib子目录不需要自己去创建,如果lib目录没有,会自动创建
# 测试程序生成的路径
set(EXEC_PATH ${
    
    CMAKE_CURRENT_SOURCE_DIR}/bin) #bin 也会自动创建
# 头文件目录
set(HEAD_PATH ${
    
    CMAKE_CURRENT_SOURCE_DIR}/include)
# 静态库的名字
set(CALC_LIB calc)
set(SORT_LIB sort)
# 可执行程序的名字
set(APP_NAME_1 test1)
set(APP_NAME_2 test2)
# 添加子目录
add_subdirectory(calc)
add_subdirectory(sort)
add_subdirectory(test1)
add_subdirectory(test2)

There are two main things done in the file corresponding to the root node: 定义全局变量and 添加子目录.

  • Defined 全局变量主要是给子节点使用, the purpose is to improve the readability and maintainability of the CMakeLists.txt file in the child node, avoid redundancy and reduce the probability of business trips.
  • A total of four subdirectories have been added, and each subdirectory has a CMakeLists.txt file , so their parent-child relationship is determined.

2.2 CMakeList.txt in the subdirectory

CMakeList.txtAfter writing the top-level CMakeList.txt, files in the 4 subdirectories can now be written .

2.2.1 calc directory

The content of the CMakeLists.txt file in the calc directory is as follows:

cmake_minimum_required(VERSION 3.0)
project(CALCLIB)
aux_source_directory(./  SRC)          # ./   表示当前 cmakelist.txt所在目录下
include_directories(${
    
    HEAD_PATH})      # 定义依赖的头文件的搜索路径
set(LIBRARY_OUTPUT_PATH ${
    
    LIB_PATH})
add_library(${
    
    CALC_LIB} STATIC ${
    
    SRC})
  • Line 3 aux_source_directory: Search all source files in the current directory (calc directory)
  • Line 4 include_directories: Contains the header file path, HEAD_PATH is defined in the root node file
  • Line 5 set: Set the generated path of the library, LIB_PATH is defined in the root node file
  • Line 6 add_library: Generate a static library, the name of the static library CALC_LIBis defined in the root node file

2.2.2 sort directory

The sort directory is also a generated library file, which is actually similar to the writing method of CMakelists.txt in calc. The content of the CMakeLists.txt file in the sort directory is as follows:

cmake_minimum_required(VERSION 3.0)
project(SORTLIB)
aux_source_directory(./  SRC)
include_directories(${
    
    HEAD_PATH})
set(LIBRARY_OUTPUT_PATH ${
    
    LIB_PATH})
add_library(${
    
    SORT_LIB} SHARED ${
    
    SRC}) # 也可以生成静态库
  • Line 6 add_library: Generate a dynamic library, the dynamic library name SORT_LIB is defined in the root node file
  • The content in this file is similar to that in the calc node file, except that this time a dynamic library is generated.

When generating 库文件, this library can be 静态库or can be 动态库, and generally needs to be determined according to the actual situation. If generated 库比较大,建议将其制作成动态库.

2.2.3 test1 directory

test1 is generated 可执行文件, the executable depends on libcalc.a the static library generated above. It needs to 链接静态库be compiled to get the current executable program. The content of the CMakeLists.txt file in the test1 directory is as follows:

cmake_minimum_required(VERSION 3.0)
project(CALCTEST)
aux_source_directory(./ SRC)       # 搜索的还是当前cmakelist目录下的源文件
include_directories(${
    
    HEAD_PATH})
# include_directories(${
      
      HEAD_PATH})
link_directories(${
    
    LIB_PATH})
link_libraries(${
    
    CALC_LIB})
set(EXECUTABLE_OUTPUT_PATH ${
    
    EXEC_PATH})
add_executable(${
    
    APP_NAME_1} ${
    
    SRC})

Since the static library is generated by itself, just specify the static library name, cmake does not know the storage path of the static library, so you also need to specify the path of the static library that needs to be linked:link_directories

  • Line 4 include_directories: Specify the header file path, the HEAD_PATH variable is defined in the root node file
  • Line 6 link_libraries: Specifies 可执行程序要链接的静态库that the CALC_LIB variable is defined in the root node file
  • Line 7 set: Specify the path generated by the executable program, the EXEC_PATH variable is defined in the root node file
  • Line 8 add_executable: Generate an executable program, the APP_NAME_1 variable is defined in the root node file

The executable program here is linked to the static library, and finally the static library will be packaged into the executable program. After the executable program is started, the static library will be loaded into the memory.

2.2.4 test2 directory

Like test1, the CMakeLists in the test2 directory also generates an executable file, and the instructions in CMakelists.txt are basically the same. The content of the CMakeLists.txt file in the test2 directory is as follows:

cmake_minimum_required(VERSION 3.0)
project(SORTTEST)
aux_source_directory(./ SRC)
include_directories(${
    
    HEAD_PATH})
set(EXECUTABLE_OUTPUT_PATH ${
    
    EXEC_PATH})
link_directories(${
    
    LIB_PATH})
add_executable(${
    
    APP_NAME_2} ${
    
    SRC})
target_link_libraries(${
    
    APP_NAME_2} ${
    
    SORT_LIB})
  • The fourth line include_directories: contains the header file path, the HEAD_PATH variable is defined in the root node file
  • The fifth line set: specify the path generated by the executable program, the EXEC_PATH variable is defined in the root node file
  • Line 6 link_directories: Specify the path of the dynamic library to be linked by the executable program, the LIB_PATH variable is defined in the root node file
  • Line 7 add_executable: Generate an executable program, the APP_NAME_2 variable is defined in the root node file
  • The eighth line target_link_libraries: Specify the name of the dynamic library to be linked by the executable program, target_link_librarieswhich needs to be add_executableafter the command, because the executable file must be generated first, and then target_link_libraries can link the library file to the executable file

When generating an executable program, 动态库不会被打包到可执行程序内部。当可执行程序启动之后动态库也不会被加载到内存,只有可执行程序调用了动态库中的函数的时候,动态库才会被加载到内存中,且多个进程可以共用内存中的同一个动态库,所以动态库又叫共享库.

2.2 Build the project

After everything is ready, start building the project, enter the build directory of the root node directory, and execute cmakethe command as follows:

$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.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
-- Configuring done
-- Generating done
-- Build files have been written to: /home/robin/abc/cmake/calc/build

一些文件You can see that and are generated in the build directory 目录, as follows:

$ tree build -L 1     
build
├── calc                  # 目录
├── CMakeCache.txt        # 文件
├── CMakeFiles            # 目录
├── cmake_install.cmake   # 文件
├── Makefile              # 文件
├── sort                  # 目录
├── test1                 # 目录
└── test2                 # 目录

Then execute the make command in the build directory:
insert image description here
the following information can be obtained through the above figure:

  • libThe static library is generated in a directory in the root of the project libcalc.a
  • libThe dynamic library is generated in the directory of the root of the projectlibsort.so
  • The executable program is generated in binthe directory test1
  • binThe executable program is generated in the directory of the root directory of the projecttest2

Finally, let’s take a look at whether the files mentioned above are actually generated into the corresponding directories:

$ tree bin/ lib/

bin/
├── test1
└── test2
lib/
├── libcalc.a
└── libsort.so

At this point, the project is built.

In the project, if a module in the program is made into a dynamic library or a static library 并且在CMakeLists.txt 中指定了库的输出目录, and then other modules need to load the generated library file, it can be used directly at this time, 如果没有指定库的输出路径或者需要直接加载外部提供的库文件,此时就需要使用 link_directories 将库文件路径指定出来.

Guess you like

Origin blog.csdn.net/weixin_38346042/article/details/131158601