CMake nanny-level tutorial (Part 1)

Reprint

Author: Su Bingqu

Link: https://subingwen.cn/cmake/CMake-advanced/

Source: DaBing of iProgramming. The copyright belongs to the author. For commercial reprinting, please contact the author for authorization. For non-commercial reprinting, please indicate the source.


ColorMarkDown

Insert image description here

Gitee: CMake nanny-level tutorial - Big C who loves programming


1.CMake overview

CMake is a project build tool and is cross-platform. Regarding project construction, we are also familiar with Makefile (project construction through the make command). Most IDE software integrates make, such as: VS's nmake, GNU make under Linux, Qt's qmake, etc. If you write it yourself Makefile, you will find that makefile usually depends on the current compilation platform, and the workload of writing makefile is relatively large, and it is easy to make mistakes when resolving dependencies.

CMake can exactly solve the above problems. It allows developers to specify the compilation process of the entire project. According to the compilation platform, the 自动生成本地化的Makefile和工程文件final user only needs to makecompile. Therefore, CMake can be regarded as a tool that automatically generates Makefiles. Its compilation process is as follows picture:

image-20230309130644912

  • makefileThe blue dashed line represents the process of building a project using
  • The solid red line represents cmakethe process of building a project using

After introducing the role of CMake, let's summarize its advantages:

  • Cross-platform

  • Ability to manage large projects

  • Simplify the compilation build process and compilation process

  • Extensible: You can write modules with specific functions for cmake to expand cmake functions

2. Use of CMake

CMakeSupports uppercase, lowercase, and mixed case commands. If CMakeLists.txtthe tool you use when writing a file has a corresponding command prompt, then just leave it as it is and don't pay too much attention to it.

2.1 Notes

2.1.1 Comment lines

CMakeUse , #which 行注释can be placed anywhere.

# 这是一个 CMakeLists.txt 文件
cmake_minimum_required(VERSION 3.0.0)

2.1.2 Comment block

CMakeUse #[[ ]]the form 块注释.

#[[ 这是一个 CMakeLists.txt 文件。
这是一个 CMakeLists.txt 文件
这是一个 CMakeLists.txt 文件]]
cmake_minimum_required(VERSION 3.0.0)

2.1 Only source files

