如何使用 Conan 交叉编译你的应用程序:主机和构建上下文

请先克隆源代码以重新创建此项目。您可以在 GitHub 上的examples2 仓库中找到它们。

$ git clone https://github.com/conan-io/examples2.git
$ cd examples2/tutorial/consuming_packages/cross_building

在之前的示例中,我们学习了如何使用 conanfile.pyconanfile.txt 构建一个使用 ZlibCMake Conan 包来压缩字符串的应用程序。 此外,我们解释了你可以在名为 Conan profile 的文件中设置操作系统、编译器或构建配置等信息。 你可以将该 profile 作为参数 (--profile) 来调用 conan install 命令。 我们还解释了不指定该 profile 等同于使用 --profile=default 参数。

对于所有这些示例,我们使用相同的平台来构建和运行应用程序。 但是,如果你想在你的运行 Ubuntu Linux 的机器上构建应用程序,然后在另一个平台(如 Raspberry Pi)上运行它呢? Conan 可以使用两个不同的 profile 来模拟这种情况,一个用于构建应用程序的机器(Ubuntu Linux),另一个用于运行应用程序的机器(Raspberry Pi)。 我们将在下一节中解释这种“两个 profile”的方法。

Conan 两个 profile 模型:构建和主机 profile

即使你在调用 Conan 时只指定一个 --profile 参数,Conan 也会在内部使用两个 profile。 一个用于构建二进制文件的机器(称为 **build** profile),另一个用于运行这些二进制文件的机器(称为 **host** profile)。 调用此命令

$ conan install . --build=missing --profile=someprofile

等同于

$ conan install . --build=missing --profile:host=someprofile --profile:build=default

正如你所看到的,我们使用了两个新的参数

  • profile:host:这个 profile 定义了构建的二进制文件将运行的平台。 对于我们的字符串压缩器应用程序,这个 profile 将应用于将在 **Raspberry Pi** 上运行的 Zlib 库。

  • profile:build:这个 profile 定义了将构建二进制文件的平台。 对于我们的字符串压缩器应用程序,这个 profile 将被 CMake 工具用来在 **Ubuntu Linux** 机器上编译它。

请注意,当你只使用一个参数来指定 profile --profile 时,它等同于 --profile:host。 如果你没有指定 --profile:build 参数,Conan 将在内部使用 *default* profile。

因此,如果我们想在 Ubuntu Linux 机器上构建压缩器应用程序,但在 Raspberry Pi 上运行它,我们应该使用两个不同的 profile。 对于 **build** 机器,我们可以使用默认 profile,在我们的例子中,它看起来像这样

<conan home>/profiles/default
[settings]
os=Linux
arch=x86_64
build_type=Release
compiler=gcc
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=12

而 Raspberry Pi 的 profile,即 **host** 机器的 profile

<local folder>/profiles/raspberry
[settings]
os=Linux
arch=armv7hf
compiler=gcc
build_type=Release
compiler.cppstd=gnu17
compiler.libcxx=libstdc++11
compiler.version=12
[buildenv]
CC=arm-linux-gnueabihf-gcc-12
CXX=arm-linux-gnueabihf-g++-12
LD=arm-linux-gnueabihf-ld

重要

请注意,为了成功构建这个示例,你应该已经安装了一个包含编译器和所有构建应用程序所需工具的工具链。 在这种情况下,host 机器是具有 armv7hf 架构操作系统的 Raspberry Pi 3,并且我们在 Ubuntu 机器上安装了 arm-linux-gnueabihf 工具链。

如果你查看 raspberry profile,会有一个名为 [buildenv] 的部分。 这个部分用于设置构建应用程序所需的环境变量。 在这种情况下,我们声明了 CCCXXLD 变量,分别指向交叉构建工具链编译器和链接器。 将此部分添加到 profile 将在每次执行 conan install 时调用 VirtualBuildEnv 生成器。 该生成器会将这些环境信息添加到 conanbuild.sh 脚本中,我们将在使用 CMake 构建之前 source 它,以便它可以利用交叉构建工具链。

