alarm-tegra-x1 / src/packages.rs

raw file 4985 bytes
use anyhow::{Context, Result};
use std::path::Path;
use std::process::Command;

use crate::config::BuildConfig;

/// Download and build U-Boot (for reference, though we use hekate)
pub fn download_uboot(build_dir: &Path) -> Result<()> {
    let uboot_dir = build_dir.join("uboot/src");
    
    if uboot_dir.exists() {
        println!("U-Boot source already exists, updating...");
        let status = Command::new("git")
            .args(&["-C", uboot_dir.to_str().unwrap(), "pull"])
            .status()?;
        if !status.success() {
            anyhow::bail!("Failed to update U-Boot source");
        }
        return Ok(());
    }

    println!("Downloading U-Boot for Tegra...");
    // Clone to absolute path without changing working directory
    let status = Command::new("git")
        .args(&[
            "clone",
            "--depth=1",
            "--branch", "v2024.01",
            "https://source.denx.de/u-boot/u-boot.git",
            uboot_dir.to_str().unwrap(),
        ])
        .status()
        .context("Failed to run git")?;

    if !status.success() {
        anyhow::bail!("Failed to clone U-Boot");
    }

    Ok(())
}

/// Build U-Boot for Tegra X1 (optional, hekate handles boot)
pub fn build_uboot(
    config: &BuildConfig,
    build_dir: &Path,
) -> Result<()> {
    let uboot_src = build_dir.join("uboot/src");
    let uboot_build = build_dir.join("uboot/build");
    let uboot_out = build_dir.join("uboot/out");

    if !uboot_src.exists() {
        anyhow::bail!("U-Boot source not found. Run 'init' first.");
    }

    std::fs::create_dir_all(&uboot_build)?;
    std::fs::create_dir_all(&uboot_out)?;

    let cross_compile = format!("{}-", config.cross_compiler);

    println!("Configuring U-Boot...");
    run_cmd_in_dir(
        "make",
        &[
            "tegra210-nintendo-switch-defconfig",  // Switch-specific config
        ],
        &uboot_src,
    )?;

    println!("Building U-Boot...");
    let jobs = num_cpus::get();
    run_cmd_in_dir(
        "make",
        &[
            &format!("ARCH={}", config.target_arch),
            &format!("CROSS_COMPILE={}", cross_compile),
            &format!("-j{}", jobs),
        ],
        &uboot_src,
    )?;

    // Copy output
    std::fs::copy(
        uboot_src.join("u-boot-dtb.bin"),
        build_dir.join("uboot/u-boot-dtb.bin"),
    ).context("Failed to copy U-Boot binary")?;

    println!("U-Boot build complete!");
    Ok(())
}

/// Package ALARM packages
#[allow(dead_code)]
pub fn build_packages(
    _config: &BuildConfig,
    build_dir: &Path,
) -> Result<()> {
    let pkg_dir = build_dir.join("packages");
    std::fs::create_dir_all(&pkg_dir)?;

    // Create package list
    let packages = vec![
        "base",
        "linux-arm",
        "firmware",
        "uboot-switch",
        "alarm-base",
        "bash-completion",
        "ca-certificates",
        "cronie",
        "dhcpcd",
        "dnsmasq",
        "dosfstools",
        "e2fsprogs",
        "ethtool",
        "gcc",
        "git",
        "haveged",
        "htop",
        "iproute2",
        "iptables-nft",
        "make",
        "mtools",
        "nano",
        "nfs-utils",
        "nss",
        "openssh",
        "pacman-contrib",
        "parted",
        "pkg-config",
        "rsync",
        "sudo",
        "systemd",
        "systemd-sysvcompat",
        "texinfo",
        "tmux",
        "udev",
        "unzip",
        "vim",
        "wget",
        "wpa_supplicant",
    ];

    // Desktop packages (if enabled)
    let desktop_packages = vec![
        "xorg-server",
        "xorg-xinit",
        "xorg-xrandr",
        "xorg-xinput",
        "mesa",
        "mesa-utils",
        "xfce4",
        "xfce4-terminal",
        "lightdm",
        "lightdm-gtk-greeter",
        "noto-fonts",
        "noto-fonts-cjk",
        "firefox",
        "pulseaudio",
        "pavucontrol",
        "alsa-utils",
    ];

    // Create package list file
    let mut pkg_list = packages.join("\n");
    pkg_list.push_str("\n\n# Desktop packages\n");
    pkg_list.push_str(&desktop_packages.join("\n"));
    
    std::fs::write(pkg_dir.join("package-list.txt"), pkg_list)?;

    println!("Package list created: {}", pkg_dir.join("package-list.txt").display());
    Ok(())
}

/// Run a command
fn run_cmd(program: &str, args: &[&str], work_dir: &Path) -> Result<()> {
    let status = Command::new(program)
        .args(args)
        .current_dir(work_dir)
        .status()
        .context(format!("Failed to execute {}", program))?;

    if !status.success() {
        anyhow::bail!("{} failed with status: {}", program, status);
    }

    Ok(())
}

/// Run a command in a specific directory
fn run_cmd_in_dir(program: &str, args: &[&str], dir: &Path) -> Result<()> {
    run_cmd(program, args, dir)
}

/// Get number of CPUs
mod num_cpus {
    pub fn get() -> usize {
        std::thread::available_parallelism()
            .map(|n| n.get())
            .unwrap_or(4)
    }
}