Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

change target info to json and display them neatly #818

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions xtask/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
159 changes: 150 additions & 9 deletions xtask/src/target_info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -63,14 +66,13 @@ fn image_info(
tag: &str,
verbose: bool,
has_test: bool,
) -> cross::Result<()> {
) -> cross::Result<TargetInfoOutput> {
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 {
Expand All @@ -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<Item = &'a cross::Result<(ImageTarget, TargetInfoOutput)>>,
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(
Expand Down Expand Up @@ -111,14 +213,53 @@ pub fn target_info(
.collect();
}

for target in targets {
let process = |target: &ImageTarget| {
let image = target.image_name(&format_repo(&registry, &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: <libc_w$}| {cc: <cc_w$}|{cxx: ^5}| {qemu: <qemu_w$}|{has_test: ^5}|",
target = format!("`{}`{}", target.alt(), info.flags())
)
});

Ok(())
}
Loading