Browse Source

Merge different commands under a common infrastructure

pull/36/head
Andrea Ciliberti 2 years ago
parent
commit
eaa93126f4
  1. 56
      src/command.rs
  2. 119
      src/lib.rs

56
src/command.rs

@ -129,6 +129,45 @@ pub struct New {
} }
impl CargoCmd { impl CargoCmd {
/// Returns the additional arguments run by the "official" cargo subcommand.
pub fn cargo_args(&self) -> Vec<String> {
match self {
CargoCmd::Build(build) => build.cargo_args.cargo_args(),
CargoCmd::Run(run) => run.build_args.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
let mut cargo_args = test.run_args.build_args.cargo_args.cargo_args();
cargo_args.push("--no-run".to_string());
cargo_args
}
CargoCmd::New(new) => {
// We push the original path in the new command (we captured it in [`New`] to learn about the context)
let mut cargo_args = new.cargo_args.cargo_args();
cargo_args.push(new.path.clone());
cargo_args
}
CargoCmd::Passthrough(other) => other.clone().split_off(1),
}
}
/// Returns the cargo subcommand run by `cargo-3ds` when handling a [`CargoCmd`].
///
/// # Notes
///
/// This is not equivalent to the lowercase name of the [`CargoCmd`] variant.
/// Commands may use different commands under the hood to function (e.g. [`CargoCmd::Run`] uses `build`).
pub fn subcommand_name(&self) -> &str {
match self {
CargoCmd::Build(_) | CargoCmd::Run(_) => "build",
CargoCmd::Test(_) => "test",
CargoCmd::New(_) => "new",
CargoCmd::Passthrough(cmd) => &cmd[0],
}
}
/// Whether or not this command should compile any code, and thus needs import the custom environment configuration (e.g. target spec). /// Whether or not this command should compile any code, and thus needs import the custom environment configuration (e.g. target spec).
pub fn should_compile(&self) -> bool { pub fn should_compile(&self) -> bool {
matches!( matches!(
@ -221,6 +260,7 @@ impl CargoCmd {
/// - `cargo 3ds build` and other "build" commands will use their callbacks to build the final `.3dsx` file and link it. /// - `cargo 3ds build` and other "build" commands will use their callbacks to build the final `.3dsx` file and link it.
/// - `cargo 3ds new` and other generic commands will use their callbacks to make 3ds-specific changes to the environment. /// - `cargo 3ds new` and other generic commands will use their callbacks to make 3ds-specific changes to the environment.
pub fn run_callback(&self, messages: &[Message]) { pub fn run_callback(&self, messages: &[Message]) {
// Process the metadata only for commands that have it/use it
let config = if self.should_build_3dsx() { let config = if self.should_build_3dsx() {
eprintln!("Getting metadata"); eprintln!("Getting metadata");
@ -242,20 +282,24 @@ impl CargoCmd {
impl RemainingArgs { impl RemainingArgs {
/// Get the args to be passed to the executable itself (not `cargo`). /// Get the args to be passed to the executable itself (not `cargo`).
pub fn cargo_args(&self) -> &[String] { pub fn cargo_args(&self) -> Vec<String> {
self.split_args().0 self.split_args().0
} }
/// Get the args to be passed to the executable itself (not `cargo`). /// Get the args to be passed to the executable itself (not `cargo`).
pub fn exe_args(&self) -> &[String] { pub fn exe_args(&self) -> Vec<String> {
self.split_args().1 self.split_args().1
} }
fn split_args(&self) -> (&[String], &[String]) { fn split_args(&self) -> (Vec<String>, Vec<String>) {
if let Some(split) = self.args.iter().position(|s| s == "--") { let mut args = self.args.clone();
self.args.split_at(split + 1)
if let Some(split) = args.iter().position(|s| s == "--") {
let second_half = args.split_off(split + 1);
(args, second_half)
} else { } else {
(&self.args[..], &[]) (args, Vec::new())
} }
} }
} }

119
src/lib.rs

@ -21,11 +21,7 @@ use std::{env, io, process};
/// For commands that produce an executable output, this function will build the /// For commands that produce an executable output, this function will build the
/// `.elf` binary that can be used to create other 3ds files. /// `.elf` binary that can be used to create other 3ds files.
pub fn run_cargo(cmd: &CargoCmd, message_format: Option<String>) -> (ExitStatus, Vec<Message>) { pub fn run_cargo(cmd: &CargoCmd, message_format: Option<String>) -> (ExitStatus, Vec<Message>) {
let mut command = if cmd.should_compile() { let mut command = make_cargo_command(cmd, &message_format);
make_cargo_build_command(cmd, &message_format)
} else {
make_cargo_generic_command(cmd)
};
let mut process = command.spawn().unwrap(); let mut process = command.spawn().unwrap();
let command_stdout = process.stdout.take().unwrap(); let command_stdout = process.stdout.take().unwrap();
@ -58,98 +54,37 @@ pub fn run_cargo(cmd: &CargoCmd, message_format: Option<String>) -> (ExitStatus,
(process.wait().unwrap(), messages) (process.wait().unwrap(), messages)
} }
/// Create a cargo command used for building based on the context. /// Create a cargo command based on the context.
/// If there is no pre-built std detected in the sysroot, `build-std` is used. ///
pub fn make_cargo_build_command(cmd: &CargoCmd, message_format: &Option<String>) -> Command { /// For "build" commands (which compile code, such as `cargo 3ds build` or `cargo 3ds clippy`),
/// if there is no pre-built std detected in the sysroot, `build-std` will be used instead.
pub fn make_cargo_command(cmd: &CargoCmd, message_format: &Option<String>) -> Command {
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let sysroot = find_sysroot();
let mut command = Command::new(cargo); let mut command = Command::new(cargo);
let cmd_str = match cmd { command.arg(cmd.subcommand_name());
CargoCmd::Build(_) | CargoCmd::Run(_) => "build",
CargoCmd::Test(_) => "test", // Any command that needs to compile code will run under this environment.
CargoCmd::Passthrough(cmd) => &cmd[0], // Even `clippy` and `check` need this kind of context, so we'll just assume any other `Passthrough` command uses it too.
_ => panic!("tried to build an executable using an unsupported cargo subcommand"), if cmd.should_compile() {
}; command
.arg("--target")
command .arg("armv6k-nintendo-3ds")
.arg(cmd_str) .arg("--message-format")
.arg("--target") .arg(
.arg("armv6k-nintendo-3ds") message_format
.arg("--message-format") .as_deref()
.arg( .unwrap_or(CargoCmd::DEFAULT_MESSAGE_FORMAT),
message_format );
.as_deref()
.unwrap_or(CargoCmd::DEFAULT_MESSAGE_FORMAT), let sysroot = find_sysroot();
); if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() {
eprintln!("No pre-build std found, using build-std");
if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() { command.arg("-Z").arg("build-std");
eprintln!("No pre-built std found, using build-std");
command.arg("-Z").arg("build-std");
}
let cargo_args = match cmd {
CargoCmd::Build(build) => build.cargo_args.cargo_args(),
CargoCmd::Run(run) => run.build_args.cargo_args.cargo_args(),
CargoCmd::Test(test) => {
// 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 {
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");
}
test.run_args.cargo_args.cargo_args()
} }
CargoCmd::Passthrough(other) => &other[1..], }
_ => panic!("tried to build an executable using an unsupported cargo subcommand"),
};
command
.args(cargo_args)
.stdout(Stdio::piped())
.stdin(Stdio::inherit())
.stderr(Stdio::inherit());
command
}
/// Create a cargo command used for generic purposes based on the context.
pub fn make_cargo_generic_command(cmd: &CargoCmd) -> Command {
let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string());
let mut command = Command::new(cargo);
let cmd_str = match cmd {
CargoCmd::New(_) => "new",
_ => panic!("tried to run an unsupported generic cargo subcommand"),
};
command.arg(cmd_str);
let cargo_args = match cmd {
CargoCmd::New(new) => {
println!("{}", new.path);
command.arg(&new.path);
new.cargo_args.cargo_args() let cargo_args = cmd.cargo_args();
}
_ => panic!("tried to build an executable using an unsupported cargo subcommand"),
};
command command
.args(cargo_args) .args(cargo_args)

Loading…
Cancel
Save