制作ROS2交叉编译环境

 

制作ROS2交叉编译环境

本文的行文一开始参考了很多资料,后续淘出了在参考资料章节列出的一些优质的讨论和实现。也感谢同事sgf在这一过程中提供的资料和帮助。

制作sysroot

官方的教程1上有制作sysroot的详细指南,但是笔者由于已经有一块开发板配置好了环境,所以并没有尝试使用官方的方式来制作sysroot。而是在先行看到关于交叉编译的讨论2的情况下,找到了某个开发者关于交叉编译的看法3,并且在这一讨论里看见了4所提供的制作sysroot的方法,因此借鉴了其做法之后,直接执行如下命令从开发板拷贝依赖库:

mkdir -p /home/<username>/sysroot/
rsync -rlR --safe-links --exclude='/usr/share' <hostname>@<ip>:/{lib,usr,opt} /home/<username>/sysroot/

一般来说上面这样就好了,但是还会有一些库,链接到了 /etc/alternatives/ 或者其他笔者不知道的地方,可以当真正遇到的时候再按照开发板上的文件/链接方式对sysroot内的文件做修改,比如:

${SYSROOT}/usr/lib/aarch64-linux-gnu# ln -s ../../../etc/alternatives/libblas.a-aarch64-linux-gnu libblas.a

主要是笔者比较关心sysroot的大小,如果无所谓的话,可以直接将开发板的整个文件系统拷贝过来。

链接host目录到sysroot目录

在host上安装了 g++-aarch64-linux-gnu 交叉编译工具链之后,就会产生 /usr/lib/aarch64-linux-gnu/ 目录,但是里面其实什么都没有。按照笔者的了解,这好像是写在交叉编译工具里面的搜索路径,所以会去这里面找库,那么为了让交叉编译工具链能够顺利的找到target平台上面的库:

cd /usr/lib
rm -rf aarch64-linux-gnu
ln -s ${SYSROOT}/usr/lib/aarch64-linux-gnu

另外为了模拟板子上面的ros路径,对于我司开发开发的ros来说:

cd /opt
ln -s ${SYSROOT}/opt/nros
ln -s ${SYSROOT}/opt/ros3rd

如果是官方的ros,应该直接执行前两行即可(没有试验过)。

制作toolchain.cmake

此文件内容基本来自官方用例,用来传给 CMAKE_TOOLCHAIN_FILE 变量,设置一系列和交叉编译相关的变量。

# Copyright (c) 2018, ARM Limited.
# SPDX-License-Identifier: Apache-2.0

if("$ENV{TARGET_ARCH}" STREQUAL "" OR
   "$ENV{CROSS_COMPILE}" STREQUAL "" OR
   "$ENV{SYSROOT}" STREQUAL "" OR
   "$ENV{PYTHON_SOABI}" STREQUAL "")
    message(FATAL_ERROR "Target environment variables not defined")
endif()

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_VERSION 1)
set(CMAKE_SYSTEM_PROCESSOR $ENV{TARGET_ARCH})

# Specify the cross compiler
set(CMAKE_C_COMPILER $ENV{CROSS_COMPILE}gcc)
set(CMAKE_CXX_COMPILER $ENV{CROSS_COMPILE}g++)

# Specify the target file system
set(CMAKE_SYSROOT $ENV{SYSROOT})
# set(CMAKE_SYSROOT_LINK $ENV{SYSROOT}/usr/lib)

set(CMAKE_FIND_ROOT_PATH $ENV{ROS2_INSTALL_PATH})
string(REPLACE ":" ";" CMAKE_PREFIX_PATH_TMP "$ENV{CMAKE_PREFIX_PATH}")
list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH_TMP})
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

# Specify the python SOABI
set(PYTHON_SOABI $ENV{PYTHON_SOABI})

if(NOT TARGET Qt5::moc)
  set(QT_MOC_EXECUTABLE /usr/bin/moc)
  add_executable(Qt5::moc IMPORTED)
  set_property(TARGET Qt5::moc PROPERTY IMPORTED_LOCATION ${QT_MOC_EXECUTABLE})
endif()

# This assumes that pthread will be available on the target system
# (this emulates that the return of the TRY_RUN is a return code "0")
set(THREADS_PTHREAD_ARG "0"
  CACHE STRING "Result from TRY_RUN" FORCE)

Ps: 这两句

string(REPLACE ":" ";" CMAKE_PREFIX_PATH_TMP "$ENV{CMAKE_PREFIX_PATH}")
list(APPEND CMAKE_FIND_ROOT_PATH ${CMAKE_PREFIX_PATH_TMP})

将环境变量CMAKE_PREFIX_PATH添加到CMAKE_FIND_ROOT_PATH是为了能够通过改变ENV{CMAKE_PREFIX_PATH} 来增加别的搜索路径。这也是受到6中的问答的启发。

设置环境变量

这些环境变量会被toolchain.cmake消费。

