cmake从入门到精通(附完整代码)

一.前言

纸上学来终觉浅,觉知此时要躬行。学习一项技术最好的武器就是去实践和复盘。接触cmake有一年多了,但是真正学会cmake也是在用cmake编译项目的过程中,逐渐掌握了这门技术。本文想带着读者从一个小的点开始逐渐完善,最后做出一个涵盖绝大部分功能和满足日常需求的cmake demo工程。作者将在github中新建一个新的仓库,然后逐渐向上提交代码。最后作者会开源本项目的github网址,读者可以自行根据commit版本提示找到每一部分的源码。

二.增量开发打造一个cmake demo

2.1 github 版本管理的使用

在这里我要安利一下,前些天写的这篇github教程。涵盖从进课题组开始,完成项目所需要的所有日常指令。有需要的朋友可以参考一下。
四个指令玩转git

2.2 构建最小系统

  • 安装cmake
    因为是构建demo不需要依赖复杂的包,只需要ubuntu apt库中cmake即可,如果要更高版本,请自行到官网下载。
sudo apt install cmake# ubuntu16.04 默认 3.0.5

最基本的项目是将一个源代码文件生成可执行文件。对于这么简单的项目,只需要一个三行的 CMakeLists.txt 文件即可,这是本篇教程的起点。在 目录中创建一个 CMakeLists.txt 文件,如下所示:

cmake_minimum_required(VERSION 3.0)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cpp)

cmake_minimum_required 指定使用 CMake 的最低版本号,project 指定项目名称,add_executable 用来生成可执行文件,需要指定生成可执行文件的名称和相关源文件。

注意,此示例在 CMakeLists.txt 文件中使用小写命令。CMake 支持大写、小写和混合大小写命令。tutorial.cpp 文件在commit first目录中,可用于计算数字的平方根。

// tutorial.cpp

#include <cmath>
#include <cstdlib>
#include <iostream>
#include <string>

int main(int argc, char* argv[])
{
    if (argc < 2) {
        std::cout << "Usage: " << argv[0] << " number" << std::endl;
        return 1;
    }

    // convert input to double
    const double inputValue = atof(argv[1]);

    // calculate square root
    const double outputValue = sqrt(inputValue);
    std::cout << "The square root of " << inputValue
              << " is " << outputValue
              << std::endl;
    return 0;
}
  • 构建 编译 运行
    先从命令行进入到 目录,并创建一个构建目录 build,接下来,进入 build 目录并运行 CMake 来配置项目,并生成构建系统:
mkdir build
cd build
cmake ..
cmake --build .

可执行文件Tutorial就在build目录下。

zzy@zzy-virtual-machine:~/LearnCmake/build$ ./Tutorial 5
The square root of 5 is 2.23607

2.3 优化 CMakeLists.txt 文件

之前CMakeList.txt如下:

cmake_minimum_required(VERSION 3.0)

# set the project name
project(Tutorial)

# add the executable
add_executable(Tutorial tutorial.cpp)

指定了项目名后,后面可能会有多个地方用到这个项目名,如果更改了这个名字,就要改多个地方,比较麻烦,那么可以使用 == PROJECT_NAME== 来表示项目名。

add_executable(${PROJECT_NAME} tutorial.cpp)

生成可执行文件需要指定相关的源文件,如果有多个,那么就用空格隔开,比如:

add_executable(${PROJECT_NAME} test1.cpp test2.cpp test3.cpp)

我们也可以用一个变量来表示这多个源文件:

set(SRC_LIST test1.cpp test2.cpp test3.cpp )
add_executable(${PROJECT_NAME} ${SRC_LIST})

set 命令指定 SRC_LIST 变量来表示多个源文件,用 ${var_name} 获取变量的值。
于是原来的 CMakeLists.txt 文件就可以变成如下所示:

cmake_minimum_required(VERSION 3.0)

# set the project name
project(Tutorial)

SET(SRC_LIST tutorial.cpp)

# add the executable
add_executable(${PROJECT_NAME} ${SRC_LIST})

这样可配置性就增强了很多,有利于后续项目扩展。

  • 添加版本号和配置头文件

我们可以在 CMakeLists.txt 为可执行文件和项目提供一个版本号。首先,修改 CMakeLists.txt 文件,使用 project 命令设置项目名称和版本号。

cmake_minimum_required(VERSION 3.0)

