CMake 秘籍(七)(3)

简介: CMake 秘籍(七)

CMake 秘籍(七)(2)https://developer.aliyun.com/article/1525424

如何操作

我们将按照以下步骤在这个交叉编译的“Hello World”示例中创建三个文件:

  1. 创建一个目录,其中包含hello-world.cpp和前面列出的CMakeLists.txt
  2. 创建一个toolchain.cmake文件,其中包含以下内容:
# the name of the target operating system
set(CMAKE_SYSTEM_NAME Windows)
# which compilers to use
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
# adjust the default behaviour of the find commands:
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
  1. CMAKE_CXX_COMPILER调整为相应的编译器(路径)。
  2. 然后,通过指向CMAKE_TOOLCHAIN_FILE到工具链文件来配置代码(在本例中,使用了从源代码构建的 MXE 编译器):
$ mkdir -p build
$ cd build
$ cmake -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake .. 
-- The CXX compiler identification is GNU 5.4.0
-- Check for working CXX compiler: /home/user/mxe/usr/bin/i686-w64-mingw32.static-g++
-- Check for working CXX compiler: /home/user/mxe/usr/bin/i686-w64-mingw32.static-g++ -- 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-recipes/chapter-13/recipe-01/cxx-example/build
  1. 现在,让我们构建可执行文件:
$ cmake --build .
Scanning dependencies of target hello-world
[ 50%] Building CXX object CMakeFiles/hello-world.dir/hello-world.cpp.obj
[100%] Linking CXX executable bin/hello-world.exe
[100%] Built target hello-world
  1. 请注意,我们在 Linux 上获得了hello-world.exe。将二进制文件复制到 Windows 计算机。
  2. 在 Windows 计算机上,我们可以观察到以下输出:
Hello from Windows
  1. 如您所见,该二进制文件在 Windows 上运行!

它是如何工作的

由于我们在与目标环境(Windows)不同的宿主环境(在这种情况下,GNU/Linux 或 macOS)上配置和构建代码,我们需要向 CMake 提供有关目标环境的信息,我们已经在toolchain.cmake文件中对其进行了编码(cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling)。

首先,我们提供目标操作系统的名称:

set(CMAKE_SYSTEM_NAME Windows)

然后,我们指定编译器,例如:

set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
set(CMAKE_Fortran_COMPILER i686-w64-mingw32-gfortran)

在这个简单的例子中,我们不需要检测任何库或头文件,但如果需要,我们将使用以下方式指定根路径:

set(CMAKE_FIND_ROOT_PATH /path/to/target/environment)

目标环境可以是例如由 MXE 安装提供的环境。

最后,我们调整 find 命令的默认行为。我们指示 CMake 在目标环境中搜索头文件和库:

set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

并在宿主环境中搜索程序:

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

另请参阅

有关各种选项的更详细讨论,请参阅cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling

使用 OpenMP 并行化交叉编译 Windows 二进制文件

本食谱的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-13/recipe-02找到,并包含 C++和 Fortran 示例。本食谱适用于 CMake 版本 3.9(及以上),并在 GNU/Linux 上进行了测试。

在本食谱中,我们将应用在前一个食谱中学到的知识,尽管是针对一个更有趣和更现实的例子:我们将交叉编译一个使用 OpenMP 并行化的 Windows 二进制文件。

准备工作

我们将使用第三章,检测外部库和程序,食谱 5,检测 OpenMP 并行环境中的未修改源代码。示例代码计算所有自然数到N的总和(example.cpp):

#include <iostream>
#include <omp.h>
#include <string>
int main(int argc, char *argv[]) {
  std::cout << "number of available processors: " << omp_get_num_procs()
            << std::endl;
  std::cout << "number of threads: " << omp_get_max_threads() << std::endl;
  auto n = std::stol(argv[1]);
  std::cout << "we will form sum of numbers from 1 to " << n << std::endl;
  // start timer
  auto t0 = omp_get_wtime();
  auto s = 0LL;
#pragma omp parallel for reduction(+ : s)
  for (auto i = 1; i <= n; i++) {
    s += i;
  }
  // stop timer
  auto t1 = omp_get_wtime();
  std::cout << "sum: " << s << std::endl;
  std::cout << "elapsed wall clock time: " << t1 - t0 << " seconds" << std::endl;
  return 0;
}