2.1.1 Sharing a room

  1. Preparations, in order to facilitate testing, I have prepared several test files on my local computer

    • add.c

      #include <stdio.h>
      #include "head.h"
      
      int add(int a, int b)
      {
          return a+b;
      }
      
    • sub.c

      #include <stdio.h>
      #include "head.h"
      
      // 你好
      int subtract(int a, int b)
      {
          return a-b;
      }
      
    • a lot. c

      #include <stdio.h>
      #include "head.h"
      
      int multiply(int a, int b)
      {
          return a*b;
      }
      
    • div.c

      #include <stdio.h>
      #include "head.h"
      
      double divide(int a, int b)
      {
          return (double)a/b;
      }
      
    • head.h

      #ifndef _HEAD_H
      #define _HEAD_H
      // 加法
      int add(int a, int b);
      // 减法
      int subtract(int a, int b);
      // 乘法
      int multiply(int a, int b);
      // 除法
      double divide(int a, int b);
      #endif
      
    • main.c

      #include <stdio.h>
      #include "head.h"
      
      int main()
      {
          int a = 20;
          int b = 12;
          printf("a = %d, b = %d\n", a, b);
          printf("a + b = %d\n", add(a, b));
          printf("a - b = %d\n", subtract(a, b));
          printf("a * b = %d\n", multiply(a, b));
          printf("a / b = %f\n", divide(a, b));
          return 0;
      }
      
  2. The directory structure of the above files is as follows:

    $ tree
    .
    ├── add.c
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
  3. add CMakeLists.txtfile

    Add a new file CMakeLists.txt in the directory where the above source file is located. The content of the file is as follows:

    cmake_minimum_required(VERSION 3.0)
    project(CALC)
    add_executable(app add.c div.c main.c mult.c sub.c)
    

    Next, we will introduce the three commands added in the CMakeLists.txt file in turn:

    • cmake_minimum_required:Specify the minimum version of cmake used

      • Optional, not required, there may be a warning if not added
    • project: Define the project name, and specify the project version, project description, web homepage address, and supported languages ​​(all languages ​​are supported by default). If you do not need these, you can ignore them. You only need to specify the project name.

      # PROJECT 指令的语法是:
      project(<PROJECT-NAME> [<language-name>...])
      project(<PROJECT-NAME>
             [VERSION <major>[.<minor>[.<patch>[.<tweak>]]]]
             [DESCRIPTION <project-description-string>]
             [HOMEPAGE_URL <url-string>]
             [LANGUAGES <language-name>...])
      
    • add_executable: Defining the project will generate an executable program

      add_executable(可执行程序名 源文件名称)
      
      • The executable program name here has projectnothing to do with the project name in

      • The source file name can be one or multiple, if there are multiple available spaces or ;intervals

        # 样式1
        add_executable(app add.c div.c main.c mult.c sub.c)
        # 样式2
        add_executable(app add.c;div.c;main.c;mult.c;sub.c)
        
  4. execute CMakecommand

    Everything is ready. After editing the CMakeLists.txt file, you can execute cmakethe command.

    # cmake 命令原型
    $ cmake CMakeLists.txt文件所在路径
    
    $ tree
    .
    ├── add.c
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── mult.c
    └── sub.c
    
    0 directories, 7 files
    robin@OS:~/Linux/3Day/calc$ cmake .
    

    When the command is executed cmake, the commands in CMakeLists.txt will be executed, so be sure to make sure there cmakeare no errors when specifying the path for the command.

    After executing the command, check whether there are more files in the directory where the source file is located:

    $ tree -L 1
    .
    ├── add.c
    ├── CMakeCache.txt            #  new add file
    ├── CMakeFiles                   #  new add dir
    ├── cmake_install.cmake     #  new add file
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile                        # new add file
    ├── mult.c
    └── sub.c
    

    makefileWe can see that a file is generated in the corresponding directory , and then execute makethe command at this time to build the project to obtain the required executable program.

    $ make
    Scanning dependencies of target app
    [ 16%] Building C object CMakeFiles/app.dir/add.c.o
    [ 33%] Building C object CMakeFiles/app.dir/div.c.o
    [ 50%] Building C object CMakeFiles/app.dir/main.c.o
    [ 66%] Building C object CMakeFiles/app.dir/mult.c.o
    [ 83%] Building C object CMakeFiles/app.dir/sub.c.o
    [100%] Linking C executable app
    [100%] Built target app
    
    # 查看可执行程序是否已经生成
    $ tree -L 1
    .
    ├── add.c
    ├── app					# 生成的可执行程序
    ├── CMakeCache.txt
    ├── CMakeFiles
    ├── cmake_install.cmake
    ├── CMakeLists.txt
    ├── div.c
    ├── head.h
    ├── main.c
    ├── Makefile
    ├── mult.c
    └── sub.c
    

    Finally, the executable program appis compiled (the name is CMakeLists.txtspecified in).

2.1.2 VIP private room

As can be seen from the above example, if you CMakeLists.txtexecute cmakethe command in the directory where the file is located, some directories and files will be generated ( 包括 makefile 文件). If you makefile文件execute makethe command again, the program will also generate some intermediate files and an executable file during the compilation process. This will cause the entire project directory to look confusing and difficult to manage and maintain. At this time, we can put the generated files that have nothing to do with the project source code into a corresponding directory. For example, name this directory build:

$ mkdir build
$ cd build
$ 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/Linux/build

Now cmakethe command is buildexecuted in the directory, but CMakeLists.txtthe file is buildin the directory one level above the directory, so cmakethe path specified after the command ..is the directory one level above the current directory.

When the command is executed, a file buildwill be generated in the directorymakefile

$ tree build -L 1
build
├── CMakeCache.txt
├── CMakeFiles
├── cmake_install.cmake
└── Makefile

1 directory, 3 files

