交叉编译grpc

近期需要使用grpc在目标主机使用,参考了官方示例,大部分网上教程完成了本篇grpc的交叉编译,并对参考的教程做出了引用

文章目录

  • 交叉编译grpc
      • 1 安装交叉编译库
      • 2 Pre-requisites
        • 2.1 ubuntu16.04升级gcc/g++/cmake
      • 3 安装源代码
      • 4 构建(使用CMake)
        • 4.1 宿主机平台的构建
        • 4.2 宿主机平台下的测试
        • 4.3 交叉编译
        • 4.4 目标主机平台下的测试
      • 5 可能遇到的问题
        • 获取第三方依赖库失败
        • plugin-notfound
        • execinfo-notfound
        • package-configuration-notfound
        • libtool: *.la was moved
        • libtool error: relink 'libprotoc.la' with the above command before installing it
        • sudo: arm-linux-gcc: command not found
        • CMake error / usr / bin / ld:找不到-lpthreads
      • 其他注意事项
        • 完整删除 protoc
        • 其他方法
          • 安装 protobuf
          • 安装 grpc
          • 交叉编译 protobuf
          • 交叉编译 grpc

交叉编译grpc

实验环境:
docker ubuntu16.04
cmake version 3.16.1
gcc version 7.5.0
set(PACKAGE_NAME “grpc”)
set(PACKAGE_VERSION “1.28.2”)
set(gRPC_CORE_VERSION “9.0.0”)

  • 目标:C++中能够使用 grpc(静态/动态链接库皆可,本文根据官方cmake的编译示例,最后生成的是静态)

!!! Note 如果使用的是make或者参考的非官方的方法,可见其他注意事项,强烈建议根据官方方法,虽然可能可能也会遇到许多问题(官方以及非官方方法本人最终都在该环境下编译成功)

1 安装交叉编译库

添加进环境变量

sudo vim .zshrc/.bashrc

export PATH="$PATH:/cross-compile_toolchain/toolchain/bin"
export STAGING_DIR="/cross-compile_toolchain/toolchain/bin:$STAGING_DIR" 

❯ source ~/.zshrc/.bashrc
❯ arm-linux-gcc -v

2 Pre-requisites

安装必要的依赖工具,这里使用cmake进行编译

❯ sudo apt-get install build-essential autoconf libtool pkg-config 
❯ sudo apt-get install cmake automake

ubuntu16.04自带的cmake和gcc版本可能会在build中发生意想不到的错误!

同时查看源码中的CMakeLists.txt可以发现

cmake_minimum_required(VERSION 3.5.1)
# We can install dependencies from submodules if we're running
# CMake v3.13 or newer.
if(CMAKE_VERSION VERSION_LESS 3.13)
  set(_gRPC_INSTALL_SUPPORTED_FROM_MODULE OFF)
else()
  set(_gRPC_INSTALL_SUPPORTED_FROM_MODULE ON)
endif()

目前还不知道该版本编译是否有gcc cmake具体的版本要求。

2.1 ubuntu16.04升级gcc/g++/cmake
# Install CMake 3.16
❯ apt-get update && apt-get install -y wget
❯ wget -q -O cmake-linux.sh https://github.com/Kitware/CMake/releases/download/v3.16.1/cmake-3.16.1-Linux-x86_64.sh
❯ sh cmake-linux.sh -- --skip-license --prefix=/usr
❯ rm cmake-linux.sh
❯ ❯ cmake -v

# update gcc/g++
❯ sudo apt-get install -y software-properties-common
❯ sudo add-apt-repository ppa:ubuntu-toolchain-r/test
❯ sudo apt update
❯ sudo apt install g++-7 -y

❯ sudo update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-7 60 \
                         --slave /usr/bin/g++ g++ /usr/bin/g++-7 
❯ sudo update-alternatives --config gcc
❯ gcc -v
❯ g++ -v

3 安装源代码

这里选用 v1.28.2

❯ git clone https://github.com/grpc/grpc.git docker-grpc
❯ cd docker-grp 
# 切换到指定版本 
❯ git checkout v1.28.2
# 下载第三方依赖库
❯ git submodule update --init

git submodule update --init失败可查看

4 构建(使用CMake)

