Skip to content

Commit c8df353

Browse files
Merge #719
719: Add --list to known subcommands. r=Alexhuszagh a=Alexhuszagh `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. Fixes #715. Co-authored-by: Alex Huszagh <[email protected]>
2 parents e4d429d + 0938182 commit c8df353

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)