注意

在某些情况下,你没有在构建平台上可用的工具链。 对于这些情况,你可以使用 Conan 包来获取交叉编译器,并将其添加到 profile 的 [tool_requires] 部分。 有关使用工具链包进行交叉构建的示例,请查看 这个示例

构建和主机上下文

现在我们已经准备好了两个 profile,让我们看一下我们的 conanfile.py

conanfile.py
from conan import ConanFile
from conan.tools.cmake import cmake_layout

class CompressorRecipe(ConanFile):
    settings = "os", "compiler", "build_type", "arch"
    generators = "CMakeToolchain", "CMakeDeps"

    def requirements(self):
        self.requires("zlib/1.3.1")

    def build_requirements(self):
        self.tool_requires("cmake/3.27.9")

    def layout(self):
        cmake_layout(self)

正如你所看到的,这与我们在 上一个示例 中使用的 conanfile.py 几乎相同。 我们将需要 zlib/1.3.1 作为常规依赖项,并将 cmake/3.27.9 作为构建应用程序所需的工具。

我们需要应用程序为 Raspberry Pi 构建,并使用交叉构建工具链,并链接为同一平台构建的 zlib/1.3.1 库。 另一方面,我们需要 cmake/3.27.9 二进制文件在 Ubuntu Linux 上运行。 Conan 在内部管理这些内容,在依赖关系图中区分我们所说的“构建上下文”和“主机上下文”

  • **host 上下文** 包含根包(在 conan installconan create 命令中指定的包)及其通过 self.requires() 添加的所有需求。 在这种情况下,这包括压缩器应用程序和 zlib/1.3.1 依赖项。

  • **build 上下文** 包含构建机器上使用的工具需求。 此类别通常包括所有开发工具,如 CMake、编译器和链接器。 在这种情况下,这包括 cmake/3.27.9 工具。

这些上下文定义了 Conan 如何管理每个依赖项。 例如,由于 zlib/1.3.1 属于 **host 上下文**,我们在 **raspberry** profile(host profile)中定义的 [buildenv] 构建环境仅在构建 zlib/1.3.1 库时应用,并且不会影响属于 **build 上下文** 的任何内容,例如 cmake/3.27.9 依赖项。

现在,让我们构建应用程序。 首先,使用构建和主机平台的 profile 调用 conan install。 这将安装为 armv7hf 架构构建的 zlib/1.3.1 依赖项,以及在 64 位架构上运行的 cmake/3.27.9 版本。

$ conan install . --build missing -pr:b=default -pr:h=./profiles/raspberry

然后,让我们调用 CMake 来构建应用程序。 像在之前的示例中一样,我们必须通过运行 source Release/generators/conanbuild.sh 来激活 **build 环境**。 这将设置定位交叉构建工具链和构建应用程序所需的环境变量。

$ cd build
$ source Release/generators/conanbuild.sh
Capturing current environment in deactivate_conanbuildenv-release-armv7hf.sh
Configuring environment variables
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=Release/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Release
$ cmake --build .
...
-- Conan toolchain: C++ Standard 17 with extensions ON
-- The C compiler identification is GNU 12.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/arm-linux-gnueabihf-gcc-12 - skipped
-- Detecting C compile features
-- Detecting C compile features - done    [100%] Built target compressor
...
$ source Release/generators/deactivate_conanbuild.sh

你可以通过运行 Linux 实用程序 file 来检查我们是否为正确的架构构建了应用程序

$ file compressor
compressor: ELF 32-bit LSB shared object, ARM, EABI5 version 1 (SYSV), dynamically
linked, interpreter /lib/ld-linux-armhf.so.3,
BuildID[sha1]=2a216076864a1b1f30211debf297ac37a9195196, for GNU/Linux 3.2.0, not
stripped