树莓派是一款令人惊叹的微型电脑,功能强大且价格低廉,是全球业余爱好者和开发者的梦想。凭借其在嵌入式系统中的应用潜力,使用 C/C++ 开发其代码非常普遍,尤其是在效率和性能至关重要的场合。然而,即使它是一款性价比很高的强大系统,在构建相对较大的 C/C++ 库时也可能速度较慢,因此交叉编译对于开发人员来说非常方便。

本文将介绍如何在 Windows 上设置到树莓派的交叉编译(从 Linux 上也可以实现,配置非常相似)。我们将交叉编译一个库,将 Conan 包上传到服务器(可以是 conan_server、conan.io 或 Artifactory),然后从树莓派安装和使用该库,即构建一个应用程序并将其链接到这个交叉编译的库。作为额外收获,本文还将解释如何将源代码捆绑到包本身,以便稍后从树莓派调试库。

Hello world 库

将要构建和打包的库存在于此 GitHub 仓库中。它只是一个简单的 C++ “Hello world” 库项目,使用 CMake,没有任何特殊或与 Conan 相关的内容。

然后,我们将从使用 conan new 命令创建的包模板开始。

$ conan new Hello/0.1@user/testing -t  # use your own user

现在,让我们用以下内容替换根目录下的 conanfile.py

from conans import ConanFile, CMake
import os

class HelloConan(ConanFile):
    name = "Hello"
    version = "0.1"
    settings = "os", "compiler", "build_type", "arch"

    def source(self):
        self.run("git clone https://github.com/memsharded/hello.git")

    def build(self):
        cmake = CMake(self.settings)
        gcc_dbg_src = ""
        if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
            gcc_dbg_src =  ' -DCMAKE_CXX_FLAGS="-fdebug-prefix-map=%s/hello=src"' % os.getcwd()
        self.run('cmake hello %s %s' % (cmake.command_line, gcc_dbg_src))
        self.run("cmake --build . %s" % cmake.build_config)

    def package(self):
        self.copy("*.h", dst="include", src="hello")
        if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
            self.copy("*.cpp", dst="src", src="hello")
        self.copy("*.lib", dst="lib", keep_path=False)
        self.copy("*.a", dst="lib", keep_path=False)

    def package_info(self):
        self.cpp_info.libs = ["hello"]

它与模板创建的非常相似,但有两个细微的差别。首先,由于我们希望能够在树莓派中调试包,因此有必要定义 gcc 标志 debug-prefix-map,以便它指向相对源文件夹 src 而不是原始文件夹。因为我们将在不同的机器上进行调试,原始的绝对源路径将毫无意义。因此,我们只是为 CMake 定义了该标志(有条件地针对 gccDebug 设置)。

if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
    gcc_dbg_src =  ' -DCMAKE_CXX_FLAGS="-fdebug-prefix-map=%s/hello=src"' % os.getcwd()

然后,我们只需将源文件 *.cpp 复制到最终包中(也仅针对相同的设置)。

if self.settings.compiler == "gcc" and self.settings.build_type == "Debug":
    self.copy("*.cpp", dst="src", src="hello")

可以通过运行以下命令测试此包配方:

$ conan test_package
Hello world!

设置交叉编译工具链

在本例中,我们将使用 SysProg 工具链。我们使用 4.6.3 工具链,并带有完整的 sysroot,这非常方便。我们下载工具,将其安装到 C:\SysGCC\Raspberry 中,并将 C:/SysGCC/Raspberry/bin/ 添加到系统 PATH 中。

现在,我们可以像 conan test_package -e CXX=some_gcc_compiler 那样将交叉编译器作为参数指定给 Conan 命令,但我们可以使用 **Conan 配置文件** 使其更简单。因此,我们在 <userhome>/.conan/profiles/rpi_gcc46 中创建一个包含以下内容的文件:

[settings]
    os: Linux
    compiler: gcc
    compiler.version: 4.6
    compiler.libcxx: libstdc++
    build_type: Debug
    arch: armv6
[env]
    CC=arm-linux-gnueabihf-gcc
    CXX=arm-linux-gnueabihf-g++

请注意,需要定义 armv6 架构和 Linux 设置,因为默认的 Conan 设置将对应于 Windows 开发环境。

由于生成的二进制文件在 Windows 上不可执行,因此我们更改了 test_package/conanfile.py,以便 test() 方法只检查二进制文件是否存在。

def test(self):
    if platform.system () != self.settings.os:
        assert os.path.exists("bin/example")
    else:
        self.run(os.sep.join([".", "bin", "example"]))

通过此配置,创建用于 R-PI 的调试包只需执行以下操作:

$ conan test_package -pr=rpi_gcc46

上传并安装到树莓派

在本地创建包后,可以将其上传到任何 Conan 远程服务器(conan.io、Artifactory、conan_server)。

$ conan upload Hello/0.1@user/testing -r=myremote --all

在树莓派端,我们将创建一个非常简单的使用者项目,其中包含一个 example.cpp 文件。

#include "hello.h"
int main(){
   hello();
}

一个用于构建它的 CMakeLists.txt 脚本。

Project(Consumer)
cmake_minimum_required(VERSION 2.8.9)

include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()

add_executable(example example.cpp)
target_link_libraries(example ${CONAN_LIBS})

以及用于安装依赖项的 conanfile.txt

[requires]
Hello/0.1@diego/testing

[generators]
cmake

[imports]
src, *.cpp -> src

请注意,.cpp 源文件是如何从包中复制(“导入”)到当前二进制文件夹的,以便调试器可以轻松找到它们。

安装交叉编译的“Hello”包很容易,现在我们不需要配置文件,因为 R-PI 的默认设置很好,所以我们只需设置 build_type

$ conan install .. -s build_type=Debug

构建和调试

构建我们的 R-PI 应用程序现在是一个标准的 CMake 过程。

$ mkdir build && cd build
$ cmake .. -DCMAKE_BUILD_TYPE=Debug
$ cmake --build .
$ bin/example
Hello World!

好消息是,在本例中,我们将其构建为 Debug 模式,因此我们可以调试应用程序并 step 进入包库代码。

$ gdb bin/example
> ...
> Reading symbols from /home/pi/consumer/build/bin/example...done.
(gdb) start
Temporary breakpoint 1 at 0x8914: file /home/pi/consumer/example.cpp, line 4.
Starting program: /home/pi/consumer/build/bin/example
(gdb) step
hello () at src/hello.cpp:5
5               std::cout << "Hello World!\n";

瞧,我们可以看到 hello.cpp 源代码行!

结论

Conan 是一个纯 Python 应用程序,因此在树莓派上安装它就像 sudo pip install conan 一样简单。

在本例中,我们演示了如何为 gccgdb 创建包含调试信息的包,但类似的方法也可以应用于其他平台,例如打包 Visual Studio 的 .pdb 文件。

由于 Conan 与构建系统和编译器完全正交,因此使用 Conan 交叉编译包非常简单。配置文件是一个非常方便的功能,可以将设置、选项和环境变量集中在一起,以便轻松地在不同的开发环境和目标之间切换。

当交叉编译工具链更复杂时,可能需要更多配置才能考虑这些工具链的多样性,但这肯定是可以做到的。我们知道 Conan 用户正在积极使用 Conan 打包 Android 和 iOS 系统。无论如何,我们正在准备一些针对构建需求管理的主要改进,例如 Android 工具链,您一定会喜欢的。请保持关注,在 x 上关注我们或订阅我们的发行公告邮件列表!