yggdrasil/xtask/src/main.rs

193 lines
5.3 KiB
Rust

#![deny(warnings)]
#![feature(exit_status_error)]
use std::{fs, io::stdout, path::PathBuf, process::ExitCode};
use build::CheckAction;
use clap::{Parser, Subcommand};
use env::{Arch, Board, Profile, XTaskConfig};
use error::Error;
#[macro_use]
pub mod util;
pub mod build;
pub mod check;
pub mod env;
pub mod error;
pub mod qemu;
#[derive(Parser)]
struct Args {
#[clap(long, env, help = "Use --release profile for built components")]
release: bool,
#[clap(
short,
long,
env,
default_value_t,
value_enum,
help = "Target architecture"
)]
arch: Arch,
#[clap(short, long, env, default_value_t, value_enum, help = "Target board")]
board: Board,
#[clap(
short,
long,
help = "Do a clean userspace rebuild (e.g. toolchain has changed)"
)]
clean_userspace: bool,
#[clap(short, long, help = "Be verbose where possible")]
verbose: bool,
#[clap(short = 'C', long, help = "Configuration path")]
config_path: Option<PathBuf>,
#[clap(subcommand)]
action: Option<SubArgs>,
}
#[derive(Default, Subcommand)]
enum SubArgs {
#[default]
#[clap(about = "Build the OS")]
Build,
#[clap(about = "Run `cargo check` on components")]
Check,
#[clap(about = "Run `cargo clippy` on components")]
Clippy,
#[clap(about = "Run `cargo test` on components")]
Test,
#[clap(about = "Clean all build artifacts")]
Clean {
#[clap(short, long, help = "Clean toolchain as well")]
toolchain: bool,
#[clap(short, long, help = "Clean cloned repositories")]
repos: bool,
},
// #[clap(about = "Print `git status` for the components", alias = "gst")]
// GitStatus,
#[clap(about = "Run the OS image in QEMU")]
Qemu {
#[clap(
short,
long,
env,
help = "Override the default QEMU binary for the target"
)]
qemu: Option<PathBuf>,
#[clap(short, long, help = "Disk image to use for persistent data")]
disk: Option<PathBuf>,
#[clap(help = "Extra arguments for QEMU")]
extra_args: Vec<String>,
},
#[clap(about = "Build the Yggdrasil OS Rust toolchain")]
Toolchain { branch: Option<String> },
#[clap(about = "Print the host triple")]
HostTriple,
#[clap(about = "Print information about the build environment and configuration")]
Env,
}
fn run(args: Args) -> Result<(), Error> {
let action = args.action.unwrap_or_default();
let workspace_root = std::env::current_dir()?;
let profile = if args.release {
Profile::Release
} else {
Profile::Debug
};
let default_config = workspace_root.join("xtask.toml");
let config_path = args.config_path.as_ref().or_else(|| {
if default_config.exists() {
Some(&default_config)
} else {
None
}
});
let config = if let Some(path) = config_path {
toml::from_str(&fs::read_to_string(path)?)
.map_err(|e| Error::TomlParseError(path.to_owned(), e))?
} else {
XTaskConfig::default()
};
let env = env::BuildEnv::new(
config,
args.verbose,
profile,
args.arch,
args.board,
workspace_root,
);
if args.clean_userspace && !matches!(&action, SubArgs::Clean { .. }) {
build::clean_userspace(&env)?;
}
match action {
SubArgs::Build => build::build_all(&env).map(|_| ()),
SubArgs::Check => build::check_all(env, CheckAction::Check),
SubArgs::Clippy => build::check_all(env, CheckAction::Clippy),
SubArgs::Test => build::test_all(env),
SubArgs::Clean { toolchain, repos } => build::clean_all(&env, toolchain, repos),
// SubArgs::GitStatus => util::git_status_all(&env),
SubArgs::Qemu {
qemu,
disk,
extra_args,
} => qemu::run(env, qemu, disk, extra_args),
SubArgs::Toolchain { branch } => {
let branch = branch.as_ref().unwrap_or(&env.config.toolchain.branch);
build::build_toolchain(&env, branch)
}
SubArgs::HostTriple => {
println!("{}", env.host_triple);
Ok(())
}
SubArgs::Env => {
println!("--- Build information ---");
println!();
match config_path {
Some(path) => println!(" * Using {}:", path.display()),
None => println!(" * Using default xtask config:"),
}
println!();
env.dump(&mut stdout());
qemu::dump_config(&env, &mut stdout());
println!("--- End build information ---");
Ok(())
}
}
}
fn main() -> ExitCode {
env_logger::builder()
.filter_level(log::LevelFilter::Info)
.parse_default_env()
.init();
let args = Args::parse();
match run(args) {
Ok(()) => ExitCode::SUCCESS,
Err(Error::MissingTools(list)) => {
eprintln!("Missing some required tools:");
for (tool, guide) in list {
eprintln!(" * {}: {}", tool, guide);
}
ExitCode::FAILURE
}
Err(err) => {
eprintln!("Error: {}", err);
ExitCode::FAILURE
}
}
}