以下是 Android (arm64-v8a) 交叉编译 C/C++ 项目的完整脚本模板,基于 NDK 工具链,支持自定义源文件编译为静态库/动态库/可执行文件:
1. 基础交叉编译脚本 (build_android.sh
)
bash
复制
#!/bin/bash
# Android 交叉编译脚本 (arm64-v8a)
# 支持:可执行文件、静态库(.a)、动态库(.so)
# 使用方法:./build_android.sh [all|clean]
# 配置区 ================================================
NDK_PATH="/home/wu/android/android-ndk-r27c" # 修改为你的NDK路径
ANDROID_API=30 # Android API 级别
TARGET_ARCH="aarch64" # arm64-v8a
OUTPUT_DIR="android_${TARGET_ARCH}" # 输出目录
# 编译目标配置
TARGET_TYPE="shared" # 可选项: exec(可执行文件)|static(静态库)|shared(动态库)
OUTPUT_NAME="native-lib" # 输出文件名(不含后缀)
SOURCE_FILES=( # 源文件列表
"src/main.cpp"
"src/utils.c"
)
INCLUDE_DIRS=( # 头文件搜索路径
"include"
"third_party/headers"
)
# 编译参数
CFLAGS="-DANDROID -fPIC -fstack-protector-strong -Wall -march=armv8-a"
LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-undefined -Wl,-z,noexecstack"
# 工具链配置(通常无需修改)
TOOLCHAIN="${NDK_PATH}/toolchains/llvm/prebuilt/linux-x86_64"
SYSROOT="${TOOLCHAIN}/sysroot"
CC="${TOOLCHAIN}/bin/${TARGET_ARCH}-linux-android${ANDROID_API}-clang"
CXX="${TOOLCHAIN}/bin/${TARGET_ARCH}-linux-android${ANDROID_API}-clang++"
AR="${TOOLCHAIN}/bin/llvm-ar"
STRIP="${TOOLCHAIN}/bin/llvm-strip"
# 函数区 ================================================
function build() {
echo "===== 开始 Android (${TARGET_ARCH}) 编译 ====="
mkdir -p ${OUTPUT_DIR}
# 拼接编译命令
local COMPILE_CMD
case ${TARGET_TYPE} in
"exec")
COMPILE_CMD="${CXX} --sysroot=${SYSROOT}"
OUTPUT="${OUTPUT_DIR}/${OUTPUT_NAME}"
;;
"static")
COMPILE_CMD="${AR} rcs"
OUTPUT="${OUTPUT_DIR}/lib${OUTPUT_NAME}.a"
;;
"shared")
COMPILE_CMD="${CXX} --sysroot=${SYSROOT} -shared"
OUTPUT="${OUTPUT_DIR}/lib${OUTPUT_NAME}.so"
;;
*)
echo "错误:未知编译类型 ${TARGET_TYPE}"
exit 1
;;
esac
# 添加头文件路径
local INCLUDE_FLAGS
for dir in "${INCLUDE_DIRS[@]}"; do
INCLUDE_FLAGS+=" -I${dir}"
done
# 执行编译
${COMPILE_CMD} \
${CFLAGS} \
${INCLUDE_FLAGS} \
"${SOURCE_FILES[@]}" \
${LDFLAGS} \
-o "${OUTPUT}"
# 剥离调试符号(发布时使用)
if [[ -f "${OUTPUT}" ]]; then
${STRIP} --strip-all "${OUTPUT}"
echo "编译成功:${OUTPUT}"
file "${OUTPUT}"
else
echo "错误:编译失败!"
exit 1
fi
}
function clean() {
rm -rf ${OUTPUT_DIR}
echo "已清理输出目录"
}
# 主流程 ================================================
case "$1" in
"all")
build
;;
"clean")
clean
;;
*)
echo "用法: $0 [all|clean]"
exit 1
;;
esac
2. 关键功能说明
三种输出类型支持:
exec
:可执行文件(如命令行工具)static
:静态库(.a
文件)shared
:动态库(.so
,JNI 常用)
自动处理:
- 头文件搜索路径 (
-I
) - NDK 工具链选择
- 调试符号剥离(减小体积)
- 头文件搜索路径 (
使用方法:
bash
复制
# 编译项目 ./build_android.sh all # 清理输出 ./build_android.sh clean
3. 进阶用法示例
示例1:编译JNI动态库
bash
复制
# 修改配置区
TARGET_TYPE="shared"
OUTPUT_NAME="jni-native" # 最终生成 libjni-native.so
SOURCE_FILES=(
"src/jni_interface.cpp"
"src/algorithm.c"
)
INCLUDE_DIRS=(
"${NDK_PATH}/sources/android/support/include"
"jni"
)
CFLAGS+=" -D__ARM_NEON__" # 启用NEON指令集
示例2:编译可执行文件
bash
复制
TARGET_TYPE="exec"
OUTPUT_NAME="android-cli-tool"
SOURCE_FILES=(
"src/main.c"
"src/crypto.c"
)
LDFLAGS+=" -lm" # 链接数学库
4. 常见问题解决
找不到头文件:
- 检查
INCLUDE_DIRS
是否包含所有必要路径 - 使用
-v
参数查看详细搜索路径:bash
复制
${CC} -v ${CFLAGS} ${INCLUDE_FLAGS} test.c
- 检查
链接失败:
- 确保
LDFLAGS
包含需要的库(如-llog
用于Android Log) - 检查ABI兼容性:
bash
复制
readelf -h ${OUTPUT} | grep Machine
- 确保
调试版本:
- 移除
STRIP
步骤 - 添加调试符号:
bash
复制
CFLAGS+=" -g -O0"
- 移除
5. 与FFmpeg联合编译
如果需要链接已编译的FFmpeg库:
bash
复制
# 在配置区添加
INCLUDE_DIRS+=(
"/home/wu/my-ffmpeg/ffmpeg-7.1/android/arm64-v8a/include"
)
LDFLAGS+=" -L/home/wu/my-ffmpeg/ffmpeg-7.1/android/arm64-v8a/lib -lavcodec -lavformat"
这个脚本提供了灵活的交叉编译解决方案,可根据实际需求调整参数。