介绍几种常见的Linux源码交叉编译方式
(一)源码目录中存在configure文件的
(二)源码目录中存在configure.ac, Makefile.am文件的
(三)源码目录中存在Android.mk, Application.mk文件的
(四)源码目录中只存在Android.bp文件的
(五)源码目录中存在meson.build文件的
(六)源码目录中存在CmakeLists.txt文件的
(七)源码目录中存在Makefile文件的
建议在Linux环境下去编译源码,Ubuntu/Kali之类的都可以,Windows下缺失很多必要的环境
(一)源码目录中存在configure文件的
这种一般是跨平台编译的配置文件,我们直接在源码目录下新建一个build.sh脚本,根据本机环境指定一下NDK编译环境信息就好,在第一次运行脚本时,请将脚本的末尾的make clean, make, make install注释掉,然后运行脚本,检查是否出现配置错误,如clang,clang++编译器是否正常,如果不正常,说明构建的ndk环境或依赖环境没有配置好,修改构建参数再次运行,直到没有出现错误可以取消make clean, make, make install注释进行正常编译【备注:./configure --help可以查看当前源码可用的参数】
#以下是构建ffmpeg源码是使用的脚本
#!/bin/bash
set -x
###########根据自己电脑环境进行修改, 确保CC,CXX文件存在################Start
#Compile android api level, if compile armv7a,change to eabi21
API=21
#arm64,armv7-a,i686,x86-64
ARCH=arm64
#armv8-a,armv7-a,i686,x86-64
CPU=armv8-a
#aarch64,armv7a, i686, x86_64
TOOL_CPU_NAME=aarch64
#NDK path
NDK=/root/Android/Sdk/ndk/21.4.7075529
#Compile output
OUTPUT=/home/qlx/ffmpeg_build/$CPU
#Toolchain path, make sure this file exists
TOOLCHAIN=$NDK/toolchains/llvm/prebuilt/linux-x86_64
#Sysroot, Default
SYSROOT=$TOOLCHAIN/sysroot
TOOL_PREFIX="$TOOLCHAIN/bin/$TOOL_CPU_NAME-linux-android"
#Clang C/C++ executable file
CC="$TOOL_PREFIX$API-clang"
CXX="$TOOL_PREFIX$API-clang++"
#Strip executable file path, use to reduce .so file size, if compile release .so file,
# can remove --disable-stripping attribute from below
STRIP="$TOOL_PREFIX-strip"
#If compile armv7,change to this
#STRIP="$TOOLCHAIN/bin/arm-linux-androideabi-strip"
#########################################################################End
#启用优化
OPTIMIZE_CFLAGS="-march=$CPU"
function build
{
./configure \
--prefix=$OUTPUT \
--target-os=android \
--arch=$ARCH \
--cpu=$CPU \
--disable-asm \
--disable-stripping \
--disable-programs \
--disable-static \
--disable-doc \
--disable-ffplay \
--disable-ffprobe \
--disable-symver \
--disable-ffmpeg \
--enable-shared \
--enable-cross-compile \
--cc=$CC \
--cxx=$CXX \
--strip=$STRIP \
--sysroot=$SYSROOT \
--extra-cflags="-Os -fpic $OPTIMIZE_CFLAGS" \
make clean all
# 这里是定义用几个CPU编译
make -j8
make install
}
build
(二)源码目录中存在configure.ac, Makefile.am文件的
如果源码中存在configure文件的,我们可以直接用第一种方式编译,如果没有,我们就需要根据.ac,.am去生成configure文件,然后再使用**方式(一)**进行编译, 以下是从编译libimobiledevice时得到的模板,大同小异,如果源码目录有自身的.sh文件来生成,我们就用源码自身的.sh文件来进行修改
#!/bin/sh
#脚本文件来自libimobiledevice开源代码的autogen.sh
aclocal -I m4
libtoolize
autoheader
automake --add-missing
autoconf
if [ -z "$NOCONFIGURE" ]; then
./configure "$@"
fi
【备注:当缺乏依赖时,或者要去除依赖时,我们可以修改对应的源码目录下的.am文件,来配置编译依赖和附加参数信息,如下】
AM_CPPFLAGS = -I$(top_srcdir)/include
##############改动 Start##################
AM_CFLAGS = $(GLOBAL_CFLAGS) $(libusb_CFLAGS)
AM_LDFLAGS = $(libusb_LIBS) -lreadline -lncurses
##############改动 End###################
bin_PROGRAMS = irecovery
irecovery_SOURCES = irecovery.c
irecovery_CFLAGS = $(AM_CFLAGS)
irecovery_LDFLAGS = $(AM_LDFLAGS)
irecovery_LDADD = $(top_builddir)/src/libirecovery-1.0.la
(三)源码目录中存在Android.mk, Application.mk文件的
如果Android.mk文件在souce/jni目录下,这种情况一般我们可以通过ndk直接编译,直接在source目录下执行/your_ndk_path/ndk-build, 就可以得到编译结果,如果源码不在jni目录下,我们可以新建source/jni文件夹,将源码全部移动到jni目录下,再回到source目录执行/your_ndk_path/ndk-build【备注,编译不通过可能需要适当的修改Android.mk文件来进行编译,因为这种情况有不少是从aosp源码拿出来的,需要注释掉多余的信息】 以下是在编译libncurses_android时jni/ncurses/Android.mk文件,作为观察参考
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
serial := tty
base := base
trace := trace
tinfo := tinfo
LOCAL_MODULE := ncurses
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/ \
$(LOCAL_PATH)/../include \
$(serial) $(base) $(trace) \
$(tinfo)
LOCAL_CFLAGS := \
--param max-inline-insns-single=1200 \
-DHAVE_CONFIG_H \
-D_XOPEN_SOURCE=500 -DNDEBUG
LOCAL_SRC_FILES := \
$(serial)/hardscroll.c \
$(serial)/hashmap.c \
$(base)/lib_addch.c \
$(base)/lib_addstr.c \
$(base)/lib_beep.c \
$(base)/lib_bkgd.c \
$(base)/lib_box.c \
$(base)/lib_chgat.c \
$(base)/lib_clear.c \
$(base)/lib_clearok.c \
$(base)/lib_clrbot.c \
$(base)/lib_clreol.c \
$(base)/lib_color.c \
$(base)/lib_colorset.c \
$(base)/lib_delch.c \
$(base)/lib_delwin.c \
$(base)/lib_echo.c \
$(base)/lib_endwin.c \
$(base)/lib_erase.c \
$(base)/lib_flash.c \
./lib_gen.c \
$(base)/lib_getch.c \
$(base)/lib_getstr.c \
$(base)/lib_hline.c \
$(base)/lib_immedok.c \
$(base)/lib_inchstr.c \
$(base)/lib_initscr.c \
$(base)/lib_insch.c \
$(base)/lib_insdel.c \
$(base)/lib_insnstr.c \
$(base)/lib_instr.c \
$(base)/lib_isendwin.c \
$(base)/lib_leaveok.c \
$(base)/lib_mouse.c \
$(base)/lib_move.c \
$(serial)/lib_mvcur.c \
$(base)/lib_mvwin.c \
$(base)/lib_newterm.c \
$(base)/lib_newwin.c \
$(base)/lib_nl.c \
$(base)/lib_overlay.c \
$(base)/lib_pad.c \
$(base)/lib_printw.c \
$(base)/lib_redrawln.c \
$(base)/lib_refresh.c \
$(base)/lib_restart.c \
$(base)/lib_scanw.c \
$(base)/lib_screen.c \
$(base)/lib_scroll.c \
$(base)/lib_scrollok.c \
$(base)/lib_scrreg.c \
$(base)/lib_set_term.c \
$(base)/lib_slk.c \
$(base)/lib_slkatr_set.c \
$(base)/lib_slkatrof.c \
$(base)/lib_slkatron.c \
$(base)/lib_slkatrset.c \
$(base)/lib_slkattr.c \
$(base)/lib_slkclear.c \
$(base)/lib_slkcolor.c \
$(base)/lib_slkinit.c \
$(base)/lib_slklab.c \
$(base)/lib_slkrefr.c \
$(base)/lib_slkset.c \
$(base)/lib_slktouch.c \
$(base)/lib_touch.c \
$(serial)/lib_tstp.c \
$(base)/lib_ungetch.c \
$(serial)/lib_vidattr.c \
$(base)/lib_vline.c \
$(base)/lib_wattroff.c \
$(base)/lib_wattron.c \
$(base)/lib_winch.c \
$(base)/lib_window.c \
$(base)/nc_panel.c \
$(base)/safe_sprintf.c \
$(serial)/tty_update.c \
$(trace)/varargs.c \
$(base)/vsscanf.c \
$(base)/lib_freeall.c \
./expanded.c \
$(base)/legacy_coding.c \
$(base)/lib_dft_fgbg.c \
$(tinfo)/lib_print.c \
$(base)/resizeterm.c \
$(tinfo)/use_screen.c \
$(base)/use_window.c \
$(base)/wresize.c \
$(tinfo)/access.c \
$(tinfo)/add_tries.c \
$(tinfo)/alloc_ttype.c \
./codes.c \
./comp_captab.c \
$(tinfo)/comp_error.c \
$(tinfo)/comp_hash.c \
$(tinfo)/db_iterator.c \
$(tinfo)/doalloc.c \
$(tinfo)/entries.c \
./fallback.c \
$(tinfo)/free_ttype.c \
$(tinfo)/getenv_num.c \
$(tinfo)/home_terminfo.c \
$(tinfo)/init_keytry.c \
$(tinfo)/lib_acs.c \
$(tinfo)/lib_baudrate.c \
$(tinfo)/lib_cur_term.c \
$(tinfo)/lib_data.c \
$(tinfo)/lib_has_cap.c \
$(tinfo)/lib_kernel.c \
./lib_keyname.c \
$(tinfo)/lib_longname.c \
$(tinfo)/lib_napms.c \
$(tinfo)/lib_options.c \
$(tinfo)/lib_raw.c \
$(tinfo)/lib_setup.c \
$(tinfo)/lib_termcap.c \
$(tinfo)/lib_termname.c \
$(tinfo)/lib_tgoto.c \
$(tinfo)/lib_ti.c \
$(tinfo)/lib_tparm.c \
$(tinfo)/lib_tputs.c \
$(trace)/lib_trace.c \
$(tinfo)/lib_ttyflags.c \
$(serial)/lib_twait.c \
$(tinfo)/name_match.c \
./names.c \
$(tinfo)/obsolete.c \
$(tinfo)/read_entry.c \
$(tinfo)/read_termcap.c \
$(tinfo)/strings.c \
$(base)/tries.c \
$(tinfo)/trim_sgr0.c \
./unctrl.c \
$(trace)/visbuf.c \
$(tinfo)/alloc_entry.c \
$(tinfo)/captoinfo.c \
$(tinfo)/comp_expand.c \
$(tinfo)/comp_parse.c \
$(tinfo)/comp_scan.c \
$(tinfo)/parse_entry.c \
$(tinfo)/write_entry.c \
$(base)/define_key.c \
$(tinfo)/hashed_db.c \
$(base)/key_defined.c \
$(base)/keybound.c \
$(base)/keyok.c \
$(base)/version.c \
include $(BUILD_STATIC_LIBRARY)
#include $(BUILD_SHARED_LIBRARY)
(四)源码目录中只存在Android.bp文件的
这种情况的源码一般是从aosp系统源码中提取出来的,应该是aosp8.0左右将Android.mk改为Android.bp,我们最好拿到aosp源码中进行编译,比如将libxml2放入aosp源码中,lunch想要的cpu架构后,执行mmm souce/libxml2进行编译,这种没有什么好的方式,有能力也可以将其转为Android.mk等其它的方式编译【备注:这种源码大多数都可以在aosp源码中找到相对应的,如果可以的话,直接在现有的aosp中提取,除非你想要特定的版本再拉进去(部分源码还得要指定的aosp版本,这是最头疼的)】 以下列举该类源码一般存放在aosp哪个目录(如常见的libcutils,libxml2)
Y:\smr_core\operation\alps\external
#####system/core也存在部分源码,如libcutils,这在ndk14是被移除了的
Y:\smr_core\operation\alps\system\core
(五)源码目录中存在meson.build文件的
这应该是一种新的构建方式(用的是外源编译,也就编译时可以将配置信息和编译结果生成再源码外,不影响源码),依赖python,meson,nijia。编译方式是通过python里的meson工具,根据源码中的meson.build依赖信息,生成ninjia的执行文件,再由ninjia编译,下面是跨平台编译后写好的脚本,以下是编译步骤 1.源码目录新建build文件夹,用于存放编译脚本和编译结果 2.新建build/cross_file.txt(名字随意)
#规范是用单引号,而且不能做if判断
[host_machine]
system = 'android'
cpu_family = 'arm'
cpu = 'arm'
endian = 'little'
#system = 'linux'
#cpu_family = 'arm'
#cpu = 'armv7'
#endian = 'little'
[constants]
ndk = '/root/Android/Sdk/ndk/21.4.7075529'
#可能不需要,其实真正意义上,binaries配置正确对了,sys_root对了问题就不大了
#toolchain = '/home/git/work/ndk/toolchain/bin/arm-linux-androideabi-'
outdir = '/home/git/work/ndk/builddir/out/'
[binaries]
c = ndk + 'arm-linux-androideabi-gcc'
cpp = ndk + 'arm-linux-androideabi-g++'
ar = ndk + 'arm-linux-androideabi-ar'
#ld = ndk + 'arm-linux-androideabi-ld'
#objcopy = ndk + 'arm-linux-androideabi-objcopy'
strip = ndk + 'arm-linux-androideabi-strip'
#这里指的是pkg-config的可执行文件,如果是编译安卓,我们可能需要给定错误的路径,让系统默认的不工作,因为是跨平台编译,可能没法用主机的
#pkgconfig = '/usr/bin/pkg-config'
[built-in options]
#c_std = 'c11'
prefix = '/home/kali/Desktop/buildout'
c_args = '-fpic -I/usr/c_head/include'
#c_args = '--sysroot=/home/ppetraki/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIE -fPIC --target=armv7-none-linux-androideabi'
#c_link_args = ''
cpp_args = '-fpic -I/usr/c_head/include'
#cpp_args = '--sysroot=/home/ppetraki/Android/Sdk/ndk-bundle/toolchains/llvm/prebuilt/linux-x86_64/sysroot -fPIE -fPIC --target=armv7-none-linux-androideabi'
#cpp_link_args = ''
#默认.so,.a的pkg-config路径,指的不是so文件夹路径,如果没有,那就不填,这一般是ndk编译其它configure源码文件会自动产生的
pkg_config_path = '/home/git/work/ndk/builddir/out/lib/pkgconfig'
default_library = 'static'
# vim:ft=python
3.新建编build/build.sh译脚本文件,这里我们可以将第一步的NDK参数复制到下面的脚本信息替换就好,其实最重要的就是CC,CXX这两个参数,以及CFLAGS,LDFLAGS等,我们可以在meson setup后附加和configure一样的参数【备注:关于setup参数,我们可以执行meson setup --help】 这里的脚本我可能没有完善的那么好,因为当初编译libfuse3.9时,后面因为太新不用了就删了,没来得及把脚本拷贝出来,但实际情况也是这么改过来的,网上的教程非常少,当时编译的基本是看到结果了的,所以不用纠结这种方式有问题,编译的ndk路径写对了,看报错信息就好
#!/bin/bash
# https://gitlab.kitware.com/cmake/cmake/issues/18739
ARCH="arm"
ABI="armeabi-v7a"
API_LEVEL="21"
BUILD_DIR="armv7a-build"
ANDROID_SDK_HOME="/home/ppetraki/Android/Sdk"
ANDROID_NDK_HOME="${ANDROID_SDK_HOME}/ndk-bundle"
TOOLCHAIN="$ANDROID_NDK_HOME/build/cmake/android.toolchain.cmake"
HOST_ROOT="${ANDROID_NDK_HOME}/toolchains/llvm/prebuilt/linux-x86_64"
SYS_ROOT="${HOST_ROOT}/sysroot"
LIB_PATH="/${SYS_ROOT}/usr/lib/${ARCH}-linux-androideabi:${SYS_ROOT}/usr/lib/${ARCH}-linux-androideabi/${API_LEVEL}:${ANDROID_NDK_HOME}/platforms/android-${API_LEVEL}/arch-${ARCH}/usr/lib"
INC_PATH="${SYS_ROOT}/usr/include"
export PATH="${HOST_ROOT}/bin:${PATH}"
# XXX I not sure how much of this cmake config is actually having an impact
export CMAKE_PREFIX_PATH="${SYS_ROOT}"
export CMAKE_ROOT="${ANDROID_SDK_HOME}/Android/Sdk/cmake/3.10.2.4988404"
export CMAKE_LIBRARY_PATH=${LIB_PATH}
export CMAKE_INCLUDE_PATH=${INC_PATH}
export CFLAGS="-DCMAKE_TOOLCHAIN_FILE=${TOOLCHAIN} \
-DANDROID_STL=c++_shared \
-DANDROID_TOOLCHAIN=clang \
-DANDROID_PLATFORM=android-${API_LEVEL} \
-DANDROID_ABI=${ABI}"
echo "building..."
echo "Toolchain: ${TOOLCHAIN}"
rm -rf ${BUILD_DIR}
#..应该是上级目录,关于setup参数,我们可以执行meson setup --help
meson setup --errorlogs \
--prefix=${ANDROID_NDK_HOME} \
--includedir=${INC_PATH} \
--libdir=${LIB_PATH} \
--build.cmake-prefix-path=${SYS_ROOT} \
--cross-file cross/android-armhf.ini \
${BUILD_DIR} ..
ninja -C ${BUILD_DIR}
4.编译
meson release build
这是meson官网关于跨平台编译的教程,截至目前为止帮助信息少得可怜,mesonbuild.com/Cross-compi…
(六)源码目录中存在CmakeLists.txt文件的
这种情况源码一般都会存在.sh脚本来辅助编译,我们只需要执行脚本就好了,以下时libzip1.7.3/source/android.do.sh脚本代码,我们可以参考【备注:libzip1.7.3/CmakeLists.txt是cmake脚本的地址,实际上编译的时候就调用了CmakeLists.txt,如果源码没有脚本,我们可以拷贝进行稍微调整,基本上只是配置编译工具链,因为库和编译信息都在CmakeLists.txt,不用我们配置】 示例脚本1 以下是构建ethtool-Android二进制的脚本
#!/bin/sh
export ANDROID_NDK_ROOT=$NDK_HOME
export INSTALL_DIR=$(pwd)/install
echo "INSTALL_DIR=$INSTALL_DIR"
export ANDROID_TARGET_PLATFORM=arm64-v8a
# 生成Makefile
cmake -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX:PATH=../../${INSTALL_DIR}/${ANDROID_TARGET_PLATFORM} \
-DANDROID_ABI=${ANDROID_TARGET_PLATFORM} \
-DENABLE_OPENSSL:BOOL=OFF \
-DENABLE_COMMONCRYPTO:BOOL=OFF \
-DENABLE_GNUTLS:BOOL=OFF \
-DENABLE_MBEDTLS:BOOL=OFF \
-DENABLE_OPENSSL:BOOL=OFF \
-DENABLE_WINDOWS_CRYPTO:BOOL=OFF \
-DBUILD_TOOLS:BOOL=OFF \
-DBUILD_REGRESS:BOOL=OFF \
-DBUILD_EXAMPLES:BOOL=OFF \
-DBUILD_SHARED_LIBS:BOOL=$want_shared \
-DBUILD_DOC:BOOL=OFF \
-DANDROID_TOOLCHAIN=clang -B$BUILD_DIR/${ANDROID_TARGET_PLATFORM}
# Makefile的方式编译,cd $BUILD_DIR/${ANDROID_TARGET_PLATFORM}目录,然后得到源文件
make -j4
# 可以用这种方式循环编译所有
# for ANDROID_TARGET_PLATFORM in arm64-v8a, armeabi-v7a, x64, x86_64
# done
CMake原理:cmake命令其实是根据参数,以及CMakeList.txt文件生成Makefile的编译参数(执行完命令并不会得到二进制文件或者动态库),再通过make去编译的(make才能得到二进制文件)
ANDROID_TARGET_PLATFORM 表示目标编译平台
DCMAKE_INSTALL_PREFIX 表示执行make install之后,生成二进制文件的路径,但是这里我们的规则没有install,直接执行make就可以了,而make一般是生成二进制在当前目录
DANDROID_TOOLCHAIN 指定构建工具链为clang,以前还有gcc的,现在似乎都没有了,clang/clang++足够了
DANDROID_ABI 表示生成的二进制文件
DCMAKE_TOOLCHAIN_FILE 表示跨平台编译器的文件路径
示例脚本2
#do.sh
# Author: Declan Moran
# www.silverglint.com
# Thanks to damaex (https://github.com/damaex), for significant contributions
ANDROID_NDK_ROOT=/home/android/android-ndk-r19c
INSTALL_DIR=install
BUILD_DIR=build
START_DIR=$(pwd)
rm -rf $INSTALL_DIR
rm -rf $BUILD_DIR
mkdir -p $BUILD_DIR #"${ANDROID_TARGET_PLATFORM}"
#--------------------------------------------------------------------
build_it()
{
# builds either a static or shared lib depending on parm passed (ON or OFF)
want_shared=$1
cmake -DCMAKE_TOOLCHAIN_FILE=${ANDROID_NDK_ROOT}/build/cmake/android.toolchain.cmake \
-DCMAKE_INSTALL_PREFIX:PATH=../../${INSTALL_DIR}/${ANDROID_TARGET_PLATFORM} \
-DANDROID_ABI=${ANDROID_TARGET_PLATFORM} \
-DENABLE_OPENSSL:BOOL=OFF \
-DENABLE_COMMONCRYPTO:BOOL=OFF \
-DENABLE_GNUTLS:BOOL=OFF \
-DENABLE_MBEDTLS:BOOL=OFF \
-DENABLE_OPENSSL:BOOL=OFF \
-DENABLE_WINDOWS_CRYPTO:BOOL=OFF \
-DBUILD_TOOLS:BOOL=OFF \
-DBUILD_REGRESS:BOOL=OFF \
-DBUILD_EXAMPLES:BOOL=OFF \
-DBUILD_SHARED_LIBS:BOOL=$want_shared \
-DBUILD_DOC:BOOL=OFF \
-DANDROID_TOOLCHAIN=clang cmake -H.. -B$BUILD_DIR/${ANDROID_TARGET_PLATFORM}
#run make with all system threads and install
cd $BUILD_DIR/${ANDROID_TARGET_PLATFORM}
make install -j$(nproc --all)
cd $START_DIR
}
#--------------------------------------------------------------------
for ANDROID_TARGET_PLATFORM in armeabi-v7a arm64-v8a x86 x86_64
do
echo "Building libzip for ${ANDROID_TARGET_PLATFORM}"
build_it ON
build_it OFF
if [ $? -ne 0 ]; then
echo "Error executing: cmake"
exit 1
fi
if [ $? -ne 0 ]; then
echo "Error executing make install for platform: ${ANDROID_TARGET_PLATFORM}"
exit 1
fi
done
(七)源码目录中存在Makefile文件的
这种情况遇到的比较少,一般都是上面6种情况,如果遇到只有Makefile的情况,我们需要观察一下里面的文件,想办法将ndk的clang,以及clang++提供给其编译就行,其实跨平台编译主要就是拿ndk对应的c/c++环境去编译源码
写在最后:这是分析了很久做的,当时三天编译了十几个源码,大部分都编译出来了的,不过因为文件夹太乱,删除了不少文件,导致写的时候有些build.sh已经被删了,并且源码也删了还没有备份,所以这里有部分buid.sh说的不明白,但是一定是根据build.sh改过来的,跨平台要有耐心,有时候遇到编译的库需要依赖其它库,又得先编译其它库的跨平台库,相当麻烦,而且编译了还不知道对端环境系统支不支持这样的操作,所以编译要有耐心。