Skip to content

交叉编译

hxy edited this page Oct 29, 2024 · 9 revisions

用户在使用源码编译时总会遇到一些问题,本篇文章旨在介绍交叉编译的流程及一些参数说明。

CMakeLists.txt 文件解析

基础配置及参数配置

# 设置 cmake 所需要的最低版本,如果用的 cmake 版本低于该版本,将报错
cmake_minimum_required(VERSION 3.12)

# 声明项目名称
project(neuron)

# 打开当前及其下级目录的测试功能
enable_testing()

# 打开 c 语言的支持
enable_language(C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_CXX_STANDARD 17)

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

# build 类型,可取值 Debug,Release 等
if(NOT CMAKE_BUILD_TYPE)
  set(CMAKE_BUILD_TYPE "Debug")
endif()

# 要构建的目标平台的 CMake 标识符
if(NOT CMAKE_SYSTEM_NAME)
  set(CMAKE_SYSTEM_NAME "Linux")
endif()

# 禁用告警:=ON 表示禁用,=OFF 表示不禁用
if(NOT DISABLE_WERROR)
  set(CMAKE_C_FLAGS "$ENV{CFLAGS} -Werror")
endif()

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -g")
set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} -O1")

# 禁用内存错误检查:=ON 表示禁用,=OFF 表示不禁用
if(NOT DISABLE_ASAN)
  set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} -fsanitize=address")
  set(CMAKE_CXX_FLAGS_DEBUG "-Wall -g -fsanitize=address")
endif()
DISABLE_UT 参数,禁用单元测试,=ON 禁用,=OFF 不禁用。

除 cmake.lists.txt 文档中配置变量外,.cmake 文件也用于配置 cmake。CMAKE_TOOLCHAIN_FILE 参数用于指定 .cmake 文件的路径。

# 目标系统名称
set(CMAKE_SYSTEM_NAME Linux)
set(COMPILER_PREFIX arm-linux-gnueabihf)
# 目标平台架构
set(CMAKE_SYSTEM_PROCESSOR armv7l)
# 库的目录
set(LIBRARY_DIR /opt/externs/libs)

# 语言编译器
set(CMAKE_C_COMPILER ${COMPILER_PREFIX}-gcc)
set(CMAKE_CXX_COMPILER ${COMPILER_PREFIX}-g++)
# 静态库的归档工具名称
set(CMAKE_AR ${COMPILER_PREFIX}-ar)
set(CMAKE_LINKER ${COMPILER_PREFIX}-ld)
set(CMAKE_NM ${COMPILER_PREFIX}-nm)
set(CMAKE_OBJDUMP ${COMPILER_PREFIX}-objdump)
# 静态库随机化工具名称
set(CMAKE_RANLIB ${COMPILER_PREFIX}-ranlib)
# CMAKE_STAGING_PREFIX 变量用于指定安装到主机的路经
set(CMAKE_STAGING_PREFIX ${LIBRARY_DIR}/${COMPILER_PREFIX})
# CMAKE_PREFIX_PATH 变量用于指定要编译的文件所在的安装位置
set(CMAKE_PREFIX_PATH ${CMAKE_STAGING_PREFIX})

include_directories(SYSTEM ${CMAKE_STAGING_PREFIX}/include)
include_directories(SYSTEM ${CMAKE_STAGING_PREFIX}/openssl/include)
# 指定交叉编译环境
set(CMAKE_FIND_ROOT_PATH ${CMAKE_STAGING_PREFIX})
# 从来不在指定目录下查找工具程序
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
# 只在指定目录下查找库文件
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
# 只在指定目录下查头文件
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
link_directories(${CMAKE_STAGING_PREFIX})

依赖库的查找

# 由 CMAKE_STAGING_PREFIX 参数选择依赖库文件查找的位置
if (CMAKE_STAGING_PREFIX)
  # 当进行交叉编译时,指定头文件的搜索路径
  include_directories(${CMAKE_STAGING_PREFIX}/include)
  # 添加需要链接的库文件目录
  link_directories(${CMAKE_STAGING_PREFIX}/lib)
else()
  # 当不进行交叉编译时,指定头文件的搜索路径
  include_directories(/usr/local/include)
  link_directories(/usr/local/lib)
endif()

CMAKE_STAGING_PREFIX 参数在 .cmake 文件中配置。.cmake 文件用于配置 cmake 的变量和属性,例如 arm-linux-gnueabihf.cmake 用于设置适用于 ARM 平台的编译器。

交叉编译说明&流程

交叉编译,可以理解为在当前编译平台下,编译出来的的程序能运行在体系结构不同的另一种目标平台上,但是编译平台本身却不能运行该程序。

