OrangePi_CM5/compile-orangepi5ultra.sh

542 lines
17 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# ============================================================================
# Orange Pi 5 Ultra - update.img 完整编译脚本
# 官方orangepi-build系统 - 支持多种源码获取方式
# ============================================================================
set -e
LEGACY_WORK_DIR="/data/OrangePi CM5"
WORK_DIR="/data/OrangePi_CM5"
CACHE_ROOT="/data/OrangePi_cache"
BUILD_DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${WORK_DIR}/build_${BUILD_DATE}.log"
MODE="auto"
# 颜色输出
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# ============================================================================
# 日志记录函数
# ============================================================================
log() {
echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" | tee -a "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[WARNING]${NC} $1" | tee -a "$LOG_FILE"
}
# ============================================================================
# 处理工作目录(避免路径中空格导致官方脚本异常)
# ============================================================================
prepare_workspace() {
mkdir -p "$WORK_DIR"
if [ -d "$LEGACY_WORK_DIR" ]; then
log "检测到旧目录: $LEGACY_WORK_DIR"
# 同步关键目录,尽量复用已有下载/缓存,避免重复拉取。
if [ -d "$LEGACY_WORK_DIR/SDKs" ] && [ ! -e "$WORK_DIR/SDKs" ]; then
cp -a "$LEGACY_WORK_DIR/SDKs" "$WORK_DIR/"
log "✓ 已迁移 SDKs 到新目录"
fi
if [ -d "$LEGACY_WORK_DIR/sources" ] && [ ! -e "$WORK_DIR/sources" ]; then
cp -a "$LEGACY_WORK_DIR/sources" "$WORK_DIR/"
log "✓ 已迁移 sources 到新目录"
fi
if [ -d "$LEGACY_WORK_DIR/orangepi-build" ] && [ ! -e "$WORK_DIR/orangepi-build" ]; then
cp -a "$LEGACY_WORK_DIR/orangepi-build" "$WORK_DIR/"
log "✓ 已迁移 orangepi-build 到新目录"
fi
fi
}
# ============================================================================
# 检查环境和依赖
# ============================================================================
check_environment() {
log "检查编译环境..."
# 检查必需工具
local required_tools=("git" "gcc" "make" "device-tree-compiler")
for tool in "${required_tools[@]}"; do
if ! command -v "$tool" &> /dev/null; then
log_warn "$tool 未安装,尝试安装..."
apt-get update
apt-get install -y "$tool"
else
log "$tool 已安装"
fi
done
# 检查交叉编译工具链
if ! command -v aarch64-linux-gnu-gcc &> /dev/null; then
log_warn "aarch64-linux-gnu-gcc 未安装,尝试安装..."
apt-get install -y gcc-aarch64-linux-gnu build-essential
else
log "✓ aarch64-linux-gnu-gcc 已安装"
fi
log "环境检查完成"
}
# ============================================================================
# 配置 git 以应对网络问题
# ============================================================================
configure_git() {
log "配置 git 网络参数..."
git config --global http.connectTimeout 120
git config --global http.postBuffer 524288000
git config --global core.compression 0
git config --global url."https://".insteadOf git://
log "✓ git 配置完成"
}
# ============================================================================
# 下载源码 - 方式1: Gitee镜像国内优先
# ============================================================================
download_from_gitee() {
local repo_name=$1
local repo_url=$2
local branch=$3
local target_dir=$4
log "尝试从 Gitee 下载 $repo_name (分支: $branch)..."
if [ -d "$target_dir" ]; then
log "目录已存在,更新代码..."
cd "$target_dir"
git pull origin "$branch" 2>&1 && return 0
cd - > /dev/null
fi
if git clone --depth=1 -b "$branch" "$repo_url" "$target_dir" 2>&1; then
log "✓ 从 Gitee 下载成功"
return 0
else
log_warn "从 Gitee 下载失败"
return 1
fi
}
# ============================================================================
# 下载源码 - 方式2: GitHub (带重试)
# ============================================================================
download_from_github() {
local repo_name=$1
local repo_url=$2
local branch=$3
local target_dir=$4
local retry_count=3
log "尝试从 GitHub 下载 $repo_name (分支: $branch)..."
if [ -d "$target_dir" ]; then
log "目录已存在,更新代码..."
cd "$target_dir"
git pull origin "$branch" 2>&1 && return 0
cd - > /dev/null
fi
for ((i=1; i<=retry_count; i++)); do
log "GitHub 克隆尝试 $i/$retry_count..."
if git clone --depth=1 -b "$branch" "$repo_url" "$target_dir" 2>&1; then
log "✓ 从 GitHub 下载成功"
return 0
fi
sleep 5
done
log_warn "GitHub 下载最终失败"
return 1
}
# ============================================================================
# 获取 orangepi-build
# ============================================================================
get_orangepi_build() {
log "获取 orangepi-build 编译系统..."
local BUILD_DIR="${WORK_DIR}/orangepi-build"
# 第1优先级: 从本地 SDKs 目录提取
if extract_from_sdks "orangepi-build" "$BUILD_DIR"; then
ORANGEPI_BUILD_DIR="$BUILD_DIR"
return 0
fi
# 第2优先级: Gitee 镜像
if download_from_gitee \
"orangepi-build" \
"https://gitee.com/orangepi-xunlong/orangepi-build.git" \
"next" \
"$BUILD_DIR"; then
ORANGEPI_BUILD_DIR="$BUILD_DIR"
return 0
fi
# 第3优先级: GitHub
if download_from_github \
"orangepi-build" \
"https://github.com/orangepi-xunlong/orangepi-build.git" \
"next" \
"$BUILD_DIR"; then
ORANGEPI_BUILD_DIR="$BUILD_DIR"
return 0
fi
log_error "无法获取 orangepi-build"
return 1
}
# ============================================================================
# 建立持久化缓存(避免每次重复下载工具链)
# ============================================================================
setup_build_cache_links() {
local build_dir=$1
local shared_dl="${CACHE_ROOT}/dl"
local shared_toolchains="${CACHE_ROOT}/toolchains"
local shared_ext_cache="${CACHE_ROOT}/external-cache"
mkdir -p "$shared_dl" "$shared_toolchains" "$shared_ext_cache"
# 如果旧目录里已有下载内容,首次迁移到共享缓存。
if [ -d "$build_dir/dl" ] && [ ! -L "$build_dir/dl" ] && [ -z "$(ls -A "$shared_dl" 2>/dev/null)" ]; then
cp -a "$build_dir/dl/." "$shared_dl/" 2>/dev/null || true
fi
if [ -d "$build_dir/toolchains" ] && [ ! -L "$build_dir/toolchains" ] && [ -z "$(ls -A "$shared_toolchains" 2>/dev/null)" ]; then
cp -a "$build_dir/toolchains/." "$shared_toolchains/" 2>/dev/null || true
fi
if [ -d "$build_dir/external/cache" ] && [ ! -L "$build_dir/external/cache" ] && [ -z "$(ls -A "$shared_ext_cache" 2>/dev/null)" ]; then
cp -a "$build_dir/external/cache/." "$shared_ext_cache/" 2>/dev/null || true
fi
mkdir -p "$build_dir/external"
rm -rf "$build_dir/dl" "$build_dir/toolchains" "$build_dir/external/cache"
ln -s "$shared_dl" "$build_dir/dl"
ln -s "$shared_toolchains" "$build_dir/toolchains"
ln -s "$shared_ext_cache" "$build_dir/external/cache"
log "✓ 已启用共享缓存"
log " dl: $shared_dl"
log " toolchains: $shared_toolchains"
log " external cache: $shared_ext_cache"
}
# ============================================================================
# 方式3: 从本地 SDKs 目录提取源码包
# ============================================================================
extract_from_sdks() {
local repo_name=$1
local target_dir=$2
log "尝试从 SDKs 目录提取 $repo_name..."
# 检查目标目录是否已存在
if [ -d "$target_dir" ]; then
log "✓ 目录已存在: $target_dir"
return 0
fi
# 方式1: 查找 orangepi-build-next.zip
if [ "$repo_name" = "orangepi-build" ]; then
local archive="${WORK_DIR}/SDKs/orangepi-build-next.zip"
if [ -f "$archive" ]; then
log "找到源码包: $archive"
log "解压中..."
mkdir -p "$target_dir"
unzip -q "$archive" -d "$target_dir" 2>/dev/null || return 1
# 处理可能的子目录结构
if [ -d "${target_dir}/orangepi-build"* ]; then
mv "${target_dir}"/orangepi-build* "${target_dir}_tmp"
rm -rf "$target_dir"
mv "${target_dir}_tmp" "$target_dir"
elif [ -d "${target_dir}/orangepi-build" ]; then
local temp_build="${target_dir}/orangepi-build"
mv "$temp_build"/* "$target_dir"/ 2>/dev/null || true
rm -rf "$temp_build"
fi
if [ -f "${target_dir}/build.sh" ]; then
log "✓ 从 SDKs 解压成功"
return 0
fi
fi
# 方式2: 查找 OrangePi_Build-master.zip
archive="${WORK_DIR}/SDKs/OrangePi_Build-master.zip"
if [ -f "$archive" ]; then
log "找到备选源码包: $archive"
log "解压中..."
mkdir -p "$target_dir"
unzip -q "$archive" -d "$target_dir" 2>/dev/null || return 1
# 处理可能的子目录结构
if [ -d "${target_dir}/OrangePi_Build-master" ]; then
mv "${target_dir}/OrangePi_Build-master"/* "$target_dir"/ 2>/dev/null || true
rmdir "${target_dir}/OrangePi_Build-master" 2>/dev/null || true
fi
if [ -f "${target_dir}/build.sh" ]; then
log "✓ 从 SDKs 解压成功"
return 0
fi
fi
return 1
fi
return 1
}
# ============================================================================
# 检查本地预准备的源码包
# ============================================================================
check_local_sources() {
log "检查本地是否存在预准备的源码包..."
local sources_dir="${WORK_DIR}/sources"
if [ ! -d "$sources_dir" ]; then
return 1
fi
# 检查是否存在 orangepi-build 源码
for archive in "$sources_dir"/{orangepi-build,u-boot,linux,kernel}*.tar* "$sources_dir"/*.zip; do
if [ -f "$archive" ]; then
log "找到本地源码包: $archive"
return 0
fi
done
return 1
}
# ============================================================================
# 编译 update.img (使用 orangepi-build)
# ============================================================================
build_update_img() {
local BUILD_DIR=$1
log "开始编译 update.img..."
cd "$BUILD_DIR"
# 显示已有源码信息
log "编译系统目录结构:"
ls -la | head -20 >> "$LOG_FILE"
# 确保脚本有执行权限
chmod +x build.sh
# 使用官方编译命令编译完整镜像
# 参数说明:
# BOARD=orangepi5ultra - 开发板型号
# BRANCH=legacy - 使用 linux5.10 (支持 IMX586 相机)
# BUILD_OPT=image - 编译完整镜像
# RELEASE=bullseye - Debian Bullseye 发行版
# BUILD_DESKTOP=no - 编译服务器版 (更小体积,可选改为 yes)
if [ "$MODE" = "menu" ]; then
log "执行官方编译脚本(交互模式)..."
log "将进入 ncurses 菜单,请手动选择编译类型"
# ncurses 必须直连终端,不能通过 tee 管道输出
sudo ./build.sh
local build_rc=$?
else
log "执行官方编译脚本(自动模式)..."
log "编译参数: BOARD=orangepi5ultra BRANCH=legacy BUILD_OPT=image KERNEL_CONFIGURE=no"
sudo ./build.sh \
BOARD=orangepi5ultra \
BRANCH=legacy \
BUILD_OPT=image \
RELEASE=bullseye \
BUILD_DESKTOP=no \
KERNEL_CONFIGURE=no 2>&1 | tee -a "$LOG_FILE"
local build_rc=${PIPESTATUS[0]}
fi
if [ ${build_rc:-1} -eq 0 ]; then
log "✓ 编译成功"
return 0
else
# 某些上游脚本会在收尾阶段返回非0但镜像已成功产出。
local produced_img
produced_img=$(find "$BUILD_DIR/output/images" -name "*.img" -type f 2>/dev/null | head -1)
if [ -n "$produced_img" ]; then
log_warn "build.sh 返回非0但已检测到镜像产物: $produced_img"
log_warn "按成功继续执行验证步骤"
return 0
fi
log_error "编译失败"
return 1
fi
}
# ============================================================================
# 验证编译结果
# ============================================================================
verify_build() {
local BUILD_DIR=$1
log "验证编译结果..."
local output_dir="${BUILD_DIR}/output/images"
if [ ! -d "$output_dir" ]; then
log_error "输出目录不存在: $output_dir"
return 1
fi
# 优先查找 update.img找不到则接受任意 .img 作为总包产物。
local update_img
update_img=$(find "$output_dir" -name "*update*.img" -type f 2>/dev/null | head -1)
if [ -z "$update_img" ]; then
update_img=$(find "$output_dir" -name "*.img" -type f 2>/dev/null | head -1)
fi
if [ -z "$update_img" ]; then
log_error "未找到镜像文件 (*.img)"
return 1
fi
log "✓ 找到编译结果: $update_img"
local img_size=$(du -sh "$update_img" | awk '{print $1}')
log "镜像大小: $img_size"
# 生成校验和
log "生成 SHA256 校验和..."
sha256sum "$update_img" > "${update_img}.sha256"
log "编译结果信息:"
cat >> "$LOG_FILE" << EOF
=== 编译完成 ===
时间: $(date)
镜像文件: $update_img
大小: $img_size
校验和: $(cat "${update_img}.sha256")
EOF
return 0
}
# ============================================================================
# 主流程
# ============================================================================
main() {
case "${1:-}" in
menu|interactive)
MODE="menu"
;;
auto|"")
MODE="auto"
;;
*)
echo "用法: $0 [auto|menu]"
echo " auto: 默认自动编译(无 ncurses 菜单)"
echo " menu: 进入官方 ncurses 菜单手动选择"
exit 1
;;
esac
log "=========================================="
log "Orange Pi 5 Ultra - update.img 编译"
log "=========================================="
log "工作目录: $WORK_DIR"
log "旧目录(兼容): $LEGACY_WORK_DIR"
log "日志文件: $LOG_FILE"
log "运行模式: $MODE"
prepare_workspace
cd "$WORK_DIR" || { log_error "无法进入工作目录"; exit 1; }
# 第1步: 检查环境
check_environment
# 第2步: 配置git
configure_git
# 第3步: 获取 orangepi-build
log ""
log "========== 步骤1: 获取源码 =========="
if ! get_orangepi_build; then
log_error "获取 orangepi-build 失败"
log ""
log "解决方案:"
log "1. ✓ 自动优先使用 \${WORK_DIR}/SDKs/ 目录下的源码包:"
log " - orangepi-build-next.zip"
log " - OrangePi_Build-master.zip"
log ""
log "2. 如果上述 SDKs 目录缺少文件,请:"
log " a) 检查 ${WORK_DIR}/SDKs/ 目录是否存在"
log " b) 确保源码包完整且未损坏"
log " c) 或将官方源码包放入该目录并重新运行"
log ""
log "3. 作为备选,可将源码放在 ${WORK_DIR}/sources/ 目录下"
log ""
log "4. 若选择网络下载:"
log " - 确保有稳定的网络连接"
log " - 如需认证,请配置 git 凭证: git config --global credential.helper store"
exit 1
fi
BUILD_DIR="$ORANGEPI_BUILD_DIR"
# 第3.5步: 配置持久缓存
setup_build_cache_links "$BUILD_DIR"
# 第4步: 编译 update.img
log ""
log "========== 步骤2: 编译镜像 =========="
if ! build_update_img "$BUILD_DIR"; then
log_error "编译失败,详情查看日志: $LOG_FILE"
exit 1
fi
# 第5步: 验证结果
log ""
log "========== 步骤3: 验证结果 =========="
if ! verify_build "$BUILD_DIR"; then
log_error "验证失败"
exit 1
fi
log ""
log "=========================================="
log "✓ 编译完成!"
log "=========================================="
log "后续步骤:"
log "1. 下载镜像: scp root@www.yuquanjun.cn:/data/OrangePi_CM5/orangepi-build/output/images/*/update.img ./"
log "2. 使用 RKDevTool 或 balenaEtcher 烧录镜像"
log "=========================================="
}
# 运行主程序
main "$@"