diff --git a/Cargo.lock b/Cargo.lock index d1d39c0..ec5f0fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -245,6 +245,17 @@ dependencies = [ "port", ] +[[package]] +name = "rustup-configurator" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1ea4d26348e7916570c4cc67014aad9e017f148cd5bb8a4ee0f5b95f2db788d" +dependencies = [ + "strip-ansi-escapes", + "target-lexicon", + "thiserror", +] + [[package]] name = "serde" version = "1.0.186" @@ -274,6 +285,15 @@ dependencies = [ "serde", ] +[[package]] +name = "strip-ansi-escapes" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55ff8ef943b384c414f54aefa961dd2bd853add74ec75e7ac74cf91dba62bcfa" +dependencies = [ + "vte", +] + [[package]] name = "strsim" version = "0.10.0" @@ -302,6 +322,35 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c39fd04924ca3a864207c66fc2cd7d22d7c016007f9ce846cbb9326331930a" +dependencies = [ + "serde", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.29", +] + [[package]] name = "toml" version = "0.8.0" @@ -348,6 +397,26 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "vte" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5022b5fbf9407086c180e9557be968742d839e68346af7792b8592489732197" +dependencies = [ + "utf8parse", + "vte_generate_state_changes", +] + +[[package]] +name = "vte_generate_state_changes" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "windows-sys" version = "0.48.0" @@ -448,6 +517,7 @@ name = "xtask" version = "0.1.0" dependencies = [ "clap", + "rustup-configurator", "serde", "toml", ] diff --git a/riscv64/Cargo.toml b/riscv64/Cargo.toml index ef37c0b..2ea5ad9 100644 --- a/riscv64/Cargo.toml +++ b/riscv64/Cargo.toml @@ -4,7 +4,7 @@ cargo-features = ["per-package-target"] name = "riscv64" version = "0.1.0" edition = "2021" -default-target = "riscv64imac-unknown-none-elf" +default-target = "riscv64gc-unknown-none-elf" [dependencies] port = { path = "../port" } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 6e48daf..3ee535f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -3,6 +3,6 @@ channel = "nightly-2023-06-05" components = [ "rustfmt", "rust-src", "clippy", "llvm-tools" ] targets = [ "aarch64-unknown-none", - "riscv64imac-unknown-none-elf", + "riscv64gc-unknown-none-elf", "x86_64-unknown-none" ] diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 3439d13..9038372 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -7,5 +7,6 @@ edition = "2021" [dependencies] clap = { version = "4.2.4", features = ["derive"] } +rustup-configurator = "0.1.1" serde = { version = "1.0.160", features = ["derive"] } toml = "0.8.0" diff --git a/xtask/src/main.rs b/xtask/src/main.rs index b7a6f2c..3d0447d 100644 --- a/xtask/src/main.rs +++ b/xtask/src/main.rs @@ -1,3 +1,5 @@ +use crate::config::{generate_args, read_config, Configuration}; +use rustup_configurator::Triple; use std::{ env, fmt, path::{Path, PathBuf}, @@ -5,7 +7,6 @@ use std::{ }; mod config; -use crate::config::{generate_args, read_config, Configuration}; type DynError = Box; type Result = std::result::Result; @@ -73,7 +74,9 @@ impl BuildParams { config_file )); - let json_output = matches.try_contains_id("json").unwrap_or(false); + // This is a very awkward way to check a boolean which may not exist... + let json_output = matches.try_contains_id("json").unwrap_or(false) + && matches.value_source("json") != Some(clap::parser::ValueSource::DefaultValue); Self { arch: *arch, profile, verbose, wait_for_gdb, dump_dtb, config, json_output } } @@ -108,6 +111,42 @@ impl BuildParams { } } +struct RustupState { + installed_targets: Vec, + curr_toolchain: String, +} + +impl RustupState { + /// Runs rustup command to get a list of all installed toolchains. + /// Also caches the current toolchain. + fn new() -> Self { + Self { + installed_targets: rustup_configurator::installed().unwrap(), + curr_toolchain: env::var("RUSTUP_TOOLCHAIN").unwrap(), + } + } + + /// For the given arch, return a compatible toolchain triple that is + /// installed and can be used by cargo check. It will prefer the default + /// toolchain if it's a match, otherwise it will look for the + /// toolchain. + fn std_supported_target(&self, arch: &str) -> Option<&Triple> { + let arch = Self::target_arch(arch); + self.installed_targets.iter().filter(|&t| t.architecture.to_string() == arch).find(|&t| { + self.curr_toolchain.ends_with(&t.to_string()) + || t.to_string() == arch.to_owned() + "-unknown-linux-gnu" + }) + } + + /// Return the arch in a form compatible with the supported targets and toolchains + fn target_arch(arch: &str) -> &str { + match arch { + "riscv64" => "riscv64gc", + _ => arch, + } + } +} + fn main() { let matches = clap::Command::new("xtask") .version("0.1.0") @@ -172,12 +211,10 @@ fn main() { clap::arg!(--verbose "Print commands"), ]), ) - .subcommand( - clap::Command::new("check") - .about("Runs check") - .args(&[clap::arg!(--json "Output messages as json")]) - .args(&[clap::arg!(--verbose "Print commands")]), - ) + .subcommand(clap::Command::new("check").about("Runs check").args(&[ + clap::arg!(--json "Output messages as json"), + clap::arg!(--verbose "Print commands"), + ])) .subcommand( clap::Command::new("qemu").about("Run r9 under QEMU").args(&[ clap::arg!(--release "Build a release version").conflicts_with("debug"), @@ -407,19 +444,47 @@ fn dist(build_params: &BuildParams) -> Result<()> { Ok(()) } +/// Run tests for the current host toolchain. fn test(build_params: &BuildParams) -> Result<()> { - let mut cmd = Command::new(cargo()); - cmd.current_dir(workspace()); - cmd.arg("test"); - cmd.arg("--workspace"); - cmd.arg("--target").arg("x86_64-unknown-linux-gnu"); - build_params.add_build_arg(&mut cmd); - if build_params.verbose { - println!("Executing {cmd:?}"); + let mut all_cmd_args = Vec::new(); + + all_cmd_args.push(vec![ + "test".to_string(), + "--package".to_string(), + "port".to_string(), + "--lib".to_string(), + ]); + + let rustup_state = RustupState::new(); + + let arch = std::env::consts::ARCH; + if let Some(target) = rustup_state.std_supported_target(arch) { + all_cmd_args.push(vec![ + "test".to_string(), + "--package".to_string(), + arch.to_string(), + "--bins".to_string(), + "--target".to_string(), + target.to_string(), + ]); } - let status = annotated_status(&mut cmd)?; - if !status.success() { - return Err("test failed".into()); + + for cmd_args in all_cmd_args { + let mut cmd = Command::new(cargo()); + cmd.current_dir(workspace()); + + cmd.args(cmd_args); + if build_params.json_output { + cmd.arg("--message-format=json").arg("--quiet"); + } + + if build_params.verbose { + println!("Executing {cmd:?}"); + } + let status = annotated_status(&mut cmd)?; + if !status.success() { + return Err("check failed".into()); + } } Ok(()) } @@ -446,71 +511,69 @@ fn clippy(build_params: &BuildParams) -> Result<()> { Ok(()) } +/// Run check for all packages for all relevant toolchains. +/// This assumes that the -unknown-linux-gnu toolchain has been installed +/// for any arch we care about. fn check(build_params: &BuildParams) -> Result<()> { - let all_check_args = vec![ - vec!["check", "--package", "aarch64", "--bins"], - vec!["check", "--package", "x86_64", "--bins"], - vec!["check", "--package", "riscv64", "--bins"], - vec!["check", "--package", "port", "--lib"], - vec![ - "check", - "--package", - "aarch64", - "--tests", - "--benches", - "--target", - "aarch64-unknown-linux-gnu", - ], - vec![ - "check", - "--package", - "x86_64", - "--tests", - "--benches", - "--target", - "x86_64-unknown-linux-gnu", - ], + // To run check for bins and lib we use the default toolchain, which has + // been set to the OS-independent arch toolchain in each Cargo.toml file. + // The same applies to tests and benches for non-arch-specific lib packages. + let bins_lib_package_cmd_args = vec![ vec![ - "check", - "--package", - "riscv64", - "--tests", - "--benches", - "--target", - "riscv64gc-unknown-linux-gnu", + "check".to_string(), + "--package".to_string(), + "aarch64".to_string(), + "--bins".to_string(), ], vec![ - "check", - "--package", - "port", - "--tests", - "--benches", - "--target", - "aarch64-unknown-linux-gnu", + "check".to_string(), + "--package".to_string(), + "riscv64".to_string(), + "--bins".to_string(), ], vec![ - "check", - "--package", - "port", - "--tests", - "--benches", - "--target", - "x86_64-unknown-linux-gnu", + "check".to_string(), + "--package".to_string(), + "x86_64".to_string(), + "--bins".to_string(), ], vec![ - "check", - "--package", - "port", - "--tests", - "--benches", - "--target", - "riscv64gc-unknown-linux-gnu", + "check".to_string(), + "--package".to_string(), + "port".to_string(), + "--lib".to_string(), + "--tests".to_string(), + "--benches".to_string(), ], ]; - for check_args in all_check_args { + let rustup_state = RustupState::new(); + + // However, running check for tests and benches in arch packages requires + // that we use a toolchain with `std`, so we need an OS-specific toolchain. + // If the arch matches that of the current toolchain, then that will be used + // for check. Otherwise we'll always default to -unknown-linux-gnu. + let mut benches_tests_package_cmd_args = Vec::new(); + + for arch in ["aarch64", "riscv64", "x86_64"] { + let Some(target) = rustup_state.std_supported_target(arch) else { + continue; + }; + + benches_tests_package_cmd_args.push(vec![ + "check".to_string(), + "--package".to_string(), + arch.to_string(), + "--tests".to_string(), + "--benches".to_string(), + "--target".to_string(), + target.to_string(), + ]); + } + + for cmd_args in [bins_lib_package_cmd_args, benches_tests_package_cmd_args].concat() { let mut cmd = Command::new(cargo()); - cmd.args(check_args); + cmd.args(cmd_args); if build_params.json_output { cmd.arg("--message-format=json").arg("--quiet"); }