交叉编译工具链,是为了编译跨平台体系结构的程序代码而形成的由多个子工具构成的一套完整的工具集。当指定了源文件(.c)时,它会自动按照编译流程调用不同的子工具,自动生成可执行文件。交叉编译工具链的重点在于交叉编译器,使用不同平台的编译器用来生成可在该平台运行的可执行程序,流程实例如下。

set(PERSIST_SOURCES
    src/persist/persist.c
    src/persist/json/persist_json_plugin.c)
set(NEURON_SOURCES
    src/main.c
    src/argparse.c
    src/daemon.c
    src/core/manager_internal.c
    src/core/manager.c
    src/core/subscribe.c
    src/core/sub_msg.c
    src/core/plugin_manager.c
    src/core/node_manager.c
    src/core/storage.c
    src/adapter/storage.c
    src/adapter/adapter.c
    src/adapter/driver/cache.c
    src/adapter/driver/driver.c
    plugins/restful/handle.c
    plugins/restful/license.c
    plugins/restful/license_handle.c
    plugins/restful/log_handle.c
    plugins/restful/normal_handle.c
    plugins/restful/rw_handle.c
    plugins/restful/adapter_handle.c
    plugins/restful/datatag_handle.c
    plugins/restful/group_config_handle.c
    plugins/restful/plugin_handle.c
    plugins/restful/version_handle.c
    plugins/restful/rest.c
    plugins/restful/http.c
    plugins/restful/proxy.c
    plugins/restful/websocket.c
    ${PERSIST_SOURCES})

# 设置 build 路径变量为当前路径
set(CMAKE_BUILD_RPATH ./)
# 定义可执行文件的名称为 neuron,编译可执行程序
add_executable(neuron)
# 指定源文件, 与 add_executable 合用,用于将源文件 NEURON_SOURCES 生成动态链接文件到 neuron 中。
target_sources(neuron PRIVATE ${NEURON_SOURCES}) 
# 将头文件库路径添加到 neuron 中
target_include_directories(neuron PRIVATE include/neuron src plugins)
# 将目标文件 neuron 与库文件进行链接
target_link_libraries(neuron dl neuron-base sqlite3 -lm)

交叉编译示例

第一步,安装编译器

执行以下指令安装适用于 armv4 架构的编译器。

$ sudo apt-get update
$ sudo apt-get install -y gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf pkg-config libtool alien unzip

第二步,依赖库的交叉编译

在源码交叉编译前,用户需要先对在交叉编译中使用的依赖库进行交叉编译,使得依赖库与交叉编译的平台保持一致。新建一个目录文件用于存放安装文件,例如 install。

随着代码的不断更新,交叉编译依赖库也会有所变化,详细内容请参考 安装依赖

cmake 通用参数说明:

  • -D 配置 cmake 的参数,功能类似于 set;
  • CMAKE_C_COMPILER ,交叉编译宏变量,指定 c 的编译工具;
  • CMAKE_CXX_COMPILER ,交叉编译宏变量,指定 c++ 的编译工具 ;
  • CMAKE_STAGING_PREFIX ,交叉编译变量,指定安装到主机上的路径 ;
  • CMAKE_PREFIX_PATH,交叉编译变量,指定要编译的文件所在的安装位置;

openssl

在 install 目录下,执行以下指令安装 openssl 依赖库。

$ echo "Installing openssl (1.1.1)"
$ git clone -b OpenSSL_1_1_1 https://github.com/openssl/openssl.git         
$ cd openssl         
$ sudo mkdir -p /opt/externs/libs/arm-linux-gnueabihf/openssl/ssl           
$ ./Configure linux-armv4 no-asm shared --prefix=/opt/externs/libs/arm-linux-gnueabihf --openssldir=/opt/externs/libs/arm-linux-gnueabihf/openssl/ssl --cross-compile-prefix=arm-linux-gnueabihf-       
$ make clean         
$ make      
$ sudo make install_sw         
$ make clean  

zlog

在 install 目录下,执行以下指令安装 zlog 依赖库。

$ git clone -b 1.2.15 https://github.com/HardySimpson/zlog.git
$ cd zlog     
$ make CC=arm-linux-gnueabihf-gcc
$ sudo make PREFIX=/opt/externs/libs/arm-linux-gnueabihf install

jansson

在 install 目录下,执行以下指令安装 jansson 依赖库。

$ git clone https://github.com/neugates/jansson.git
$ cd jansson
$ mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf -DJANSSON_BUILD_DOCS=OFF -DJANSSON_EXAMPLES=OFF
$ make
$ sudo make install