# set the project name and version
project(Tutorial VERSION 1.0)

configure_file(TutorialConfig.h.in TutorialConfig.h)//配置头文件将版本号传递给源代码

由于 TutorialConfig.h 文件会被自动写入 build 目录,因此必须将该目录添加到搜索头文件的路径列表中。将以下行添加到 CMakeLists.txt 文件的末尾:

target_include_directories(${PROJECT_NAME} PUBLIC
                           ${PROJECT_BINARY_DIR}
                           )

安装刚才的方式用cmake … 构建项目的过程中会在build目录中生成TutorialConfig.h

#define Tutorial_VERSION_MAJOR 1
#define Tutorial_VERSION_MINOR 0

下一步在 tutorial.cpp 包含头文件 TutorialConfig.h,最后通过以下代码打印出可执行文件的名称和版本号。

    if (argc < 2) {
      // report version
      std::cout << argv[0] << " Version " << Tutorial_VERSION_MAJOR << "."
                << Tutorial_VERSION_MINOR << std::endl;
      std::cout << "Usage: " << argv[0] << " number" << std::endl;
      return 1;
    }
  • 制定 c++标准

接下来将tutorial.cpp 源码中的 atof 替换为 std::stod,这是 C++11 的特性。

const double inputValue = std::stod(argv[1]);

在 CMake 中支持特定 C++标准的最简单方法是使用 CMAKE_CXX_STANDARD 标准变量。在 CMakeLists.txt 中设置 CMAKE_CXX_STANDARD 为11,CMAKE_CXX_STANDARD_REQUIRED 设置为True。确保在 add_executable 命令之前添加 CMAKE_CXX_STANDARD_REQUIRED 命令。

# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)

此时完整的CMakeList.txt

cmake_minimum_required(VERSION 3.0)
# set the project name
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in TutorialConfig.h)

SET(SRC_LIST tutorial.cpp)
# add the executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_include_directories(${PROJECT_NAME} PUBLIC
                           ${PROJECT_BINARY_DIR}
                           )

根据刚才的方法编译运行

zzy@zzy-virtual-machine:~/LearnCmake/build$ ./Tutorial 5
The square root of 5 is 2.23607
zzy@zzy-virtual-machine:~/LearnCmake/build$ ./Tutorial
./Tutorial Version 1.0
Usage: ./Tutorial number

代码保存在github commit second版本中

Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 20:03:34 2022 +0800

    commit second

commit f5d7e8288b3956fa78028dec6fbb3d0268cfbbb0
Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 19:37:38 2022 +0800

    commit first

2.4 添加库

现在我们将向项目中添加一个库,这个库包含计算数字平方根的实现,可执行文件使用这个库,而不是编译器提供的标准平方根函数。
我们把库放在名为 MathFunctions 的子目录中。此目录包含头文件 MathFunctions.h 和源文件 mysqrt.cpp。源文件有一个名为 mysqrt 的函数,它提供了与编译器的 sqrt 函数类似的功能,MathFunctions.h 则是该函数的声明。
在 MathFunctions 目录下创建一个 CMakeLists.txt 文件,并添加以下一行:

add_library(MathFunctions mysqrt.cpp)

为了使用== MathFunctions== 这个库,我们将在顶级 CMakeLists.txt 文件中添加一个 add_subdirectory(MathFunctions) 命令指定库所在子目录,该子目录下应包含 CMakeLists.txt 文件和代码文件。

可执行文件要使用库文件,需要能够找到库文件和对应的头文件,可以分别通过 target_link_librariestarget_include_directories 来指定。

使用 target_link_libraries 将新的库文件添加到可执行文件中,使用 target_include_directories 将 MathFunctions 添加为头文件目录,添加到 Tutorial 目标上,以便 mysqrt.h 可以被找到。
然后在测试文件中把函数名设置为mysqrt。

const double outputValue = mysqrt(inputValue);

完整的CMakeList.txt

cmake_minimum_required(VERSION 3.0)
# set the project name
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in TutorialConfig.h)

SET(SRC_LIST tutorial.cpp)
add_subdirectory(MathFunctions)
# add the executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} PUBLIC MathFunctions)
target_include_directories(${PROJECT_NAME} PUBLIC
                           ${PROJECT_BINARY_DIR}
			   ${PROJECT_SOURCE_DIR}/MathFunctions
                           )

安装刚才的方式编译运行,可以得到。

