Linux 下交叉编译 ARM64-linux 版本 GDAL-3.2.0
Linux 下交叉编译 ARM64-linux 版本 GDAL-3.2.0
1、下载安装编译环境
这里的主机环境是 linux x86_64
,具体哪个版本不重要,安装相关工具的时候使用对应版本的命令即可(可参考:GEOS/GDAL 交叉编译ARM64-linux版本)。
我这里下载的是 gcc-arm-8.3
版本的交叉编译工具链下载网站,命令如下:
# 1、下载安装编译器
axel https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu.tar.xz?revision=2e88a73f-d233-4f96-b1f4-d8b36e9bb0b9&la=en&hash=167687FADA00B73D20EED2A67D0939A197504ACD
# 解压到 /opt 目录下
tar -xJvf gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu.tar.xz -C /opt/
还需要安装cmake
、make
、automake
、autoconf
、libtool
、zip
、sqlite
等软件,具体安装方法根据 Linux 发行版本确定,这里不再赘述。
备注:ARM64 都是带 fpu 的,所以不需要区分-mfloat-abi
的设置。
2、使用 VCPKG 编译一些基础的依赖库
下载安装 vcpkg
的命令如下:
# 下载 vcpkg
git clone https://github.com/microsoft/vcpkg.git
# 编译出 vcpkg 工具
cd vckpg
./bootstrap-vcpkg.sh
编译 sqlite3
、curl
、geos
等第三方库的 ARM64-linux
版本:
# 导入 gcc-arm 工具链的路径到 PATH
export PATH=${PATH}:/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin
# 下载和编译第三方库
./vcpkg install sqlite3:arm64-linux # 当前版本 3.33.0
./vcpkg install zstd:arm64-linux # 当前版本 1.4.5
./vcpkg install liblzma:arm64-linux # 当前版本 5.2.5
./vcpkg install tiff:arm64-linux # 当前版本 4.1.0
./vcpkg install curl:arm64-linux # 当前版本 7.73.0
./vcpkg install hdf5:arm64-linux
./vcpkg install libwebp:arm64-linux # 当前版本 1.1.0
./vcpkg install openjpeg:arm64-linux # 当前版本 2.3.1
./vcpkg install geos:arm64-linux # 当前版本 3.8.1
libgeos 3.8与之前的版本略有差异,也可以选择自己编译。自己编译要注意是否使用 -DGEOS_INLINE=1
条件,对编译出的库的影响。
hdf5 编译失败可以参考 https://www.cnblogs.com/oloroso/p/14606052.html 。
3、交叉编译 PROJ-7.2.0
之所以不使用 vcpkg
来编译 proj4
库,是因为当前 vcpkg
内的版本比较低,就自己编译了。
编译过程命令记录如下:
# 下载源码包
axel https://github.com/OSGeo/PROJ/releases/download/7.2.0/proj-7.2.0.tar.gz
# 解压并进入目录
tar -xzf proj-7.2.0.tar.gz && cd proj-7.2.0
# 使用 cmake 命令生成 Makefile
# CMAKE_INCLUDE_PATH、CMAKE_LIBRARY_PATH 指向上一步编译第三方库的输出目录
PATH=${PATH}:/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin \
cmake .. -DCMAKE_C_COMPILER=aarch64-linux-gnu-gcc \
-DCMAKE_CXX_COMPILER=aarch64-linux-gnu-g++ \
-DCMAKE_FIND_ROOT_PATH=/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu \
-DCMAKE_INCLUDE_PATH=/home/test/code/vcpkg/installed/arm64-linux/include \
-DCMAKE_LIBRARY_PATH=/home/test/code/vcpkg/installed/arm64-linux/lib \
-DCMAKE_C_FLAGS=-fPIC -DCMAKE_CXX_FLAGS=-fPIC \
-DCMAKE_CXX_STANDARD=11 -DCMAKE_INSTALL_PREFIX="./output" \
-DPROJ_TESTS=OFF -DBUILD_GMOCK=OFF \
-DBUILD_LIBPROJ_SHARED=OFF -Dgtest_force_shared_crt=OFF \
-DBUILD_PROJSYNC=OFF -DBUILD_PROJINFO=OFF -DBUILD_CCT=OFF \
-DBUILD_CS2CS=OFF -DBUILD_GEOD=OFF -DBUILD_GIE=OFF -DBUILD_PROJ=OFF
# 上面一堆 -DBUILD_XXX=OFF 关闭相应的构建,是因为前面编译的第三方库都是静态库
# 这里要用的话某些库的依赖(比如libcurl依赖的openssl)需要手动修改 CMakeLists.txt 去引入
# 所以干脆不要了,编译 proj 本来也只是给编译 gdal 使用的。
# 编译并输出编译结果到 output 目录
make && make install
编译之后,可以拷贝到 vcpkg
的安装目录下,后面编译 GDAL
的时候,方便第三方库路径的指定。
CMAKE 编译工具链指定
上面使用了直接指定 CMAKE_CXX_COMPILER
等相关变量的方式,需要指定的变量比较多。
推荐通过创建一个 gcc_arm_toolchain.cmake
文件,然后使用 -DCMAKE_TOOLCHAIN_FILE=gcc_arm_toolchain.cmake
选项的方式来指定编译器等相关设置。
gcc_arm_toolchain.cmake 文件内容
# 指定目标系统
SET(CMAKE_SYSTEM_NAME Linux)
# 指定编译器
SET(CMAKE_C_COMPILER aarch64-linux-gnu-gcc)
SET(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
# 指定搜索的根路径
SET(CMAKE_FIND_ROOT_PATH /opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu)
SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # 使用本机程序
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # 仅使用 FIND_ROOT 下的库
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # 仅使用 FIND_ROOT 下的头文件
4、交叉编译 GDAL-3.2.0
先下载并解压源代码
axel https://github.com/OSGeo/gdal/releases/download/v3.2.0/gdal-3.2.0.tar.gz
tar -xzf gdal-3.2.0.tar.gz
然后参考 linux下编译GDAL3.x > configure 完成,输出 geos 等为 no 修改 configure
文件。
大概修改如下:
-
搜索
CURL_SETTING=
找到 CURL 库检查这一段的结尾,添加CURL_SETTING=yes
CURL_INC=
CURL_LIB="-lcurl -lssl -lcrypt $LIBS" -
回到第一行,搜索
checking for sqlite3_open
找到后将上面一行
LIBS=""
注释掉(因为会出有很多个,只修改这一个即可)
-
搜索
LIBLZMA_SETTING=
修改等于号后面部分LIBLZMA_SETTING="yes"
-
搜索
HAVE_OPENJPEG=no
找到 OPENJPEG 库检查这一段的结尾,添加HAVE_OPENJPEG="yes"
OPENJPEG_LIBS="-lopenjp2 ${LIBS}"
OPT_GDAL_FORMATS="openjpeg $OPT_GDAL_FORMATS"
-
搜索
HAVE_GEOS=
找到 GEOS 库检查这一段的结尾,添加HAVE_GEOS="yes"
HAVE_GEOS_RESULT="yes"
GEOS_LIBS="-lgeos -lgeos_c ${LIBS}"
然后执行 configure
脚本,生成 Makefile
文件。
# 注意这里的环境变量设置
PATH=${PATH}:/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin \
CC=aarch64-linux-gnu-gcc CXX=aarch64-linux-gnu-g++ \
CFLAGS=-I/home/test/code/vcpkg/installed/arm64-linux/include \
CXXFLAGS=-I/home/test/code/vcpkg/installed/arm64-linux/include \
LDFLAGS=-L/home/test/code/vcpkg/installed/arm64-linux/lib \
LIBS="-lpthread -dl" LT_SYS_LIBRAR_PATH=./lib \
./configure --prefix=/home/test/code/gdal_output --host=aarch64-linux \
--with-sqlite3=/home/test/code/vcpkg/installed/arm64-linux \
--with-rename-internal-libtiff-symbols=yes \
--with-rename-internal-shapelib-symbols=yes \
-with-curl=/home/test/code/vcpkg/installed/arm64-linux \
-with-proj=/home/test/code/vcpkg/installed/arm64-linux \
-with-proj-extra-lib-for-test="-lcurl -ltiff -lssl -lcrypto -ljpeg -llzma -lz" \
--with-zstd=/home/test/code/vcpkg/installed/arm64-linux \
--with-geotiff=internal \
--with-jpeg=/home/test/code/vcpkg/installed/arm64-linux \
--with-openjpeg=yes --with-liblzma=yes \
-with-webp=/home/test/code/vcpkg/installed/arm64-linux \
-with-geos=/home/test/code/vcpkg/installed/arm64-linux
最后进行编译
PATH=${PATH}:/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin make
4、遇到的错误及解决办法
基本上都可以按照 linux下编译GDAL3.x(集成Proj和Geos等) 里面对错误的解决办法进行解决。
这里修改后的 LIBS
变量内容如下:
OTHER_LIB_PATH="/home/test/code/vcpkg/installed/arm64-linux/lib"
LIBS = ${OTHER_LIB_PATH}/libwebp.a \
${OTHER_LIB_PATH}/libwebpdecoder.a \
${OTHER_LIB_PATH}/libwebpdemux.a \
${OTHER_LIB_PATH}/libwebpmux.a \
${OTHER_LIB_PATH}/libopenjp2.a \
${OTHER_LIB_PATH}/libturbojpeg.a \
${OTHER_LIB_PATH}/libzstd.a \
${OTHER_LIB_PATH}/libproj.a \
${OTHER_LIB_PATH}/libcurl.a \
${OTHER_LIB_PATH}/libtiff.a \
${OTHER_LIB_PATH}/libssl.a \
${OTHER_LIB_PATH}/libcrypto.a \
${OTHER_LIB_PATH}/libjpeg.a \
${OTHER_LIB_PATH}/libszip.a \
${OTHER_LIB_PATH}/libsqlite3.a \
${OTHER_LIB_PATH}/libgeos_c.a \
${OTHER_LIB_PATH}/libgeos.a \
${OTHER_LIB_PATH}/liblzma.a \
${OTHER_LIB_PATH}/libz.a \
$(KAK_LIBS) $(DWG_LIBS) $(CURL_LIB) \
$(MRSID_LIBS) $(MRSID_LIDAR_LIBS) $(ECW_LIBS) $(INGRES_LIB) \
$(PCIDSK_LIB) $(RASDAMAN_LIB) $(SOSI_LIB) \
$(OPENCL_LIB) $(JVM_LIB) $(LIBICONV) $(FGDB_LIB) $(LIBXML2_LIB) $(MONGODB_LIB) \
$(MONGOCXXV3_LIBS) $(JNI_LIB) $(HDFS_LIB) \
-lpthread -lm -lrt -ldl \
找不到 png_riffle_palette_neon
等相关定义的问题
报错的内容如下:
/bin/sh /home/test/code/gdal-3.2.0/gdal/libtool --mode=link --silent aarch64-linux-gnu-g++ -L/home/test/code/vcpkg/installed/arm64-linux/lib gdalinfo_bin.lo /home/test/code/gdal-3.2.0/gdal/libgdal.la -o gdalinfo
/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/8.3.0/../../../../aarch64-linux-gnu/bin/ld: /home/test/code/gdal-3.2.0/gdal/.libs/libgdal.so: undefined reference to `png_riffle_palette_neon'
/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/8.3.0/../../../../aarch64-linux-gnu/bin/ld: /home/test/code/gdal-3.2.0/gdal/.libs/libgdal.so: undefined reference to `png_do_expand_palette_rgb8_neon'
/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/8.3.0/../../../../aarch64-linux-gnu/bin/ld: /home/test/code/gdal-3.2.0/gdal/.libs/libgdal.so: undefined reference to `png_init_filter_functions_neon'
/opt/gcc-arm-8.3-2019.03-x86_64-aarch64-linux-gnu/bin/../lib/gcc/aarch64-linux-gnu/8.3.0/../../../../aarch64-linux-gnu/bin/ld: /home/test/code/gdal-3.2.0/gdal/.libs/libgdal.so: undefined reference to `png_do_expand_palette_rgba8_neon'
查看编译好的 .libs/libgdal.so
的导出函数
objdump -T .libs/libgdal.so|grep png_
0000000000000000 D *UND* 0000000000000000 png_riffle_palette_neon
0000000000000000 D *UND* 0000000000000000 png_do_expand_palette_rgb8_neon
0000000000000000 D *UND* 0000000000000000 png_init_filter_functions_neon
0000000000000000 D *UND* 0000000000000000 png_do_expand_palette_rgba8_neon
000000000168d064 g DF .text 000000000000005c Base png_push_restore_buffer
000000000168b9ec g DF .text 0000000000000068 Base png_malloc_base
....
果然这里几个函数没有定义,找到这几个函数所在的文件
grep png_riffle_palette_neon -R frmts/png/libpng/*
frmts/png/libpng/pngpriv.h: png_riffle_palette_neon,
frmts/png/libpng/pngrtran.c: png_riffle_palette_neon(png_ptr);
查看 frmts/png/libpng/pngpriv.h
文件,找到这个函数的声明,可知这是一个内部函数,并且只有在 PNG_ARM_NEON_IMPLEMENTATION
为 1
的时候才存在。这个函数本身没有定义,只是在 pngrtran.c
文件里面调用了一次。
// frmts/png/libpng/pngpriv.h
2120 #if PNG_ARM_NEON_IMPLEMENTATION == 1
2121 PNG_INTERNAL_FUNCTION(void,
2122 png_riffle_palette_neon,
2123 (png_structrp),
2124 PNG_EMPTY);
// frmts/png/libpng/pngrtran.c
4774 #ifdef PNG_ARM_NEON_INTRINSICS_AVAILABLE
4775 if ((png_ptr->num_trans > 0) && (png_ptr->bit_depth == 8))
4776 {
4777 if (png_ptr->riffled_palette == NULL)
4778 {
4779 /* Initialize the accelerated palette expansion. */
4780 png_ptr->riffled_palette =
4781 (png_bytep)png_malloc(png_ptr, 256 * 4);
4782 png_riffle_palette_neon(png_ptr);
4783 }
4784 }
4785 #endif
通过查看相关的宏定义可知,只有在 PNG_ARM_NEON_IMPLEMENTATION
定义了,且值为 1
的时候,这些函数才会被调用。找到frmts/png/libpng/pngpriv.h
文件中对 PNG_ARM_NEON_IMPLEMENTATION
的定义相关语句,可以知道在没有设置 PNG_ARM_NEON_OPT
宏的时候,会去根据进行相关设置。
所以这里的解决办法也很简单,直接在这里顶上加上一行 #define PNG_ARM_NEON_OPT 0
即可。
#ifndef PNG_ARM_NEON_OPT
/* ARM NEON optimizations are being controlled by the compiler settings,
* typically the target FPU. If the FPU has been set to NEON (-mfpu=neon
* with GCC) then the compiler will define __ARM_NEON__ and we can rely
* unconditionally on NEON instructions not crashing, otherwise we must
* disable use of NEON instructions.
*
* NOTE: at present these optimizations depend on 'ALIGNED_MEMORY', so they
* can only be turned on automatically if that is supported too. If
* PNG_ARM_NEON_OPT is set in CPPFLAGS (to >0) then arm/arm_init.c will fail
* to compile with an appropriate #error if ALIGNED_MEMORY has been turned
* off.
*
* Note that gcc-4.9 defines __ARM_NEON instead of the deprecated
* __ARM_NEON__, so we check both variants.
*
* To disable ARM_NEON optimizations entirely, and skip compiling the
* associated assembler code, pass --enable-arm-neon=no to configure
* or put -DPNG_ARM_NEON_OPT=0 in CPPFLAGS.
*/
# if (defined(__ARM_NEON__) || defined(__ARM_NEON)) && \
defined(PNG_ALIGNED_MEMORY_SUPPORTED)
# define PNG_ARM_NEON_OPT 2
# else
# define PNG_ARM_NEON_OPT 0
# endif
#endif