Files
yggdrasil/xtask/src/build/userspace.rs
T

244 lines
6.6 KiB
Rust

use std::{
fs::{self, File},
io::BufWriter,
path::{Path, PathBuf},
};
use walkdir::WalkDir;
use crate::{
build::{c, cargo::CargoBuilder},
check::AllOk,
env::BuildEnv,
error::Error,
util,
};
use super::InitrdGenerated;
const PROGRAMS: &[(&str, &str)] = &[
// init
("init", "init"),
("rc", "sbin/rc"),
("service", "sbin/service"),
("logd", "sbin/logd"),
("kmod", "sbin/kmod"),
// shell
("shell", "bin/sh"),
// sysutils
("mount", "sbin/mount"),
("login", "sbin/login"),
("strace", "bin/strace"),
("ls", "bin/ls"),
("mv", "bin/mv"),
("ln", "bin/ln"),
("mkdir", "bin/mkdir"),
("touch", "bin/touch"),
("rm", "bin/rm"),
("cat", "bin/cat"),
("hexd", "bin/hexd"),
("dd", "bin/dd"),
("random", "bin/random"),
("view", "bin/view"),
("grep", "bin/grep"),
("chmod", "bin/chmod"),
("sha256sum", "bin/sha256sum"),
("sysmon", "bin/sysmon"),
("date", "bin/date"),
("sync", "bin/sync"),
("sleep", "bin/sleep"),
("lspci", "bin/lspci"),
("ps", "bin/ps"),
("top", "bin/top"),
("tst", "bin/tst"),
("md2txt", "bin/md2txt"),
// netutils
("netconf", "sbin/netconf"),
("dhcp-client", "sbin/dhcp-client"),
("nc", "bin/nc"),
("http", "bin/http"),
("dnsq", "bin/dnsq"),
("ping", "bin/ping"),
("ncap", "sbin/ncap"),
// colors
("colors", "bin/colors"),
("colors-bar", "bin/colors-bar"),
("term", "bin/term"),
// red
("red", "bin/red"),
// rdb
("rdb", "bin/rdb"),
// rsh
("rsh", "bin/rsh"),
("rshd", "sbin/rshd"),
// crypt
("crypt", "bin/crypt"),
("dyn-loader", "libexec/dyn-loader"),
// TODO: proper process for C program builds
];
fn build_userspace(
env: &BuildEnv,
extra: &mut Vec<(PathBuf, PathBuf)>,
_: AllOk,
) -> Result<(), Error> {
log::info!("Building userspace");
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace"))?;
CargoBuilder::Userspace(env).build(env.workspace_root.join("userspace/dynload-program"))?;
if env.config.components(env).libc || env.config.components(env).libcxx {
c::build_c(env, extra)?;
}
Ok(())
}
fn build_rootfs<S: AsRef<Path>, D: AsRef<Path>>(
env: &BuildEnv,
install_extra: Vec<(PathBuf, PathBuf)>,
build_dir: S,
rootfs_dir: D,
_: AllOk,
) -> Result<(), Error> {
let user_dir = env.workspace_root.join("userspace");
let build_dir = build_dir.as_ref();
let rootfs_dir = rootfs_dir.as_ref();
log::info!("Building rootfs: {}", rootfs_dir.display());
for dir in ["sbin", "bin", "dev", "sys", "tmp", "mnt"] {
fs::create_dir_all(rootfs_dir.join(dir))?;
File::create(rootfs_dir.join(dir).join(".do_not_remove"))?;
}
// TODO grab this from cargo somehow?
for &(src, dst) in PROGRAMS {
let src_path = build_dir.join(src);
let dst_path = rootfs_dir.join(dst);
if let Some(dst_parent) = dst_path.parent() {
fs::create_dir_all(dst_parent)?;
}
log::trace!(
"Install binary: {} -> {}",
src_path.display(),
dst_path.display()
);
util::copy_file(src_path, dst_path)?;
}
if let Ok(read_dir) = fs::read_dir("userspace/scripts") {
for entry in read_dir {
let Ok(entry) = entry else {
continue;
};
let path = entry.path();
let Some(filename) = path.file_name() else {
continue;
};
fs::copy(&path, rootfs_dir.join("bin").join(filename))?;
}
}
// TODO this is a temporary hack
fs::create_dir_all(rootfs_dir.join("lib"))?;
// TODO other architectures
util::copy_file(
env.workspace_root.join(format!(
"userspace/dynload-program/target/{}-unknown-yggdrasil/{}/dynload-program",
env.arch.name(),
env.profile.name()
)),
rootfs_dir.join("dynload-program"),
)?;
let libstd_so = env.workspace_root.join(format!(
"toolchain/build/host/stage1-std/{}-unknown-yggdrasil/release/libstd.so",
env.arch.name()
));
util::copy_file(libstd_so, rootfs_dir.join("lib/libstd.so"))?;
log::info!("Installing extras");
for (src, dst) in install_extra {
util::copy_file(src, rootfs_dir.join(dst))?;
}
util::copy_file(&env.kernel_symbol_file, rootfs_dir.join("kernel.sym"))?;
// Copy /etc
util::copy_dir_recursive(user_dir.join("etc"), rootfs_dir.join("etc"))?;
// Copy architecture-specific directories
let arch_dir = user_dir.join("arch").join(env.arch.name());
let arch_rc_d = arch_dir.join("rc.d");
let arch_inittab = arch_dir.join("inittab");
if arch_rc_d.exists() {
util::copy_dir_recursive(arch_rc_d, rootfs_dir.join("etc/rc.d"))?;
}
if arch_inittab.exists() {
util::copy_file(arch_inittab, rootfs_dir.join("etc/inittab"))?;
}
Ok(())
}
fn pack_initrd<P: AsRef<Path>>(env: &BuildEnv, rootfs_dir: P) -> Result<InitrdGenerated, Error> {
let initrd_tar = env.userspace_output_dir.join("initrd.tar");
log::info!("Building initrd");
let tar = File::create(&initrd_tar)?;
let tar = BufWriter::new(tar);
let mut tar = tar::Builder::new(tar);
for entry in WalkDir::new(&rootfs_dir)
.follow_links(false)
.into_iter()
.filter(|e| e.as_ref().map(|e| !e.file_type().is_dir()).unwrap_or(false))
{
let entry = entry?;
let src_path = entry.path();
let rel_path = src_path
.strip_prefix(&rootfs_dir)
.map_err(|_| Error::InvalidPath(src_path.into()))?;
if entry.file_type().is_file() {
let mut file = File::open(entry.path())?;
tar.append_file(rel_path, &mut file)?;
} else if entry.file_type().is_symlink() {
let target = fs::read_link(entry.path())?;
let mut header = tar::Header::new_ustar();
header.set_entry_type(tar::EntryType::symlink());
header.set_mode(0o755);
tar.append_link(&mut header, rel_path, target)?;
} else {
todo!()
}
}
Ok(InitrdGenerated(initrd_tar))
}
pub fn build_initrd(
env: &BuildEnv,
mut install_extra: Vec<(PathBuf, PathBuf)>,
check: AllOk,
) -> Result<InitrdGenerated, Error> {
let rootfs_dir = env.userspace_output_dir.join("rootfs");
build_userspace(env, &mut install_extra, check)?;
build_rootfs(
env,
install_extra,
&env.userspace_output_dir,
&rootfs_dir,
check,
)?;
pack_initrd(env, rootfs_dir)
}