📘
CMake Cookbook
  • Introduction
  • 前言
  • 第0章 配置环境
    • 0.1 获取代码
    • 0.2 Docker镜像
    • 0.3 安装必要的软件
    • 0.4 测试环境
    • 0.5 上报问题并提出改进建议
  • 第1章 从可执行文件到库
    • 1.1 将单个源文件编译为可执行文件
    • 1.2 切换生成器
    • 1.3 构建和链接静态库和动态库
    • 1.4 用条件句控制编译
    • 1.5 向用户显示选项
    • 1.6 指定编译器
    • 1.7 切换构建类型
    • 1.8 设置编译器选项
    • 1.9 为语言设定标准
    • 1.10 使用控制流
  • 第2章 检测环境
    • 2.1 检测操作系统
    • 2.2 处理与平台相关的源代码
    • 2.3 处理与编译器相关的源代码
    • 2.4 检测处理器体系结构
    • 2.5 检测处理器指令集
    • 2.6 为Eigen库使能向量化
  • 第3章 检测外部库和程序
    • 3.1 检测Python解释器
    • 3.2 检测Python库
    • 3.3 检测Python模块和包
    • 3.4 检测BLAS和LAPACK数学库
    • 3.5 检测OpenMP的并行环境
    • 3.6 检测MPI的并行环境
    • 3.7 检测Eigen库
    • 3.8 检测Boost库
    • 3.9 检测外部库:Ⅰ. 使用pkg-config
    • 3.10 检测外部库:Ⅱ. 自定义find模块
  • 第4章 创建和运行测试
    • 4.1 创建一个简单的单元测试
    • 4.2 使用Catch2库进行单元测试
    • 4.3 使用Google Test库进行单元测试
    • 4.4 使用Boost Test进行单元测试
    • 4.5 使用动态分析来检测内存缺陷
    • 4.6 预期测试失败
    • 4.7 使用超时测试运行时间过长的测试
    • 4.8 并行测试
    • 4.9 运行测试子集
    • 4.10 使用测试固件
  • 第5章 配置时和构建时的操作
    • 5.1 使用平台无关的文件操作
    • 5.2 配置时运行自定义命令
    • 5.3 构建时运行自定义命令:Ⅰ. 使用add_custom_command
    • 5.4 构建时运行自定义命令:Ⅱ. 使用add_custom_target
    • 5.5 构建时为特定目标运行自定义命令
    • 5.6 探究编译和链接命令
    • 5.7 探究编译器标志命令
    • 5.8 探究可执行命令
    • 5.9 使用生成器表达式微调配置和编译
  • 第6章 生成源码
    • 6.1 配置时生成源码
    • 6.2 使用Python在配置时生成源码
    • 6.3 构建时使用Python生成源码
    • 6.4 记录项目版本信息以便报告
    • 6.5 从文件中记录项目版本
    • 6.6 配置时记录Git Hash值
    • 6.7 构建时记录Git Hash值
  • 第7章 构建项目
    • 7.1 使用函数和宏重用代码
    • 7.2 将CMake源代码分成模块
    • 7.3 编写函数来测试和设置编译器标志
    • 7.4 用指定参数定义函数或宏
    • 7.5 重新定义函数和宏
    • 7.6 使用废弃函数、宏和变量
    • 7.7 add_subdirectory的限定范围
    • 7.8 使用target_sources避免全局变量
    • 7.9 组织Fortran项目
  • 第8章 超级构建模式
    • 8.1 使用超级构建模式
    • 8.2 使用超级构建管理依赖项:Ⅰ.Boost库
    • 8.3 使用超级构建管理依赖项:Ⅱ.FFTW库
    • 8.4 使用超级构建管理依赖项:Ⅲ.Google Test框架
    • 8.5 使用超级构建支持项目
  • 第9章 语言混合项目
    • 9.1 使用C/C++库构建Fortran项目
    • 9.2 使用Fortran库构建C/C++项目
    • 9.3 使用Cython构建C++和Python项目
    • 9.4 使用Boost.Python构建C++和Python项目
    • 9.5 使用pybind11构建C++和Python项目
    • 9.6 使用Python CFFI混合C,C++,Fortran和Python
  • 第10章 编写安装程序
    • 10.1 安装项目
    • 10.2 生成输出头文件
    • 10.3 输出目标
    • 10.4 安装超级构建
  • 第11章 打包项目
    • 11.1 生成源代码和二进制包
    • 11.2 通过PyPI发布使用CMake/pybind11构建的C++/Python项目
    • 11.3 通过PyPI发布使用CMake/CFFI构建C/Fortran/Python项目
    • 11.4 以Conda包的形式发布一个简单的项目
    • 11.5 将Conda包作为依赖项发布给项目
  • 第12章 构建文档
    • 12.1 使用Doxygen构建文档
    • 12.2 使用Sphinx构建文档
    • 12.3 结合Doxygen和Sphinx
  • 第13章 选择生成器和交叉编译
    • 13.1 使用CMake构建Visual Studio 2017项目
    • 13.2 交叉编译hello world示例
    • 13.3 使用OpenMP并行化交叉编译Windows二进制文件
  • 第14章 测试面板
    • 14.1 将测试部署到CDash
    • 14.2 CDash显示测试覆盖率
    • 14.3 使用AddressSanifier向CDash报告内存缺陷
    • 14.4 使用ThreadSaniiser向CDash报告数据争用
  • 第15章 使用CMake构建已有项目
    • 15.1 如何开始迁移项目
    • 15.2 生成文件并编写平台检查
    • 15.3 检测所需的链接和依赖关系
    • 15.4 复制编译标志
    • 15.5 移植测试
    • 15.6 移植安装目标
    • 15.7 进一步迁移的措施
    • 15.8 项目转换为CMake的常见问题
  • 第16章 可能感兴趣的书
    • 16.1 留下评论——让其他读者知道你的想法