You can use CMake to cross-compile gRPC for another architecture. In order to do so, you will first need to build protoc and grpc_cpp_plugin for the host architecture. These tools are used during the build of gRPC, so we need copies of executables that can be run natively.

☝出自官方参考教程Cross-compiling中,可以看到grpc交叉编译时的一些注意事项:

  • gRPC交叉编译之前,需要先交叉编译好protobuf
  • gRPC需要先生成宿主机平台的protobuf和gRPC
    • 原因是在gRPC交叉编译的过程中,需要使用对应的编译平台的bin文件(protoc,grpc_cpp_plugin等等)

所以第一步是需要在宿主机平台完成gRPC和protobuf的安装。

4.1 宿主机平台的构建

官方参考教程中的Linux部分
参考教程-知乎

mkdir -p "cmake/build"
pushd "cmake/build"
cmake \
  -DCMAKE_BUILD_TYPE=Release \
  -DgRPC_INSTALL=ON \
  -DgRPC_BUILD_TESTS=OFF \
  -DgRPC_SSL_PROVIDER=package \
  ../..
sudo make install
popd

!!! Note 如果想要编译动态库.so文件,运行cmake时添加-DBUILD_SHARED_LIBS=ON

构建完成后可在 /usr/local/lib下发现grpc及子模块的静态连接库,类似 libgrpc.a/libgrpc++.a/…/libgrpc_plugin_support.a/…
同时也可以发现 protoc也已经生成

❯ protoc --version                                    
libprotoc 3.11.2
4.2 宿主机平台下的测试

编译cpp/helloworld

cd grpc/examples/cpp/helloworld/
mkdir build
cd build/
cmake ..
make

启动服务和客户端

# 启动服务端,监听在50051端口
./greeter_server
Server listening on 0.0.0.0:50051
# 启动客户端,服务端返回Hello world
./greeter_client 
Greeter received: Hello world
4.3 交叉编译

参考官方示例

write_cross_toolchain.sh(代码取自官方,具体设置根据目标主机和个人情况而定)

# Write a toolchain file to use for cross-compiling.
#  /tmp/toolchain.cmake 存放 toolchain的路径,自行决定
# CMAKE_SYSTEM_PROCESSOR 可以根据目标主机中的文件属性查看 file filename
# CMAKE_STAGING_PREFIX 交叉编译时要安装到的路径,编译成功后的链接库就在这儿,建议同 toolchain.cmake路径相同
# CMAKE_C_COMPILER/CMAKE_CXX_COMPILER 交叉编译的工具链,根据目标主机下载配置
cat > /tmp/toolchain.cmake <<'EOT'
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_PROCESSOR aarch64) 
set(CMAKE_STAGING_PREFIX /tmp/stage)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc-6)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++-6)
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)
EOT
./write_cross_toolchain.sh
mkdir -p "cmake/build_arm"
pushd "cmake/build_arm"
cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/toolchain.cmake \ # 同上步骤中的 toolchain存放路径
      -DCMAKE_BUILD_TYPE=Release \
      -DCMAKE_INSTALL_PREFIX=/tmp/install \ # 安装路径
      ../..
make install
popd

成功后可以在 CMAKE_STAGING_PREFIX下发现 grpc及其依赖库的静态链接

❯ readelf -h libcrypto.a
文件:libgrpc++.a(codegen_init.cc.o)
ELF 头:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  类别:                              ELF32
  数据:                              2 补码,小端序 (little endian)
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI 版本:                          0
  类型:                              REL (可重定位文件)
  系统架构:                          ARM
  版本:                              0x1
  入口点地址:               0x0
  程序头起点:          0 (bytes into file)
  Start of section headers:          236 (bytes into file)
  标志:             0x5000000, Version5 EABI
  Size of this header:               52 (bytes)
  Size of program headers:           0 (bytes)
  Number of program headers:         0
  Size of section headers:           40 (bytes)
  Number of section headers:         10
  Section header string table index: 7


❯ objdump -p libgrpc++.a
在归档文件 libgrpc++.a 中:
insecure_credentials.cc.o:     文件格式 elf32-little

make install 可能遇到各种各样的问题:

  1. gRPC_CPP_PLUGIN NOTFOUND
  2. error: execinfo.h: No such file or directory
4.4 目标主机平台下的测试