zzy@zzy-virtual-machine:~/LearnCmake/build$ ./Tutorial 5
Computing sqrt of 5 to be 3
Computing sqrt of 5 to be 2.33333
Computing sqrt of 5 to be 2.2381
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
Computing sqrt of 5 to be 2.23607
The square root of 5 is 2.23607

代码在github仓库提交版本 commit third中

2.5 将库设置为可选选项

现在将 MathFunctions 库设为可选的,虽然对于本教程来说,功能已经完成了,但对于较大的项目来说,这种情况很常见。

第一步是向顶级 CMakeLists.txt 文件添加一个选项。

option(USE_MYMATH "Use tutorial provided math implementation" ON)

option 表示提供用户可以选择的选项。命令格式为:option( "description [initial value])。

==USE_MYMATH ==这个选项缺省值为 ON,用户可以更改这个值。此设置将存储在缓存中,以便用户不需要在每次构建项目时设置该值。

下一个更改是使 MathFunctions 库的构建和链接成为条件。于是创建一个 if 语句,该语句检查选项 USE_MYMATH 的值。
在 if 块中,有 add_subdirectory 命令和 list 命令,APPEND表示将元素MathFunctions追加到列表EXTRA_LIBS中,将元素 ${PROJECT_SOURCE_DIR}/MathFunctions 追加到列表EXTRA_INCLUDES中。EXTRA_LIBS 存储 MathFunctions 库,EXTRA_INCLUDES 存储 MathFunctions 头文件。
变量EXTRA_LIBS用来保存需要链接到可执行程序的可选库,变量EXTRA_INCLUDES用来保存可选的头文件搜索路径。这是处理可选组件的经典方法,我将在下一步介绍现代方法。
接下来对源代码的进行修改。首先,在 tutorial.cpp 中包含 MathFunctions.h 头文件,
然后,还在 tutorial.cpp 中,使用 USE_MYMATH 选择使用哪个平方根函数:

#ifdef USE_MYMATH
    #include "MathFunctions.h"
#endif
#ifdef USE_MYMATH
  const double outputValue = mysqrt(inputValue);
#else
  const double outputValue = sqrt(inputValue);
#endif

因为源代码使用了 USE_MYMATH 宏,可以用下面的行添加到 tutorialconfig.h.in 文档中:

// TutorialConfig.h.in
#cmakedefine USE_MYMATH

完整的CMakeLists如下:

cmake_minimum_required(VERSION 3.0)
# set the project name
project(Tutorial VERSION 1.0)
# specify the C++ standard
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED True)
configure_file(TutorialConfig.h.in TutorialConfig.h)

SET(SRC_LIST tutorial.cpp)
if(USE_MYMATH)
	add_subdirectory(MathFunctions)
  	list(APPEND EXTRA_LIBS MathFunctions)
  	list(APPEND EXTRA_INCLUDES ${PROJECT_SOURCE_DIR}/MathFunctions)
endif()
# add the executable
add_executable(${PROJECT_NAME} ${SRC_LIST})
target_link_libraries(${PROJECT_NAME} PUBLIC ${EXTRA_LIBS})
target_include_directories(${PROJECT_NAME} PUBLIC
                           ${PROJECT_BINARY_DIR}
			   ${EXTRA_INCLUDES}
                           )
option(USE_MYMATH "Use tutorial provided math implementation" ON)

测试:

zzy@zzy-virtual-machine:~/LearnCmake/build$ cmake .. -USE_MYMATH=OFF
zzy@zzy-virtual-machine:~/LearnCmake/build$ ./Tutorial 5
The square root of 5 is 2.23607

代码在commit four版本中

commit 31e20019090c3fc6b0edc4e1bb96a56d818b63ee
Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 20:27:57 2022 +0800

    commit four

commit a48dbcd622b99708dd4fac0af6e82a769737b7bf
Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 20:16:35 2022 +0800

    third commit

commit 14a1b053935e4d2d2036044470b3efcd86600010
Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 20:03:34 2022 +0800

    commit second

commit f5d7e8288b3956fa78028dec6fbb3d0268cfbbb0
Author: zzy <825830916@qq.com>
Date:   Sun Dec 4 19:37:38 2022 +0800

    commit first

三.结语

到这里整个教程就结束了,教程源码在https://github.com/tongjiaxuan666/CmakeLearn
在这里插入图片描述

  • 5
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

雪中奇侠

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值