From cc03d78ba5c38116cd77f463ea7ccec066144c93 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sat, 16 Jul 2022 00:33:03 -0400 Subject: [PATCH 01/16] Initial use of clap --- .gitignore | 3 +- Cargo.toml | 4 +- src/commands/mod.rs | 39 ++++ src/lib.rs | 348 +++++++++++++++++++++++++++++ src/main.rs | 527 ++++---------------------------------------- 5 files changed, 439 insertions(+), 482 deletions(-) create mode 100644 src/commands/mod.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore index f2f9e58..2da6cde 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target -Cargo.lock \ No newline at end of file +Cargo.lock +.idea \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index 7ac54bd..fbe3b4a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" cargo_metadata = "0.14.0" rustc_version = "0.4.0" semver = "1.0.10" -serde = "1.0.111" -serde_derive = "1.0.111" +serde = {version = "1.0.139", features = ['derive']} tee = "0.1.0" toml = "0.5.6" +clap = { version = "3.2.12", features = ["derive"]} \ No newline at end of file diff --git a/src/commands/mod.rs b/src/commands/mod.rs new file mode 100644 index 0000000..df56fed --- /dev/null +++ b/src/commands/mod.rs @@ -0,0 +1,39 @@ +use clap::{ArgEnum, Parser}; +use std::fmt::{Display, Formatter}; + +#[derive(Parser)] +pub struct Input { + #[clap(arg_enum)] + pub cmd: CargoCommand, + pub cargo_opts: Vec, +} + +#[derive(ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +pub enum CargoCommand { + Build, + Run, + Test, + Check, + Clippy, +} + +impl CargoCommand { + pub fn should_build_3dsx(&self) -> bool { + matches!( + self, + CargoCommand::Build | CargoCommand::Run | CargoCommand::Test + ) + } +} + +impl Display for CargoCommand { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + CargoCommand::Build => write!(f, "build"), + CargoCommand::Run => write!(f, "run"), + CargoCommand::Test => write!(f, "test"), + CargoCommand::Check => write!(f, "check"), + CargoCommand::Clippy => write!(f, "clippy"), + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f3a4cd3 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,348 @@ +extern crate core; + +pub mod commands; + +use crate::commands::CargoCommand; +use cargo_metadata::{Message, MetadataCommand}; +use core::fmt; +use rustc_version::Channel; +use semver::Version; +use serde::Deserialize; +use std::io::{BufRead, BufReader}; +use std::path::{Path, PathBuf}; +use std::process::{Command, ExitStatus, Stdio}; +use std::{env, io, process}; +use tee::TeeReader; + +pub fn build_elf( + cmd: CargoCommand, + message_format: &str, + args: &Vec, +) -> (ExitStatus, Vec) { + let mut command = make_cargo_build_command(cmd, message_format, args); + let mut process = command.spawn().unwrap(); + let command_stdout = process.stdout.take().unwrap(); + + let mut tee_reader; + let mut stdout_reader; + let buf_reader: &mut dyn BufRead = if message_format == "json-render-diagnostics" { + stdout_reader = BufReader::new(command_stdout); + &mut stdout_reader + } else { + tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); + &mut tee_reader + }; + + let messages = Message::parse_stream(buf_reader) + .collect::>() + .unwrap(); + + (process.wait().unwrap(), messages) +} + +fn make_cargo_build_command( + cmd: CargoCommand, + message_format: &str, + args: &Vec, +) -> Command { + let rust_flags = env::var("RUSTFLAGS").unwrap_or_default() + + &format!( + " -L{}/libctru/lib -lctru", + env::var("DEVKITPRO").expect("DEVKITPRO is not defined as an environment variable") + ); + let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); + let sysroot = find_sysroot(); + let mut command = Command::new(cargo); + + if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() { + eprintln!("No pre-build std found, using build-std"); + command.arg("-Z").arg("build-std"); + } + + command + .env("RUSTFLAGS", rust_flags) + .arg(&cmd.to_string()) + .arg("--target") + .arg("armv6k-nintendo-3ds") + .arg("--message-format") + .arg(message_format) + .args(args) + .stdout(Stdio::piped()) + .stdin(Stdio::inherit()) + .stderr(Stdio::inherit()); + + command +} + +fn find_sysroot() -> PathBuf { + let sysroot = env::var("SYSROOT").ok().unwrap_or_else(|| { + let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); + + let output = Command::new(&rustc) + .arg("--print") + .arg("sysroot") + .output() + .unwrap_or_else(|_| panic!("Failed to run `{rustc} -- print sysroot`")); + String::from_utf8(output.stdout).expect("Failed to parse sysroot path into a UTF-8 string") + }); + + PathBuf::from(sysroot.trim()) +} + +pub fn check_rust_version() { + let rustc_version = rustc_version::version_meta().unwrap(); + + if rustc_version.channel > Channel::Nightly { + eprintln!("cargo-3ds requires a nightly rustc version."); + eprintln!( + "Please run `rustup override set nightly` to use nightly in the \ + current directory." + ); + process::exit(1); + } + + let old_version = MINIMUM_RUSTC_VERSION + > Version { + // Remove `-nightly` pre-release tag for comparison. + pre: semver::Prerelease::EMPTY, + ..rustc_version.semver.clone() + }; + + let old_commit = match rustc_version.commit_date { + None => false, + Some(date) => { + MINIMUM_COMMIT_DATE + > CommitDate::parse(&date).expect("could not parse `rustc --version` commit date") + } + }; + + if old_version || old_commit { + eprintln!( + "cargo-3ds requires rustc nightly version >= {}", + MINIMUM_COMMIT_DATE, + ); + eprintln!("Please run `rustup update nightly` to upgrade your nightly version"); + + process::exit(1); + } +} + +pub fn get_metadata(messages: &[Message]) -> CTRConfig { + let metadata = MetadataCommand::new() + .exec() + .expect("Failed to get cargo metadata"); + + let mut package = None; + let mut artifact = None; + + // Extract the final built executable. We may want to fail in cases where + // multiple executables, or none, were built? + for message in messages.iter().rev() { + if let Message::CompilerArtifact(art) = message { + if art.executable.is_some() { + package = Some(metadata[&art.package_id].clone()); + artifact = Some(art.clone()); + + break; + } + } + } + if package.is_none() || artifact.is_none() { + eprintln!("No executable found from build command output!"); + process::exit(1); + } + + let (package, artifact) = (package.unwrap(), artifact.unwrap()); + + let mut icon = String::from("./icon.png"); + + if !Path::new(&icon).exists() { + icon = format!( + "{}/libctru/default_icon.png", + env::var("DEVKITPRO").unwrap() + ); + } + + // for now assume a single "kind" since we only support one output artifact + let name = match artifact.target.kind[0].as_ref() { + "bin" | "lib" | "rlib" | "dylib" if artifact.target.test => { + format!("{} tests", artifact.target.name) + } + "example" => { + format!("{} - {} example", artifact.target.name, package.name) + } + _ => artifact.target.name, + }; + + let author = match package.authors.as_slice() { + [name, ..] => name.to_owned(), + [] => String::from("Unspecified Author"), // as standard with the devkitPRO toolchain + }; + + CTRConfig { + name, + author, + description: package + .description + .clone() + .unwrap_or_else(|| String::from("Homebrew Application")), + icon, + target_path: artifact.executable.unwrap().into(), + cargo_manifest_path: package.manifest_path.into(), + } +} + +pub fn build_smdh(config: &CTRConfig) { + let mut process = Command::new("smdhtool") + .arg("--create") + .arg(&config.name) + .arg(&config.description) + .arg(&config.author) + .arg(&config.icon) + .arg(config.path_smdh()) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn() + .expect("smdhtool command failed, most likely due to 'smdhtool' not being in $PATH"); + + let status = process.wait().unwrap(); + + if !status.success() { + process::exit(status.code().unwrap_or(1)); + } +} + +pub fn build_3dsx(config: &CTRConfig) { + let mut command = Command::new("3dsxtool"); + let mut process = command + .arg(&config.target_path) + .arg(config.path_3dsx()) + .arg(format!("--smdh={}", config.path_smdh().to_string_lossy())); + + // If romfs directory exists, automatically include it + let (romfs_path, is_default_romfs) = get_romfs_path(config); + if romfs_path.is_dir() { + println!("Adding RomFS from {}", romfs_path.display()); + process = process.arg(format!("--romfs={}", romfs_path.to_string_lossy())); + } else if !is_default_romfs { + eprintln!( + "Could not find configured RomFS dir: {}", + romfs_path.display() + ); + process::exit(1); + } + + let mut process = process + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn() + .expect("3dsxtool command failed, most likely due to '3dsxtool' not being in $PATH"); + + let status = process.wait().unwrap(); + + if !status.success() { + process::exit(status.code().unwrap_or(1)); + } +} + +pub fn link(config: &CTRConfig) { + let mut process = Command::new("3dslink") + .arg(config.path_3dsx()) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()) + .spawn() + .unwrap(); + + let status = process.wait().unwrap(); + + if !status.success() { + process::exit(status.code().unwrap_or(1)); + } +} + +/// Read the `RomFS` path from the Cargo manifest. If it's unset, use the default. +/// The returned boolean is true when the default is used. +pub fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) { + let manifest_path = &config.cargo_manifest_path; + let manifest_str = std::fs::read_to_string(manifest_path) + .unwrap_or_else(|e| panic!("Could not open {}: {e}", manifest_path.display())); + let manifest_data: toml::Value = + toml::de::from_str(&manifest_str).expect("Could not parse Cargo manifest as TOML"); + + // Find the romfs setting and compute the path + let mut is_default = false; + let romfs_dir_setting = manifest_data + .as_table() + .and_then(|table| table.get("package")) + .and_then(toml::Value::as_table) + .and_then(|table| table.get("metadata")) + .and_then(toml::Value::as_table) + .and_then(|table| table.get("cargo-3ds")) + .and_then(toml::Value::as_table) + .and_then(|table| table.get("romfs_dir")) + .and_then(toml::Value::as_str) + .unwrap_or_else(|| { + is_default = true; + "romfs" + }); + let mut romfs_path = manifest_path.clone(); + romfs_path.pop(); // Pop Cargo.toml + romfs_path.push(romfs_dir_setting); + + (romfs_path, is_default) +} + +#[derive(Deserialize, Default)] +pub struct CTRConfig { + name: String, + author: String, + description: String, + icon: String, + target_path: PathBuf, + cargo_manifest_path: PathBuf, +} + +impl CTRConfig { + pub fn path_3dsx(&self) -> PathBuf { + self.target_path.with_extension("3dsx") + } + + pub fn path_smdh(&self) -> PathBuf { + self.target_path.with_extension("smdh") + } +} + +#[derive(Ord, PartialOrd, PartialEq, Eq, Debug)] +pub struct CommitDate { + year: i32, + month: i32, + day: i32, +} + +impl CommitDate { + fn parse(date: &str) -> Option { + let mut iter = date.split('-'); + + let year = iter.next()?.parse().ok()?; + let month = iter.next()?.parse().ok()?; + let day = iter.next()?.parse().ok()?; + + Some(Self { year, month, day }) + } +} + +impl fmt::Display for CommitDate { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day) + } +} + +const MINIMUM_COMMIT_DATE: CommitDate = CommitDate { + year: 2022, + month: 6, + day: 15, +}; +const MINIMUM_RUSTC_VERSION: Version = Version::new(1, 63, 0); diff --git a/src/main.rs b/src/main.rs index 6d28c46..e3c480d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,501 +1,70 @@ -use cargo_metadata::{Message, MetadataCommand}; -use rustc_version::{Channel, Version}; -use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; -use std::process::ExitStatus; -use std::{ - env, fmt, io, - process::{self, Command, Stdio}, -}; -use tee::TeeReader; - -#[derive(serde_derive::Deserialize, Default)] -struct CTRConfig { - name: String, - author: String, - description: String, - icon: String, - target_path: PathBuf, - cargo_manifest_path: PathBuf, -} - -impl CTRConfig { - fn path_3dsx(&self) -> PathBuf { - self.target_path.with_extension("3dsx") - } - - fn path_smdh(&self) -> PathBuf { - self.target_path.with_extension("smdh") - } -} - -#[derive(Ord, PartialOrd, PartialEq, Eq, Debug)] -struct CommitDate { - year: i32, - month: i32, - day: i32, -} - -impl CommitDate { - fn parse(date: &str) -> Option { - let mut iter = date.split('-'); - - let year = iter.next()?.parse().ok()?; - let month = iter.next()?.parse().ok()?; - let day = iter.next()?.parse().ok()?; - - Some(Self { year, month, day }) - } -} - -impl fmt::Display for CommitDate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day) - } -} - -const MINIMUM_COMMIT_DATE: CommitDate = CommitDate { - year: 2022, - month: 6, - day: 15, -}; -const MINIMUM_RUSTC_VERSION: Version = Version::new(1, 63, 0); +use cargo_3ds::commands::{CargoCommand, Input}; +use cargo_3ds::{build_3dsx, build_elf, build_smdh, check_rust_version, get_metadata, link}; +use clap::Parser; +use std::process; fn main() { check_rust_version(); - if env::args().any(|arg| arg == "--help" || arg == "-h") { - print_usage(&mut io::stdout()); - return; - } - - // Get the command and collect the remaining arguments - let cargo_command = CargoCommand::from_args().unwrap_or_else(|| { - print_usage(&mut io::stderr()); - process::exit(2) - }); - - eprintln!("Running Cargo"); - let (status, messages) = cargo_command.build_elf(); - if !status.success() { - process::exit(status.code().unwrap_or(1)); - } - - if !cargo_command.should_build_3dsx() { - return; - } - - eprintln!("Getting metadata"); - let app_conf = get_metadata(&messages); - - eprintln!("Building smdh:{}", app_conf.path_smdh().display()); - build_smdh(&app_conf); - - eprintln!("Building 3dsx: {}", app_conf.path_3dsx().display()); - build_3dsx(&app_conf); - - if cargo_command.should_link { - eprintln!("Running 3dslink"); - link(&app_conf); - } -} - -struct CargoCommand { - command: String, - should_link: bool, - args: Vec, - message_format: String, -} - -impl CargoCommand { - const DEFAULT_MESSAGE_FORMAT: &'static str = "json-render-diagnostics"; - - fn from_args() -> Option { - // Skip `cargo 3ds`. `cargo-3ds` isn't supported for now - let mut args = env::args().skip(2); - - let command = args.next()?; - let mut remaining_args: Vec = args.collect(); - - let (command, should_link) = match command.as_str() { - "run" => ("build".to_string(), true), - "test" => { - let no_run = String::from("--no-run"); - - if remaining_args.contains(&no_run) { - (command, false) - } else { - remaining_args.push(no_run); - (command, true) - } - } - _ => (command, false), - }; - - let message_format = match Self::extract_message_format(&mut remaining_args) { - Some(format) => { - if !format.starts_with("json") { - eprintln!("error: non-JSON `message-format` is not supported"); - process::exit(1); - } - format - } - None => Self::DEFAULT_MESSAGE_FORMAT.to_string(), - }; - - Some(Self { - command, - should_link, - args: remaining_args, - message_format, - }) - } - - fn extract_message_format(args: &mut Vec) -> Option { - for (i, arg) in args.iter().enumerate() { - if arg.starts_with("--message-format") { - return { - let arg = args.remove(i); - - if let Some((_, format)) = arg.split_once('=') { - Some(format.to_string()) - } else { - Some(args.remove(i)) - } - }; - } - } - - None - } - - fn build_elf(&self) -> (ExitStatus, Vec) { - let mut command = self.make_cargo_build_command(); - let mut process = command.spawn().unwrap(); - let command_stdout = process.stdout.take().unwrap(); - - let mut tee_reader; - let mut stdout_reader; - - let buf_reader: &mut dyn BufRead = if self.message_format == Self::DEFAULT_MESSAGE_FORMAT { - stdout_reader = BufReader::new(command_stdout); - &mut stdout_reader + let mut input: Input = Input::parse(); + + let should_link = input.cmd == CargoCommand::Build + || (input.cmd == CargoCommand::Test + && if input.cargo_opts.contains(&"--no-run".to_string()) { + false + } else { + input.cargo_opts.push("--no-run".to_string()); + true + }); + + let message_format = if let Some(pos) = input + .cargo_opts + .iter() + .position(|s| s.starts_with("--message-format")) + { + input.cargo_opts.remove(pos); + let format = if let Some((_, format)) = input + .cargo_opts + .get(pos) + .unwrap() + .to_string() + .split_once('=') + { + format.to_string() } 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 - // information out of it that we need for 3dsxtool etc. - tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); - &mut tee_reader + input.cargo_opts.remove(pos).to_string() }; - - let messages = Message::parse_stream(buf_reader) - .collect::>() - .unwrap(); - - (process.wait().unwrap(), messages) - } - - /// Create the cargo build command, but don't execute it. - /// If there is no pre-built std detected in the sysroot, `build-std` is used. - fn make_cargo_build_command(&self) -> Command { - let rustflags = env::var("RUSTFLAGS").unwrap_or_default() - + &format!(" -L{}/libctru/lib -lctru", env::var("DEVKITPRO").expect("DEVKITPRO is not defined as an environment variable")); - let cargo = env::var("CARGO").unwrap_or_else(|_| "cargo".to_string()); - let sysroot = Self::find_sysroot(); - let mut command = Command::new(cargo); - - if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() { - eprintln!("No pre-built std found, using build-std"); - command.arg("-Z").arg("build-std"); - } - - command - .env("RUSTFLAGS", rustflags) - .arg(&self.command) - .arg("--target") - .arg("armv6k-nintendo-3ds") - .arg("--message-format") - .arg(&self.message_format) - .args(&self.args) - .stdout(Stdio::piped()) - .stdin(Stdio::inherit()) - .stderr(Stdio::inherit()); - - command - } - - /// Get the compiler's sysroot path - fn find_sysroot() -> PathBuf { - let sysroot = env::var("SYSROOT").ok().unwrap_or_else(|| { - // Get sysroot from rustc - let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); - - let output = Command::new(&rustc) - .arg("--print") - .arg("sysroot") - .output() - .unwrap_or_else(|_| panic!("Failed to run `{rustc} --print sysroot`")); - - String::from_utf8(output.stdout) - .expect("Failed to parse sysroot path into a UTF-8 string") - }); - - PathBuf::from(sysroot.trim()) - } - - fn should_build_3dsx(&self) -> bool { - matches!(self.command.as_str(), "build" | "run" | "test") - } -} - -fn print_usage(f: &mut impl io::Write) { - let invocation = { - let mut args = env::args(); - - // We do this to properly display `cargo-3ds` if invoked that way - let bin = args.next().unwrap(); - if let Some("3ds") = args.next().as_deref() { - "cargo 3ds".to_string() + if !format.starts_with("json") { + eprintln!("error: non-JSON `message-format` is not supported"); + process::exit(1); } else { - bin - } - }; - - writeln!( - f, - "{name}: {description}. - -Usage: - {invocation} build [CARGO_OPTS...] - {invocation} run [CARGO_OPTS...] - {invocation} test [CARGO_OPTS...] - {invocation} [CARGO_OPTS...] - {invocation} -h | --help - -Commands: - build build a 3dsx executable. - run build a 3dsx executable and send it to a device with 3dslink. - test build a 3dsx executable from unit/integration tests and send it to a device. - execute some other Cargo command with 3ds options configured (ex. check or clippy). - -Options: - -h --help Show this screen. - -Additional arguments will be passed through to ``. Some that are supported include: - - [build | run | test] --release - test --no-run - -Other flags may work, but haven't been tested. -", - name = env!("CARGO_BIN_NAME"), - description = env!("CARGO_PKG_DESCRIPTION"), - invocation = invocation, - ) - .unwrap(); -} - -fn check_rust_version() { - let rustc_version = rustc_version::version_meta().unwrap(); - - if rustc_version.channel > Channel::Nightly { - eprintln!("cargo-3ds requires a nightly rustc version."); - eprintln!( - "Please run `rustup override set nightly` to use nightly in the \ - current directory." - ); - process::exit(1); - } - - let old_version = MINIMUM_RUSTC_VERSION > Version { - // Remove `-nightly` pre-release tag for comparison. - pre: semver::Prerelease::EMPTY, - ..rustc_version.semver.clone() - }; - - let old_commit = match rustc_version.commit_date { - None => false, - Some(date) => { - MINIMUM_COMMIT_DATE - > CommitDate::parse(&date).expect("could not parse `rustc --version` commit date") - } - }; - - if old_version || old_commit { - eprintln!( - "cargo-3ds requires rustc nightly version >= {}", - MINIMUM_COMMIT_DATE, - ); - eprintln!("Please run `rustup update nightly` to upgrade your nightly version"); - - process::exit(1); - } -} - -fn get_metadata(messages: &[Message]) -> CTRConfig { - let metadata = MetadataCommand::new() - .exec() - .expect("Failed to get cargo metadata"); - - let mut package = None; - let mut artifact = None; - - // Extract the final built executable. We may want to fail in cases where - // multiple executables, or none, were built? - for message in messages.iter().rev() { - if let Message::CompilerArtifact(art) = message { - if art.executable.is_some() { - package = Some(metadata[&art.package_id].clone()); - artifact = Some(art.clone()); - - break; - } + format } - } - if package.is_none() || artifact.is_none() { - eprintln!("No executable found from build command output!"); - process::exit(1); - } - - let (package, artifact) = (package.unwrap(), artifact.unwrap()); - - let mut icon = String::from("./icon.png"); - - if !Path::new(&icon).exists() { - icon = format!( - "{}/libctru/default_icon.png", - env::var("DEVKITPRO").unwrap() - ); - } - - // for now assume a single "kind" since we only support one output artifact - let name = match artifact.target.kind[0].as_ref() { - "bin" | "lib" | "rlib" | "dylib" if artifact.target.test => { - format!("{} tests", artifact.target.name) - } - "example" => { - format!("{} - {} example", artifact.target.name, package.name) - } - _ => artifact.target.name, + } else { + "json-render-diagnostics".to_string() }; - let author = match package.authors.as_slice() { - [name, ..] => name.to_owned(), - [] => String::from("Unspecified Author"), // as standard with the devkitPRO toolchain - }; - - CTRConfig { - name, - author, - description: package - .description - .clone() - .unwrap_or_else(|| String::from("Homebrew Application")), - icon, - target_path: artifact.executable.unwrap().into(), - cargo_manifest_path: package.manifest_path.into(), - } -} - -fn build_smdh(config: &CTRConfig) { - let mut process = Command::new("smdhtool") - .arg("--create") - .arg(&config.name) - .arg(&config.description) - .arg(&config.author) - .arg(&config.icon) - .arg(config.path_smdh()) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn() - .expect("smdhtool command failed, most likely due to 'smdhtool' not being in $PATH"); - - let status = process.wait().unwrap(); + let (status, messages) = build_elf(input.cmd, &message_format, &input.cargo_opts); if !status.success() { process::exit(status.code().unwrap_or(1)); } -} - -fn build_3dsx(config: &CTRConfig) { - let mut command = Command::new("3dsxtool"); - let mut process = command - .arg(&config.target_path) - .arg(config.path_3dsx()) - .arg(format!("--smdh={}", config.path_smdh().to_string_lossy())); - // If romfs directory exists, automatically include it - let (romfs_path, is_default_romfs) = get_romfs_path(config); - if romfs_path.is_dir() { - eprintln!("Adding RomFS from {}", romfs_path.display()); - process = process.arg(format!("--romfs={}", romfs_path.to_string_lossy())); - } else if !is_default_romfs { - eprintln!( - "Could not find configured RomFS dir: {}", - romfs_path.display() - ); - process::exit(1); + if !input.cmd.should_build_3dsx() { + return; } - let mut process = process - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn() - .expect("3dsxtool command failed, most likely due to '3dsxtool' not being in $PATH"); - - let status = process.wait().unwrap(); - - if !status.success() { - process::exit(status.code().unwrap_or(1)); - } -} + println!("Getting metadata"); + let app_conf = get_metadata(&messages); -fn link(config: &CTRConfig) { - let mut process = Command::new("3dslink") - .arg(config.path_3dsx()) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()) - .spawn() - .unwrap(); + println!("Building smdh:{}", app_conf.path_smdh().display()); + build_smdh(&app_conf); - let status = process.wait().unwrap(); + println!("Building 3dsx: {}", app_conf.path_3dsx().display()); + build_3dsx(&app_conf); - if !status.success() { - process::exit(status.code().unwrap_or(1)); + if should_link { + println!("Running 3dslink"); + link(&app_conf); } } - -/// Read the `RomFS` path from the Cargo manifest. If it's unset, use the default. -/// The returned boolean is true when the default is used. -fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) { - let manifest_path = &config.cargo_manifest_path; - let manifest_str = std::fs::read_to_string(manifest_path) - .unwrap_or_else(|e| panic!("Could not open {}: {e}", manifest_path.display())); - let manifest_data: toml::Value = - toml::de::from_str(&manifest_str).expect("Could not parse Cargo manifest as TOML"); - - // Find the romfs setting and compute the path - let mut is_default = false; - let romfs_dir_setting = manifest_data - .as_table() - .and_then(|table| table.get("package")) - .and_then(toml::Value::as_table) - .and_then(|table| table.get("metadata")) - .and_then(toml::Value::as_table) - .and_then(|table| table.get("cargo-3ds")) - .and_then(toml::Value::as_table) - .and_then(|table| table.get("romfs_dir")) - .and_then(toml::Value::as_str) - .unwrap_or_else(|| { - is_default = true; - "romfs" - }); - let mut romfs_path = manifest_path.clone(); - romfs_path.pop(); // Pop Cargo.toml - romfs_path.push(romfs_dir_setting); - - (romfs_path, is_default) -} From 3cb1169dcb225aeeae09c6af198962a4b2e7785f Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sat, 16 Jul 2022 01:02:42 -0400 Subject: [PATCH 02/16] Fix message format parsing --- src/main.rs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/main.rs b/src/main.rs index e3c480d..5ca3595 100644 --- a/src/main.rs +++ b/src/main.rs @@ -22,17 +22,12 @@ fn main() { .iter() .position(|s| s.starts_with("--message-format")) { - input.cargo_opts.remove(pos); - let format = if let Some((_, format)) = input - .cargo_opts - .get(pos) - .unwrap() - .to_string() - .split_once('=') + let arg = input.cargo_opts.remove(pos); + let format = if let Some((_, format)) = arg.split_once('=') { format.to_string() } else { - input.cargo_opts.remove(pos).to_string() + arg.to_string() }; if !format.starts_with("json") { eprintln!("error: non-JSON `message-format` is not supported"); From 11855472df45ab8cd86b4fa7e9f116093c77b5e4 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sat, 16 Jul 2022 01:04:18 -0400 Subject: [PATCH 03/16] Fix another indexing issue --- src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main.rs b/src/main.rs index 5ca3595..4742229 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,7 +27,7 @@ fn main() { { format.to_string() } else { - arg.to_string() + input.cargo_opts.remove(pos).to_string() }; if !format.starts_with("json") { eprintln!("error: non-JSON `message-format` is not supported"); From f05b57f5ac978b458318b334c811e542e356b8a5 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sat, 16 Jul 2022 12:06:09 -0400 Subject: [PATCH 04/16] Use ValueEnum over deprecated ArgEnum --- src/commands/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index df56fed..4c1af8e 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,14 +1,14 @@ -use clap::{ArgEnum, Parser}; +use clap::{ValueEnum, Parser}; use std::fmt::{Display, Formatter}; #[derive(Parser)] pub struct Input { - #[clap(arg_enum)] + #[clap(value_enum)] pub cmd: CargoCommand, pub cargo_opts: Vec, } -#[derive(ArgEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] +#[derive(ValueEnum, Copy, Clone, Ord, PartialOrd, Eq, PartialEq)] pub enum CargoCommand { Build, Run, From 51917f8fcffe081c20214b145bb5a50707428c74 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sun, 17 Jul 2022 10:34:46 -0400 Subject: [PATCH 05/16] Add AllowLeadingHyphen --- src/commands/mod.rs | 3 ++- src/lib.rs | 2 ++ src/main.rs | 5 ++--- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 4c1af8e..13d95f0 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,7 +1,8 @@ -use clap::{ValueEnum, Parser}; +use clap::{ValueEnum, Parser, AppSettings}; use std::fmt::{Display, Formatter}; #[derive(Parser)] +#[clap(global_setting(AppSettings::AllowLeadingHyphen))] pub struct Input { #[clap(value_enum)] pub cmd: CargoCommand, diff --git a/src/lib.rs b/src/lib.rs index f3a4cd3..0046f5a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -59,6 +59,8 @@ fn make_cargo_build_command( command.arg("-Z").arg("build-std"); } + println!("{:?}", args); + command .env("RUSTFLAGS", rust_flags) .arg(&cmd.to_string()) diff --git a/src/main.rs b/src/main.rs index 4742229..415b5a0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ use cargo_3ds::commands::{CargoCommand, Input}; use cargo_3ds::{build_3dsx, build_elf, build_smdh, check_rust_version, get_metadata, link}; -use clap::Parser; +use clap::{Parser}; use std::process; fn main() { @@ -17,8 +17,7 @@ fn main() { true }); - let message_format = if let Some(pos) = input - .cargo_opts + let message_format = if let Some(pos) = input.cargo_opts .iter() .position(|s| s.starts_with("--message-format")) { From 239556abf8dc32e9bd17207f97e28e5ab525561c Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sun, 17 Jul 2022 22:56:55 -0400 Subject: [PATCH 06/16] Revert to original cargo usage Re-add TeeReader comment Remove debugging line --- src/commands/mod.rs | 11 ++++++++++- src/lib.rs | 5 +++-- src/main.rs | 14 +++++++------- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/commands/mod.rs b/src/commands/mod.rs index 13d95f0..09bc894 100644 --- a/src/commands/mod.rs +++ b/src/commands/mod.rs @@ -1,7 +1,16 @@ -use clap::{ValueEnum, Parser, AppSettings}; +use clap::{AppSettings, Parser, ValueEnum}; use std::fmt::{Display, Formatter}; #[derive(Parser)] +#[clap(name = "cargo")] +#[clap(bin_name = "cargo")] +pub enum Cargo { + #[clap(name = "3ds")] + Input(Input), +} + +#[derive(clap::Args)] +#[clap(about)] #[clap(global_setting(AppSettings::AllowLeadingHyphen))] pub struct Input { #[clap(value_enum)] diff --git a/src/lib.rs b/src/lib.rs index 0046f5a..d2279ef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -29,6 +29,9 @@ pub fn build_elf( stdout_reader = BufReader::new(command_stdout); &mut stdout_reader } 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 + // information out of it that we need for 3dsxtool etc. tee_reader = BufReader::new(TeeReader::new(command_stdout, io::stdout())); &mut tee_reader }; @@ -59,8 +62,6 @@ fn make_cargo_build_command( command.arg("-Z").arg("build-std"); } - println!("{:?}", args); - command .env("RUSTFLAGS", rust_flags) .arg(&cmd.to_string()) diff --git a/src/main.rs b/src/main.rs index 415b5a0..6897aaa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,12 +1,12 @@ -use cargo_3ds::commands::{CargoCommand, Input}; +use cargo_3ds::commands::{Cargo, CargoCommand}; use cargo_3ds::{build_3dsx, build_elf, build_smdh, check_rust_version, get_metadata, link}; -use clap::{Parser}; +use clap::Parser; use std::process; fn main() { check_rust_version(); - let mut input: Input = Input::parse(); + let Cargo::Input(mut input) = Cargo::parse(); let should_link = input.cmd == CargoCommand::Build || (input.cmd == CargoCommand::Test @@ -17,16 +17,16 @@ fn main() { true }); - let message_format = if let Some(pos) = input.cargo_opts + let message_format = if let Some(pos) = input + .cargo_opts .iter() .position(|s| s.starts_with("--message-format")) { let arg = input.cargo_opts.remove(pos); - let format = if let Some((_, format)) = arg.split_once('=') - { + let format = if let Some((_, format)) = arg.split_once('=') { format.to_string() } else { - input.cargo_opts.remove(pos).to_string() + input.cargo_opts.remove(pos) }; if !format.starts_with("json") { eprintln!("error: non-JSON `message-format` is not supported"); From 06bdf9f289f4b6eead6f24f9aebdb337af287865 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sun, 17 Jul 2022 23:08:45 -0400 Subject: [PATCH 07/16] Move should_link and message_format to separate functions --- src/lib.rs | 40 ++++++++++++++++++++++++++++++++++++++-- src/main.rs | 38 +++++++------------------------------- 2 files changed, 45 insertions(+), 33 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d2279ef..5ea8379 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ extern crate core; pub mod commands; -use crate::commands::CargoCommand; +use crate::commands::{CargoCommand, Input}; use cargo_metadata::{Message, MetadataCommand}; use core::fmt; use rustc_version::Channel; @@ -14,6 +14,42 @@ use std::process::{Command, ExitStatus, Stdio}; use std::{env, io, process}; use tee::TeeReader; +const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; + +pub fn get_should_link(input: &mut Input) -> bool { + input.cmd == CargoCommand::Build + || (input.cmd == CargoCommand::Test + && if input.cargo_opts.contains(&"--no-run".to_string()) { + false + } else { + input.cargo_opts.push("--no-run".to_string()); + true + }) +} + +pub fn get_message_format(input: &mut Input) -> String { + if let Some(pos) = input + .cargo_opts + .iter() + .position(|s| s.starts_with("--message-format")) + { + let arg = input.cargo_opts.remove(pos); + let format = if let Some((_, format)) = arg.split_once('=') { + format.to_string() + } else { + input.cargo_opts.remove(pos) + }; + if !format.starts_with("json") { + eprintln!("error: non-JSON `message-format` is not supported"); + process::exit(1); + } else { + format + } + } else { + DEFAULT_MESSAGE_FORMAT.to_string() + } +} + pub fn build_elf( cmd: CargoCommand, message_format: &str, @@ -25,7 +61,7 @@ pub fn build_elf( let mut tee_reader; let mut stdout_reader; - let buf_reader: &mut dyn BufRead = if message_format == "json-render-diagnostics" { + let buf_reader: &mut dyn BufRead = if message_format == DEFAULT_MESSAGE_FORMAT { stdout_reader = BufReader::new(command_stdout); &mut stdout_reader } else { diff --git a/src/main.rs b/src/main.rs index 6897aaa..cda6c62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,8 @@ -use cargo_3ds::commands::{Cargo, CargoCommand}; -use cargo_3ds::{build_3dsx, build_elf, build_smdh, check_rust_version, get_metadata, link}; +use cargo_3ds::commands::Cargo; +use cargo_3ds::{ + build_3dsx, build_elf, build_smdh, check_rust_version, get_message_format, get_metadata, + get_should_link, link, +}; use clap::Parser; use std::process; @@ -8,35 +11,8 @@ fn main() { let Cargo::Input(mut input) = Cargo::parse(); - let should_link = input.cmd == CargoCommand::Build - || (input.cmd == CargoCommand::Test - && if input.cargo_opts.contains(&"--no-run".to_string()) { - false - } else { - input.cargo_opts.push("--no-run".to_string()); - true - }); - - let message_format = if let Some(pos) = input - .cargo_opts - .iter() - .position(|s| s.starts_with("--message-format")) - { - let arg = input.cargo_opts.remove(pos); - let format = if let Some((_, format)) = arg.split_once('=') { - format.to_string() - } else { - input.cargo_opts.remove(pos) - }; - if !format.starts_with("json") { - eprintln!("error: non-JSON `message-format` is not supported"); - process::exit(1); - } else { - format - } - } else { - "json-render-diagnostics".to_string() - }; + let should_link = get_should_link(&mut input); + let message_format = get_message_format(&mut input); let (status, messages) = build_elf(input.cmd, &message_format, &input.cargo_opts); From 8eac835c4cbc634ec1009cb3d7331cd3162f3287 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Mon, 18 Jul 2022 00:47:44 -0400 Subject: [PATCH 08/16] Add comments --- src/lib.rs | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5ea8379..392b47b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -16,7 +16,11 @@ use tee::TeeReader; const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; +/// Gets whether the executable should link the generated files to a 3ds +/// based on the parsed input. pub fn get_should_link(input: &mut Input) -> bool { + // When running compile only commands, don't link the executable to the 3ds. + // Otherwise, link and run on the 3ds but do not run locally. input.cmd == CargoCommand::Build || (input.cmd == CargoCommand::Test && if input.cargo_opts.contains(&"--no-run".to_string()) { @@ -27,18 +31,28 @@ pub fn get_should_link(input: &mut Input) -> bool { }) } +/// Extracts the user-defined message format and if there is none, +/// default to `json-render-diagnostics`. pub fn get_message_format(input: &mut Input) -> String { + // Checks for a position within the args where '--message-format' is located if let Some(pos) = input .cargo_opts .iter() .position(|s| s.starts_with("--message-format")) { + // Remove the arg from list let arg = input.cargo_opts.remove(pos); + + // Allows for usage of '--message-format=' and also using space separation. + // Check for a '=' delimiter and use the second half of the split as the format, + // otherwise remove next arg which is now at the same position as the original flag. let format = if let Some((_, format)) = arg.split_once('=') { format.to_string() } else { input.cargo_opts.remove(pos) }; + + // Non-json formats are not supported so the executable exits. if !format.starts_with("json") { eprintln!("error: non-JSON `message-format` is not supported"); process::exit(1); @@ -46,10 +60,14 @@ pub fn get_message_format(input: &mut Input) -> String { format } } else { + // Default to 'json-render-diagnostics' DEFAULT_MESSAGE_FORMAT.to_string() } } +/// Build the elf that will be used to create other 3ds files. +/// The command built from [`make_cargo_build_command`] is executed +/// and the messages from the spawned process are parsed and returned. pub fn build_elf( cmd: CargoCommand, message_format: &str, @@ -79,7 +97,9 @@ pub fn build_elf( (process.wait().unwrap(), messages) } -fn make_cargo_build_command( +/// Create the cargo build command, but don't execute it. +/// If there is no pre-built std detected in the sysroot, `build-std` is used. +pub fn make_cargo_build_command( cmd: CargoCommand, message_format: &str, args: &Vec, @@ -113,7 +133,8 @@ fn make_cargo_build_command( command } -fn find_sysroot() -> PathBuf { +/// Finds the sysroot path of the current toolchain +pub fn find_sysroot() -> PathBuf { let sysroot = env::var("SYSROOT").ok().unwrap_or_else(|| { let rustc = env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string()); @@ -128,6 +149,8 @@ fn find_sysroot() -> PathBuf { PathBuf::from(sysroot.trim()) } +/// Checks the current rust version and channel. +/// Exits if the minimum requirement is not met. pub fn check_rust_version() { let rustc_version = rustc_version::version_meta().unwrap(); @@ -166,6 +189,9 @@ pub fn check_rust_version() { } } +/// Parses messages returned by the executed cargo command from [`build_elf`]. +/// The returned [`CTRConfig`] is then used for further building in and execution +/// in [`build_smdh`], ['build_3dsx'], and [`link`]. pub fn get_metadata(messages: &[Message]) -> CTRConfig { let metadata = MetadataCommand::new() .exec() @@ -231,6 +257,8 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig { } } +/// Builds the smdh using `smdhtool`. +/// This will fail if `smdhtool` is not within the running directory or in a directory found in $PATH pub fn build_smdh(config: &CTRConfig) { let mut process = Command::new("smdhtool") .arg("--create") @@ -252,6 +280,8 @@ pub fn build_smdh(config: &CTRConfig) { } } +/// Builds the 3dsx using `3dsxtool`. +/// This will fail if `3dsxtool` is not within the running directory or in a directory found in $PATH pub fn build_3dsx(config: &CTRConfig) { let mut command = Command::new("3dsxtool"); let mut process = command @@ -286,6 +316,8 @@ pub fn build_3dsx(config: &CTRConfig) { } } +/// Link the generated 3dsx to a 3ds to execute and test using `3dslink`. +/// This will fail if `3dslink` is not within the running directory or in a directory found in $PATH pub fn link(config: &CTRConfig) { let mut process = Command::new("3dslink") .arg(config.path_3dsx()) From ef6a2f672470364292d241155bd9827cb9e3077e Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Mon, 18 Jul 2022 10:38:27 -0400 Subject: [PATCH 09/16] Revert println! to eprintln! --- src/lib.rs | 2 +- src/main.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 392b47b..732f745 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -292,7 +292,7 @@ pub fn build_3dsx(config: &CTRConfig) { // If romfs directory exists, automatically include it let (romfs_path, is_default_romfs) = get_romfs_path(config); if romfs_path.is_dir() { - println!("Adding RomFS from {}", romfs_path.display()); + eprintln!("Adding RomFS from {}", romfs_path.display()); process = process.arg(format!("--romfs={}", romfs_path.to_string_lossy())); } else if !is_default_romfs { eprintln!( diff --git a/src/main.rs b/src/main.rs index cda6c62..2df44b9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -24,17 +24,17 @@ fn main() { return; } - println!("Getting metadata"); + eprintln!("Getting metadata"); let app_conf = get_metadata(&messages); - println!("Building smdh:{}", app_conf.path_smdh().display()); + eprintln!("Building smdh:{}", app_conf.path_smdh().display()); build_smdh(&app_conf); - println!("Building 3dsx: {}", app_conf.path_3dsx().display()); + eprintln!("Building 3dsx: {}", app_conf.path_3dsx().display()); build_3dsx(&app_conf); if should_link { - println!("Running 3dslink"); + eprintln!("Running 3dslink"); link(&app_conf); } } From 4c5929715cfe509863c1b18eecf4b7caf3e352c5 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Wed, 20 Jul 2022 11:11:48 -0400 Subject: [PATCH 10/16] Move command struct and parser to command.rs --- src/{commands/mod.rs => command.rs} | 0 src/lib.rs | 4 ++-- src/main.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) rename src/{commands/mod.rs => command.rs} (100%) diff --git a/src/commands/mod.rs b/src/command.rs similarity index 100% rename from src/commands/mod.rs rename to src/command.rs diff --git a/src/lib.rs b/src/lib.rs index 732f745..50b084b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ extern crate core; -pub mod commands; +pub mod command; -use crate::commands::{CargoCommand, Input}; +use crate::command::{CargoCommand, Input}; use cargo_metadata::{Message, MetadataCommand}; use core::fmt; use rustc_version::Channel; diff --git a/src/main.rs b/src/main.rs index 2df44b9..3edd05a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -use cargo_3ds::commands::Cargo; +use cargo_3ds::command::Cargo; use cargo_3ds::{ build_3dsx, build_elf, build_smdh, check_rust_version, get_message_format, get_metadata, get_should_link, link, From 9aa632622c38a625105b55318d29106fbf8cade3 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Thu, 21 Jul 2022 02:25:17 -0400 Subject: [PATCH 11/16] Remove fully qualified derive --- src/command.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/command.rs b/src/command.rs index 09bc894..7a1ecad 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,4 +1,4 @@ -use clap::{AppSettings, Parser, ValueEnum}; +use clap::{AppSettings, Parser, ValueEnum, Args}; use std::fmt::{Display, Formatter}; #[derive(Parser)] @@ -9,7 +9,7 @@ pub enum Cargo { Input(Input), } -#[derive(clap::Args)] +#[derive(Args)] #[clap(about)] #[clap(global_setting(AppSettings::AllowLeadingHyphen))] pub struct Input { From 8d350a8c6ba52e133110246b66cb89be246b6f84 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Thu, 21 Jul 2022 02:25:41 -0400 Subject: [PATCH 12/16] Only link for `run` and `test` subcommands. --- src/lib.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 50b084b..e423669 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,13 +21,12 @@ const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; pub fn get_should_link(input: &mut Input) -> bool { // When running compile only commands, don't link the executable to the 3ds. // Otherwise, link and run on the 3ds but do not run locally. - input.cmd == CargoCommand::Build + input.cmd == CargoCommand::Run || (input.cmd == CargoCommand::Test - && if input.cargo_opts.contains(&"--no-run".to_string()) { - false - } else { - input.cargo_opts.push("--no-run".to_string()); + && if !input.cargo_opts.contains(&"--no-run".to_string()) { true + } else { + false }) } From eb1d4345b74f4ee1b4cfcf8f89ce6fd5a3e3536c Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Thu, 21 Jul 2022 02:29:42 -0400 Subject: [PATCH 13/16] Fix logic for when to link --- src/lib.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index e423669..2ddd597 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,13 +21,16 @@ const DEFAULT_MESSAGE_FORMAT: &str = "json-render-diagnostics"; pub fn get_should_link(input: &mut Input) -> bool { // When running compile only commands, don't link the executable to the 3ds. // Otherwise, link and run on the 3ds but do not run locally. - input.cmd == CargoCommand::Run - || (input.cmd == CargoCommand::Test - && if !input.cargo_opts.contains(&"--no-run".to_string()) { - true - } else { - false - }) + match input.cmd { + CargoCommand::Run => { + true + } + CargoCommand::Test if !input.cargo_opts.contains(&"--no-run".to_string()) => { + input.cargo_opts.push("--no-run".to_string()); + true + } + _ => false + } } /// Extracts the user-defined message format and if there is none, From 1a562d0c1a70878a3086a79db51be8387fe3a271 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Thu, 21 Jul 2022 02:35:36 -0400 Subject: [PATCH 14/16] Build and not run under the hood when calling `cargo 3ds run` Cargo fmt --- src/command.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/command.rs b/src/command.rs index 7a1ecad..1efc74b 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,4 +1,4 @@ -use clap::{AppSettings, Parser, ValueEnum, Args}; +use clap::{AppSettings, Args, Parser, ValueEnum}; use std::fmt::{Display, Formatter}; #[derive(Parser)] @@ -39,8 +39,7 @@ impl CargoCommand { impl Display for CargoCommand { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - CargoCommand::Build => write!(f, "build"), - CargoCommand::Run => write!(f, "run"), + CargoCommand::Build | CargoCommand::Run => write!(f, "build"), CargoCommand::Test => write!(f, "test"), CargoCommand::Check => write!(f, "check"), CargoCommand::Clippy => write!(f, "clippy"), From 84ac10371bd77a97327740ea9e198976460209ba Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Thu, 21 Jul 2022 02:35:46 -0400 Subject: [PATCH 15/16] Cargo fmt --- src/lib.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 2ddd597..e50023d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,14 +22,12 @@ pub fn get_should_link(input: &mut Input) -> bool { // When running compile only commands, don't link the executable to the 3ds. // Otherwise, link and run on the 3ds but do not run locally. match input.cmd { - CargoCommand::Run => { - true - } + CargoCommand::Run => true, CargoCommand::Test if !input.cargo_opts.contains(&"--no-run".to_string()) => { input.cargo_opts.push("--no-run".to_string()); true } - _ => false + _ => false, } } From c7b6482f9c8e8e0a511fcef04609a369ff355719 Mon Sep 17 00:00:00 2001 From: Steve Cook Date: Sat, 23 Jul 2022 19:49:08 -0400 Subject: [PATCH 16/16] Remove Display trait Inline match command to subcommand for make_cargo_build_command --- src/command.rs | 12 ------------ src/lib.rs | 9 ++++++++- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/command.rs b/src/command.rs index 1efc74b..54f9eef 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,5 +1,4 @@ use clap::{AppSettings, Args, Parser, ValueEnum}; -use std::fmt::{Display, Formatter}; #[derive(Parser)] #[clap(name = "cargo")] @@ -35,14 +34,3 @@ impl CargoCommand { ) } } - -impl Display for CargoCommand { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - CargoCommand::Build | CargoCommand::Run => write!(f, "build"), - CargoCommand::Test => write!(f, "test"), - CargoCommand::Check => write!(f, "check"), - CargoCommand::Clippy => write!(f, "clippy"), - } - } -} diff --git a/src/lib.rs b/src/lib.rs index e50023d..045690d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -118,9 +118,16 @@ pub fn make_cargo_build_command( command.arg("-Z").arg("build-std"); } + let cmd = match cmd { + CargoCommand::Build | CargoCommand::Run => "build", + CargoCommand::Test => "test", + CargoCommand::Check => "check", + CargoCommand::Clippy => "clippy", + }; + command .env("RUSTFLAGS", rust_flags) - .arg(&cmd.to_string()) + .arg(cmd) .arg("--target") .arg("armv6k-nintendo-3ds") .arg("--message-format")