CMakeLists.txt文件与第三章,检测外部库和程序,食谱 5,检测 OpenMP 并行环境相比,基本上没有变化,除了增加了一个安装目标:

# set minimum cmake version
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
# project name and language
project(recipe-02 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})
find_package(OpenMP REQUIRED)
add_executable(example example.cpp)
target_link_libraries(example
  PUBLIC
    OpenMP::OpenMP_CXX
  )
install(
  TARGETS
    example
  DESTINATION
    ${CMAKE_INSTALL_BINDIR}
  )

如何操作

通过以下步骤,我们将能够交叉编译一个使用 OpenMP 并行化的 Windows 可执行文件:

  1. 创建一个目录,其中包含之前列出的example.cppCMakeLists.txt
  2. 我们将使用与前一个食谱相同的toolchain.cmake
# the name of the target operating system
set(CMAKE_SYSTEM_NAME Windows)
# which compilers to use
set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)
# adjust the default behaviour of the find commands:
# search headers and libraries in the target environment
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# search programs in the host environment
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
  1. CMAKE_CXX_COMPILER调整为相应的编译器(路径)。
  2. 然后,通过指向CMAKE_TOOLCHAIN_FILE到工具链文件来配置代码(在本例中,使用了从源代码构建的 MXE 编译器):
$ mkdir -p build
$ cd build
$ cmake -D CMAKE_TOOLCHAIN_FILE=toolchain.cmake .. 
-- The CXX compiler identification is GNU 5.4.0
-- Check for working CXX compiler: /home/user/mxe/usr/bin/i686-w64-mingw32.static-g++
-- Check for working CXX compiler: /home/user/mxe/usr/bin/i686-w64-mingw32.static-g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Found OpenMP_CXX: -fopenmp (found version "4.0")
-- Found OpenMP: TRUE (found version "4.0")
-- Configuring done
-- Generating done
-- Build files have been written to: /home/user/cmake-recipes/chapter-13/recipe-02/cxx-example/build
  1. 现在,让我们构建可执行文件:
$ cmake --build .
Scanning dependencies of target example
[ 50%] Building CXX object CMakeFiles/example.dir/example.cpp.obj
[100%] Linking CXX executable bin/example.exe
[100%] Built target example
  1. 将二进制文件example.exe复制到 Windows 计算机。
  2. 在 Windows 计算机上,我们可以看到以下示例输出:
$ set OMP_NUM_THREADS=1
$ example.exe 1000000000
number of available processors: 2
number of threads: 1
we will form sum of numbers from 1 to 1000000000
sum: 500000000500000000
elapsed wall clock time: 2.641 seconds
$ set OMP_NUM_THREADS=2
$ example.exe 1000000000
number of available processors: 2
number of threads: 2
we will form sum of numbers from 1 to 1000000000
sum: 500000000500000000
elapsed wall clock time: 1.328 seconds
  1. 正如我们所见,二进制文件在 Windows 上运行,并且我们可以观察到由于 OpenMP 并行化带来的速度提升!

它是如何工作的

我们已成功使用简单的工具链进行交叉编译,在 Windows 平台上构建了用于并行执行的可执行文件。我们能够通过设置OMP_NUM_THREADS来指定 OpenMP 线程的数量。从 1 个线程增加到 2 个线程,我们观察到运行时间从 2.6 秒减少到 1.3 秒。有关工具链文件的讨论,请参阅之前的配方。

还有更多

可以为一组目标平台进行交叉编译,例如 Android。有关示例,我们请读者参考cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html

第十五章:测试仪表板

在本章中,我们将介绍以下内容:

  • 将测试部署到 CDash 仪表板
  • 向 CDash 仪表板报告测试覆盖率
  • 使用 AddressSanitizer 并向 CDash 报告内存缺陷
  • 使用 ThreadSanitizer 并向 CDash 报告数据竞争

引言

CDash 是一个 Web 服务,用于聚合 CTest 在测试运行、夜间测试或在持续集成设置中报告的测试结果。向仪表板报告就是我们所说的CDash 时间,如下图所示:

