Skip to content

Commit 0938182

Browse files
committed
Add --list to known subcommands.
`cross --list` should list the subcommands present for cargo in the image, rather on the host. This works because any option provided before a subcommand has priority over the subcommand. For example, `cargo build --help` prints the help menu for `cargo build`, but `cargo --help build` ignores `build` and prints the help for `cargo`. Therefore, the options `--help`, `--version`, and `--list` can be treated as pseudo-subcommands. This works by capturing the output subcommand list, and then classifying them as either subcommands from the host or container. This also future proofs our logic, if we ever get support for custom subcommands, since only `Other` (unrecognized) subcommands are treated as host commands. Sample Output: ``` Cross Commands: b alias: build bench Execute all benchmarks of a local package Host Commands: afl asm clean Remove artifacts that cargo has generated in the past ``` Co-authored-by: Emil Gardström <[email protected]> Fixes #715.
1 parent e4d429d commit 0938182

File tree

5 files changed

+87
-11
lines changed

5 files changed

+87
-11
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77

88
- #722 - boolean environment variables are evaluated as truthy or falsey.
99
- #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`.
10-
- #719 - add android runner to preload `libc++_shared.so`.
10+
- #720 - add android runner to preload `libc++_shared.so`.
11+
- #719 - add `--list` to known subcommands.
1112
- #718 - remove deb subcommand.
1213
- #714 - use host target directory when falling back to host cargo.
1314
- #713 - convert relative target directories to absolute paths.

src/cargo.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,12 @@ pub enum Subcommand {
1717
Bench,
1818
Clippy,
1919
Metadata,
20+
List,
2021
}
2122