In this way, you can buildexecute makecommands in the directory to compile the project, and the generated related files will naturally be stored in buildthe directory. In this way, all the files passed cmakeand makegenerated are completely isolated from the project source files, and everyone goes back to their own homes to find their own mother.

2.2 Private order

2.2.1 Define variables

In the above example, a total of 5 source files are provided. Assuming that these five source files need to be used repeatedly, it is really troublesome to write out their names directly every time. At this time, we need to define a variable to refer to the file. The string corresponding to the name is stored and needs to be used to define variables in cmake set.

# SET 指令的语法是:
# [] 中的参数为可选项, 如不需要可以不写
SET(VAR [VALUE] [CACHE TYPE DOCSTRING [FORCE]])
  • VAR:variable name
  • VALUE:variable
# 方式1: 各个源文件之间使用空格间隔
# set(SRC_LIST add.c  div.c   main.c  mult.c  sub.c)

# 方式2: 各个源文件之间使用分号 ; 间隔
set(SRC_LIST add.c;div.c;main.c;mult.c;sub.c)
add_executable(app  ${SRC_LIST})

2.2.2 Specify the C++ standard used

When writing a C++ program, you may use new features such as C++11, C++14, C++17, C++20, etc. Then you need to determine which one to use in the compilation command when compiling. standard:

$ g++ *.cpp -std=c++11 -o app

In the above example, the parameters -std=c++11are used to specify that the program should be compiled using the c++11 standard, and the C++ standard corresponds to a macro called DCMAKE_CXX_STANDARD. There are two ways to specify the C++ standard in CMake:

  1. Specified through the set command in CMakeLists.txt

    #增加-std=c++11
    set(CMAKE_CXX_STANDARD 11)
    #增加-std=c++14
    set(CMAKE_CXX_STANDARD 14)
    #增加-std=c++17
    set(CMAKE_CXX_STANDARD 17)
    
  2. Specify the value of this macro when executing the cmake command

    #增加-std=c++11
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=11
    #增加-std=c++14
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=14
    #增加-std=c++17
    cmake CMakeLists.txt文件路径 -DCMAKE_CXX_STANDARD=17
    

    In the above example, the path after CMake needs to be modified as appropriate according to the actual situation.

2.2.3 Specify the output path

Specify the output path of the executable program in CMake, which also corresponds to a macro called EXECUTABLE_OUTPUT_PATH, and its value is still setset by the command:

set(HOME /home/robin/Linux/Sort)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin)
  • The first line: define a variable to store an absolute path
  • The second line: Set the spliced ​​path value to EXECUTABLE_OUTPUT_PATHthe macro
    • If the subdirectory in this path does not exist, it will be automatically generated, no need to create it manually

Since the executable program is obtained based on the makefile generated by the cmake command and then executed by the make command, if the relative path ./xxx/xxx is used when specifying the executable program generation path here, then ./ in this path will Corresponding to the directory where the makefile file is located.

2.3 Search files

If there are many source files in a project, CMakeLists.txtit is impossible to list each file in the project directory one by one when writing the file. This is too troublesome and unrealistic. Therefore, CMake provides us with commands to search for files, and you can use aux_source_directorycommands or filecommands.

2.3.1 Method 1

Use the command in CMake aux_source_directoryto search under a certain path 所有源文件. The command format is:

aux_source_directory(< dir > < variable >)
  • dir: Directory to search
  • variable: dirStore the list of source files searched from the directory into this variable
cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
# 搜索 src 目录下的源文件
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src SRC_LIST)
add_executable(app  ${SRC_LIST})

2.3.2 Method 2

If there are many source files in a project, CMakeLists.txtit is impossible to list each file in the project directory one by one when writing the file. This is too troublesome. So, in CMake, we are provided with a command to search for files, and he is file(当然,除了搜索以外通过 file 还可以做其他事情).

file(GLOB/GLOB_RECURSE 变量名 要搜索的文件路径和文件类型)
  • GLOB: Generate a list of all file names that meet the conditions searched in the specified directory and store it in a variable.
  • GLOB_RECURSE: Search the specified directory recursively, generate a list of the searched file names that meet the conditions, and store it in a variable.

