Initial commit: OrangePi CM5 fixed build system - auto/menu modes, no-space paths, shared cache, improved verification. Successfully produced Orangepi5ultra_1.0.0_debian_bullseye_server_linux5.10.160.img (2.9GB)

This commit is contained in:
OrangePi CM5 Builder 2026-03-13 16:34:32 +08:00
commit b8b547fb11
2 changed files with 570 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@ -0,0 +1,29 @@
# 大型编译产物(镜像、工具链等)
*.img
*.img.sha256
*.zip
*.tar.gz
*.tar.xz
# 编译缓存与临时文件
orangepi-build/output/
orangepi-build/dl/
orangepi-build/toolchains/
orangepi-build/external/cache/
orangepi-build/.tmp/
build_*.log
# 共享缓存目录
OrangePi_cache/
# 编译日志
compile-output.log
*.log
# 系统文件
.DS_Store
Thumbs.db
# SDK 源码包(外部下载)
SDKs/
sources/

541
compile-orangepi5ultra.sh Executable file
View File

@ -0,0 +1,541 @@
#!/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 "$@"