NanoSDK

在 install 目录下,执行以下指令安装 nng 依赖库。

$ git clone -b neuron https://github.com/neugates/NanoSDK.git
$ cd NanoSDK && mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf -DBUILD_SHARED_LIBS=OFF -DNNG_TESTS=OFF -DNNG_ENABLE_SQLITE=ON -DNNG_ENABLE_TLS=ON
$ make
$ sudo make install

jwt

在 install 目录下,执行以下指令安装 jwt 依赖库。

$ git clone -b v1.13.1 https://github.com/benmcollins/libjwt.git
$ cd libjwt
$ mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf -DENABLE_PIC=ON -DBUILD_SHARED_LIBS=OFF
$ make
$ sudo make install

mbedtls

在 install 目录下,执行以下指令安装 mbedtls 依赖库。

$ git clone -b v2.16.12 https://github.com/Mbed-TLS/mbedtls.git
$ cd mbedtls && mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf -DBUILD_SHARED_LIBS=OFF  -DUSE_SHARED_MBEDTLS_LIBRARY=OFF -DENABLE_TESTING=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
$ make
$ sudo make install

googletest

在 install 目录下,执行以下指令安装 googletest 依赖库。

$ git clone -b release-1.11.0 https://github.com/google/googletest.git 
$ cd googletest && mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf -DBUILD_SHARED_LIBS=OFF
$ make
$ sudo make install

sqlite3

在 install 目录下,执行以下指令安装 sqlite3 依赖库。

$ curl https://www.sqlite.org/2022/sqlite-autoconf-3390000.tar.gz --output sqlite3.tar.gz
$ mkdir -p sqlite3
$ tar xzf sqlite3.tar.gz --strip-components=1 -C sqlite3
$ cd sqlite3
$ ./configure --prefix=/opt/externs/libs/arm-linux-gnueabihf --disable-shared --disable-readline --host armv4 CC=arm-linux-gnueabihf-gcc
$ make
$ sudo make install     

libxml2

在 install 目录下,执行以下指令安装 libxml2 依赖库。

$ git clone -b v2.9.14 https://github.com/GNOME/libxml2
$ cd libxml2 && mkdir build && cd build
$ cmake .. -DCMAKE_C_COMPILER=arm-linux-gnueabihf-gcc -DCMAKE_CXX_COMPILER=arm-linux-gnueabihf-g++ -DCMAKE_STAGING_PREFIX=/opt/externs/libs/arm-linux-gnueabihf -DCMAKE_PREFIX_PATH=/opt/externs/libs/arm-linux-gnueabihf  -DBUILD_SHARED_LIBS=OFF -DCMAKE_BUILD_TYPE=Release -DLIBXML2_WITH_ICONV=OFF -DLIBXML2_WITH_LZMA=OFF -DLIBXML2_WITH_PYTHON=OFF -DLIBXML2_WITH_ZLIB=OFF -DLIBXML2_WITH_HTTP=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON
$ make
$ sudo make install

protobuf

在 install 目录下,执行以下指令安装 protobuf 依赖库。

$ wget --no-check-certificate --content-disposition https://github.com/protocolbuffers/protobuf/releases/download/v3.20.1/protobuf-cpp-3.20.1.tar.gz
$ tar -xzvf protobuf-cpp-3.20.1.tar.gz
$ cd protobuf-3.20.1
$ ./configure --prefix=/opt/externs/libs/arm-linux-gnueabihf --enable-shared=no --host=armv4 CFLAGS=-fPIC CXXFLAGS=-fPIC CC=arm-linux-gnueabihf-gcc CXX=arm-linux-gnueabihf-g++
$ make
$ sudo make install

protobuf-c

在 install 目录下,执行以下指令安装 protobuf-c 依赖库。

$ git clone -b v1.4.0 https://github.com/protobuf-c/protobuf-c.git
$ cd protobuf-c
$ ./autogen.sh
$ ./configure --prefix=/opt/externs/libs/arm-linux-gnueabihf --disable-protoc --enable-shared=no --host=armv4 CFLAGS=-fPIC CXXFLAGS=-fPIC CC=arm-linux-gnueabihf-gcc
$ make
$ sudo make install

第三步,源码的交叉编译

执行以下指令下载源码并进行交叉编译。

$ git clone https://github.com/emqx/neuron
$ cd neuron
$ git submodule update --init
$ mkdir build && cd build
$ cmake .. -DCMAKE_TOOLCHAIN_FILE=../cmake/arm-linux-gnueabihf.cmake -DCMAKE_BUILD_TYPE=Release -DDISABLE_UT=ON
$ make