Rockchip RK3566向けOrange Piビルドシステムの内部構造とスクリプト解析

ビルドエントリポイントと環境初期化

ビルドシステムの実行は最上位のシェルスクリプトから開始され、ワークスペースの検証、権限の昇格、設定ファイルの読み込み、および依存するモジュールの展開を担当します。スクリプトはまず自身の配置ディレクトリを基準に相対パスを解決し、空白文字を含むパスが検出された場合は安全のため即時終了します。

#!/usr/bin/env bash
WORKSPACE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "${WORKSPACE_DIR}" || exit 1

# ルート権限またはDockerグループ所属の確認
if [[ "$(id -u)" -ne 0 ]]; then
    echo "管理者権限が必要です。sudo経由で再実行します。"
    sudo "${WORKSPACE_DIR}/$(basename "$0")" "$@"
    exit $?
fi

# 共通モジュールの読み込み
if [[ -f "${WORKSPACE_DIR}/scripts/base_utils.sh" ]]; then
    source "${WORKSPACE_DIR}/scripts/base_utils.sh"
else
    echo "ビルドディレクトリ構造が破損しています。リポジトリを再クローンしてください。"
    exit 255
fi

# ユーザー設定ディレクトリの初期化
mkdir -p "${WORKSPACE_DIR}/userpatches"
if ! ls "${WORKSPACE_DIR}/userpatches/"*.conf 1>/dev/null 2>&1; then
    cp "${WORKSPACE_DIR}/external/config/templates/"*.conf "${WORKSPACE_DIR}/userpatches/"
fi

# 設定ファイルの適用
CONFIG_FILE="${WORKSPACE_DIR}/userpatches/config-default.conf"
if [[ ! -f "${CONFIG_FILE}" ]]; then
    echo "設定ファイルが見つかりません: ${CONFIG_FILE}"
    exit 254
fi
source "${CONFIG_FILE}"

# 拡張モジュールとメインプロセッサの委譲
source "${WORKSPACE_DIR}/scripts/extension_mgr.sh"
if [[ "${ENABLE_FULL_BUILD}" == "yes" ]]; then
    source "${WORKSPACE_DIR}/scripts/batch_processor.sh"
else
    source "${WORKSPACE_DIR}/scripts/core_builder.sh"
fi

この段階では、オフラインビルドの判定、基本ツールのインストールチェック、およびデバッグ用関数呼び出しトレーシングのオプション設定が行われます。設定ファイルが欠落している場合はテンプレートから自動複製され、シンボリックリンクによってデフォルト構成として認識されます。最終的に、処理は実際のビルド制御を担うメインスクリプトへ委譲されます。

メイン制御ロジックと構成変数の解決

コアビルドスクリプトは、対話型メニューの表示、変数の検証、外部モジュールの統合、およびビルドパイプラインの実行を一元管理します。端末のサイズを取得し、`whiptail`を用いて構築対象(ブートローダ、カーネル、ルートFS、完全イメージ)、ターゲットボード、カーネルブランチ、ディストリビューションのリリースを選択可能にします。

# ターゲットの決定
if [[ -z "${BUILD_TARGET}" ]]; then
    read -r BUILD_TARGET <<< $(whiptail --title "ビルド対象" \
        --menu "構築するパッケージを選択" 20 60 10 \
        "bootloader" "U-Bootのみ" \
        "kernel" "Linuxカーネルのみ" \
        "rootfs" "ルートファイルシステム" \
        "image" "起動可能なディスクイメージ" 3>&1 1>&2 2>&3)
fi

# ボード構成の適用
if [[ -z "${TARGET_BOARD}" ]]; then
    read -r TARGET_BOARD <<< $(whiptail --title "ターゲット" \
        --menu "開発ボードの選択" 20 70 10 \
        "orangepi3b" "RK3566 Quad-core 2-8GB" \
        "orangepi5" "RK3588S Octa-core" 3>&1 1>&2 2>&3)
fi

# ボード定義ファイルの読み込み
BOARD_DEF="external/config/boards/${TARGET_BOARD}.conf"
source "${BOARD_DEF}"
LINUX_FAMILY="${BOARDFAMILY}"

