简介
本文档是根据CMake的官方教程学习的笔记,同时将教程中C++实现的代码更改为C语言实现。当前还未学习完。
教程官网:CMake Tutorial — CMake 3.27.0-rc1 Documentation
中文教程:教程 — CMake 3.26.4 Documentation (cmake-doc.readthedocs.io)
学习相关的代码存放在:cmake-3.27.0-rc1-tutorial-source · 简单/CMake学习 - 码云 - 开源中国 (gitee.com)
Step 1: A Basic Starting Point
参考:[Step 1: A Basic Starting Point — CMake 3.27.0-rc2 Documentation](https://cmake.org/cmake/help/latest/guide/tutorial/A Basic Starting Point.html)
本章介绍CMake基本的一些语法、指令和变量。
各子章节说明:
Goal
:本章节学习的目的Helpful Resources
:使用到的命令帮助文档Build and Run
:构建和运行的命令
Exercise 1 - Building a Basic Project
最基本的CMake项目是从单个源文件开始构建。对于一个简单的工程,一个CMakeLists.txt
文件通常需要三个命令。
注意:尽管CMake支持大写、小写、大小写混合命令,但是首选小写命令,并一直使用小写。
- 一个
CMakeLists.txt
文件的开始命令应该使用cmake_minimum_required()
指定CMak最低版本。它建立了策略设置,并确保下面的CMake命令都运行在兼容的版本上。 - 启动工程,我们使用
project()
命令去设置工程名字。每一个工程都需要这个命令,并且应该在cmake_minimum_required()
之后调用。该命令还可以指定其它项目信息,比如项目等级和使用的编程语言或者版本号。 - 最后,使用
add_executable()
命令告诉CMake使用指定的源文件去创建一个可执行程序。
Goal
学习如何创建一个简单的CMake工程。
Helpful Resources
Build and Run
$ cd Step1
$ mkdir Step1_build
$ cd Step1_build
$ cmake ..
$ cmake --build . # 运行, 生成可执行文件
$ ./Tutorial 4294967296
The square root of 4.29497e+09 is 65536
$ ./Tutorial 10
The square root of 10 is 3.16228
Exercise 2 - Specifying the C++ Standard
CMake有一些特殊变量,这些变量要么是隐式创建的,要么是工程中设置的。大部分这些变量以CMAKE_
前缀修饰。在为项目创建变量时应该避免这种命令方式。其中两个特殊变量是CMAKE_CXX_STANDARD
和CMAKE_CXX_STANDARD_REQUIRED
,他们通常一起使用去指定工程需要的C++版本。
Goal
添加C++ 11的特性。
Helpful Resources
Files to Edit
CMakeLists.txt
tutorial.cxx
Build and Run
同Exercise1
Exercise 3 - Adding a Version Number and Configured Header File
有时,在CMakeLists.txt文件中定义的变量在源文件中可能也是有用的,这种情况下,我们希望打印项目版本。
其中一种方式是使用配置好的头文件。我们创建一个输入文件,其中包含一个或多个需要替换的变量。这些变量使用类似@VAR@
的特殊语法指定。然后,我们使用configure_file()
命令去拷贝输入文件,并产生一个输出文件替换这些VAR
变量。
虽然我们可以直接在代码中指定源码版本,但是这种方式是首选的,因为它指定了唯一来源,避免了重复定义。
Goal
定义和打印项目的版本号
Helpful Resources
Files to Edit
CMakeLists.txt
tutorial.cxx
Build and Run
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step1/Step1_build
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step1/Step1_build
$ cmake --build .
[ 50%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step1/Step1_build
$ ./Tutorial
./Tutorial Version 1.0
Usage: ./Tutorial number
生成的TutorialConfig.in文件如下:
// the configured options and settings for Tutorial
// TODO 10: Define Tutorial_VERSION_MAJOR and Tutorial_VERSION_MINOR
#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0
Step 2: Adding a Library
本章节,我们将使用CMake在工程中创建和使用库。
Exercise 1 - Creating a Library
在CMake中,使用add_library()
命令指定哪些文件用来创建库。
相比于放置所有的源文件在一个目录,我们组织我们的工程使用一个或多个子目录。在这个例子中,我们将创建一个子目录来指定我们的库生成源文件。在子目录中我们添加一个新的CMakeLists.txt
文件和源文件。在顶层CMakeLists.txt
文件中,我们使用add_subdirectory()
命令添加子目录去构建。
当我们的库创建后,链接到可执行文件使用target_include_directories()
和target_link_libraries()
命令。
Goal
添加和使用一个库。
Helpful Resources
add_library()
add_subdirectory()
target_include_directories()
target_link_libraries()
PROJECT_SOURCE_DIR
Getting Started
在这个例子中,我们将添加一个库到工程中,这个库包括计算平方根的实现。可执行文件可以使用这个库。
我们将库放在子目录MathFunctions
中,这个目录已经包含头文件MathFunctions.h
和mysqrt.h
,同时提供源码文件MathFunctions.c
和mysqrt.c
。
目录结构如下:
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2
$ tree
.
├── CMakeLists.txt
├── MathFunctions
│ ├── CMakeLists.txt
│ ├── MathFunctions.cxx
│ ├── MathFunctions.h
│ ├── mysqrt.cxx
│ └── mysqrt.h
├── TutorialConfig.h.in
└── tutorial.cxx
Build and Run
$ mkdir Step2_build
$ cd Step2_build/
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ cmake --build .
$ ./Tutorial 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
$ ls MathFunctions/ # 默认生成的是静态库
CMakeFiles/ cmake_install.cmake libMathFunctions.a Makefile
Exercise 2 - Adding an Option
在MathFunctions库添加一个选项,允许开发人员选择自定义开平方根实现或者使用标准库实现。
CMake可以使用option()
命令实现,这为用户实现了一个可以在配置CMake构建时可以更改的变量。这个变量存放在缓存中,不需要用户每次构建CMake时都去设置这个值。
Goal
添加一个选项选择不使用MathFunctions
。
Helpful Resources
Build and Run
$ cmake ..
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ cmake --build .
[ 14%] Building CXX object MathFunctions/CMakeFiles/SqrtLibrary.dir/mysqrt.cxx.o
[ 28%] Linking CXX static library libSqrtLibrary.a
[ 28%] Built target SqrtLibrary
[ 42%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.cxx.o
[ 57%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.o
[ 71%] Linking CXX static library libMathFunctions.a
[ 71%] Built target MathFunctions
[ 85%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ ./Tutorial 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ rm * -rf
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ cmake .. -DUSE_MYMATH=OFF
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ cmake --build .
[ 20%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.cxx.o
[ 40%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/mysqrt.cxx.o
[ 60%] Linking CXX static library libMathFunctions.a
[ 60%] Built target MathFunctions
[ 80%] Linking CXX executable Tutorial
[100%] Built target Tutorial
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step2/Step2_build
$ ./Tutorial 10
The square root of 10 is 3.16228
Step3: Adding Usage Requirements for a Library
Exercise 1 - Adding Usage Requirements for a Library
Usage requirements
目标参数的使用允许对库或可执行文件的链接和包含行进行更好的控制,同时还可以更好地控制CMake内部目标的传递属性。使用Usage requirements
主要的命令包括:
target_compile_definitions()
target_compile_options()
target_include_directories()
target_link_directories()
target_link_options()
target_precompile_headers()
target_sources()
Goal
添加库的使用需求
Helpful Materials
Getting Started
在本例中,我们将使用主流CMake方法来重构添加一个库。我们将让库定义它自己的使用需求,以便根据需要将其传递给其它目标。MathFunctions
将自己指定任何所需的包含目录。然后,Tutorial
目标只需要链接到MathFunctions
,而不需要担心额外的头文件目录。
Build and Run
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step3-Exercise1/Step3-build
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step3-Exercise1/Step3-build
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step3-Exercise1/Step3-build
$ cmake --build .
[ 16%] Building CXX object MathFunctions/CMakeFiles/SqrtLibrary.dir/mysqrt.cxx.o
[ 33%] Linking CXX static library libSqrtLibrary.a
[ 33%] Built target SqrtLibrary
[ 50%] Building CXX object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.cxx.o
[ 66%] Linking CXX static library libMathFunctions.a
[ 66%] Built target MathFunctions
[ 83%] Building CXX object CMakeFiles/Tutorial.dir/tutorial.cxx.o
[100%] Linking CXX executable Tutorial
[100%] Built target Tutorial
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step3-Exercise1/Step3-build
$ ./Tutorial 10
Computing sqrt of 10 to be 5.5
Computing sqrt of 10 to be 3.65909
Computing sqrt of 10 to be 3.19601
Computing sqrt of 10 to be 3.16246
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
Computing sqrt of 10 to be 3.16228
The square root of 10 is 3.16228
Exercise 2 - Setting the C++ Standard with Interface Libraries
既然我们已经切换到更现代的方法,让我们演示一种为多个目标设置属性的现代技术。
让我们使用INTERFACE
库来重构现有代码。我们将在下一步中演示generator expressions
的常用用法。
Goal
添加一个INTERFACE
库来指定所需的C++标准。
Helpful Resources
Files to Edit
CMakeLists.txt
MathFunctions/CMakeLists.txt
Build and Run
同上
Step 4: Adding Generator Expressions
Generator expressions
在构建系统期间求值,然后生成特定信息的配置信息。
很多生成目标属性的上下文都允许使用Generator expressions
,比如LINK_LIBRARIES
, INCLUDE_DIRECTORIES
, COMPILE_DEFINITIONS
。在使用命令填充这些属性时也可以使用它们,比如命令target_link_libraries()
, target_include_directories()
, target_compile_definitions()
。
Generator expressions
可用于启动条件链接、编译时的条件定义、条件头文件目录。这些条件基于构建配置、目标属性、平台信息或其它可查询信息。
Generator expressions
包括逻辑表达式、提示表达式和输出表达式。
逻辑表达式用于产生条件输出,最基本的表达式是0
和1
,一个$<0:...>
产生一个简单的字符串,一个<1:...>
产生内容...
,它们也可以嵌套。
Exercise 1 - Adding Compiler Warning Flags with Generator Expressions
Generator expressions
的一个通用用法是有条件的添加编译选项,比如语言或者告警等级。一个不错的模式是将其关联到INTERFACE
目标,从而允许这个扩展。
Goal
在构建的时候添加一个编译告警标识。
Helpful Resources
Build and Run
同上
Step 5: Installing and Testing
Exercise 1 - Install Rules
通常,仅仅构建可执行文件还不够,还应该能够安装。CMake使用特殊的命令install()
来安装。本地安装很简单,指定本地安装路径和文件即可。
Goal
安装可执行文件Tutorial
和库MathFunctions
。
Helpful Materials[¶](https://cmake.org/cmake/help/latest/guide/tutorial/Installing and Testing.html#helpful-materials)
Getting Started
- 安装库
MathFunctions
到/lib
目录,安装库MathFunctions
的头文件到/usr/include
目录。 - 安装可执行文件
Tutorial
到/usr/bin
目录,安装可执行文件Tutorial
需要的头文件到/usr/include
目录。
Build and Run
$ cmake ..
-- The C compiler identification is GNU 5.4.0
-- The CXX compiler identification is GNU 5.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done (0.3s)
-- Generating done (0.0s)
-- Build files have been written to: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions
$ cmake --build .
[ 16%] Building C object MathFunctions/CMakeFiles/SqrtLibrary.dir/mysqrt.c.o
[ 33%] Linking C static library libSqrtLibrary.a
[ 33%] Built target SqrtLibrary
[ 50%] Building C object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.c.o
[ 66%] Linking C static library libMathFunctions.a
[ 66%] Built target MathFunctions
[ 83%] Building C object CMakeFiles/Tutorial.dir/tutorial.c.o
[100%] Linking C executable Tutorial
[100%] Built target Tutorial
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions
$ cmake --install .
-- Install configuration: ""
-- Installing: /usr/local/lib/libMathFunctions.a
-- Installing: /usr/local/lib/libSqrtLibrary.a
-- Installing: /usr/local/include/MathFunctions.h
-- Installing: /usr/bin/Tutorial
-- Installing: /usr/include/TutorialConfig.h
CMake变量CMAKE_INSTALL_PREFIX
用于指定安装目录的根路径,如果使用cmake --install
命令,可以使用前缀--prefix
参数覆盖前缀,比如:
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions
$ cmake --install . --prefix="/home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions/installdir"
-- Install configuration: ""
-- Installing: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions/installdir/lib/libMathFunctions.a
-- Installing: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions/installdir/lib/libSqrtLibrary.a
-- Installing: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise1/MathFunctions/installdir/include/MathFunctions.h
-- Up-to-date: /usr/bin/Tutorial
-- Up-to-date: /usr/include/TutorialConfig.h
Exercise 2 - Testing Support
CTest提供了一种轻松的管理测试的方式。测试项可以通过命令add_test()
添加。
Helpful Materials[¶](https://cmake.org/cmake/help/latest/guide/tutorial/Installing and Testing.html#id2)
Build and Run
$ cmake ..
$ cmake --build .
[ 16%] Building C object MathFunctions/CMakeFiles/SqrtLibrary.dir/mysqrt.c.o
[ 33%] Linking C static library libSqrtLibrary.a
[ 33%] Built target SqrtLibrary
[ 50%] Building C object MathFunctions/CMakeFiles/MathFunctions.dir/MathFunctions.c.o
[ 66%] Linking C static library libMathFunctions.a
[ 66%] Built target MathFunctions
[ 83%] Building C object CMakeFiles/Tutorial.dir/tutorial.c.o
[100%] Linking C executable Tutorial
[100%] Built target Tutorial
$ ctest -N
Test project /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise2/Step5-build
Test #1: Runs
Test #2: Usage
Test #3: StandardUse
Test #4: Comp4
Test #5: Comp9
Test #6: Comp5
Test #7: Comp7
Test #8: Comp25
Test #9: Comp-25
Test #10: Comp0.0001
Total Tests: 10
[root@ubuntu] /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise2/Step5-build
$ ctest -vv
Test project /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise2/Step5-build
Start 1: Runs
1/10 Test #1: Runs ............................. Passed 0.01 sec
Start 2: Usage
2/10 Test #2: Usage ............................ Passed 0.00 sec
Start 3: StandardUse
3/10 Test #3: StandardUse ......................***Failed Required regular expression not found. Regex=[4 is 2
] 0.00 sec
Start 4: Comp4
4/10 Test #4: Comp4 ............................***Failed Required regular expression not found. Regex=[4 is 2
] 0.00 sec
Start 5: Comp9
5/10 Test #5: Comp9 ............................***Failed Required regular expression not found. Regex=[9 is 3
] 0.00 sec
Start 6: Comp5
6/10 Test #6: Comp5 ............................***Failed Required regular expression not found. Regex=[5 is 2.236
] 0.00 sec
Start 7: Comp7
7/10 Test #7: Comp7 ............................***Failed Required regular expression not found. Regex=[7 is 2.645
] 0.00 sec
Start 8: Comp25
8/10 Test #8: Comp25 ...........................***Failed Required regular expression not found. Regex=[25 is 5
] 0.00 sec
Start 9: Comp-25
9/10 Test #9: Comp-25 ..........................***Failed Required regular expression not found. Regex=[-25 is (-nan|nan|0)
] 0.00 sec
Start 10: Comp0.0001
10/10 Test #10: Comp0.0001 .......................***Failed Required regular expression not found. Regex=[0.0001 is 0.01
] 0.00 sec
20% tests passed, 8 tests failed out of 10
Total Test time (real) = 0.04 sec
The following tests FAILED:
3 - StandardUse (Failed)
4 - Comp4 (Failed)
5 - Comp9 (Failed)
6 - Comp5 (Failed)
7 - Comp7 (Failed)
8 - Comp25 (Failed)
9 - Comp-25 (Failed)
10 - Comp0.0001 (Failed)
Errors while running CTest
Output from these tests are in: /home/share/cmake/cmake-3.27.0-rc1-tutorial-source/Step5-Exercise2/Step5-build/Testing/Temporary/LastTest.log
Use "--rerun-failed --output-on-failure" to re-run the failed cases verbosely.
Step 10: Selecting Static or Shared Libraries
本章节,学习如何使用BUILD_SHARED_LIBS
变量来控制add_library()
命令的默认行为,并且允许控制没有显示类型(STATIC、SHARED、MODULE、OBJECT)的库的构建方法。
要实现这个目的,我们需要将BUILD_SHARED_LIBS
变量添加到顶层CMakeLists.txt
,可以使用option()
命令来选择是否使用它。