Skip to content

Commit

Permalink
Add --list to known subcommands.
Browse files Browse the repository at this point in the history
`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
```

Fixes #715.
  • Loading branch information
Alexhuszagh committed May 27, 2022
1 parent 459e03e commit 9d3277c
Show file tree
Hide file tree
Showing 5 changed files with 86 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).

- #722 - boolean environment variables are evaluated as truthy or falsey.
- #721 - add support for running doctests on nightly if `CROSS_UNSTABLE_ENABLE_DOCTESTS=true`.
- #719 - add `--list` to known subcommands.
- #718 - remove deb subcommand.
- #714 - use host target directory when falling back to host cargo.
- #713 - convert relative target directories to absolute paths.
Expand Down
13 changes: 12 additions & 1 deletion src/cargo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@ pub enum Subcommand {
Bench,
Clippy,
Metadata,
List,
}

impl Subcommand {
pub fn needs_docker(self) -> bool {
!matches!(self, Subcommand::Other)
!matches!(self, Subcommand::Other | Subcommand::List)
}

pub fn needs_interpreter(self) -> bool {
Expand All @@ -45,6 +46,7 @@ impl<'a> From<&'a str> for Subcommand {
"bench" => Subcommand::Bench,
"clippy" => Subcommand::Clippy,
"metadata" => Subcommand::Metadata,
"--list" => Subcommand::List,
_ => Subcommand::Other,
}
}
Expand Down Expand Up @@ -88,3 +90,12 @@ pub fn root() -> Result<Option<Root>> {
pub fn run(args: &[String], verbose: bool) -> Result<ExitStatus> {
Command::new("cargo").args(args).run_and_get_status(verbose)
}

pub fn run_and_get_output(
args: &[String],
verbose: bool,
) -> Result<std::process::Output> {
Command::new("cargo")
.args(args)
.run_and_get_output(verbose)
}
35 changes: 34 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,39 @@ fn bool_from_envvar(envvar: &str) -> bool {
}
}

pub fn is_subcommand_list(stdout: &str) -> bool {
stdout.starts_with("Installed Commands:")
}

pub fn group_subcommands(stdout: &str) -> (Vec<&str>, Vec<&str>) {
let mut cross = vec![];
let mut host = vec![];
for line in stdout.lines().skip(1) {
// trim all whitespace, then grab the command name
let first = line.trim().split_whitespace().next();
if let Some(command) = first {
match Subcommand::from(command) {
Subcommand::Other => host.push(line),
_ => cross.push(line),
}
}
}

(cross, host)
}

pub fn fmt_subcommands(stdout: &str) {
let (cross, host) = group_subcommands(stdout);
if !cross.is_empty() {
println!("Cross Commands:");
cross.iter().for_each(|line| println!("{}", line));
}
if !host.is_empty() {
println!("Host Commands:");
host.iter().for_each(|line| println!("{}", line));
}
}

pub fn parse(target_list: &TargetList) -> Result<Args> {
let mut channel = None;
let mut target = None;
Expand Down Expand Up @@ -74,7 +107,7 @@ pub fn parse(target_list: &TargetList) -> Result<Args> {
all.push("--target-dir=/target".into());
}
} else {
if !arg.starts_with('-') && sc.is_none() {
if (!arg.starts_with('-') || arg == "--list") && sc.is_none() {
sc = Some(Subcommand::from(arg.as_ref()));
}

Expand Down
23 changes: 19 additions & 4 deletions src/extensions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub trait CommandExt {
fn run(&mut self, verbose: bool) -> Result<()>;
fn run_and_get_status(&mut self, verbose: bool) -> Result<ExitStatus>;
fn run_and_get_stdout(&mut self, verbose: bool) -> Result<String>;
fn run_and_get_output(&mut self, verbose: bool) -> Result<std::process::Output>;
}

impl CommandExt for Command {
Expand Down Expand Up @@ -42,14 +43,28 @@ impl CommandExt for Command {

/// Runs the command to completion and returns its stdout
fn run_and_get_stdout(&mut self, verbose: bool) -> Result<String> {
let out = self.run_and_get_output(verbose)?;
self.status_result(out.status)?;
out.stdout()
}

/// Runs the command to completion and returns the status and its stdout.
fn run_and_get_output(&mut self, verbose: bool) -> Result<std::process::Output> {
self.print_verbose(verbose);
let out = self
self
.output()
.wrap_err_with(|| format!("couldn't execute `{:?}`", self))?;
.wrap_err_with(|| format!("couldn't execute `{:?}`", self))
.map_err(Into::into)
}
}

self.status_result(out.status)?;
pub trait OutputExt {
fn stdout(&self) -> Result<String>;
}

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

Expand Down
23 changes: 20 additions & 3 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ mod rustc;
mod rustup;

use std::env;
use std::io::{self, Write};
use std::path::PathBuf;
use std::process::ExitStatus;

Expand All @@ -27,6 +28,7 @@ use serde::Deserialize;
use self::cargo::{Root, Subcommand};
use self::cross_toml::CrossToml;
use self::errors::*;
use self::extensions::OutputExt;
use self::rustc::{TargetList, VersionMetaExt};

#[allow(non_camel_case_types)]
Expand Down Expand Up @@ -420,11 +422,26 @@ fn run() -> Result<ExitStatus> {
}
}

eprintln!("Warning: Falling back to `cargo` on the host.");

// if we fallback to the host cargo, use the same invocation that was made to cross
let argv: Vec<String> = env::args().skip(1).collect();
cargo::run(&argv, verbose)
eprintln!("Warning: Falling back to `cargo` on the host.");
match args.subcommand {
Some(Subcommand::List) => {
// this won't print in order if we have both stdout and stderr.
let out = cargo::run_and_get_output(&argv, verbose)?;
let stdout = out.stdout()?;
if out.status.success() && cli::is_subcommand_list(&stdout) {
cli::fmt_subcommands(&stdout);
} else {
// Not a list subcommand, which can happen with weird edge-cases.
print!("{}", stdout);
io::stdout().flush().unwrap();
}
Ok(out.status)
}
None | Some(Subcommand::Other) => cargo::run(&argv, verbose),
_ => unreachable!(),
}
}

#[derive(PartialEq, Debug)]
Expand Down

0 comments on commit 9d3277c

Please sign in to comment.