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) } }