#![deny(warnings)] #![feature(exit_status_error)] use std::{fs, 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, #[clap(subcommand)] action: Option, } #[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, #[clap(short, long, help = "Disk image to use for persistent data")] disk: Option, #[clap(help = "Extra arguments for QEMU")] extra_args: Vec, }, #[clap(about = "Build the Yggdrasil OS Rust toolchain")] Toolchain { branch: Option }, #[clap(about = "Print the host triple")] HostTriple, } 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(()) } } } 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 } } }