2223
impl Subcommand {
2324
pub fn needs_docker(self) -> bool {
24-
!matches!(self, Subcommand::Other)
25+
!matches!(self, Subcommand::Other | Subcommand::List)
2526
}
2627

2728
pub fn needs_interpreter(self) -> bool {
@@ -45,6 +46,7 @@ impl<'a> From<&'a str> for Subcommand {
4546
"bench" => Subcommand::Bench,
4647
"clippy" => Subcommand::Clippy,
4748
"metadata" => Subcommand::Metadata,
49+
"--list" => Subcommand::List,
4850
_ => Subcommand::Other,
4951
}
5052
}
@@ -88,3 +90,8 @@ pub fn root() -> Result<Option<Root>> {
8890
pub fn run(args: &[String], verbose: bool) -> Result<ExitStatus> {
8991
Command::new("cargo").args(args).run_and_get_status(verbose)
9092
}
93+
94+
/// run cargo and get the output, does not check the exit status
95+
pub fn run_and_get_output(args: &[String], verbose: bool) -> Result<std::process::Output> {
96+
Command::new("cargo").args(args).run_and_get_output(verbose)
97+
}

src/cli.rs

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,39 @@ fn bool_from_envvar(envvar: &str) -> bool {
3636
}
3737
}
3838

39+
pub fn is_subcommand_list(stdout: &str) -> bool {
40+
stdout.starts_with("Installed Commands:")
41+
}
42+
43+
pub fn group_subcommands(stdout: &str) -> (Vec<&str>, Vec<&str>) {
44+
let mut cross = vec![];
45+
let mut host = vec![];
46+
for line in stdout.lines().skip(1) {
47+
// trim all whitespace, then grab the command name
48+
let first = line.trim().split_whitespace().next();
49+
if let Some(command) = first {
50+
match Subcommand::from(command) {
51+
Subcommand::Other => host.push(line),
52+
_ => cross.push(line),
53+
}
54+
}
55+
}
56+
57+
(cross, host)
58+
}
59+
60+
pub fn fmt_subcommands(stdout: &str) {
61+
let (cross, host) = group_subcommands(stdout);
62+
if !cross.is_empty() {
63+
println!("Cross Commands:");
64+
cross.iter().for_each(|line| println!("{}", line));
65+
}
66+
if !host.is_empty() {
67+
println!("Host Commands:");
68+
host.iter().for_each(|line| println!("{}", line));
69+
}
70+
}
71+
3972
pub fn parse(target_list: &TargetList) -> Result<Args> {
4073
let mut channel = None;
4174
let mut target = None;
@@ -74,7 +107,7 @@ pub fn parse(target_list: &TargetList) -> Result<Args> {
74107
all.push("--target-dir=/target".into());
75108
}
76109
} else {
77-
if !arg.starts_with('-') && sc.is_none() {
110+
if (!arg.starts_with('-') || arg == "--list") && sc.is_none() {
78111
sc = Some(Subcommand::from(arg.as_ref()));
79112
}
80113

src/extensions.rs

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ pub trait CommandExt {
1010
fn run(&mut self, verbose: bool) -> Result<()>;
1111
fn run_and_get_status(&mut self, verbose: bool) -> Result<ExitStatus>;
1212
fn run_and_get_stdout(&mut self, verbose: bool) -> Result<String>;
13+
fn run_and_get_output(&mut self, verbose: bool) -> Result<std::process::Output>;
1314
}
1415

1516
impl CommandExt for Command {
@@ -42,14 +43,32 @@ impl CommandExt for Command {
4243

4344
/// Runs the command to completion and returns its stdout
4445
fn run_and_get_stdout(&mut self, verbose: bool) -> Result<String> {
46+
let out = self.run_and_get_output(verbose)?;
47+
self.status_result(out.status)?;
48+
out.stdout()
49+
}
50+
51+
/// Runs the command to completion and returns the status and its [output](std::process::Output).
52+
///
53+
/// # Notes
54+
///
55+
/// This command does not check the status.
56+
fn run_and_get_output(&mut self, verbose: bool) -> Result<std::process::Output> {
4557
self.print_verbose(verbose);
46-
let out = self
47-
.output()
48-
.wrap_err_with(|| format!("couldn't execute `{:?}`", self))?;
58+
self.output()
59+
.wrap_err_with(|| format!("couldn't execute `{:?}`", self))
60+
.map_err(Into::into)
61+
}
62+
}
4963

50-
self.status_result(out.status)?;
64+
pub trait OutputExt {
65+
fn stdout(&self) -> Result<String>;
66+
}
5167

52-
String::from_utf8(out.stdout).wrap_err_with(|| format!("`{:?}` output was not UTF-8", self))
68+
impl OutputExt for std::process::Output {
69+
fn stdout(&self) -> Result<String> {
70+
String::from_utf8(self.stdout.clone())
71+
.wrap_err_with(|| format!("`{:?}` output was not UTF-8", self))
5372
}
5473
}
5574

src/main.rs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod rustc;
1717
mod rustup;
1818

1919
use std::env;
20+
use std::io::{self, Write};
2021
use std::path::PathBuf;
2122
use std::process::ExitStatus;
2223

@@ -27,6 +28,7 @@ use serde::Deserialize;
2728
use self::cargo::{Root, Subcommand};
2829
use self::cross_toml::CrossToml;
2930
use self::errors::*;
31+
use self::extensions::OutputExt;
3032
use self::rustc::{TargetList, VersionMetaExt};
3133

3234
#[allow(non_camel_case_types)]
@@ -420,11 +422,25 @@ fn run() -> Result<ExitStatus> {
420422
}
421423
}
422424

423-
eprintln!("Warning: Falling back to `cargo` on the host.");
424-
425425
// if we fallback to the host cargo, use the same invocation that was made to cross
426426
let argv: Vec<String> = env::args().skip(1).collect();
427-
cargo::run(&argv, verbose)
427+
eprintln!("Warning: Falling back to `cargo` on the host.");
428+
match args.subcommand {
429+
Some(Subcommand::List) => {
430+
// this won't print in order if we have both stdout and stderr.
431+
let out = cargo::run_and_get_output(&argv, verbose)?;
432+
let stdout = out.stdout()?;
433+
if out.status.success() && cli::is_subcommand_list(&stdout) {
434+
cli::fmt_subcommands(&stdout);
435+
} else {
436+
// Not a list subcommand, which can happen with weird edge-cases.
437+
print!("{}", stdout);
438+
io::stdout().flush().unwrap();
439+
}
440+
Ok(out.status)
441+
}
442+
_ => cargo::run(&argv, verbose),
443+
}
428444
}
429445

430446
#[derive(PartialEq, Debug)]

0 commit comments

Comments
 (0)