!!! warning 注意下述的 DCMAKE_TOOLCHAIN_FILE、Dabsl_DIR和 DProtobuf_DIR必须和交叉编译时设置的 toolchain.cmake/CMAKE_STAGING_PREFIX的路径相同

# Build helloworld example for ARM.
# As above, it will find and use protoc and grpc_cpp_plugin
# for the host architecture.
mkdir -p "examples/cpp/helloworld/cmake/build_arm"
pushd "examples/cpp/helloworld/cmake/build_arm"
cmake -DCMAKE_TOOLCHAIN_FILE=/tmp/toolchain.cmake \
      -DCMAKE_BUILD_TYPE=Release \
      -Dabsl_DIR=/tmp/stage/lib/cmake/absl \ 
      -DProtobuf_DIR=/tmp/stage/lib/cmake/grpc \
      ../..

make "-j${GRPC_CPP_DISTRIBTEST_BUILD_COMPILER_JOBS}"
popd

将 /build_arm中生成的可执行文件拷贝到目标主机进行测试即可,现象同在宿主机下的测试

5 可能遇到的问题

获取第三方依赖库失败

打开 .gitmodules进行手动下载,列举grpc中的部分 modules

[submodule "third_party/googleapis"]
    path = third_party/googleapis
    url = https://github.com/googleapis/googleapis.git
[submodule "third_party/protoc-gen-validate"]
    path = third_party/protoc-gen-validate
    url = https://github.com/envoyproxy/protoc-gen-validate.git
[submodule "third_party/udpa"]
    path = third_party/udpa
    url = https://github.com/cncf/udpa.git
[submodule "third_party/libuv"]
    path = third_party/libuv
    url = https://github.com/libuv/libuv.git

可以看到 modules的具体存放路径以及 url,这里仅提供一下两种途径

  1. github镜像地址:https://github.com.cnpmjs.org
    1. 将https://github.com换成https://github.com.cnpmjs.org即可
  2. gitee
plugin-notfound
  • 出现类似以下的错误
    [ 70%] Running gRPC C++ protocol buffer compiler on src/proto/grpc/reflection/v1alpha/reflection.proto
    _gRPC_CPP_PLUGIN-NOTFOUND: program not found or is not executable
    Please specify a program using absolute path or make sure the program is available in your PATH system variable
    --grpc_out: protoc-gen-grpc: Plugin failed with status code 1.
    make: *** [all] Error 2
    

是因为交叉编译版本的grpc_cpp_plugin在宿主机中无法运行的问题

  • 解决方案
    • 替换/bins/opt中对应的文件,用之前步骤中安装的宿主机版本的grpc_cpp_plugin等文件即可通过编译
    • 最直接有效的方法是从头在宿主机安装一遍
execinfo-notfound
  • 出现类似以下的错误
    In file included from /home/autowise/Work/wxz_workspace/docker-grpc/third_party/abseil-cpp/absl/debugging/stacktrace.cc:46:0:
    /home/autowise/Work/wxz_workspace/docker-grpc/third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc:14:22: fatal error: execinfo.h: No such file or directory
    compilation terminated.
    third_party/abseil-cpp/absl/debugging/CMakeFiles/absl_stacktrace.dir/build.make:62: recipe for target 'third_party/abseil-cpp/absl/debugging/CMakeFiles/absl_stacktrace.dir/stacktrace.cc.o' failed
    
  • 解决方案
    1. 一般会在当时构建目标主机固件中的目录下找到
    2. 将其复制到 ./third_party/abseil-cpp/absl/debugging/internal/stacktrace_generic-inl.inc
    3. 修改 stacktrace_generic-inl.inc中的 #include 为 #include “execinfo.h”
package-configuration-notfound
  • 出现类似以下的错误
    CMake Error at CMakeLists.txt:103 (find_package):
    Could not find a configuration file for package "Protobuf" that is
    compatible with requested version "".
    The following configuration files were considered but not accepted:
        /usr/local/lib/cmake/protobuf/protobuf-config.cmake, version: 3.11.2.0 (64bit)
    
  • 解决方案
    1. 一般可能是配置环境的变量时候可工具链不兼容
    2. echo $GRPC_CROSS_AROPTS
    3. export GRPC_CROSS_AROPTS=“cr --target=elf32-little” # 指定交叉编译环境变量(32位)或者
    4. export GRPC_CROSS_AROPTS=“cr --target=elf64-little” # 指定交叉编译环境变量(64位)