Powered by GitBook
On this page
  • 准备工作
  • 具体实施
  • 工作原理
  • 更多信息

Was this helpful?

  1. 第1章 从可执行文件到库

1.1 将单个源文件编译为可执行文件

Previous第1章 从可执行文件到库Next1.2 切换生成器

Last updated 5 years ago

Was this helpful?

NOTE:此示例代码可以在 中找到,包含C++、C和Fortran示例。该示例在CMake 3.5版(或更高版本)中是有效的,并且已经在GNU/Linux、macOS和Windows上进行过测试。

本节示例中,我们将演示如何运行CMake配置和构建一个简单的项目。该项目由单个源文件组成,用于生成可执行文件。我们将用C++讨论这个项目,您在GitHub示例库中可以找到C和Fortran的例子。

准备工作

我们希望将以下源代码编译为单个可执行文件:

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

std::string say_hello() { return std::string("Hello, CMake world!"); }

int main() {
  std::cout << say_hello() << std::endl;
  return EXIT_SUCCESS;
}

具体实施

除了源文件之外,我们还需要向CMake提供项目配置描述。该描述使用CMake完成,完整的文档可以在 找到。我们把CMake指令放入一个名为CMakeLists.txt的文件中。

NOTE:文件的名称区分大小写,必须命名为CMakeLists.txt,CMake才能够解析。

具体步骤如下:

  1. 用编辑器打开一个文本文件,将这个文件命名为CMakeLists.txt。

  2. 第一行,设置CMake所需的最低版本。如果使用的CMake版本低于该版本,则会发出致命错误:

    cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
  3. 第二行,声明了项目的名称(recipe-01)和支持的编程语言(CXX代表C++):

    project(recipe-01 LANGUAGES CXX)
  4. 指示CMake创建一个新目标:可执行文件hello-world。这个可执行文件是通过编译和链接源文件hello-world.cpp生成的。CMake将为编译器使用默认设置,并自动选择生成工具:

    add_executable(hello-world hello-world.cpp)
  5. 将该文件与源文件hello-world.cpp放在相同的目录中。记住,它只能被命名为CMakeLists.txt。

  6. 现在,可以通过创建build目录,在build目录下来配置项目:

    $ mkdir -p build
    $ cd build
    $ cmake ..
    
    -- The CXX compiler identification is GNU 8.1.0
    -- 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/user/cmake-cookbook/chapter-01/recipe-01/cxx-example/build
  7. 如果一切顺利,项目的配置已经在build目录中生成。我们现在可以编译可执行文件:

    $ cmake --build .
    
    Scanning dependencies of target hello-world
    [ 50%] Building CXX object CMakeFiles/hello-world.dir/hello-world.cpp.o
    [100%] Linking CXX executable hello-world
    [100%] Built target hello-world

工作原理

示例中,我们使用了一个简单的CMakeLists.txt来构建“Hello world”可执行文件:

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
project(recipe-01 LANGUAGES CXX)
add_executable(hello-world hello-world.cpp)

NOTE:CMake语言不区分大小写,但是参数区分大小写。

