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