在本章中,我们将演示如何向 CDash 仪表板报告测试结果。我们将讨论报告测试覆盖率的策略,以及使用 AddressSanitizer 和 ThreadSanitizer 等工具收集的内存缺陷和数据竞争。

向 CDash 报告有两种方式:通过构建的测试目标或使用 CTest 脚本。我们将在前两个食谱中演示测试目标的方法,并在最后两个食谱中使用 CTest 脚本的方法。

设置 CDash 仪表板

CDash 的安装需要一个带有 PHP 和 SSL 启用的 Web 服务器(Apache、NGINX 或 IIS),以及访问 MySQL 或 PostgreSQL 数据库服务器的权限。本书不详细讨论 CDash Web 服务的设置;我们建议读者参考其官方文档,网址为public.kitware.com/Wiki/CDash:Installation

安装 CDash 实例不是本章食谱的必要条件,因为 Kitware 提供了两个公共仪表板(my.cdash.orgopen.cdash.org)。我们将在食谱中引用前者。

对于决定自行安装 CDash 实例的读者,我们建议使用 MySQL 后端,因为这似乎是my.cdash.orgopen.cdash.org所使用的配置,并且社区对其进行了更充分的测试。

也可以使用 Docker 来部署 CDash 实例,而无需太多努力。目前,在 CDash 问题跟踪器上有一个关于官方镜像的请求,网址为github.com/Kitware/CDash/issues/562

将测试部署到 CDash 仪表板

本食谱的代码可在github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-14/recipe-01找到,并包含一个 C++示例。该食谱适用于 CMake 版本 3.5(及以上),并在 GNU/Linux、macOS 和 Windows 上进行了测试。

在本食谱中,我们将扩展第 1 个食谱,即“创建一个简单的单元测试”,来自第四章,“创建和运行测试”,并将测试结果部署到my.cdash.org/index.php?project=cmake-cookbook,这是我们在公共仪表板(my.cdash.org)上为本书创建的,由 Kitware 提供给社区。

准备工作

我们将从重用第 1 个食谱,即“创建一个简单的单元测试”,来自第四章,“创建和运行测试”的示例源代码开始,该示例对作为命令行参数给出的整数求和。示例由三个源文件组成:main.cppsum_integers.cppsum_integers.hpp。这些源文件保持不变。我们还将重用来自第四章,“创建和运行测试”的文件test.cpp,但将其重命名为test_short.cpp。我们将通过test_long.cpp扩展示例,其中包含以下代码:

#include "sum_integers.hpp"
#include <numeric>
#include <vector>
int main() {
  // creates vector {1, 2, 3, ..., 999, 1000}
  std::vector<int> integers(1000);
  std::iota(integers.begin(), integers.end(), 1);
  if (sum_integers(integers) == 500500) {
    return 0;
  } else {
    return 1;
  }
}

然后,我们将这些文件组织成以下文件树:

.
├── CMakeLists.txt
├── CTestConfig.cmake
├── src
│   ├── CMakeLists.txt
│   ├── main.cpp
│   ├── sum_integers.cpp
│   └── sum_integers.hpp
└── tests
    ├── CMakeLists.txt
    ├── test_long.cpp
    └── test_short.cpp

如何做到这一点

现在,我们将描述如何配置、构建、测试,最后,将我们示例项目的测试结果提交到仪表板:

  1. 源目标在src/CMakeLists.txt中定义,如下所示:
# example library
add_library(sum_integers "")
target_sources(sum_integers
  PRIVATE
    sum_integers.cpp
  PUBLIC
    ${CMAKE_CURRENT_LIST_DIR}/sum_integers.hpp
  )
target_include_directories(sum_integers
  PUBLIC
    ${CMAKE_CURRENT_LIST_DIR}
  )
# main code
add_executable(sum_up main.cpp)
target_link_libraries(sum_up sum_integers)
  1. 测试在tests/CMakeLists.txt中定义:
add_executable(test_short test_short.cpp)
target_link_libraries(test_short sum_integers)
add_executable(test_long test_long.cpp)
target_link_libraries(test_long sum_integers)
add_test(
  NAME
    test_short
  COMMAND
    $<TARGET_FILE:test_short>
  )