TIPS:CMake中,C++是默认的编程语言。不过,我们还是建议使用LANGUAGES选项在project命令中显式地声明项目的语言。

要配置项目并生成构建器,我们必须通过命令行界面(CLI)运行CMake。CMake CLI提供了许多选项,cmake -help将输出以显示列出所有可用选项的完整帮助信息,我们将在书中对这些选项进行更多地了解。正如您将从cmake -help的输出中显示的内容,它们中的大多数选项会让你您访问CMake手册,查看详细信息。通过下列命令生成构建器:

$ mkdir -p build
$ cd build
$ cmake ..

这里,我们创建了一个目录build(生成构建器的位置),进入build目录,并通过指定CMakeLists.txt的位置(本例中位于父目录中)来调用CMake。可以使用以下命令行来实现相同的效果:

$ cmake -H. -Bbuild

该命令是跨平台的,使用了-H和-B为CLI选项。-H表示当前目录中搜索根CMakeLists.txt文件。-Bbuild告诉CMake在一个名为build的目录中生成所有的文件。

运行cmake命令会输出一系列状态消息,显示配置信息:

$ cmake ..

-- The CXX compiler identification is GNU 8.1.0
-- 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/user/cmake-cookbook/chapter-01/recipe-01/cxx-example/build

NOTE:在与CMakeLists.txt相同的目录中执行cmake .,原则上足以配置一个项目。然而,CMake会将所有生成的文件写到项目的根目录中。这将是一个源代码内构建,通常是不推荐的,因为这会混合源代码和项目的目录树。我们首选的是源外构建。

CMake是一个构建系统生成器。将描述构建系统(如:Unix Makefile、Ninja、Visual Studio等)应当如何操作才能编译代码。然后,CMake为所选的构建系统生成相应的指令。默认情况下,在GNU/Linux和macOS系统上,CMake使用Unix Makefile生成器。Windows上,Visual Studio是默认的生成器。在下一个示例中,我们将进一步研究生成器,并在第13章中重新讨论生成器。

GNU/Linux上,CMake默认生成Unix Makefile来构建项目:

  • Makefile: make将运行指令来构建项目。

  • CMakefile:包含临时文件的目录,CMake用于检测操作系统、编译器等。此外,根据所选的生成器,它还包含特定的文件。

  • cmake_install.cmake:处理安装规则的CMake脚本,在项目安装时使用。

  • CMakeCache.txt:如文件名所示,CMake缓存。CMake在重新运行配置时使用这个文件。

要构建示例项目,我们运行以下命令:

$ cmake --build .

最后,CMake不强制指定构建目录执行名称或位置,我们完全可以把它放在项目路径之外。这样做同样有效:

$ mkdir -p /tmp/someplace
$ cd /tmp/someplace
$ cmake /path/to/source
$ cmake --build .

更多信息

$ cmake --build . --target help

The following are some of the valid targets for this Makefile:
... all (the default if no target is provided)
... clean
... depend
... rebuild_cache
... hello-world
... edit_cache
... hello-world.o
... hello-world.i
... hello-world.s

CMake生成的目标比构建可执行文件的目标要多。可以使用cmake --build . --target <target-name>语法,实现如下功能:

  • all(或Visual Studio generator中的ALL_BUILD)是默认目标,将在项目中构建所有目标。

  • clean,删除所有生成的文件。

  • rebuild_cache,将调用CMake为源文件生成依赖(如果有的话)。

  • edit_cache,这个目标允许直接编辑缓存。

对于更复杂的项目,通过测试阶段和安装规则,CMake将生成额外的目标:

  • test(或Visual Studio generator中的RUN_TESTS)将在CTest的帮助下运行测试套件。我们将在第4章中详细讨论测试和CTest。

  • install,将执行项目安装规则。我们将在第10章中讨论安装规则。

  • package,此目标将调用CPack为项目生成可分发的包。打包和CPack将在第11章中讨论。

NOTE:cmake -H. -Bbuild也属于CMake标准使用方式: 。不过,我们将在本书中使用传统方法(创建一个构建目录,进入其中,并通过将CMake指向CMakeLists.txt的位置来配置项目)。

官方文档 给出了运行CMake的简要概述。由CMake生成的构建系统,即上面给出的示例中的Makefile,将包含为给定项目构建目标文件、可执行文件和库的目标及规则。hello-world可执行文件是在当前示例中的唯一目标,运行以下命令:

https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-01/recipe-01
https://cmake.org/cmake/help/latest/
https://cmake.org/pipermail/cmake-developers/2018-January/030520.html
https://cmake.org/runningcmake/