libtool: *.la was moved
  • 出现类似以下的警告
libtool: warning: library '/toolchain/toolchain-arm_cortex-a7_gcc-5.2.0_musl-1.1.16_eabi/bin/../lib/gcc/arm-linux-muslgnueabi/5.2.0/../../../../arm-linux-muslgnueabi/lib/libstdc++.la' was moved.

参考
libtool提供统一的接口,隐藏了不同平台间库的名称的差异等细节,生成一个抽象的后缀名为la高层库libxx.la(其实是个文本文件),并将该库对其它库的依赖关系,都写在该la的文件中
该文件中的dependency_libs记录该库依赖的所有库(其中有些是以.la文件的形式加入的);libdir则指出了库的安装位置;
library_names记录了共享库的名字;old_library记录了静态库的名字

  • 解决方案
    1. 一般不会影响后续的安装
    2. 一般时libdir出现错误,消除的办法就是将 .la中的 libdir修改成现在的路径(libdir的路径一般由构建交叉编译工具链时生成)
libtool error: relink ‘libprotoc.la’ with the above command before installing it
  • 可能的原因
    1. 交叉编译找的是宿主机的库,所以读不了
    2. 在configure的时把路径修改
sudo: arm-linux-gcc: command not found

首先确定配置了环境变量,在此不再赘述
(我直接在主机ubuntu20,没有使用docker时遇到相同的问题)

  • 可能的原因

    1. 执行./configure或者make install时,加了sudo,变成了root的工作环境和root的权限
    2. 此时是在root下做的,而arm-linux-gnueabihf-gcc在用户的工作环境中才能找到。所以产生了错误
  • 解决方案

    1. 执行./configure之前,先用sudo -i命令取得root权限。然后再执行./configure(这里不建议直接修改系统环境变量)
CMake error / usr / bin / ld:找不到-lpthreads

是在docker(ubuntu16.04 cmake13 gcc5)环境,编译宿主机的测试demo(/cpp/helloworld)时出现该错误的
实际原因没有找到,后续更新了cmake gcc未再出现

其他注意事项

完整删除 protoc

一般进行重构时或者系统已存在 protoc,需要将其完全删除(当然也可以指定路径)

❯ which protoc
/usr/local/bin/protoc

sudo rm /usr/local/bin/protoc  //执行文件
sudo rm -rf /usr/local/include/google //头文件
sudo rm -rf /usr/local/lib/lib
❯ which protoc
protoc not found # 删除干净
其他方法

宿主机安装 protobuf -> 宿主机安装 grpc -> 交叉编译 protobuf -> 交叉编译 grpc
参考资料1
参考资料2
参考资料3

安装 protobuf

!!! error 注意:不用手动安装protobuf,不然版本可能和grcp不匹配,必须在 grpc 执行 git submodule update --init 命令之后生成的 third_party/protobuf 里面编译安装对应的 protobuf

❯ cd third_party/protobuf/
❯ ./autogen.sh 
❯ ./configure --prefix=/usr/local
❯ make

❯ sudo make install
❯ sudo ldconfig  # 使得新安装的动态库能被加载

❯ protoc --version
安装 grpc
交叉编译 protobuf

protoc交叉编译教程

交叉编译 grpc

!!! Note 需要指定protoc这个程序的路径,这个程序就是我们上面在宿主机安装gRPC的时候,生成的bin文件

export GRPC_CROSS_COMPILE=true # 打开交叉编译
export GRPC_CROSS_AROPTS="cr --target=elf32-little" # 指定交叉编译环境变量(32位)
make -j8 HAS_PKG_CONFIG=false \ # 将grpc编译设置为静态
    CC=arm-linux-gcc \
    CXX=arm-linux-g++ \
    RANLIB=arm-linux-gcc-ranlib \
    LD=arm-linux-ld \
    LDXX=arm-linux-g++ \
    AR=arm-linux-muslgnueabi-ar \
    STRIP=arm-linux-muslgnueabi-strip \
    prefix=/cross_dynamiclink_lib/grpc \
    PROTOBUF_CONFIG_OPTS="--host=arm-linux --with-protoc=/grpc/third_party/protobuf/install/bin/protoc" 

你可能感兴趣的:(交叉编译,linux)