Search all source files in the src directory of the current directory and store them in variables

file(GLOB MAIN_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB MAIN_HEAD ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
  • The CMAKE_CURRENT_SOURCE_DIR macro indicates the path where the currently accessed CMakeLists.txt file is located.

  • You can add double quotes or not for the file path and type to be searched:

    file(GLOB MAIN_HEAD "${CMAKE_CURRENT_SOURCE_DIR}/src/*.h")
    

2.4 Include header files

When compiling project source files, it is often necessary to specify the header file path corresponding to the source file, so as to ensure that the compiler can find these header files during the compilation process and successfully pass the compilation. Setting the directory to be included in CMake is also very simple. It can be done with one command, which is include_directories:

include_directories(headpath)

For example, there are several source files, and their directory structure is as follows:

$ tree
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
└── src
    ├── add.cpp
    ├── div.cpp
    ├── main.cpp
    ├── mult.cpp
    └── sub.cpp

3 directories, 7 files

CMakeLists.txtThe content of the file is as follows:

cmake_minimum_required(VERSION 3.0)
project(CALC)
set(CMAKE_CXX_STANDARD 11)
set(HOME /home/robin/Linux/calc)
set(EXECUTABLE_OUTPUT_PATH ${HOME}/bin/)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
add_executable(app  ${SRC_LIST})

Among them, the sixth line specifies the path to the header file, and PROJECT_SOURCE_DIRthe value corresponding to the macro is the directory that follows when we use the cmake command, which is usually the root directory of the project.

2.5 Make dynamic library or static library

Sometimes the source code we write does not need to be compiled to generate executable programs, but to generate some static libraries or dynamic libraries for third-party use. The following explains how to generate these two types of library files in cmake.

2.5.1 Making a static library

  • STATIC

In cmake, if you want to make a static library, the commands you need to use are as follows:

add_library(库名称 STATIC 源文件1 [源文件2] ...) 

In Linux, the static library name is divided into three parts: lib+ 库名字+ .a. Here you only need to specify the name of the library. The other two parts will be automatically filled in when the file is generated.

Although the library name in Windows is different from the Linux format, you only need to specify the name.

srcThere is a directory below. The source files in the directory need to be compiled into a static library and then used:

$ tree
.
├── build
├── CMakeLists.txt
├── include               # 头文件目录
│   └── head.h
├── main.cpp            # 用于测试的源文件
└── src                      # 源文件目录
    ├── add.cpp
    ├── div.cpp
    ├── mult.cpp
    └── sub.cpp

According to the above directory structure, CMakeLists.txtthe file can be written like this:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc STATIC ${SRC_LIST})

This will eventually generate the corresponding static library file libcalc.a.

2.5.2 Make a dynamic library

  • SHARED

In cmake, if you want to make a dynamic library, the commands you need to use are as follows:

add_library(库名称 SHARED 源文件1 [源文件2] ...) 

In Linux, the name of the dynamic library is divided into three parts: lib+ 库名字+ .so, here you only need to specify the name of the library, and the other two parts will be automatically filled when the file is generated.

Although the library name in Windows is different from the Linux format, you only need to specify the name.

According to the above directory structure, CMakeLists.txtthe file can be written like this:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
add_library(calc SHARED ${SRC_LIST})

This will eventually generate the corresponding dynamic library file libcalc.so.

2.5.3 Specify the output path

Way 1 - for dynamic libraries

