# 13.2 交叉编译hello world示例

**NOTE**:*此示例代码可以在* <https://github.com/dev-cafe/cmake-cookbook/tree/v1.0/chapter-13/recipe-01> *中找到，其中包含一个C++示例。该示例在CMake 3.5版(或更高版本)中是有效的，并且已经在GNU/Linux、macOS和Windows上进行过测试。*

这个示例中，我们将重用“Hello World”示例，并将代码从Linux或macOS交叉编译到Windows。换句话说，我们将在Linux或macOS上配置和编译代码，并生成Windows平台的可执行文件

## 准备工作

我们从`hello world`示例(`hello-world.cpp`)开始：

```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`：

```
# 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 cross environment)从源代码构建一套交叉编译器：<http://mxe.cc>

## 具体实施

我们将按照以下步骤，在这个交叉编译的“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)
   ```
3. 将`CMAKE_CXX_COMPILER`设置为对应的编译器(路径)。
4. 然后，通过将`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
   ```
5. 现在，构建可执行文件：

   ```
   $ 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
   ```
6. 注意，我们已经在Linux上获得`hello-world.exe`。将二进制文件复制到Windows上。
7. 在WIndows上可以看到如下的输出：

   ```
   Hello from Windows
   ```
8. 如你所见，这个二进制可以在Windows下工作。

## 工作原理

由于与目标环境(Windows)不同的主机环境(在本例中是GNU/Linux或macOS)上配置和构建代码，所以我们需要向CMake提供关于目标环境的信息，这些信息记录在`toolchain.cmake`文件中( <https://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)
```

## 更多信息

有关各种选项的更详细讨论，请参见： <https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html#cross-compiling>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://chenxiaowei.gitbook.io/cmake-cookbook/13.0-chinese/13.2-chinese.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
