CMake 秘籍(七)(1)https://developer.aliyun.com/article/1525421
准备就绪
本食谱的源代码树与前两个食谱类似:
. ├── cmake │ ├── FindPythonModule.cmake │ ├── FindSphinx.cmake │ └── UseBreathe.cmake ├── CMakeLists.txt ├── docs │ ├── code-reference │ │ ├── classes-and-functions.rst │ │ └── message.rst │ ├── conf.py.in │ ├── Doxyfile.in │ └── index.rst └── src ├── CMakeLists.txt ├── hello-world.cpp ├── Message.cpp └── Message.hpp
现在,docs
子目录中包含了Doxyfile.in
和conf.py.in
模板文件,分别用于 Doxygen 和 Sphinx 的设置。此外,我们还有一个code-reference
子目录。
紧随code-reference
的文件包含 Breathe 指令,以在 Sphinx 中包含 Doxygen 生成的文档:
Messaging classes ================= Message ------- .. doxygenclass:: Message :project: recipe-03 :members: :protected-members: :private-members:
这将输出Message
类的文档。
如何操作
src
目录中的CMakeLists.txt
文件未更改。根目录中的CMakeLists.txt
文件的唯一更改如下:
- 我们包含
UseBreathe.cmake
自定义模块:
list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") include(UseBreathe)
- 我们调用了
add_breathe_doc
函数。该函数在自定义模块中定义,并接受关键字参数来设置结合 Doxygen 和 Sphinx 的构建:
add_breathe_doc( SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/docs BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR}/_build CACHE_DIR ${CMAKE_CURRENT_BINARY_DIR}/_doctrees HTML_DIR ${CMAKE_CURRENT_BINARY_DIR}/html DOXY_FILE ${CMAKE_CURRENT_SOURCE_DIR}/docs/Doxyfile.in CONF_FILE ${CMAKE_CURRENT_SOURCE_DIR}/docs/conf.py.in TARGET_NAME docs COMMENT "HTML documentation" )
让我们检查UseBreatheDoc.cmake
模块。这遵循了我们之前两个配方中描述的明确优于隐式的相同模式。该模块详细描述如下:
- 文档生成依赖于 Doxygen:
find_package(Doxygen REQUIRED) find_package(Perl REQUIRED)
- 我们还依赖于 Python 解释器和
Sphinx
:
find_package(PythonInterp REQUIRED) find_package(Sphinx REQUIRED)
- 此外,我们还必须找到
breathe
Python 模块。我们使用FindPythonModule.cmake
模块:
include(FindPythonModule) find_python_module(breathe REQUIRED)
- 我们定义了
add_breathe_doc
函数。该函数有一个单值关键字参数,我们将使用cmake_parse_arguments
命令对其进行解析:
function(add_breathe_doc) set(options) set(oneValueArgs SOURCE_DIR BUILD_DIR CACHE_DIR HTML_DIR DOXY_FILE CONF_FILE TARGET_NAME COMMENT ) set(multiValueArgs) cmake_parse_arguments(BREATHE_DOC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) # ... endfunction()
BREATHE_DOC_CONF_FILE
模板文件用于 Sphinx,配置为conf.py
在BREATHE_DOC_BUILD_DIR
中:
configure_file( ${BREATHE_DOC_CONF_FILE} ${BREATHE_DOC_BUILD_DIR}/conf.py @ONLY )
- 相应地,Doxygen 的
BREATHE_DOC_DOXY_FILE
模板文件配置为Doxyfile
在BREATHE_DOC_BUILD_DIR
中:
configure_file( ${BREATHE_DOC_DOXY_FILE} ${BREATHE_DOC_BUILD_DIR}/Doxyfile @ONLY )
- 然后我们添加了自定义目标
BREATHE_DOC_TARGET_NAME
。请注意,只运行了 Sphinx;对 Doxygen 的必要调用在BREATHE_DOC_SPHINX_FILE
内部发生:
add_custom_target(${BREATHE_DOC_TARGET_NAME} COMMAND ${SPHINX_EXECUTABLE} -q -b html -c ${BREATHE_DOC_BUILD_DIR} -d ${BREATHE_DOC_CACHE_DIR} ${BREATHE_DOC_SOURCE_DIR} ${BREATHE_DOC_HTML_DIR} COMMENT "Building ${BREATHE_DOC_TARGET_NAME} documentation with Breathe, Sphinx and Doxygen" VERBATIM )
- 最后,向用户打印一条状态消息:
message(STATUS "Added ${BREATHE_DOC_TARGET_NAME} [Breathe+Sphinx+Doxygen] target to build documentation")
- 配置完成后,我们可以像往常一样构建文档:
$ mkdir -p build $ cd build $ cmake .. $ cmake --build . --target docs
文档将可在构建树的BREATHE_DOC_HTML_DIR
子目录中找到。启动浏览器打开index.html
文件后,您可以导航到Message
类的文档:
工作原理
您会注意到,尽管在声明自定义BREATHE_DOC_TARGET_NAME
目标时只给出了对 Sphinx 的调用,但 Doxygen 和 Sphinx 都运行了。这是由于 Sphinx 的conf.py
文件中定义的以下设置:
def run_doxygen(folder): """Run the doxygen make command in the designated folder""" try: retcode = subprocess.call("cd {}; doxygen".format(folder), shell=True) if retcode < 0: sys.stderr.write( "doxygen terminated by signal {}".format(-retcode)) except OSError as e: sys.stderr.write("doxygen execution failed: {}".format(e)) def setup(app): run_doxygen('@BREATHE_DOC_BUILD_DIR@')
Doxygen 将生成 XML 输出,Breathe 插件将能够以与所选 Sphinx 文档样式一致的形式呈现这些输出。
第十四章:替代生成器和跨编译
在本章中,我们将介绍以下内容:
- 在 Visual Studio 中构建 CMake 项目
- 跨编译一个 hello world 示例
- 使用 OpenMP 并行化跨编译 Windows 二进制文件
引言
CMake 本身并不构建可执行文件和库。相反,CMake 配置一个项目并生成由另一个构建工具或框架用来构建项目的文件。在 GNU/Linux 和 macOS 上,CMake 通常生成 Unix Makefiles,但存在许多替代方案。在 Windows 上,这些通常是 Visual Studio 项目文件或 MinGW 或 MSYS Makefiles。CMake 包含了一系列针对本地命令行构建工具或集成开发环境(IDEs)的生成器。您可以在以下链接了解更多信息:cmake.org/cmake/help/latest/manual/cmake-generators.7.html
。
这些生成器可以使用cmake -G
来选择,例如:
$ cmake -G "Visual Studio 15 2017"
并非所有生成器在每个平台上都可用,根据 CMake 运行的平台,通常只有一部分可用。要查看当前平台上所有可用的生成器列表,请输入以下内容:
$ cmake -G
在本章中,我们不会遍历所有可用的生成器,但我们注意到本书中的大多数配方都使用Unix Makefiles
、MSYS Makefiles
、Ninja
和Visual Studio 15 2017
生成器进行了测试。在本章中,我们将专注于在 Windows 平台上进行开发。我们将演示如何直接使用 Visual Studio 15 2017 构建 CMake 项目,而不使用命令行。我们还将讨论如何在 Linux 或 macOS 系统上跨编译 Windows 可执行文件。
使用 Visual Studio 2017 构建 CMake 项目
本配方的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-13/recipe-01
找到,并包含一个 C++示例。该配方适用于 CMake 版本 3.5(及以上),并在 Windows 上进行了测试。
虽然早期的 Visual Studio 版本要求开发者在不同的窗口中编辑源代码和运行 CMake 命令,但 Visual Studio 2017 引入了对 CMake 项目的内置支持(aka.ms/cmake
),允许整个编码、配置、构建和测试工作流程在同一个 IDE 中发生。在本节中,我们将测试这一点,并直接使用 Visual Studio 2017 构建一个简单的“hello world”CMake 示例项目,而不求助于命令行。
准备工作
首先,我们将使用 Windows 平台,下载并安装 Visual Studio Community 2017(www.visualstudio.com/downloads/
)。在撰写本文时,该版本可免费使用 30 天试用期。我们将遵循的步骤也在此视频中得到了很好的解释:www.youtube.com/watch?v=_lKxJjV8r3Y
。
在运行安装程序时,请确保在左侧面板中选择“使用 C++的桌面开发”,并验证“Visual C++工具用于 CMake”在右侧的摘要面板中被选中:
在 Visual Studio 2017 15.4 中,您还可以为 Linux 平台编译代码。为此,请在其他工具集中选择“Linux 开发与 C++”:
启用此选项后,您可以从 Visual Studio 内部为 Windows 和 Linux 机器编译代码,前提是您已配置了对 Linux 服务器的访问。但是,我们不会在本章中演示这种方法。
在本节中,我们将在 Windows 上构建 Windows 二进制文件,我们的目标是配置和构建以下示例代码(hello-world.cpp
):
#include <cstdlib> #include <iostream> #include <string> const std::string cmake_system_name = SYSTEM_NAME; int main() { std::cout << "Hello from " << cmake_system_name << std::endl; return EXIT_SUCCESS; }
操作方法
要创建相应的源代码,请按照以下步骤操作:
- 创建一个目录并将
hello-world.cpp
文件放入新创建的目录中。 - 在此目录中,创建一个
CMakeLists.txt
文件,其中包含以下内容:
# set minimum cmake version cmake_minimum_required(VERSION 3.5 FATAL_ERROR) # project name and language project(recipe-01 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) include(GNUInstallDirs) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) # define executable and its source file add_executable(hello-world hello-world.cpp) # we will print the system name in the code target_compile_definitions(hello-world PUBLIC "SYSTEM_NAME=\"${CMAKE_SYSTEM_NAME}\"" ) install( TARGETS hello-world DESTINATION ${CMAKE_INSTALL_BINDIR} )
- 打开 Visual Studio 2017,然后导航到包含源文件和
CMakeLists.txt
的新建文件夹,通过以下方式:文件 | 打开 | 文件夹。 - 一旦文件夹打开,请注意 CMake 配置步骤是如何自动运行的(底部面板):
- 现在,我们可以右键单击
CMakeLists.txt
(右侧面板)并选择“构建”:
- 这构建了项目(请参见底部面板的输出):
- 这样就成功编译了可执行文件。在下一个子节中,我们将学习如何定位可执行文件,并可能更改构建和安装路径。
工作原理
我们已经看到,Visual Studio 2017 很好地与 CMake 接口,并且我们已经能够从 IDE 内部配置和构建代码。除了构建步骤,我们还可以运行安装或测试步骤。这些可以通过右键单击CMakeLists.txt
(右侧面板)来访问。
然而,配置步骤是自动运行的,我们可能更倾向于修改配置选项。我们还希望知道实际的构建和安装路径,以便我们可以测试我们的可执行文件。为此,我们可以选择 CMake | 更改 CMake 设置,然后我们到达以下屏幕:
在左上角的面板中,我们现在可以检查和修改生成器(在本例中为 Ninja)、设置、参数以及路径。构建路径在上面的截图中突出显示。设置被分组到构建类型(x86-Debug
、x86-Release
等)中,我们可以在顶部面板栏的中间在这些构建类型之间切换。
现在我们知道实际的构建路径,我们可以测试编译的可执行文件:
$ ./hello-world.exe Hello from Windows
当然,构建和安装路径可以进行调整。
另请参阅
- Visual Studio 中的 CMake 支持:
aka.ms/cmake
- 使用 CMake 进行 Linux 开发的 Visual C++:
blogs.msdn.microsoft.com/vcblog/2017/08/25/visual-c-for-linux-development-with-cmake/
- Visual Studio 的官方文档:
www.visualstudio.com/vs/features/ide/
交叉编译一个“Hello World”示例
本配方的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-13/recipe-01
找到,并包含一个 C++示例。本配方适用于 CMake 版本 3.5(及以上),并在 GNU/Linux 和 macOS 上进行了测试。
在本配方中,我们将重用上一个配方中的“Hello World”示例,并从 Linux 或 macOS 交叉编译到 Windows。换句话说,我们将在 Linux 或 macOS 上配置和编译代码,并获得一个 Windows 平台的可执行文件。
准备工作
我们从一个简单的“Hello World”示例开始(hello-world.cpp
):
#include <cstdlib> #include <iostream> #include <string> const std::string cmake_system_name = SYSTEM_NAME; int main() { std::cout << "Hello from " << cmake_system_name << std::endl; return EXIT_SUCCESS; }
我们还将使用上一个配方中未更改的CMakeLists.txt
:
# set minimum cmake version cmake_minimum_required(VERSION 3.5 FATAL_ERROR) # project name and language project(recipe-01 LANGUAGES CXX) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD_REQUIRED ON) include(GNUInstallDirs) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}) # define executable and its source file add_executable(hello-world hello-world.cpp) # we will print the system name in the code target_compile_definitions(hello-world PUBLIC "SYSTEM_NAME=\"${CMAKE_SYSTEM_NAME}\"" ) install( TARGETS hello-world DESTINATION ${CMAKE_INSTALL_BINDIR} )
为了交叉编译源代码,我们需要安装一个 C++的交叉编译器,以及可选的 C 和 Fortran 编译器。一个选项是使用打包的 MinGW 编译器。作为打包的交叉编译器的替代方案,我们还可以使用 MXE(M 交叉环境)从源代码构建一套交叉编译器:mxe.cc
。
CMake 秘籍(七)(3)https://developer.aliyun.com/article/1525426