For generated library files, the output path can be specified in the same way as for executable programs. 由于在Linux下生成的动态库默认是有执行权限的, so you can specify the directory it generates in the same way as generating an executable program:

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库生成路径
set(EXECUTABLE_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
add_library(calc SHARED ${SRC_LIST})

For this method, you actually set a path setfor the macro through a command , and this path is the path where the executable file is generated.EXECUTABLE_OUTPUT_PATH

Method 2 - applies to both

EXECUTABLE_OUTPUT_PATHSince the static library generated under Linux does not have executable permissions by default, macros should not be used when specifying the path generated by the static library, but should be used LIBRARY_OUTPUT_PATH. This macro is applicable to both static library files and dynamic library files.

cmake_minimum_required(VERSION 3.0)
project(CALC)
include_directories(${PROJECT_SOURCE_DIR}/include)
file(GLOB SRC_LIST "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp")
# 设置动态库/静态库生成路径
set(LIBRARY_OUTPUT_PATH ${PROJECT_SOURCE_DIR}/lib)
# 生成动态库
#add_library(calc SHARED ${SRC_LIST})
# 生成静态库
add_library(calc STATIC ${SRC_LIST})

2.6 Include library files

In the process of writing programs, some dynamic libraries provided by the system or dynamic libraries or static library files made by ourselves may be used. cmake also provides us with related commands to load dynamic libraries.

2.6.1 Link static library

src
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

Now we compile the above srcdirectory add.cpp、div.cpp、mult.cpp、sub.cppinto a static library file libcalc.a.

Make and use static link libraries through commands

The test directory structure is as follows:

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h
├── lib
│   └── libcalc.a     # 制作出的静态库的名字
└── src
    └── main.cpp

4 directories, 4 files

In cmake, the command to link the static library is as follows:

link_libraries(<static lib> [<static lib>...])
  • Parameter 1 : Specify the name of the static library to be linked
    • Can be full namelibxxx.a
    • It can also be the name after pinching the head ( lib) and removing the tail ( ).axxx
  • Parameter 2-N : the name of other static libraries to be linked

If the static library is not provided by the system (make it yourself or use a static library provided by a third party), the static library may not be found. In this case, you can also specify the path of the static library:

link_directories(<lib path>)

In this way, the modified CMakeLists.txtfile content is as follows:

cmake_minimum_required(VERSION 3.0)
project(CALC)
# 搜索指定目录下源文件
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
# 包含头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 包含静态库路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 链接静态库
link_libraries(calc)
add_executable(app ${SRC_LIST})

After adding the 8th line of code, you can find the static library according to the path specified by the parameter.

2.6.2 Linking dynamic libraries

In the process of programming, in addition to introducing static libraries into the project, many times some standard or some dynamic libraries provided by third parties are also used. Regarding the production and use of dynamic libraries and how to load them in memory, static libraries are all Different, I won’t go into details here. If you have any doubts, please refer to Linux static library and dynamic library.

cmakeThe command to link the dynamic library in is as follows :

target_link_libraries(
    <target> 
    <PRIVATE|PUBLIC|INTERFACE> <item>... 
    [<PRIVATE|PUBLIC|INTERFACE> <item>...]...) 
  • target : Specify the name of the file to load the dynamic library

    • The file may be a source file
    • This file may be a dynamic library file
    • The file may be an executable file
  • PRIVATE|PUBLIC|INTERFACE : The access rights of the dynamic library, the default isPUBLIC

    • If there are no dependencies between the various dynamic libraries, there is no need to make any settings. There is no difference between the three. Generally, there is no need to specify, just use the default PUBLIC.

    • 动态库的链接具有传递性, if dynamic library A links to dynamic libraries B and C, and dynamic library D links to dynamic library A, then dynamic library D is equivalent to also linking to dynamic libraries B and C, and can use the methods defined in dynamic libraries B and C. .

      target_link_libraries(A B C)
      target_link_libraries(D A)
      
      • PUBLIC: The library behind public will be linked to the previous target, and the symbols inside will also be exported and provided to third parties.

      • PRIVATE: The library behind private is only linked to the previous target and terminated. The third party cannot detect which library you have adjusted.

      • INTERFACE: Libraries introduced after the interface will not be linked to the previous target, only symbols will be exported.

Link system dynamic library

The linking of dynamic libraries and static libraries is completely different:

  • The static library will be packaged into the executable program during the linking phase of generating the executable program, so the executable program starts, and the static library is loaded into the memory.
  • The dynamic library will not be packaged into the executable program during the linking phase of generating the executable program. The dynamic library will only be loaded into the memory when the executable program is started and the functions in the dynamic library are called.

Therefore, cmakewhen specifying the dynamic library to be linked in,应该将命令写到生成了可执行文件之后:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 添加并指定最终生成的可执行程序名
add_executable(app ${SRC_LIST})
# 指定可执行程序要链接的动态库名字
target_link_libraries(app pthread) 

in target_link_libraries(app pthread):

  • app:Corresponds to the name of the finally generated executable program

  • pthread: This is the dynamic library to be loaded by the executable program. This library is a thread library provided by the system. Its full name is libpthread.sogenerally truncated (lib) and tailed (.so) when specified.

Link third-party dynamic library

Now, a dynamic library has been generated by myself, and the corresponding directory structure is as follows:

$ tree 
.
├── build
├── CMakeLists.txt
├── include
│   └── head.h            # 动态库对应的头文件
├── lib
│   └── libcalc.so        # 自己制作的动态库文件
└── main.cpp              # 测试用的源文件

3 directories, 4 files 

main.cppAssuming that both the self-made dynamic library libcalc.soand the thread library provided by the system are used in the test file , CMakeLists.txtthe file can be written as follows:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
include_directories(${PROJECT_SOURCE_DIR}/include)
add_executable(app ${SRC_LIST})
target_link_libraries(app pthread calc) 

In the sixth line , it is the name of the dynamic library to be linked by pthread、calcthe executable program . appWhen the executable program appis generated and executed, the following error message will be prompted:

$ ./app 
./app: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory 

This is because after the executable program starts, it loads calcthe dynamic library, but I don’t know where the dynamic library is placed to solve the problem that the dynamic library cannot be loaded , so the loading fails. In CMake, you can generate the executable program before , use the command to specify the location of the dynamic library to be linked. This command is also used to specify the location of the static library:

link_directories(path)

So the modified CMakeLists.txtfile should look like this:

cmake_minimum_required(VERSION 3.0)
project(TEST)
file(GLOB SRC_LIST ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)
# 指定源文件或者动态库对应的头文件路径
include_directories(${PROJECT_SOURCE_DIR}/include)
# 指定要链接的动态库的路径
link_directories(${PROJECT_SOURCE_DIR}/lib)
# 添加并生成一个可执行程序
add_executable(app ${SRC_LIST})
# 指定要链接的动态库
target_link_libraries(app pthread calc) 

After link_directoriesspecifying the path of the dynamic library, when executing the generated executable program, there will be no problem that the dynamic library cannot be found.

Reminder: Use the target_link_libraries command to link dynamic libraries or static library files.

2.7 Log

A message can be displayed by the user in CMake, the name of the command is message:

message([STATUS|WARNING|AUTHOR_WARNING|FATAL_ERROR|SEND_ERROR] "message to display" ...)
  • (无): important news
  • STATUS: non-important news
  • WARNING: CMake warning, execution will continue
  • AUTHOR_WARNING: CMake warning (dev), execution will continue
  • SEND_ERROR: CMake error, continue execution, but skip the generation step
  • FATAL_ERROR: CMake error, terminate all processing

CMake's command line tool displays messages on stdout STATUSand all other messages on stderr. CMake's GUI displays all messages in its log area.

The text of CMake warning and error messages is displayed using a simple markup language. The text is not indented, lines that exceed the length wrap, and paragraphs are separated by newlines.

# 输出一般日志信息
message(STATUS "source path: ${PROJECT_SOURCE_DIR}")
# 输出警告信息
message(WARNING "source path: ${PROJECT_SOURCE_DIR}")
# 输出错误信息
message(FATAL_ERROR "source path: ${PROJECT_SOURCE_DIR}") 

2.8 Variable operations

2.8.1 Addition

Sometimes the source files in the project are not necessarily in the same directory, but these source files eventually need to be compiled together to generate the final executable file or library file. If we filesearch the source files in each directory through commands, we need to perform a string splicing operation at the end. You can use setcommands or commands for string splicing list.

Use set splicing

If set is used for string splicing, the corresponding command format is as follows:

set(变量名1 ${变量名1} ${变量名2} ...)

The above command actually concatenates all the strings starting from the second parameter and finally stores the result in the first parameter. If there is original data in the first parameter, the original data will be overwritten.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
set(SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

Use list splicing

If you use list for string splicing, the corresponding command format is as follows:

list(APPEND <list> [<element> ...])

listThe function of the command is setmore powerful than that. String concatenation is only one of its functions, so we need to specify the operation we want to do in the position of its first parameter, which APPENDmeans adding data, and the following parameters setare the same.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/src1/*.cpp)
file(GLOB SRC_2 ${PROJECT_SOURCE_DIR}/src2/*.cpp)
# 追加(拼接)
list(APPEND SRC_1 ${SRC_1} ${SRC_2} ${TEMP})
message(STATUS "message: ${SRC_1}") 

In CMake, setyou can create one using the command list. A listis internally a 分号;group of strings divided by. For example, set(var a b c d e)the command will create a list:a;b;c;d;e, but end up printing the variable value abcde.

set(tmp1 a;b;c;d;e)
set(tmp2 a b c d e)
message(${tmp1})
message(${tmp2})

Output result:

abcde
abcde

2.8.2 String removal

fileWe get all the source files in a directory by searching a directory, but some of the source files are not what we need, such as:

$ tree
.
├── add.cpp
├── div.cpp
├── main.cpp
├── mult.cpp
└── sub.cpp

0 directories, 5 files

There are five source files in the current directory, one of which main.cppis a test file. If we want to generate a dynamic library from the calculator-related source files for others to use, we only need add.cpp、div.cp、mult.cpp、sub.cppthese four source files. At this point, it is necessary main.cppto remove the searched data. To achieve this function, you can also uselist

list(REMOVE_ITEM <list> <value> [<value> ...])

Through the above command prototype, you can see that deleting and appending data are similar, except that the first parameter has changed REMOVE_ITEM.

cmake_minimum_required(VERSION 3.0)
project(TEST)
set(TEMP "hello,world")
file(GLOB SRC_1 ${PROJECT_SOURCE_DIR}/*.cpp)
# 移除前日志
message(STATUS "message: ${SRC_1}")
# 移除 main.cpp
list(REMOVE_ITEM SRC_1 ${PROJECT_SOURCE_DIR}/main.cpp)
# 移除后日志
message(STATUS "message: ${SRC_1}") 

As you can see, it is enough 第8行to specify the name of the file to be removed . listBut be sure to note that when searching for source files through the file command, you get the absolute path of the file (the path corresponding to each file in the list is an item, and they are all absolute paths), so you must also remove it when removing The absolute path of the file must be specified, otherwise the removal operation will not succeed.

There are other functions about listcommands, but they are not commonly used, so I won’t give examples one by one here.

  1. Get the length of list.

    list(LENGTH <list> <output variable>)
    
    • LENGTH:Subcommand LENGTH is used to read the length of the list

    • <list>: List of current operations

    • <output variable>: Newly created variable used to store the length of the list.

  2. Read the element at the specified index in the list. Multiple indexes can be specified.

    list(GET <list> <element index> [<element index> ...] <output variable>)
    
    • <list>: List of current operations

    • <element index>: Index of list element

      • Numbering starts from 0, and the element with index 0 is the first element in the list;
      • The index can also be a negative number, -1which means the last element of the list, -2which means the second-to-last element of the list, and so on.
      • When the index (whether positive or negative) exceeds the length of the list, the operation will report an error
    • <output variable>: The newly created variable stores the return result of the specified index element, which is also a list.

  3. Join the elements in the list with a connector (string) to form a string

    list (JOIN <list> <glue> <output variable>)
    
    • <list>: List of current operations

    • <glue>:Specified connector (string)

    • <output variable>: Newly created variable to store the returned string

  4. Finds whether the specified element exists in the list, if not found, returns -1

    list(FIND <list> <value> <output variable>)
    
    • <list>: List of current operations

    • <value>: The element that needs to be searched in the list

    • <output variable>: newly created variable

      • If the list <list>exists <value>, then return <value>the index in the list

      • Returns -1 if not found.

  5. Append elements to list

    list (APPEND <list> [<element> ...])
    
  6. Insert several elements at the specified position in the list

    list(INSERT <list> <element_index> <element> [<element> ...])
    
  7. Insert an element into the list at index 0

    list (PREPEND <list> [<element> ...])
    
  8. Remove the last element from the list

    list (POP_BACK <list> [<out-var>...])
    
  9. Remove the first element from the list

    list (POP_FRONT <list> [<out-var>...])
    
  10. Removes the specified element from the list

    list (REMOVE_ITEM <list> <value> [<value> ...])
    
  11. Removes the element at the specified index from the list

    list (REMOVE_AT <list> <index> [<index> ...])
    
  12. Remove duplicate elements from list

    list (REMOVE_DUPLICATES <list>)
    
  13. List flipping

    list(REVERSE <list>)
    
  14. list sort

    list (SORT <list> [COMPARE <compare>] [CASE <case>] [ORDER <order>])
    
    • COMPARE: Specify the sorting method. The following values ​​are available:

      • STRING: Sort alphabetically, which is the default sorting method
      • FILE_BASENAME: If it is a series of path names, basename will be used for sorting
      • NATURAL: Sort using natural number order
    • CASE: Indicate whether it is case sensitive. The following values ​​are available:

      • SENSITIVE: Sort in a case-sensitive manner, which is the default value
      • INSENSITIVE: Sort in a case-insensitive manner
    • ORDER: Specify the sorting order. The following values ​​are available:

      • ASCENDING: Sort in ascending order, default value

      • DESCENDING: Sort in descending order

2.9 Macro definition

When testing the program, we can add some macro definitions to the code and use these macros to control whether the codes take effect, as shown below:

#include <stdio.h>
#define NUMBER  3

int main()
{
    int a = 10;
#ifdef DEBUG
    printf("我是一个程序猿, 我不会爬树...\n");
#endif
    for(int i=0; i<NUMBER; ++i)
    {
        printf("hello, GCC!!!\n");
    }
    return 0;
} 

The macro is judged on the seventh line of the program DEBUG. If the macro is defined, the log output will be performed on the eighth line. If the macro is not defined, the eighth line is equivalent to being commented out, so it cannot be viewed in the end. To log input and output ( this macro is not defined in the above code ).

In order to make testing more flexible, we can not define this macro in the code, but define it during testing. One way is to gcc/g++specify it in the command, as follows:

$ gcc test.c -DDEBUG -o app

Specify the name of the macro to be defined gcc/g++through parameters in the command , which is equivalent to defining a macro in the code with the name .-DDEBUG

CMakeWe can also do similar things in , and the corresponding command is called add_definitions:

add_definitions(-D宏名称)

Write one for the above source file CMakeLists.txt, the content is as follows:

cmake_minimum_required(VERSION 3.0)
project(TEST)
# 自定义 DEBUG 宏
add_definitions(-DDEBUG)
add_executable(app ./test.c) 

In this way, the eighth line of log in the above code can be output.

3. Predefined macros

The following list has compiled some CMakecommonly used macros for you:

Macro Function
PROJECT_SOURCE_DIR The directory immediately following the cmake command is usually the root directory of the project.
PROJECT_BINARY_DIR Directory where cmake command is executed
CMAKE_CURRENT_SOURCE_DIR The path where the currently processed CMakeLists.txt is located
CMAKE_CURRENT_BINARY_DIR target compilation directory
EXECUTABLE_OUTPUT_PATH Redefine the storage location of the target binary executable [not suitable for static libraries ]
LIBRARY_OUTPUT_PATH Redefine the storage location of the target link library file
PROJECT_NAME Returns the project name defined through the PROJECT directive
CMAKE_BINARY_DIR The actual build path of the project. Assuming that the build is performed in builda directory, the path obtained is the path of this directory.

Author: Su Bingqu

Link: https://subingwen.cn/cmake/CMake-advanced/

Source: DaBing of iProgramming. The copyright belongs to the author. For commercial reprinting, please contact the author for authorization. For non-commercial reprinting, please indicate the source.

Guess you like

Origin blog.csdn.net/qq_47355554/article/details/132644405