Browse Source

Merge pull request #32 from rust3ds/feature/doctests

pull/38/head
Ian Chamberlain 2 years ago committed by GitHub
parent
commit
78a652fdfb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .github/CODEOWNERS
  2. 12
      README.md
  3. 40
      src/command.rs
  4. 48
      src/lib.rs

1
.github/CODEOWNERS

@ -0,0 +1 @@
* @rust3ds/active

12
README.md

@ -96,6 +96,18 @@ executable arguments, *another* `--` can be used. For example:
This works without two `--` instances because `--verbose` begins the set of This works without two `--` instances because `--verbose` begins the set of
`cargo` arguments and ends the set of 3DS-specific arguments. `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.
## License ## License
This project is distributed under the MIT license or the Apache-2.0 license. This project is distributed under the MIT license or the Apache-2.0 license.

40
src/command.rs

@ -66,6 +66,11 @@ pub struct Test {
#[arg(long)] #[arg(long)]
pub no_run: bool, 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. // The test command uses a superset of the same arguments as Run.
#[command(flatten)] #[command(flatten)]
pub run_args: Run, pub run_args: Run,
@ -104,15 +109,18 @@ pub struct Run {
impl CargoCmd { impl CargoCmd {
/// Whether or not this command should build a 3DSX executable file. /// Whether or not this command should build a 3DSX executable file.
pub fn should_build_3dsx(&self) -> bool { 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 /// Whether or not the resulting executable should be sent to the 3DS with
/// `3dslink`. /// `3dslink`.
pub fn should_link_to_device(&self) -> bool { pub fn should_link_to_device(&self) -> bool {
match self { match self {
CargoCmd::Test(test) => !test.no_run, Self::Test(test) => !test.no_run,
CargoCmd::Run(_) => true, Self::Run(_) => true,
_ => false, _ => false,
} }
} }
@ -120,12 +128,26 @@ impl CargoCmd {
pub const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; pub const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics";
pub fn extract_message_format(&mut self) -> Result<Option<String>, String> { pub fn extract_message_format(&mut self) -> Result<Option<String>, String> {
Self::extract_message_format_from_args(match self { let cargo_args = match self {
CargoCmd::Build(args) => &mut args.args, Self::Build(args) => &mut args.args,
CargoCmd::Run(run) => &mut run.cargo_args.args, Self::Run(run) => &mut run.cargo_args.args,
CargoCmd::Test(test) => &mut test.run_args.cargo_args.args, Self::Passthrough(args) => args,
CargoCmd::Passthrough(args) => args, Self::Test(test) => &mut test.run_args.cargo_args.args,
}) };
let format = Self::extract_message_format_from_args(cargo_args)?;
if format.is_some() {
return Ok(format);
}
if let Self::Test(Test { doc: true, .. }) = self {
// We don't care about JSON output for doctests since we're not
// building any 3dsx etc. Just use the default output as it's more
// readable compared to DEFAULT_MESSAGE_FORMAT
Ok(Some(String::from("human")))
} else {
Ok(None)
}
} }
fn extract_message_format_from_args( fn extract_message_format_from_args(

48
src/lib.rs

@ -3,6 +3,7 @@ pub mod command;
use crate::command::CargoCmd; use crate::command::CargoCmd;
use cargo_metadata::{Message, MetadataCommand}; use cargo_metadata::{Message, MetadataCommand};
use command::Test;
use rustc_version::Channel; use rustc_version::Channel;
use semver::Version; use semver::Version;
use serde::Deserialize; use serde::Deserialize;
@ -26,15 +27,23 @@ pub fn run_cargo(cmd: &CargoCmd, message_format: Option<String>) -> (ExitStatus,
let mut tee_reader; let mut tee_reader;
let mut stdout_reader; let mut stdout_reader;
let buf_reader: &mut dyn BufRead = if message_format.is_none() {
stdout_reader = BufReader::new(command_stdout); let buf_reader: &mut dyn BufRead = match (message_format, cmd) {
&mut stdout_reader // The user presumably cares about the message format if set, so we should
} else {
// The user presumably cares about the message format, so we should
// copy stuff to stdout like they expect. We can still extract the executable // copy stuff to stdout like they expect. We can still extract the executable
// information out of it that we need for 3dsxtool etc. // information out of it that we need for 3dsxtool etc.
(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())); tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout()));
&mut tee_reader &mut tee_reader
}
_ => {
stdout_reader = BufReader::new(command_stdout);
&mut stdout_reader
}
}; };
let messages = Message::parse_stream(buf_reader) let messages = Message::parse_stream(buf_reader)
@ -75,7 +84,7 @@ pub fn make_cargo_build_command(cmd: &CargoCmd, message_format: &Option<String>)
); );
if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() { 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"); command.arg("-Z").arg("build-std");
} }
@ -83,9 +92,27 @@ pub fn make_cargo_build_command(cmd: &CargoCmd, message_format: &Option<String>)
CargoCmd::Build(cargo_args) => cargo_args.cargo_args(), CargoCmd::Build(cargo_args) => cargo_args.cargo_args(),
CargoCmd::Run(run) => run.cargo_args.cargo_args(), CargoCmd::Run(run) => run.cargo_args.cargo_args(),
CargoCmd::Test(test) => { CargoCmd::Test(test) => {
// We can't run 3DS executables on the host, so pass --no-run here and // We can't run 3DS executables on the host, so unconditionally pass
// send the executable with 3dslink later, if the user wants // --no-run here and send the executable with 3dslink later, if the
// user wants
if test.doc {
eprintln!("Documentation tests requested, no 3dsx will be built or run");
// 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"); command.arg("--no-run");
}
test.run_args.cargo_args.cargo_args() test.run_args.cargo_args.cargo_args()
} }
CargoCmd::Passthrough(other) => &other[1..], CargoCmd::Passthrough(other) => &other[1..],
@ -146,10 +173,7 @@ pub fn check_rust_version() {
}; };
if old_version || old_commit { if old_version || old_commit {
eprintln!( eprintln!("cargo-3ds requires rustc nightly version >= {MINIMUM_COMMIT_DATE}");
"cargo-3ds requires rustc nightly version >= {}",
MINIMUM_COMMIT_DATE,
);
eprintln!("Please run `rustup update nightly` to upgrade your nightly version"); eprintln!("Please run `rustup update nightly` to upgrade your nightly version");
process::exit(1); process::exit(1);

Loading…
Cancel
Save