# カーネルブランチの決定
if [[ -z "${KERNEL_BRANCH}" ]]; then
    if [[ "${KERNEL_TARGET}" == *"current"* ]]; then
        KERNEL_BRANCH="current"
    else
        KERNEL_BRANCH="legacy"
    fi
fi

# ディストリビューションの決定
if [[ "${BUILD_TARGET}" =~ rootfs|image && -z "${OS_RELEASE}" ]]; then
    # debootstrap用候補の生成と選択ロジック
    OS_RELEASE="bullseye" # 例: 対話メニューで選択された値
fi

変数が確定すると、スクリプトは`configuration.sh`をsourceし、アーキテクチャ固有(`arm64.conf`)およびSoCファミリー固有(`rockchip-rk356x.conf`)の設定を階層的にマージします。これにより、コンパイラプレフィックス、クロック範囲、デフォルトのブートスクリプト、パーティションテーブル形式などが解決されます。

共通ユーティリティとリソース取得

ビルドシステム全体で共有されるヘルパー関数群は、ロギング、ホスト環境の準備、Gitリポジトリの同期、キャッシュ管理を担当します。警告メッセージはANSIエスケープシーケンスを用いてレベル別に色分けされ、ビルドログへも同時に記録されます。

リポジトリ取得関数では、ローカルクローンの存在確認、リモートURLの整合性検証、および参照(ブランチ/タグ/コミットハッシュ)のチェックアウトを行います。オフラインモード時はネットワークアクセスをバイパスし、既存のツリーをそのまま使用します。キャッシュディレクトリの管理では、古いルートFSアーカイブを保持数制限付きで自動削除し、ストレージの肥大化を抑制します。

sync_git_repository() {
    local REPO_URL="$1"
    local LOCAL_PATH="$2"
    local REF_TYPE="$3"  # branch:xxx / tag:xxx / commit:xxx
    local SUBDIR_FLAG="$4"

    mkdir -p "${LOCAL_PATH}"
    cd "${LOCAL_PATH}" || exit 1

    if [[ ! -d ".git" ]]; then
        git init -q
        git remote add origin "${REPO_URL}"
    fi

    local CURRENT_HASH
    CURRENT_HASH=$(git rev-parse HEAD 2>/dev/null)
    
    # リモートのハッシュ取得と比較(簡略化)
    local REMOTE_HASH
    REMOTE_HASH=$(git ls-remote origin "${REF_TYPE##*:}" | cut -f1)
    
    if [[ "${CURRENT_HASH}" != "${REMOTE_HASH}" ]]; then
        git fetch origin "${REF_TYPE##*:}" --depth 200
        git checkout -f FETCH_HEAD
        git clean -qdf
    fi
}

これらの関数は、ビルドの前段階でソースコードをミラーサーバーまたは公式リポジトリから取得し、ローカルの`kernel/`および`u-boot/`ディレクトリに整える役割を果たします。Rockchip系チップセット向けには、別途`rk-rootfs-build`からバイナリパッケージやファームウェア定義も取得されます。

クロスコンパイルとdebパッケージ生成

ブートローダおよびカーネルのビルドは、ホストアーキテクチャに応じてクロスコンパイラを自動的に選択し、対応する`make`ターゲットを実行します。コンパイル結果は直接システムにインストールされるのではなく、管理可能な`.deb`形式にパッケージングされます。

U-Bootのビルドでは、`defconfig`の適用、ATF(ARM Trusted Firmware)の組み合わせ、および`idbloader.img`や`u-boot.itb`の生成が行われます。Rockchipプラットフォームでは、DDR初期化バイナリとブートローダを`mkimage`を用いて結合し、SPI/eMMCへの書き込み形式に整形します。カーネルビルドでは、`.config`の適用、モジュール・DTB・カーネルイメージの構築、および`make bindeb-pkg`によるdeb生成が実行されます。

