redoxer 0.2.62

Method for quickly running programs inside of Redox from a KVM capable OS.
Documentation
use pkg::{callback::IndicatifCallback, Library, PackageName};
use std::{
    cell::RefCell,
    env, fs, io,
    os::unix,
    path::{Path, PathBuf},
    process,
    rc::Rc,
};

use crate::target;

pub const DEFAULT_PKG_SOURCE: &str = "https://static.redox-os.org/pkg";

enum PkgCommand {
    Install,
    Update,
    Remove,
}

fn pkg_inner(
    sysroot: PathBuf,
    source: String,
    cmd: PkgCommand,
    pkgs: Vec<PackageName>,
) -> io::Result<()> {
    let callback = IndicatifCallback::new();

    if !sysroot.is_dir() {
        println!("redoxer: building pkg sysroot");
        fs::create_dir_all(sysroot.join("etc/pkg.d"))?;
        fs::create_dir(sysroot.join("usr"))?;
        fs::write(sysroot.join("etc/pkg.d/10_redoxer"), source)?;
        for folder in &["bin", "include", "lib", "share"] {
            fs::create_dir(sysroot.join("usr").join(folder))?;
            unix::fs::symlink(format!("usr/{folder}"), sysroot.join(folder))?;
        }
    }

    let mut library = Library::new(sysroot, target(), Rc::new(RefCell::new(callback))).unwrap();

    match cmd {
        PkgCommand::Install => library.install(pkgs),
        PkgCommand::Update => library.update(pkgs),
        PkgCommand::Remove => library.uninstall(pkgs),
    }
    .unwrap();

    library
        .apply()
        .map_err(|e: pkg::Error| {
            eprintln!("Unable to complete pkg action: {e}");
            process::exit(1);
        })
        .unwrap();

    Ok(())
}

pub fn main(args: &[String]) {
    let args: Vec<String> = args.iter().cloned().skip(2).collect();

    let mut i = 0;
    let mut pkgs = Vec::new();
    let mut cmd = PkgCommand::Install;
    let mut cmd_set = false;
    while i < args.len() {
        match (args[i].as_str(), cmd_set) {
            ("install", false) => {
                cmd = PkgCommand::Install;
                cmd_set = true;
            }
            ("update", false) => {
                cmd = PkgCommand::Update;
                cmd_set = true;
            }
            ("remove", false) => {
                cmd = PkgCommand::Remove;
                cmd_set = true;
            }
            ("--help", _) => {
                pkg_usage();
            }
            (arg, _) => {
                pkgs.push(arg);
            }
        }
        i += 1;
    }

    if pkgs.len() == 0 {
        pkg_usage();
    }

    let sysroot = get_sysroot()
        .or_else(|| get_cargo_sysroot_default_path())
        .expect("Please define REDOXER_SYSROOT as destination to install packages");

    let source = env::var("REDOXER_PKG_SOURCE").unwrap_or(DEFAULT_PKG_SOURCE.to_string());

    let pkgs = pkgs
        .iter()
        .map(|p| PackageName::new(p.to_string()).expect(&format!("Invalid pkg name: {p}")))
        .collect();

    match pkg_inner(sysroot, source, cmd, pkgs) {
        Ok(_) => {
            process::exit(0);
        }
        Err(err) => {
            eprintln!("redoxer toolchain: {}", err);
            process::exit(1);
        }
    }
}

pub fn get_sysroot() -> Option<PathBuf> {
    env::var("REDOXER_SYSROOT")
        .ok()
        .map(|p| Path::new(&p).to_owned())
        .or_else(|| {
            let path = get_cargo_sysroot_default_path()?;
            if path.join("lib").is_dir() {
                return Some(path);
            }
            return None;
        })
}

fn get_cargo_sysroot_default_path() -> Option<PathBuf> {
    if Path::new("Cargo.toml").is_file() {
        Some(Path::new(&format!("target/{}/sysroot", target())).to_path_buf())
    } else {
        None
    }
}

fn pkg_usage() {
    eprintln!("redoxer pkg [install|remove|update] pkg-1 pkg-2 ...");
    eprintln!(" arguments:");
    eprintln!("   [install|remove|update]  whether to install, update, or remove pkg (optional, default is install)");
    eprintln!("");
    eprintln!(" environment variables:");
    eprintln!("   REDOXER_SYSROOT          where to install sysroot (required when no Cargo.toml)");
    eprintln!("   REDOXER_PKG_SOURCE       whether to install custom source instead of {DEFAULT_PKG_SOURCE}");
    eprintln!("");
    process::exit(0);
}