From e8535a130dab94a665e6ad09f0ae72a70c338d93 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Mon, 3 Apr 2023 12:39:07 -0400 Subject: [PATCH] Add limited support for `cargo 3ds test --doc` --- README.md | 13 +++++++++++++ src/command.rs | 10 +++++++++- src/lib.rs | 52 +++++++++++++++++++++++++++++++++++--------------- 3 files changed, 59 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5ac5dab..d566bd5 100644 --- a/README.md +++ b/README.md @@ -95,3 +95,16 @@ executable arguments, *another* `--` can be used. For example: This works without two `--` instances because `--verbose` begins the set of `cargo` arguments and ends the set of 3DS-specific arguments. + + +### Caveats + +Due to the fact that only one executable at a time can be sent with `3dslink`, +by default only the "last" executable built will be used. If a `test` or `run` +command builds more than one binary, you may need to filter it in order to run +the executable you want. + +Doc tests sort of work, but `cargo-3ds` uses a number of unstable cargo and +rustdoc features to make them work, so the output won't be as pretty and will +require some manual workarounds to actually run the tests and see output from them. +For now, `cargo 3ds test --doc` will not build a 3dsx file or use `3dslink` at all. diff --git a/src/command.rs b/src/command.rs index a014b31..f3ddb55 100644 --- a/src/command.rs +++ b/src/command.rs @@ -66,6 +66,11 @@ pub struct Test { #[arg(long)] pub no_run: bool, + /// If set, documentation tests will be built instead of unit tests. + /// This implies `--no-run`. + #[arg(long)] + pub doc: bool, + // The test command uses a superset of the same arguments as Run. #[command(flatten)] pub run_args: Run, @@ -104,7 +109,10 @@ pub struct Run { impl CargoCmd { /// Whether or not this command should build a 3DSX executable file. pub fn should_build_3dsx(&self) -> bool { - matches!(self, Self::Build(_) | Self::Run(_) | Self::Test(_)) + matches!( + self, + Self::Build(_) | Self::Run(_) | Self::Test(Test { doc: false, .. }) + ) } /// Whether or not the resulting executable should be sent to the 3DS with diff --git a/src/lib.rs b/src/lib.rs index aeb1bfd..a88773e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ pub mod command; use crate::command::CargoCmd; use cargo_metadata::{Message, MetadataCommand}; +use command::Test; use rustc_version::Channel; use semver::Version; use serde::Deserialize; @@ -26,15 +27,23 @@ pub fn run_cargo(cmd: &CargoCmd, message_format: Option) -> (ExitStatus, let mut tee_reader; let mut stdout_reader; - let buf_reader: &mut dyn BufRead = if message_format.is_none() { - stdout_reader = BufReader::new(command_stdout); - &mut stdout_reader - } else { - // The user presumably cares about the message format, so we should + + let buf_reader: &mut dyn BufRead = match (message_format, cmd) { + // The user presumably cares about the message format if set, so we should // copy stuff to stdout like they expect. We can still extract the executable // information out of it that we need for 3dsxtool etc. - tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); - &mut tee_reader + (Some(_), _) | + // Rustdoc unfortunately prints to stdout for compile errors, so + // we also use a tee when building doc tests too. + // Possibly related: https://github.com/rust-lang/rust/issues/75135 + (None, CargoCmd::Test(Test { doc: true, .. })) => { + tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); + &mut tee_reader + } + _ => { + stdout_reader = BufReader::new(command_stdout); + &mut stdout_reader + } }; let messages = Message::parse_stream(buf_reader) @@ -75,7 +84,7 @@ pub fn make_cargo_build_command(cmd: &CargoCmd, message_format: &Option) ); if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() { - eprintln!("No pre-build std found, using build-std"); + eprintln!("No pre-built std found, using build-std"); command.arg("-Z").arg("build-std"); } @@ -83,9 +92,25 @@ pub fn make_cargo_build_command(cmd: &CargoCmd, message_format: &Option) CargoCmd::Build(cargo_args) => cargo_args.cargo_args(), CargoCmd::Run(run) => run.cargo_args.cargo_args(), CargoCmd::Test(test) => { - // We can't run 3DS executables on the host, so pass --no-run here and - // send the executable with 3dslink later, if the user wants - command.arg("--no-run"); + // We can't run 3DS executables on the host, so unconditionally pass + // --no-run here and send the executable with 3dslink later, if the + // user wants + + if test.doc { + // https://github.com/rust-lang/cargo/issues/7040 + command.args(["--doc", "-Z", "doctest-xcompile"]); + + // Cargo doesn't like --no-run for doctests: + // https://github.com/rust-lang/rust/issues/87022 + let rustdoc_flags = std::env::var("RUSTDOCFLAGS").unwrap_or_default() + // TODO: should we make this output directory depend on profile etc? + + " --no-run --persist-doctests target/doctests"; + + command.env("RUSTDOCFLAGS", rustdoc_flags); + } else { + command.arg("--no-run"); + } + test.run_args.cargo_args.cargo_args() } CargoCmd::Passthrough(other) => &other[1..], @@ -146,10 +171,7 @@ pub fn check_rust_version() { }; if old_version || old_commit { - eprintln!( - "cargo-3ds requires rustc nightly version >= {}", - MINIMUM_COMMIT_DATE, - ); + eprintln!("cargo-3ds requires rustc nightly version >= {MINIMUM_COMMIT_DATE}"); eprintln!("Please run `rustup update nightly` to upgrade your nightly version"); process::exit(1);