diff --git a/Cargo.lock b/Cargo.lock index 12d12e52b..8ac23c68a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -170,6 +170,51 @@ dependencies = [ "winapi", ] +[[package]] +name = "crossbeam-channel" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "once_cell", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ff1f980957787286a554052d03c7aee98d99cc32e09f6d45f0a814133c87978" +dependencies = [ + "cfg-if", + "once_cell", +] + [[package]] name = "dunce" version = "1.0.2" @@ -274,6 +319,15 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +[[package]] +name = "memoffset" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.5.3" @@ -294,6 +348,16 @@ dependencies = [ "libc", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "object" version = "0.28.4" @@ -369,6 +433,30 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rayon" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "num_cpus", +] + [[package]] name = "regex" version = "1.5.6" @@ -416,6 +504,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "semver" version = "1.0.10" @@ -693,6 +787,7 @@ dependencies = [ "cross", "eyre", "once_cell", + "rayon", "serde", "serde_json", "serde_yaml", diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 2afc92840..03091e073 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -18,3 +18,4 @@ serde = { version = "1", features = ["derive"] } serde_yaml = "0.8" serde_json = "1.0" once_cell = "1.12" +rayon = { version = "1", optional = true } diff --git a/xtask/src/target_info.rs b/xtask/src/target_info.rs index bc7fdd48a..37918e0c2 100644 --- a/xtask/src/target_info.rs +++ b/xtask/src/target_info.rs @@ -6,6 +6,9 @@ use std::{ use clap::Args; use cross::CommandExt; +use serde::Deserialize; + +use crate::util::ImageTarget; // Store raw text data in the binary so we don't need a data directory // when extracting all targets, or running our target info script. @@ -63,14 +66,13 @@ fn image_info( tag: &str, verbose: bool, has_test: bool, -) -> cross::Result<()> { +) -> cross::Result { if !tag.starts_with("local") { pull_image(engine, image, verbose)?; } let mut command = Command::new(engine); command.arg("run"); - command.arg("-it"); command.arg("--rm"); command.args(&["-e", &format!("TARGET={}", target.triplet)]); if has_test { @@ -79,11 +81,111 @@ fn image_info( command.arg(image); command.args(&["bash", "-c", TARGET_INFO_SCRIPT]); - if !verbose { - // capture stderr to avoid polluting table - command.stderr(Stdio::null()); + serde_json::from_str(&command.run_and_get_stdout(verbose)?).map_err(Into::into) +} + +#[derive(Debug, Deserialize)] +struct TargetInfoOutput { + pub libc: Version, + pub cc: Version, + pub cxx: Truthy, + pub qemu: Version, + pub has_test: Truthy, + pub libc_is_newlib: Truthy, + pub libc_os: Truthy, + pub bionic: Truthy, +} + +impl TargetInfoOutput { + pub fn flags(&self) -> String { + let mut string = String::new(); + + if self.libc_is_newlib.is_yes() { + string.push_str("[4]") + } + if self.libc_os.is_yes() { + string.push_str("[3]") + } + if self.bionic.is_yes() { + string.push_str("[1]") + } + + if !string.is_empty() { + string.insert(0, ' '); + } + string + } +} + +#[derive(Debug, Deserialize)] +#[serde(from = "&str")] +pub enum Version { + NotApplicable, + Version(String), +} + +impl std::fmt::Display for Version { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Version::NotApplicable => f.pad("N/A"), + Version::Version(v) => f.pad(v), + } + } +} + +impl From<&str> for Version { + fn from(version: &str) -> Self { + match version { + "" => Version::NotApplicable, + v => Version::Version(v.to_string()), + } + } +} + +#[derive(Debug, Deserialize, PartialEq, Eq)] +#[serde(from = "&str")] +pub enum Truthy { + Yes, + No, +} + +impl Truthy { + /// Returns `true` if the truthy is [`Yes`]. + /// + /// [`Yes`]: Truthy::Yes + #[must_use] + pub fn is_yes(&self) -> bool { + matches!(self, Self::Yes) + } +} + +impl std::fmt::Display for Truthy { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Truthy::Yes => f.pad("✓"), + Truthy::No => f.pad(""), + } } - command.run(verbose, false).map_err(Into::into) +} + +impl From<&str> for Truthy { + fn from(version: &str) -> Self { + match version { + "" => Truthy::No, + _ => Truthy::Yes, + } + } +} + +fn calc_width<'a>( + iter: impl Iterator>, + f: impl Fn(&'a ImageTarget, &'a TargetInfoOutput) -> usize, +) -> usize { + iter.filter_map(|r| r.as_ref().ok()) + .map(|(target, info)| f(target, info)) + .max() + .unwrap_or_default() + + 2 } pub fn target_info( @@ -111,14 +213,53 @@ pub fn target_info( .collect(); } - for target in targets { + let process = |target: &ImageTarget| { let image = target.image_name(&format_repo(®istry, &repository), &tag); let has_test = test_map - .get(&target) + .get(target) .cloned() .ok_or_else(|| eyre::eyre!("invalid target name {}", target))?; - image_info(engine, &target, &image, &tag, verbose, has_test)?; + eprintln!("doing {target}"); + match image_info(engine, target, &image, &tag, verbose, has_test) { + Ok(r) => Ok((target.clone(), r)), + Err(e) => Err(eyre::eyre!("target {target} failed with: {e}")), + } + }; + + let results: Vec<_>; + #[cfg(feature = "rayon")] + { + use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; + results = targets.par_iter().map(process).collect(); + } + + #[cfg(not(feature = "rayon"))] + { + results = targets.into_iter().map(|ref t| process(t)).collect(); } + let t_w = calc_width(results.iter(), |t, info| { + t.alt().len() + info.flags().len() + 2 + }); + let libc_w = calc_width(results.iter(), |_, info| info.libc.to_string().len() + 2); + let cc_w = calc_width(results.iter(), |_, info| info.cc.to_string().len() + 2); + let qemu_w = calc_width(results.iter(), |_, info| info.qemu.to_string().len() + 2); + results.into_iter().filter_map(Result::ok).for_each(|( + target, + ref info @ TargetInfoOutput { + ref libc, + ref cc, + ref cxx, + ref qemu, + ref has_test, + .. + }, + )| { + println!( + "|{target: >t_w$} | {libc: &2 exit 1 @@ -275,6 +281,7 @@ case "${target}" in minor_version="${BASH_REMATCH[2]}" patch_version="${BASH_REMATCH[3]}" libc="${major_version}.${minor_version}.${patch_version}" + libc_os="1" else echo "Unable to get libc version for ${target}: invalid Dragonfly release found." 1>&2 exit 1 @@ -290,6 +297,7 @@ case "${target}" in minor_version="${BASH_REMATCH[2]}" patch_version="${BASH_REMATCH[3]}" libc="${major_version}.${minor_version}.${patch_version}" + libc_is_newlib="1" else echo "Unable to get libc version for ${target}: invalid NetBSD release found." 1>&2 exit 1 @@ -306,30 +314,4 @@ if command -v "qemu-${qarch}" &>/dev/null; then fi # format our output -printf "| %-36s |" "\`${target}\`" -if [ "$libc" != "" ]; then - printf " %-6s |" "${libc}" -else - printf " N/A |" -fi -if [ "$cc" != "" ]; then - printf " %-7s |" "${cc}" -else - printf " N/A |" -fi -if [ "$cxx" != "" ]; then - printf " ✓ |" -else - printf " |" -fi -if [ "$qemu" != "" ]; then - printf " %-5s |" "${qemu}" -else - printf " N/A |" -fi -if [ "${HAS_TEST}" != "" ]; then - printf " ✓ |" -else - printf " |" -fi -printf "\n" +printf '{"libc":"%s","cc":"%s","cxx":"%s","qemu":"%s","libc_is_newlib":"%s","has_test":"%s","libc_os":"%s","bionic":"%s"}\n' "$libc" "$cc" "$cxx" "$qemu" "$libc_is_newlib" "${HAS_TEST}" "$libc_os" "$bionic"