From b8b547fb11aa9569b9859c86b9629802058bd602 Mon Sep 17 00:00:00 2001 From: OrangePi CM5 Builder Date: Fri, 13 Mar 2026 16:34:32 +0800 Subject: [PATCH] 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) --- .gitignore | 29 ++ compile-orangepi5ultra.sh | 541 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 570 insertions(+) create mode 100644 .gitignore create mode 100755 compile-orangepi5ultra.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0ab8209 --- /dev/null +++ b/.gitignore @@ -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/ diff --git a/compile-orangepi5ultra.sh b/compile-orangepi5ultra.sh new file mode 100755 index 0000000..2d4d177 --- /dev/null +++ b/compile-orangepi5ultra.sh @@ -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 "$@"