近期需要使用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”)
!!! Note 如果使用的是make或者参考的非官方的方法,可见其他注意事项,强烈建议根据官方方法,虽然可能可能也会遇到许多问题(官方以及非官方方法本人最终都在该环境下编译成功)
添加进环境变量
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
安装必要的依赖工具,这里使用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具体的版本要求。
# 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
这里选用 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失败可查看
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的安装。
官方参考教程中的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
编译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
参考官方示例
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 可能遇到各种各样的问题:
!!! 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中生成的可执行文件拷贝到目标主机进行测试即可,现象同在宿主机下的测试
打开 .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,这里仅提供一下两种途径
[ 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在宿主机中无法运行的问题
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
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)
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记录了静态库的名字
首先确定配置了环境变量,在此不再赘述
(我直接在主机ubuntu20,没有使用docker时遇到相同的问题)
可能的原因
解决方案
是在docker(ubuntu16.04 cmake13 gcc5)环境,编译宿主机的测试demo(/cpp/helloworld)时出现该错误的
实际原因没有找到,后续更新了cmake gcc未再出现
一般进行重构时或者系统已存在 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
!!! 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
protoc交叉编译教程
!!! 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"