From 5ec238b22b21f14f3008fc818c48540f41e299b5 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 8 Jun 2024 01:33:04 -0400 Subject: [PATCH 1/3] Use camino utf8-path types where applicable --- Cargo.lock | 5 +- Cargo.toml | 1 + src/command.rs | 4 +- src/lib.rs | 122 +++++++++++++++++++++---------------------------- 4 files changed, 59 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b844183..df9d7bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -58,9 +58,9 @@ checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" [[package]] name = "camino" -version = "1.1.6" +version = "1.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59e92b5a388f549b863a7bea62612c09f24c8393560709a54558a9abdfb3b9c" +checksum = "e0ec6b951b160caa93cc0c7b209e5a3bff7aae9062213451ac99493cd844c239" dependencies = [ "serde", ] @@ -69,6 +69,7 @@ dependencies = [ name = "cargo-3ds" version = "0.1.3" dependencies = [ + "camino", "cargo_metadata", "clap", "rustc_version", diff --git a/Cargo.toml b/Cargo.toml index e0aae4d..2dd13cf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,4 @@ toml = "0.5.6" clap = { version = "4.0.15", features = ["derive", "wrap_help"] } shlex = "1.3.0" serde_json = "1.0.108" +camino = "1.1.7" diff --git a/src/command.rs b/src/command.rs index abeeeb5..ab7c9fe 100644 --- a/src/command.rs +++ b/src/command.rs @@ -348,10 +348,10 @@ impl Build { /// This callback handles building the application as a `.3dsx` file. fn callback(&self, config: &Option) { if let Some(config) = config { - eprintln!("Building smdh: {}", config.path_smdh().display()); + eprintln!("Building smdh: {}", config.path_smdh()); build_smdh(config, self.verbose); - eprintln!("Building 3dsx: {}", config.path_3dsx().display()); + eprintln!("Building 3dsx: {}", config.path_3dsx()); build_3dsx(config, self.verbose); } } diff --git a/src/lib.rs b/src/lib.rs index 5b6cb5c..d2ad9d0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,11 +4,12 @@ mod graph; use core::fmt; use std::ffi::OsStr; use std::io::{BufRead, BufReader}; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::process::{Command, ExitStatus, Stdio}; use std::{env, io, process}; -use cargo_metadata::{Message, MetadataCommand}; +use camino::{Utf8Path, Utf8PathBuf}; +use cargo_metadata::{Message, MetadataCommand, Package}; use command::{Input, Test}; use rustc_version::Channel; use semver::Version; @@ -281,13 +282,11 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig { let (package, artifact) = (package.unwrap(), artifact.unwrap()); - let mut icon = String::from("./icon.png"); + let mut icon_path = Utf8PathBuf::from("./icon.png"); - if !Path::new(&icon).exists() { - icon = format!( - "{}/libctru/default_icon.png", - env::var("DEVKITPRO").unwrap() - ); + if !icon_path.exists() { + icon_path = Utf8PathBuf::from(env::var("DEVKITPRO").unwrap()) + .join(Utf8Path::new("libctru/default_icon.png")); } // for now assume a single "kind" since we only support one output artifact @@ -301,34 +300,51 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig { _ => artifact.target.name, }; - let author = match package.authors.as_slice() { - [name, ..] => name.clone(), - [] => String::from("Unspecified Author"), // as standard with the devkitPRO toolchain - }; + let romfs_dir = get_romfs_dir(&package); CTRConfig { name, - author, + authors: package.authors, 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(), + icon_path, + romfs_dir, + target_path: artifact.executable.unwrap(), + } +} + +fn get_romfs_dir(package: &Package) -> Option { + if let Some(romfs_dir) = package + .metadata + .get("cargo-3ds")? + .get("romfs_dir")? + .as_str() + { + Some(package.manifest_path.parent()?.join(romfs_dir)) + } else { + None } } /// Builds the smdh using `smdhtool`. /// This will fail if `smdhtool` is not within the running directory or in a directory found in $PATH +// TODO: maybe this should be a method of CTRConfig? pub fn build_smdh(config: &CTRConfig, verbose: bool) { + let author = if config.authors.is_empty() { + String::from("Unspecified Author") // as standard with the devkitPRO toolchain + } else { + config.authors.join(", ") + }; + let mut command = Command::new("smdhtool"); command .arg("--create") .arg(&config.name) .arg(&config.description) - .arg(&config.author) - .arg(&config.icon) + .arg(author) + .arg(&config.icon_path) .arg(config.path_smdh()) .stdin(Stdio::inherit()) .stdout(Stdio::inherit()) @@ -356,18 +372,18 @@ pub fn build_3dsx(config: &CTRConfig, verbose: bool) { 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()); - command.arg(format!("--romfs={}", romfs_path.to_string_lossy())); - } else if !is_default_romfs { - eprintln!( - "Could not find configured RomFS dir: {}", - romfs_path.display() - ); + .arg(format!("--smdh={}", config.path_smdh())); + + let romfs = config + .romfs_dir + .as_deref() + .unwrap_or(Utf8Path::new("romfs")); + + if romfs.is_dir() { + eprintln!("Adding RomFS from {romfs}"); + command.arg(format!("--romfs={romfs}")); + } else if config.romfs_dir.is_some() { + eprintln!("Could not find configured RomFS dir: {romfs}"); process::exit(1); } @@ -411,54 +427,22 @@ pub fn link(config: &CTRConfig, run_args: &Run, verbose: bool) { } } -/// 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(Default)] +#[derive(Default, Debug)] pub struct CTRConfig { name: String, - author: String, + authors: Vec, description: String, - icon: String, - target_path: PathBuf, - cargo_manifest_path: PathBuf, + icon_path: Utf8PathBuf, + target_path: Utf8PathBuf, + romfs_dir: Option, } impl CTRConfig { - pub fn path_3dsx(&self) -> PathBuf { + pub fn path_3dsx(&self) -> Utf8PathBuf { self.target_path.with_extension("3dsx") } - pub fn path_smdh(&self) -> PathBuf { + pub fn path_smdh(&self) -> Utf8PathBuf { self.target_path.with_extension("smdh") } } From e499df1a86524421dff78ef3deb8b8619b99b32f Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sat, 8 Jun 2024 01:36:32 -0400 Subject: [PATCH 2/3] Convert build_smdh to a method --- src/command.rs | 4 +-- src/lib.rs | 75 +++++++++++++++++++++++++------------------------- 2 files changed, 40 insertions(+), 39 deletions(-) diff --git a/src/command.rs b/src/command.rs index ab7c9fe..4b0dab7 100644 --- a/src/command.rs +++ b/src/command.rs @@ -6,7 +6,7 @@ use std::sync::OnceLock; use cargo_metadata::Message; use clap::{Args, Parser, Subcommand}; -use crate::{build_3dsx, build_smdh, cargo, get_metadata, link, print_command, CTRConfig}; +use crate::{build_3dsx, cargo, get_metadata, link, print_command, CTRConfig}; #[derive(Parser, Debug)] #[command(name = "cargo", bin_name = "cargo")] @@ -349,7 +349,7 @@ impl Build { fn callback(&self, config: &Option) { if let Some(config) = config { eprintln!("Building smdh: {}", config.path_smdh()); - build_smdh(config, self.verbose); + config.build_smdh(self.verbose); eprintln!("Building 3dsx: {}", config.path_3dsx()); build_3dsx(config, self.verbose); diff --git a/src/lib.rs b/src/lib.rs index d2ad9d0..56a578b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -328,43 +328,6 @@ fn get_romfs_dir(package: &Package) -> Option { } } -/// Builds the smdh using `smdhtool`. -/// This will fail if `smdhtool` is not within the running directory or in a directory found in $PATH -// TODO: maybe this should be a method of CTRConfig? -pub fn build_smdh(config: &CTRConfig, verbose: bool) { - let author = if config.authors.is_empty() { - String::from("Unspecified Author") // as standard with the devkitPRO toolchain - } else { - config.authors.join(", ") - }; - - let mut command = Command::new("smdhtool"); - command - .arg("--create") - .arg(&config.name) - .arg(&config.description) - .arg(author) - .arg(&config.icon_path) - .arg(config.path_smdh()) - .stdin(Stdio::inherit()) - .stdout(Stdio::inherit()) - .stderr(Stdio::inherit()); - - if verbose { - print_command(&command); - } - - let mut process = command - .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)); - } -} - /// 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, verbose: bool) { @@ -438,13 +401,51 @@ pub struct CTRConfig { } impl CTRConfig { + /// Get the path to the output `.3dsx` file. pub fn path_3dsx(&self) -> Utf8PathBuf { self.target_path.with_extension("3dsx") } + /// Get the path to the output `.smdh` file. pub fn path_smdh(&self) -> Utf8PathBuf { self.target_path.with_extension("smdh") } + + /// 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(&self, verbose: bool) { + let author = if self.authors.is_empty() { + String::from("Unspecified Author") // as standard with the devkitPRO toolchain + } else { + self.authors.join(", ") + }; + + let mut command = Command::new("smdhtool"); + command + .arg("--create") + .arg(&self.name) + .arg(&self.description) + .arg(author) + .arg(&self.icon_path) + .arg(self.path_smdh()) + .stdin(Stdio::inherit()) + .stdout(Stdio::inherit()) + .stderr(Stdio::inherit()); + + if verbose { + print_command(&command); + } + + let mut process = command + .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)); + } + } } #[derive(Ord, PartialOrd, PartialEq, Eq, Debug)] From a8d9d15ab5af4a06ecdf24398d72b83457385940 Mon Sep 17 00:00:00 2001 From: Ian Chamberlain Date: Sun, 9 Jun 2024 17:27:17 -0400 Subject: [PATCH 3/3] Restore fallback `romfs` behavior Address review comments and minor cleanup --- Cargo.toml | 2 +- src/lib.rs | 32 +++++++++++++++----------------- 2 files changed, 16 insertions(+), 18 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 2dd13cf..2d85859 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,4 +20,4 @@ toml = "0.5.6" clap = { version = "4.0.15", features = ["derive", "wrap_help"] } shlex = "1.3.0" serde_json = "1.0.108" -camino = "1.1.7" +camino = "1.1" diff --git a/src/lib.rs b/src/lib.rs index 56a578b..778b06d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,21 +1,19 @@ pub mod command; mod graph; -use core::fmt; use std::ffi::OsStr; use std::io::{BufRead, BufReader}; use std::path::PathBuf; use std::process::{Command, ExitStatus, Stdio}; -use std::{env, io, process}; +use std::{env, fmt, io, process}; use camino::{Utf8Path, Utf8PathBuf}; use cargo_metadata::{Message, MetadataCommand, Package}; -use command::{Input, Test}; use rustc_version::Channel; use semver::Version; use tee::TeeReader; -use crate::command::{CargoCmd, Run}; +use crate::command::{CargoCmd, Input, Run, Test}; use crate::graph::UnitGraph; /// Build a command using [`make_cargo_build_command`] and execute it, @@ -286,7 +284,8 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig { if !icon_path.exists() { icon_path = Utf8PathBuf::from(env::var("DEVKITPRO").unwrap()) - .join(Utf8Path::new("libctru/default_icon.png")); + .join("libctru") + .join("default_icon.png"); } // for now assume a single "kind" since we only support one output artifact @@ -307,25 +306,21 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig { authors: package.authors, description: package .description - .clone() .unwrap_or_else(|| String::from("Homebrew Application")), icon_path, romfs_dir, + manifest_dir: package.manifest_path.parent().unwrap().into(), target_path: artifact.executable.unwrap(), } } fn get_romfs_dir(package: &Package) -> Option { - if let Some(romfs_dir) = package + package .metadata .get("cargo-3ds")? .get("romfs_dir")? .as_str() - { - Some(package.manifest_path.parent()?.join(romfs_dir)) - } else { - None - } + .map(Utf8PathBuf::from) } /// Builds the 3dsx using `3dsxtool`. @@ -337,11 +332,7 @@ pub fn build_3dsx(config: &CTRConfig, verbose: bool) { .arg(config.path_3dsx()) .arg(format!("--smdh={}", config.path_smdh())); - let romfs = config - .romfs_dir - .as_deref() - .unwrap_or(Utf8Path::new("romfs")); - + let romfs = config.romfs_dir(); if romfs.is_dir() { eprintln!("Adding RomFS from {romfs}"); command.arg(format!("--romfs={romfs}")); @@ -397,6 +388,7 @@ pub struct CTRConfig { description: String, icon_path: Utf8PathBuf, target_path: Utf8PathBuf, + manifest_dir: Utf8PathBuf, romfs_dir: Option, } @@ -411,6 +403,12 @@ impl CTRConfig { self.target_path.with_extension("smdh") } + /// Get the absolute path to the romfs directory, defaulting to `romfs` if not specified. + pub fn romfs_dir(&self) -> Utf8PathBuf { + self.manifest_dir + .join(self.romfs_dir.as_deref().unwrap_or(Utf8Path::new("romfs"))) + } + /// 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(&self, verbose: bool) {