# ros3rd是我司管理ros三方库文件所在的目录,通过source它,就会source /opt/nros/local_setup.bash
# 即类似于官方 source $ROS2_INSTALL_PATH/setup.bash 的效果,
# 就是增加PYTHONPATH搜索package的路径和一系列环境变量。
source /opt/ros3rd/setup.bash
export TARGET_ARCH=aarch64
export TARGET_TRIPLE=aarch64-linux-gnu

export CC=/usr/bin/$TARGET_TRIPLE-gcc
export CXX=/usr/bin/$TARGET_TRIPLE-g++
export CROSS_COMPILE=/usr/bin/$TARGET_TRIPLE-

export SYSROOT=/home/<username>/sysroot
export PYTHON_SOABI=cpython-38-$TARGET_TRIPLE
export ROS2_INSTALL_PATH=$SYSROOT/opt/nros

制作交叉编译colcon build 快捷键

这里提供 cc.mixin:

{
    "build": {
        "cc": {
            "cmake-args": [
                "-DCMAKE_VERBOSE_MAKEFILE=ON",
                "-DCMAKE_TOOLCHAIN_FILE='/root/sysroot/toolchain.cmake'"
            ]
        }
    }
}

经过一系列操作之后,这些操作请看这里,而后就可以实现:

colcon build --mixin cc --packages-select <package_name>

经过这一系列准备之后,可以制作一个专门用于交叉编译的docker:

用于交叉编译的docker

Dockerfile 如下:

FROM ubuntu:20.04

ENV DEBIAN_FRONTEND=noninteractive

# 更新镜像源
RUN sed -i "s@http://.*archive.ubuntu.com@http://mirrors.huaweicloud.com@g" /etc/apt/sources.list \
    && sed -i "s@http://.*security.ubuntu.com@http://mirrors.huaweicloud.com@g" /etc/apt/sources.list

# 安装依赖包
RUN apt update && apt install -y \
    cmake \
    python3-pip \
    g++-aarch64-linux-gnu \
    pkg-config-aarch64-linux-gnu \
    vim

# 配置pip镜像源
RUN pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/

# 安装colcon-common-extensions
RUN python3 -m pip install -U colcon-common-extensions numpy lark
# 安装colcon mixin 插件和yaml文件格式校验包 
RUN python3 -m pip install colcon-mixin yamllint

# 拷贝sysroot目录到/root/sysroot
COPY sysroot /root/sysroot

# 创建软链接
RUN ln -s /root/sysroot/opt/nros /opt/nros \
    && ln -s /root/sysroot/opt/ros3rd /opt/ros3rd
RUN cd /usr/lib \
    && rm -rf aarch64-linux-gnu \
    && ln -s /root/sysroot/usr/lib/aarch64-linux-gnu

# 追加环境变量到bashrc
RUN echo 'source /opt/ros3rd/setup.bash' >> /root/.bashrc \
    && echo 'export TARGET_ARCH=aarch64' >> /root/.bashrc \
    && echo 'export TARGET_TRIPLE=aarch64-linux-gnu' >> /root/.bashrc \
    && echo 'export CC=/usr/bin/$TARGET_TRIPLE-gcc' >> /root/.bashrc \
    && echo 'export CXX=/usr/bin/$TARGET_TRIPLE-g++' >> /root/.bashrc \
    && echo 'export CROSS_COMPILE=/usr/bin/$TARGET_TRIPLE-' >> /root/.bashrc \
    && echo 'export SYSROOT=/root/sysroot' >> /root/.bashrc \
    && echo 'export PYTHON_SOABI=cpython-38-$TARGET_TRIPLE' >> /root/.bashrc \
    && echo 'export ROS2_INSTALL_PATH=$SYSROOT/opt/nros' >> /root/.bashrc

# 将toolchain.cmake复制到/root/sysroot目录下(请确保toolchain.cmake文件与Dockerfile在同一目录中)
COPY toolchain.cmake /root/sysroot/

# 将mixin目录复制到/root目录下
COPY colcon_mixin_repo /root/colcon_mixin_repo
RUN colcon mixin add default file:///root/colcon_mixin_repo/index.yaml \
    && colcon mixin update default

# 设置工作目录
WORKDIR /root

# 启动容器时执行的命令
CMD ["/bin/bash"]

可能遇到的问题:

cc1plus: error: unknown value ‘native’ for ‘-march’

检查编译的源代码里面是否在某个CMakeLists.txt里面有如下类似的设置:

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -march=native")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -march=native")

-march=native 删掉即可。

参考资料

  1. ROS2官方制作交叉编译环境教程
  2. ROS2官方寻求开发者管理交叉编译仓库的讨论
  3. ROS2 cross-compile 仓库的issue,希望用原生交叉编译取代官方的qemu模拟目标平台进行编译的操作
  4. cyberbotics开发的树莓派交叉编译环境仓库
  5. 树莓派交叉编译
  6. CMAKE_PREFIX_PATH does not work as expected