add_test(
  NAME
    test_long
  COMMAND
    $<TARGET_FILE:test_long>
  )
  1. 顶级CMakeLists.txt文件引用了前面两个文件,本食谱中的新元素是包含include(CTest)的行,它允许我们向 CDash 仪表板报告:
# set minimum cmake version
cmake_minimum_required(VERSION 3.5 FATAL_ERROR)
# project name and language
project(recipe-01 LANGUAGES CXX)
# require C++11
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
# process src/CMakeLists.txt
add_subdirectory(src)
enable_testing()
# allow to report to a cdash dashboard
include(CTest)
# process tests/CMakeLists.txt
add_subdirectory(tests)
  1. 此外,我们在顶级CMakeLists.txt文件所在的同一目录中创建了文件CTestConfig.cmake。这个新文件包含以下行:
set(CTEST_DROP_METHOD "http")
set(CTEST_DROP_SITE "my.cdash.org")
set(CTEST_DROP_LOCATION "/submit.php?project=cmake-cookbook")
set(CTEST_DROP_SITE_CDASH TRUE)
  1. 我们现在准备配置并构建项目,如下所示:
$ mkdir -p build
$ cd build
$ cmake ..
$ cmake --build .
  1. 在构建代码之后,我们可以运行测试集并将测试结果报告给仪表板:
$ ctest --dashboard Experimental
 Site: larry
 Build name: Linux-c++
Create new tag: 20180408-1449 - Experimental
Configure project
 Each . represents 1024 bytes of output
 . Size of output: 0K
Build project
 Each symbol represents 1024 bytes of output.
 '!' represents an error and '*' a warning.
 . Size of output: 0K
 0 Compiler errors
 0 Compiler warnings
Test project /home/user/cmake-recipes/chapter-15/recipe-01/cxx-example/build
 Start 1: test_short
1/2 Test #1: test_short ....................... Passed 0.00 sec
 Start 2: test_long
2/2 Test #2: test_long ........................ Passed 0.00 sec
100% tests passed, 0 tests failed out of 2
Total Test time (real) = 0.01 sec
Performing coverage
 Cannot find any coverage files. Ignoring Coverage request.
Submit files (using http)
 Using HTTP submit method
 Drop site:http://my.cdash.org/submit.php?project=cmake-cookbook
 Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Build.xml
 Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Configure.xml
 Uploaded: /home/user/cmake-recipes/chapter-14/recipe-01/cxx-example/build/Testing/20180408-1449/Test.xml
 Submission successful
  1. 最后,我们可以在浏览器中浏览测试结果(在本例中,测试结果被报告给my.cdash.org/index.php?project=cmake-cookbook):)

CMake 秘籍(七)(4)https://developer.aliyun.com/article/1525427

相关文章
|
6月前
|
编译器 Shell 开发工具
CMake 秘籍(八)(5)
CMake 秘籍(八)
36 2
|
6月前
|
消息中间件 Unix C语言
CMake 秘籍(二)(5)
CMake 秘籍(二)
131 1
|
6月前
|
Shell Linux C++
CMake 秘籍(六)(4)
CMake 秘籍(六)
49 1
|
6月前
|
Linux C++ iOS开发
CMake 秘籍(三)(4)
CMake 秘籍(三)
38 1
|
6月前
|
编译器 Linux C++
CMake 秘籍(二)(1)
CMake 秘籍(二)
36 0
|
6月前
|
Linux C++ iOS开发
CMake 秘籍(四)(1)
CMake 秘籍(四)
28 0
|
6月前
|
编译器 Linux 开发工具
CMake 秘籍(四)(2)
CMake 秘籍(四)
26 0
|
6月前
|
Linux C++ iOS开发
CMake 秘籍(七)(1)
CMake 秘籍(七)
31 0
|
6月前
|
编译器 开发工具
CMake 秘籍(八)(2)
CMake 秘籍(八)
31 0
|
6月前
|
并行计算 编译器 Linux
CMake 秘籍(二)(3)
CMake 秘籍(二)
32 0