CMake 秘籍(一)(1)https://developer.aliyun.com/article/1524596
额外软件
一些配方将需要额外的软件,这些将在以下部分中介绍。
BLAS 和 LAPACK
大多数 Linux 发行版都提供了 BLAS 和 LAPACK 的软件包。例如,在 Ubuntu 14.04 LTS 上,您可以运行以下命令:
$ sudo apt-get install libatlas-dev liblapack-dev liblapacke-dev
在 macOS 上,随 XCode 一起提供的 Accelerate 库足以满足我们的需求。
在 Windows 上,使用 MSYS2 环境,可以按照以下方式安装这些库(假设是 64 位版本):
$ pacman -S mingw64/mingw-w64-x86_64-openblas
或者,您可以从 GitHub 下载 BLAS 和 LAPACK 的参考实现(github.com/Reference-LAPACK/lapack),并从源代码编译这些库。商业供应商可能会为其 BLAS 和 LAPACK API 的实现提供软件包,这些软件包作为适用于您平台的安装程序提供。
消息传递接口(MPI)
有许多商业和非商业的 MPI 实现。对于我们的入门目的,安装任何免费提供的非商业实现就足够了。在 Ubuntu 14.04 LTS 上,我们推荐 OpenMPI。可以使用以下命令安装它:
$ sudo apt-get install openmpi-bin libopenmpi-dev
对于 macOS,Homebrew 分发 MPICH:
$ brew install mpich
也可以从www.open-mpi.org/software/公开的源代码编译 OpenMPI。
对于 Windows,Microsoft MPI 实现可以通过msdn.microsoft.com/en-us/library/bb524831(v=vs.85).aspx安装。
Eigen 线性代数模板库
有些配方需要 Eigen 线性代数模板库,版本 3.3 或更高。如果您的包管理器不提供 Eigen,您可以从在线源代码存档(eigen.tuxfamily.org)安装它。例如,在 GNU/Linux 和 macOS 上,您可以将 Eigen 安装到目录$HOME/Deps/eigen,如下所示:
$ eigen_version="3.3.4" $ mkdir -p eigen $ curl -Ls http://bitbucket.org/eigen/eigen/get/${eigen_version}.tar.gz | tar -xz -C eigen --strip-components=1 $ cd eigen $ cmake -H. -Bbuild_eigen -DCMAKE_INSTALL_PREFIX="$HOME/Deps/eigen" &> /dev/null $ cmake --build build_eigen -- install &> /dev/null
Boost 库
Boost 包适用于每个操作系统;大多数 Linux 发行版都通过其包管理器提供包。例如,在 Ubuntu 14.04 LTS 上,可以使用以下命令安装 Boost Filesystem、Boost Python 和 Boost Test 库:
$ sudo apt-get install libboost-filesystem-dev libboost-python-dev libboost-test-dev
对于 macOS,MacPorts 和 Homebrew 都为较新版本的 Boost 提供了包。我们在 macOS 上的测试设置按如下方式安装 Boost:
$ brew cask uninstall --force oclint $ brew uninstall --force --ignore-dependencies boost $ brew install boost $ brew install boost-python3
预构建的 Windows 二进制分发版也可以从 Boost 网站www.boost.org下载。或者,您可以从www.boost.org下载源代码并自行编译库。
交叉编译器
在 Debian/Ubuntu 类系统上,可以使用以下命令安装交叉编译器:
$ sudo apt-get install gcc-mingw-w64 g++-mingw-w64 gfortran-mingw-w64
在 macOS 上,使用 Brew,交叉编译器可以按如下方式安装:
$ brew install mingw-w64
其他包管理器提供相应的包。
使用打包的交叉编译器的替代方案是使用 M 交叉环境(mxe.cc)从源代码构建它们。
ZeroMQ、pkg-config、UUID 和 Doxygen
在 Ubuntu 14.04 LTS 上,这些包可以按如下方式安装:
$ sudo apt-get install pkg-config libzmq3-dev doxygen graphviz-dev uuid-dev
在 macOS 上,我们建议使用 Brew 安装:
$ brew install ossp-uuid pkg-config zeromq doxygen
pkg-config程序和 UUID 库仅在类 Unix 系统上可用。
在 Windows 上,使用 MSYS2 环境,这些依赖项可以按如下方式安装(假设是 64 位版本):
$ pacman -S mingw64/mingw-w64-x86_64-zeromq $ pacman -S mingw64/mingw-w64-x86_64-pkg-config $ pacman -S mingw64/mingw-w64-x86_64-doxygen $ pacman -S mingw64/mingw-w64-x86_64-graphviz
Conda 构建和部署工具
探索使用 Conda 打包的配方将需要系统上安装了 Miniconda 和 Conda 构建和部署工具。之前给出了安装 Miniconda 的说明。要在 GNU/Linux 和 macOS 上安装 Conda 构建和部署工具,请运行以下命令:
$ conda install --yes --quiet conda-build anaconda-client jinja2 setuptools $ conda clean -tipsy $ conda info -a
这些工具可以按如下方式在 Windows 上安装:
$conda_path = "C:\Deps\conda\Scripts\conda.exe" $args = "install --yes --quiet conda-build anaconda-client jinja2 setuptools" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru $args = "clean -tipsy" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru $args = "info -a" Start-Process -FilePath "$conda_path" -ArgumentList $args -Wait -Passthru
测试配方
这些配方在先进的持续集成(CI)服务上进行了测试:Travis(travis-ci.org)用于 GNU/Linux 和 macOS,Appveyor(www.appveyor.com)用于 Windows,以及 CircleCI(circleci.com)用于使用商业编译器的额外 GNU/Linux 测试。CI 服务的配置文件可以在仓库中找到(github.com/dev-cafe/cmake-cookbook/):.travis.yml 用于 Travis,.appveyor.yml 用于 Appveyor,以及 .circleci/config.yml 用于 CircleCI。Travis 和 Appveyor 的额外安装脚本可以在 testing/dependencies 文件夹中找到。
我们在 Travis GNU/Linux 基础设施上使用 CMake 3.5.2 和 CMake 3.12.1 测试这些配方。在 Travis macOS 基础设施上使用 CMake 3.12.1。在 Appveyor 上,测试使用 CMake 3.11.3。在 CircleCI 上,使用 CMake 3.12.1。
测试机制是一套包含在 testing 文件夹中的 Python 脚本。脚本 collect_tests.py 将运行测试并报告它们的状态。可以单独测试配方,也可以批量测试;collect_tests.py 接受一个正则表达式作为命令行输入,例如:
$ pipenv run python testing/collect_tests.py 'chapter-0[1,7]/recipe-0[1,2,5]'
此命令将运行第一章和第七章中第 1、2 和 5 个配方的测试。输出示例如下:
要获取更详细的输出,请设置VERBOSE_OUTPUT=ON:
$ env VERBOSE_OUTPUT=ON pipenv run python testing/collect_tests.py 'chapter-*/recipe-*'
报告问题和提出改进建议
请在github.com/dev-cafe/cmake-cookbook/issues报告问题。
为了贡献更改,我们建议分叉仓库github.com/dev-cafe/cmake-cookbook并使用拉取请求提交更改,遵循help.github.com/articles/creating-a-pull-request-from-a-fork/。
对于非简单的更改,我们建议首先在github.com/dev-cafe/cmake-cookbook/issues上打开一个问题来描述和讨论提议的更改,然后再发送拉取请求。
第二章:从简单的可执行文件到库
在本章中,我们将介绍以下内容:
- 将单个源文件编译成可执行文件
- 切换生成器
- 构建和链接静态和共享库
- 使用条件控制编译
- 向用户展示选项
- 指定编译器
- 切换构建类型
- 控制编译器标志
- 设置语言标准
- 使用控制流结构
引言
本章中的示例将引导您完成构建代码所需的基本任务:编译可执行文件、编译库、根据用户输入执行构建操作等。CMake 是一个构建系统生成器,特别适合于平台和编译器无关。我们努力在本章中展示这一方面。除非另有说明,所有示例都与操作系统无关;它们可以在不加修改的情况下在 GNU/Linux、macOS 和 Windows 上运行。
本书中的示例主要针对 C++项目,并使用 C++示例进行演示,但 CMake 也可用于其他语言的项目,包括 C 和 Fortran。对于任何给定的示例,只要合理,我们都尝试包括 C++、C 和 Fortran 的示例。这样,您就可以选择您喜欢的语言的示例。有些示例是专门为突出特定语言选择时需要克服的挑战而定制的。
将单个源文件编译成可执行文件
本示例的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-01/recipe-01找到,并提供了 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.org/cmake/help/latest/在线找到。我们将把 CMake 指令放入一个名为CMakeLists.txt的文件中。
文件名是区分大小写的;它必须被称为CMakeLists.txt,以便 CMake 能够解析它。
详细来说,以下是遵循的步骤:
- 使用您喜欢的编辑器打开一个文本文件。该文件将被命名为
CMakeLists.txt。 - 第一行设置 CMake 的最低要求版本。如果使用的 CMake 版本低于该版本,将发出致命错误:
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
- 第二行声明了项目的名称(recipe-01)和支持的语言(
CXX代表 C++):
project(recipe-01 LANGUAGES CXX)
- 我们指示 CMake 创建一个新的目标:可执行文件
hello-world。这个可执行文件是通过编译和链接源文件hello-world.cpp生成的。CMake 将使用所选编译器和构建自动化工具的默认设置:
add_executable(hello-world hello-world.cpp)
- 将文件保存在与源文件
hello-world.cpp相同的目录中。请记住,它只能被命名为CMakeLists.txt。 - 我们现在准备通过创建并进入构建目录来配置项目:
$ 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
- 如果一切顺利,项目配置已经在构建目录中生成。我们现在可以编译可执行文件了:
$ 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)
CMake 语言是不区分大小写的,但参数是区分大小写的。
CMake 中,C++是默认的编程语言。然而,我们建议始终在project命令中使用LANGUAGES选项明确声明项目的语言。
为了配置项目并生成其构建系统,我们必须通过命令行界面(CLI)运行 CMake。CMake CLI 提供了许多开关,cmake --help将输出屏幕上列出所有可用开关的完整帮助菜单。我们将在本书中了解更多关于它们的信息。正如您将从cmake --help的输出中注意到的,大多数开关将允许您访问 CMake 手册。生成构建系统的典型命令序列如下:
$ mkdir -p build $ cd build $ cmake ..
在这里,我们创建了一个目录,build,其中将生成构建系统,我们进入了build目录,并通过指向CMakeLists.txt的位置调用了 CMake(在这种情况下位于父目录中)。可以使用以下调用来实现相同的效果:
$ cmake -H. -Bbuild
这个调用是跨平台的,并引入了-H和-BCLI 开关。使用-H.我们指示 CMake 在当前目录中搜索根CMakeLists.txt文件。-Bbuild告诉 CMake 在名为build的目录中生成所有文件。
注意,cmake -H. -Bbuild调用 CMake 仍在进行标准化:cmake.org/pipermail/cmake-developers/2018-January/030520.html。这就是为什么我们在这本书中将使用传统方法(创建一个构建目录,进入它,并通过指向CMakeLists.txt的位置来配置项目)。
运行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
在CMakeLists.txt所在的同一目录中运行cmake .原则上足以配置项目。但是,CMake 随后会将所有生成的文件写入项目的根目录。这将是一个源内构建,通常是不希望的,因为它混合了项目的源代码和构建树。我们演示的源外构建是首选实践。
CMake 是一个生成器构建系统。您描述了构建系统(如 Unix Makefiles、Ninja、Visual Studio 等)需要执行的操作类型,以便编译您的代码。然后,CMake 为所选的构建系统生成相应的指令。默认情况下,在 GNU/Linux 和 macOS 系统上,CMake 使用 Unix Makefiles 生成器。在 Windows 上,Visual Studio 是默认生成器。我们将在下一个配方中更详细地了解生成器,并在第十三章,替代生成器和交叉编译中重新审视生成器。
在 GNU/Linux 上,CMake 将默认生成 Unix Makefiles 以构建项目:
Makefile:make将运行以构建项目的一组指令。CMakeFiles:该目录包含 CMake 用于检测操作系统、编译器等的临时文件。此外,根据所选的生成器,它还包含特定于项目的文件。cmake_install.cmake:一个 CMake 脚本,用于处理安装规则,在安装时使用。CMakeCache.txt:正如文件名所示,这是 CMake 的缓存文件。在重新运行配置时,CMake 会使用此文件。
要构建示例项目,我们运行了以下命令:
$ cmake --build .
此命令是一个通用的跨平台包装器,用于所选生成器的本地构建命令,在本例中为make。我们不应忘记测试我们的示例可执行文件:
$ ./hello-world Hello, CMake world!
最后,我们应该指出,CMake 不强制要求特定的名称或特定的位置用于构建目录。我们可以将其完全放置在项目路径之外。这将同样有效:
$ mkdir -p /tmp/someplace $ cd /tmp/someplace $ cmake /path/to/source $ cmake --build .
还有更多
官方文档位于cmake.org/runningcmake/,提供了运行 CMake 的简明概述。由 CMake 生成的构建系统,在上面的示例中为Makefile,将包含构建给定项目的对象文件、可执行文件和库的目标和规则。在当前示例中,hello-world可执行文件是我们唯一的目标,但是运行命令:
$ 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 语法选择这些目标,并实现以下目标:
all(或使用 Visual Studio 生成器时的ALL_BUILD)是默认目标,将构建项目中的所有其他目标。clean,是选择删除所有生成的文件的目标。depend,将调用 CMake 为源文件生成任何依赖项。rebuild_cache,将再次调用 CMake 来重建CMakeCache.txt。如果需要从源代码中添加新条目,这是必要的。edit_cache,这个目标将允许你直接编辑缓存条目。
对于更复杂的项目,包括测试阶段和安装规则,CMake 将生成额外的便利目标:
test(或使用 Visual Studio 生成器时的RUN_TESTS)将使用 CTest 运行测试套件。我们将在第四章,创建和运行测试中详细讨论测试和 CTest。install,将执行项目的安装规则。我们将在第十章,编写安装程序中讨论安装规则。package,这个目标将调用 CPack 来为项目生成可重新分发的包。打包和 CPack 将在第十一章,打包项目中讨论。
切换生成器
本配方的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-01/recipe-02找到,并提供了 C++、C 和 Fortran 的示例。本配方适用于 CMake 3.5(及以上)版本,并在 GNU/Linux、macOS 和 Windows 上进行了测试。
CMake 是一个构建系统生成器,单个CMakeLists.txt可以用于为不同平台上的不同工具链配置项目。你可以在CMakeLists.txt中描述构建系统需要运行的操作来配置和编译你的代码。基于这些指令,CMake 将为所选构建系统(Unix Makefiles、Ninja、Visual Studio 等)生成相应的指令。我们将在第十三章,替代生成器和交叉编译中重新讨论生成器。
准备工作
CMake 支持大量不同平台的原生构建工具。无论是命令行工具,如 Unix Makefiles 和 Ninja,还是集成开发环境(IDE)工具,都得到支持。你可以通过运行以下命令来获取你平台和已安装的 CMake 版本上可用的生成器的最新列表:
$ cmake --help
此命令的输出将列出 CMake 命令行界面的所有选项。在底部,你将找到可用生成器的列表。例如,这是在安装了 CMake 3.11.2 的 GNU/Linux 机器上的输出:
Generators The following generators are available on this platform: Unix Makefiles = Generates standard UNIX makefiles. Ninja = Generates build.ninja files. Watcom WMake = Generates Watcom WMake makefiles. CodeBlocks - Ninja = Generates CodeBlocks project files. CodeBlocks - Unix Makefiles = Generates CodeBlocks project files. CodeLite - Ninja = Generates CodeLite project files. CodeLite - Unix Makefiles = Generates CodeLite project files. Sublime Text 2 - Ninja = Generates Sublime Text 2 project files. Sublime Text 2 - Unix Makefiles = Generates Sublime Text 2 project files. Kate - Ninja = Generates Kate project files. Kate - Unix Makefiles = Generates Kate project files. Eclipse CDT4 - Ninja = Generates Eclipse CDT 4.0 project files. Eclipse CDT4 - Unix Makefiles= Generates Eclipse CDT 4.0 project files.
通过本配方,我们将展示为同一项目切换生成器是多么容易。
CMake 秘籍(一)(3)https://developer.aliyun.com/article/1524605