build_target_component() {
    local COMP_TYPE="$1"  # "uboot" or "kernel"
    local TOOLCHAIN_PATH="$2"
    local OUT_DIR="$3"
    local PKG_NAME="$4"

    export PATH="${TOOLCHAIN_PATH}:${PATH}"
    
    if [[ "${COMP_TYPE}" == "uboot" ]]; then
        make "${BOARD_DEFCONFIG}" CROSS_COMPILE="${CROSS_PREFIX}"
        make "${UBOOT_TARGETS}" CROSS_COMPILE="${CROSS_PREFIX}" -j$(nproc)
        
        # 成果物のパッケージング準備
        local STAGING_DIR
        STAGING_DIR=$(mktemp -d)
        mkdir -p "${STAGING_DIR}/${PKG_NAME}/usr/lib"
        cp -r build_output/* "${STAGING_DIR}/${PKG_NAME}/usr/lib/"
        
        cat > "${STAGING_DIR}/${PKG_NAME}/DEBIAN/control" <<EOF
Package: ${PKG_NAME}
Version: ${REVISION}
Architecture: arm64
Description: Custom U-Boot for Orange Pi
EOF
        fakeroot dpkg-deb -b "${STAGING_DIR}/${PKG_NAME}" "${OUT_DIR}/${PKG_NAME}.deb"
        
    elif [[ "${COMP_TYPE}" == "kernel" ]]; then
        make ARCH=arm64 CROSS_COMPILE="${CROSS_PREFIX}" olddefconfig
        make ARCH=arm64 CROSS_COMPILE="${CROSS_PREFIX}" -j$(nproc) Image dtbs modules
        make ARCH=arm64 CROSS_COMPILE="${CROSS_PREFIX}" KDEB_PKGVERSION=${REVISION} bindeb-pkg
    fi
}

生成されたdebファイルは`output/debs/`配下に分類され、後のchroot環境構築段階で`apt`または`dpkg`によってターゲットシステムへ注入されます。

BSPサポートパッケージの組み立て

ボードサポートパッケージ(BSP)は、特定のハードウェアに固有のブートスクリプト、udevルール、systemdサービス、およびデフォルト設定をまとめたdebです。`create_board_package`関数は、一時ディレクトリをステージングエリアとして使用し、必要なファイルをツリー構造に配置した後にパッケージ化します。

パッケージ内には`postinst`および`preinst`スクリプトが含まれ、インストール時のfstab修正、サービス有効化、およびカーネルパラメータの調整を自動化します。ブートローダスクリプト(`boot.cmd` → `boot.scr`の変換)やデバイス固有の環境変数定義(`orangepiEnv.txt`)もこの段階で生成され、ターゲットの`/boot`または`/usr/share/orangepi`へ配置されます。

assemble_bsp_package() {
    local BSP_DIR
    BSP_DIR=$(mktemp -d)
    local DEST_PKG="orangepi-bsp-${TARGET_BOARD}_${REVISION}_arm64"
    
    mkdir -p "${BSP_DIR}/${DEST_PKG}/DEBIAN"
    mkdir -p "${BSP_DIR}/${DEST_PKG}/etc/systemd/system"
    mkdir -p "${BSP_DIR}/${DEST_PKG}/boot"
    
    # ブートスクリプトのコピーと変換
    cp "external/config/bootscripts/${BOOT_SCRIPT_SRC}" "${BSP_DIR}/${DEST_PKG}/boot/"
    
    # controlファイルの作成
    cat > "${BSP_DIR}/${DEST_PKG}/DEBIAN/control" <<EOF
Package: orangepi-bsp-${TARGET_BOARD}
Version: ${REVISION}
Section: misc
Architecture: arm64
Depends: bash, systemd, util-linux
Description: Board support files for ${TARGET_BOARD}
EOF
    
    # パッケージ化
    fakeroot dpkg-deb -b "${BSP_DIR}/${DEST_PKG}" "output/debs/${DEST_PKG}.deb"
    rm -rf "${BSP_DIR}"
}

このパッケージは、最終的なディスクイメージに組み込まれる前に、chroot環境内で展開され、ハードウェア固有の初期化処理を可能にします。

ルートファイルシステム構築とディスクイメージ化

イメージ構築パイプラインは、`debootstrap`をベースとした段階的なプロセスで進行します。まず`create_rootfs_cache`が最小限のDebian/Ubuntuベースシステムを生成し、キャッシュとして保存します。初回構築時はステージ1(バイナリ展開)とステージ2(chroot内でのパッケージ設定・locale生成・カーネル導入)が実行されます。キャッシュが存在する場合は、LZ4圧縮されたtarアーカイブをメモリ上(tmpfs)に展開して高速化します。

ファイルシステムが準備されると、`prepare_partitions`関数が呼び出されます。空白のRAWイメージファイルを作成し、`sfdisk`を用いてGPTパーティションテーブルを定義します。通常、先頭にFAT32形式のブートパーティション、その後にext4形式のルートパーティションが配置されます。各パーティションは`losetup`でループデバイスにアタッチされ、`mkfs`でフォーマットされた後にマウントポイントへ結合されます。

create_raw_image() {
    local IMG_SIZE_MB=$(( $(du -sm "${CHROOT_PATH}" | cut -f1) * 1.3 + 1024 ))
    local IMG_FILE="output/images/target_system.raw"
    
    # イメージファイルの作成
    dd if=/dev/zero of="${IMG_FILE}" bs=1M count="${IMG_SIZE_MB}" status=progress
    
    # パーティションテーブルの定義
    sfdisk "${IMG_FILE}" <<EOF
label: gpt
1 : name=bootfs, start=30M, size=1024M, type=C12A7328-F81F-11D2-BA4B-00A0C93EC93B
2 : name=rootfs, start=1054M, type=0FC63DAF-8483-4772-8E79-3D69D8477DE4
EOF
    
    # ループデバイスへのマッピングとフォーマット
    local LOOP_DEV
    LOOP_DEV=$(losetup -f --show "${IMG_FILE}")
    
    mkfs.vfat -n "OPI_BOOT" "${LOOP_DEV}p1"
    mkfs.ext4 -L "OPI_ROOT" -m 1 "${LOOP_DEV}p2"
    
    # マウントとデータ同期
    mount "${LOOP_DEV}p2" "${MOUNT_ROOT}"
    mkdir -p "${MOUNT_ROOT}/boot"
    mount "${LOOP_DEV}p1" "${MOUNT_ROOT}/boot"
    
    rsync -aAX "${CHROOT_PATH}/" "${MOUNT_ROOT}/" --exclude=/dev/* --exclude=/proc/* --exclude=/sys/*
    
    # U-Bootの書き込み(オフセット0x40および0x4000)
    dd if="build/idbloader.img" of="${LOOP_DEV}" seek=64 conv=notrunc
    dd if="build/u-boot.itb" of="${LOOP_DEV}" seek=16384 conv=notrunc
    
    umount "${MOUNT_ROOT}/boot" "${MOUNT_ROOT}"
    losetup -d "${LOOP_DEV}"
}

データ同期後、`initramfs`が更新され、ブートローダのバイナリイメージが指定されたセクタ(通常は32KBオフセット以降)に直接書き込まれます。最終的にRAWイメージは`dd`コマンドでSDカード/eMMCへ転送可能となり、`pixz`または`pigz`を用いて圧縮アーカイブ化されるほか、SHA256チェックサムやGPG署名が付与されて配信準備が完了します。

設定ファイルの継承と優先度

ビルドシステムの変数解決は、複数の設定レイヤーを順にsourceすることで実現されます。優先度は以下の通りです:

  • userpatches/lib.config:ユーザー固有のオーバーライド(最終評価)
  • userpatches/config-default.conf:ビルド挙動の基本制御(キャッシュ、クリーンレベル、ミラー設定)
  • external/config/boards/*.conf:ボード固有のパラメータ(BOARDFAMILY、BOOTCONFIG、KERNEL_TARGET)
  • external/config/sources/families/*.conf:SoCファミリー共通定義(クロック、GPU/VPUドライバ、ブートシナリオ)
  • external/config/sources/arm64.conf:アーキテクチャ基本設定(クロスコンパイラパス、GitリポジトリURL、QEMUバイナリ)

RK3566プラットフォームでは、rockchip-rk356x.confがBOOT_SCENARIO="spl-blobs"を定義し、DDR初期化バイナリ(rk3566_ddr_1056MHz_v1.10.bin)とBL31(rk3568_bl31_v1.28.elf)の組み合わせを指定します。これにより、メインラインATFの代わりにRockchip公式バイナリを用いたブートフローが確立され、SPIフラッシュおよびeMMC双方に対応するパーティションレイアウトが自動的に構成されます。

タグ: rockchip-rk3566 orangepi-build embedded-linux bash-automation cross-compilation

9月25日 09:52 投稿