started work on migrating it to cargo-nds

This commit is contained in:
Your Name 2024-04-29 15:50:56 +02:00
parent af2d8bfd79
commit d991297793
5 changed files with 263 additions and 194 deletions

118
Cargo.lock generated
View File

@ -66,7 +66,7 @@ dependencies = [
] ]
[[package]] [[package]]
name = "cargo-3ds" name = "cargo-nds"
version = "0.1.2" version = "0.1.2"
dependencies = [ dependencies = [
"cargo_metadata", "cargo_metadata",
@ -91,15 +91,16 @@ dependencies = [
[[package]] [[package]]
name = "cargo_metadata" name = "cargo_metadata"
version = "0.14.2" version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa" checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [ dependencies = [
"camino", "camino",
"cargo-platform", "cargo-platform",
"semver", "semver",
"serde", "serde",
"serde_json", "serde_json",
"thiserror",
] ]
[[package]] [[package]]
@ -149,6 +150,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]] [[package]]
name = "errno" name = "errno"
version = "0.3.8" version = "0.3.8"
@ -159,12 +166,28 @@ dependencies = [
"windows-sys 0.52.0", "windows-sys 0.52.0",
] ]
[[package]]
name = "hashbrown"
version = "0.14.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
[[package]] [[package]]
name = "heck" name = "heck"
version = "0.4.1" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
[[package]]
name = "indexmap"
version = "2.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26"
dependencies = [
"equivalent",
"hashbrown",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.9" version = "1.0.9"
@ -184,19 +207,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829" checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]] [[package]]
name = "proc-macro2" name = "memchr"
version = "1.0.70" version = "2.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
[[package]]
name = "proc-macro2"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
[[package]] [[package]]
name = "quote" name = "quote"
version = "1.0.33" version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
] ]
@ -269,6 +298,15 @@ dependencies = [
"serde", "serde",
] ]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]] [[package]]
name = "shlex" name = "shlex"
version = "1.2.0" version = "1.2.0"
@ -283,9 +321,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.39" version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -309,12 +347,57 @@ dependencies = [
] ]
[[package]] [[package]]
name = "toml" name = "thiserror"
version = "0.5.11" version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" checksum = "f0126ad08bff79f29fc3ae6a55cc72352056dfff61e3ff8bb7129476d44b23aa"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.59"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d1cd413b5d558b4c5bf3680e324a6fa5014e7b7c067a51e69dbdf47eb7148b66"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "toml"
version = "0.8.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3"
dependencies = [ dependencies = [
"serde", "serde",
"serde_spanned",
"toml_datetime",
"toml_edit",
]
[[package]]
name = "toml_datetime"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1"
dependencies = [
"serde",
]
[[package]]
name = "toml_edit"
version = "0.22.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef"
dependencies = [
"indexmap",
"serde",
"serde_spanned",
"toml_datetime",
"winnow",
] ]
[[package]] [[package]]
@ -460,3 +543,12 @@ name = "windows_x86_64_msvc"
version = "0.52.0" version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04"
[[package]]
name = "winnow"
version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14b9415ee827af173ebb3f15f9083df5a122eb93572ec28741fb153356ea2578"
dependencies = [
"memchr",
]

View File

@ -1,22 +1,22 @@
[package] [package]
name = "cargo-3ds" name = "cargo-nds"
version = "0.1.2" version = "0.1.2"
authors = ["Rust3DS Org", "Andrea Ciliberti <meziu210@icloud.com>"] authors = ["Rustnds Org", "Andrea Ciliberti <meziu210@icloud.com>"]
description = "Cargo wrapper for developing Nintendo 3DS homebrew apps" description = "Cargo wrapper for developing Nintendo nds homebrew apps"
repository = "https://github.com/rust3ds/cargo-3ds" repository = "https://github.com/rustnds/cargo-nds"
keywords = ["3ds", "homebrew"] keywords = ["nds", "homebrew"]
categories = ["command-line-utilities", "development-tools::cargo-plugins"] categories = ["command-line-utilities", "development-tools::cargo-plugins"]
exclude = [".github"] exclude = [".github"]
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
edition = "2021" edition = "2021"
[dependencies] [dependencies]
cargo_metadata = "0.14.0" cargo_metadata = "0.18.1"
rustc_version = "0.4.0" rustc_version = "0.4.0"
semver = "1.0.10" semver = "1.0.10"
serde = { version = "1.0.139", features = ["derive"] } serde = { version = "1.0.139", features = ["derive"] }
tee = "0.1.0" tee = "0.1.0"
toml = "0.5.6" toml = "0.8.12"
clap = { version = "4.0.15", features = ["derive", "wrap_help"] } clap = { version = "4.0.15", features = ["derive", "wrap_help"] }
shlex = "1.1.0" shlex = "1.1.0"
serde_json = "1.0.108" serde_json = "1.0.108"

View File

@ -1,4 +1,4 @@
use std::fs; use std::{env, fs};
use std::io::Read; use std::io::Read;
use std::process::Stdio; use std::process::Stdio;
use std::sync::OnceLock; use std::sync::OnceLock;
@ -6,12 +6,12 @@ use std::sync::OnceLock;
use cargo_metadata::Message; use cargo_metadata::Message;
use clap::{Args, Parser, Subcommand}; use clap::{Args, Parser, Subcommand};
use crate::{build_3dsx, build_smdh, cargo, get_metadata, link, print_command, CTRConfig}; use crate::{build_nds, cargo, get_metadata, link, print_command, NDSConfig};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(name = "cargo", bin_name = "cargo")] #[command(name = "cargo", bin_name = "cargo")]
pub enum Cargo { pub enum Cargo {
#[command(name = "3ds")] #[command(name = "nds")]
Input(Input), Input(Input),
} }
@ -21,7 +21,7 @@ pub struct Input {
#[command(subcommand)] #[command(subcommand)]
pub cmd: CargoCmd, pub cmd: CargoCmd,
/// Print the exact commands `cargo-3ds` is running. Note that this does not /// Print the exact commands `cargo-nds` is running. Note that this does not
/// set the verbose flag for cargo itself. To set cargo's verbosity flag, add /// set the verbose flag for cargo itself. To set cargo's verbosity flag, add
/// `-- -v` to the end of the command line. /// `-- -v` to the end of the command line.
#[arg(long, short = 'v', global = true)] #[arg(long, short = 'v', global = true)]
@ -34,32 +34,32 @@ pub struct Input {
} }
/// Run a cargo command. COMMAND will be forwarded to the real /// Run a cargo command. COMMAND will be forwarded to the real
/// `cargo` with the appropriate arguments for the 3DS target. /// `cargo` with the appropriate arguments for the nds target.
/// ///
/// If an unrecognized COMMAND is used, it will be passed through unmodified /// If an unrecognized COMMAND is used, it will be passed through unmodified
/// to `cargo` with the appropriate flags set for the 3DS target. /// to `cargo` with the appropriate flags set for the nds target.
#[derive(Subcommand, Debug)] #[derive(Subcommand, Debug)]
#[command(allow_external_subcommands = true)] #[command(allow_external_subcommands = true)]
pub enum CargoCmd { pub enum CargoCmd {
/// Builds an executable suitable to run on a 3DS (3dsx). /// Builds an executable suitable to run on a DS (nds).
Build(Build), Build(Build),
/// Builds an executable and sends it to a device with `3dslink`. /// Builds an executable and sends it to a device with `dslink`.
Run(Run), Run(Run),
/// Builds a test executable and sends it to a device with `3dslink`. /// Builds a test executable and sends it to a device with `dslink`.
/// ///
/// This can be used with `--test` for integration tests, or `--lib` for /// This can be used with `--test` for integration tests, or `--lib` for
/// unit tests (which require a custom test runner). /// unit tests (which require a custom test runner).
Test(Test), Test(Test),
/// Sets up a new cargo project suitable to run on a 3DS. /// Sets up a new cargo project suitable to run on a DS.
New(New), New(New),
// NOTE: it seems docstring + name for external subcommands are not rendered // NOTE: it seems docstring + name for external subcommands are not rendered
// in help, but we might as well set them here in case a future version of clap // in help, but we might as well set them here in case a future version of clap
// does include them in help text. // does include them in help text.
/// Run any other `cargo` command with custom building tailored for the 3DS. /// Run any other `cargo` command with custom building tailored for the nds.
#[command(external_subcommand, name = "COMMAND")] #[command(external_subcommand, name = "COMMAND")]
Passthrough(Vec<String>), Passthrough(Vec<String>),
} }
@ -73,7 +73,7 @@ pub struct RemainingArgs {
/// ///
/// To pass arguments to an executable being run, a *second* `--` must be /// To pass arguments to an executable being run, a *second* `--` must be
/// used to disambiguate cargo arguments from executable arguments. /// used to disambiguate cargo arguments from executable arguments.
/// For example, `cargo 3ds run -- -- xyz` runs an executable with the argument /// For example, `cargo nds run -- -- xyz` runs an executable with the argument
/// `xyz`. /// `xyz`.
#[arg( #[arg(
trailing_var_arg = true, trailing_var_arg = true,
@ -97,23 +97,23 @@ pub struct Build {
pub struct Run { pub struct Run {
/// Specify the IP address of the device to send the executable to. /// Specify the IP address of the device to send the executable to.
/// ///
/// Corresponds to 3dslink's `--address` arg, which defaults to automatically /// Corresponds to ndslink's `--address` arg, which defaults to automatically
/// finding the device. /// finding the device.
#[arg(long, short = 'a')] #[arg(long, short = 'a')]
pub address: Option<std::net::Ipv4Addr>, pub address: Option<std::net::Ipv4Addr>,
/// Set the 0th argument of the executable when running it. Corresponds to /// Set the 0th argument of the executable when running it. Corresponds to
/// 3dslink's `--argv0` argument. /// ndslink's `--argv0` argument.
#[arg(long, short = '0')] #[arg(long, short = '0')]
pub argv0: Option<String>, pub argv0: Option<String>,
/// Start the 3dslink server after sending the executable. Corresponds to /// Start the ndslink server after sending the executable. Corresponds to
/// 3dslink's `--server` argument. /// ndslink's `--server` argument.
#[arg(long, short = 's', default_value_t = false)] #[arg(long, short = 's', default_value_t = false)]
pub server: bool, pub server: bool,
/// Set the number of tries when connecting to the device to send the executable. /// Set the number of tries when connecting to the device to send the executable.
/// Corresponds to 3dslink's `--retries` argument. /// Corresponds to ndslink's `--retries` argument.
// Can't use `short = 'r'` because that would conflict with cargo's `--release/-r` // Can't use `short = 'r'` because that would conflict with cargo's `--release/-r`
#[arg(long)] #[arg(long)]
pub retries: Option<usize>, pub retries: Option<usize>,
@ -133,7 +133,7 @@ pub struct Test {
pub no_run: bool, pub no_run: bool,
/// If set, documentation tests will be built instead of unit tests. /// If set, documentation tests will be built instead of unit tests.
/// This implies `--no-run`, unless Cargo's `target.armv6k-nintendo-3ds.runner` /// This implies `--no-run`, unless Cargo's `target.armv6k-nintendo-nds.runner`
/// is configured. /// is configured.
#[arg(long)] #[arg(long)]
pub doc: bool, pub doc: bool,
@ -158,7 +158,7 @@ impl CargoCmd {
/// Returns the additional arguments run by the "official" cargo subcommand. /// Returns the additional arguments run by the "official" cargo subcommand.
pub fn cargo_args(&self) -> Vec<String> { pub fn cargo_args(&self) -> Vec<String> {
match self { match self {
CargoCmd::Build(build) => build.passthrough.cargo_args(), CargoCmd::Build(build) =>build.passthrough.cargo_args(),
CargoCmd::Run(run) => run.build_args.passthrough.cargo_args(), CargoCmd::Run(run) => run.build_args.passthrough.cargo_args(),
CargoCmd::Test(test) => test.cargo_args(), CargoCmd::Test(test) => test.cargo_args(),
CargoCmd::New(new) => { CargoCmd::New(new) => {
@ -172,7 +172,7 @@ impl CargoCmd {
} }
} }
/// Returns the cargo subcommand run by `cargo-3ds` when handling a [`CargoCmd`]. /// Returns the cargo subcommand run by `cargo-nds` when handling a [`CargoCmd`].
/// ///
/// # Notes /// # Notes
/// ///
@ -203,13 +203,13 @@ impl CargoCmd {
) )
} }
/// Whether or not this command should build a 3DSX executable file. /// Whether or not this command should build a ndsX executable file.
pub fn should_build_3dsx(&self) -> bool { pub fn should_build_ndsx(&self) -> bool {
match self { match self {
Self::Build(_) | CargoCmd::Run(_) => true, Self::Build(_) | CargoCmd::Run(_) => true,
&Self::Test(Test { doc, .. }) => { &Self::Test(Test { doc, .. }) => {
if doc { if doc {
eprintln!("Documentation tests requested, no 3dsx will be built"); eprintln!("Documentation tests requested, no ndsx will be built");
false false
} else { } else {
true true
@ -219,8 +219,8 @@ impl CargoCmd {
} }
} }
/// Whether or not the resulting executable should be sent to the 3DS with /// Whether or not the resulting executable should be sent to the nds with
/// `3dslink`. /// `ndslink`.
pub fn should_link_to_device(&self) -> bool { pub fn should_link_to_device(&self) -> bool {
match self { match self {
Self::Test(Test { no_run: true, .. }) => false, Self::Test(Test { no_run: true, .. }) => false,
@ -247,7 +247,7 @@ impl CargoCmd {
if let Self::Test(Test { doc: true, .. }) = self { if let Self::Test(Test { doc: true, .. }) = self {
// We don't care about JSON output for doctests since we're not // 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 // building any ndsx etc. Just use the default output as it's more
// readable compared to DEFAULT_MESSAGE_FORMAT // readable compared to DEFAULT_MESSAGE_FORMAT
Ok(Some(String::from("human"))) Ok(Some(String::from("human")))
} else { } else {
@ -293,11 +293,11 @@ impl CargoCmd {
/// ///
/// # Examples /// # Examples
/// ///
/// - `cargo 3ds build` and other "build" commands will use their callbacks to build the final `.3dsx` file and link it. /// - `cargo nds build` and other "build" commands will use their callbacks to build the final `.ndsx` file and link it.
/// - `cargo 3ds new` and other generic commands will use their callbacks to make 3ds-specific changes to the environment. /// - `cargo nds new` and other generic commands will use their callbacks to make nds-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 // Process the metadata only for commands that have it/use it
let config = if self.should_build_3dsx() { let config = if self.should_build_ndsx() {
eprintln!("Getting metadata"); eprintln!("Getting metadata");
Some(get_metadata(messages)) Some(get_metadata(messages))
@ -343,78 +343,45 @@ impl RemainingArgs {
} }
impl Build { impl Build {
/// Callback for `cargo 3ds build`. /// Callback for `cargo nds build`.
/// ///
/// This callback handles building the application as a `.3dsx` file. /// This callback handles building the application as a `.ndsx` file.
fn callback(&self, config: &Option<CTRConfig>) { fn callback(&self, config: &Option<NDSConfig>) {
if let Some(config) = config { if let Some(config) = config {
eprintln!("Building smdh: {}", config.path_smdh().display()); eprintln!("Building nds: {}", config.path_nds().display());
build_smdh(config, self.verbose); build_nds(config, self.verbose);
eprintln!("Building 3dsx: {}", config.path_3dsx().display());
build_3dsx(config, self.verbose);
} }
} }
} }
impl Run { impl Run {
/// Get the args to pass to `3dslink` based on these options. /// Get the args to pass to `ndslink` based on these options.
pub fn get_3dslink_args(&self) -> Vec<String> { pub fn get_dslink_args(&self) -> Vec<String> {
let mut args = Vec::new(); let mut args = Vec::new();
if let Some(address) = self.address { if let Some(address) = self.address {
args.extend(["--address".to_string(), address.to_string()]); args.extend(["-a".to_string(), address.to_string()]);
}
if let Some(argv0) = &self.argv0 {
args.extend(["--arg0".to_string(), argv0.clone()]);
}
if let Some(retries) = self.retries {
args.extend(["--retries".to_string(), retries.to_string()]);
}
if self.server {
args.push("--server".to_string());
}
let exe_args = self.build_args.passthrough.exe_args();
if !exe_args.is_empty() {
// For some reason 3dslink seems to want 2 instances of `--`, one
// in front of all of the args like this...
args.extend(["--args".to_string(), "--".to_string()]);
let mut escaped = false;
for arg in exe_args.iter().cloned() {
if arg.starts_with('-') && !escaped {
// And one before the first `-` arg that is passed in.
args.extend(["--".to_string(), arg]);
escaped = true;
} else {
args.push(arg);
}
}
} }
args args
} }
/// Callback for `cargo 3ds run`. /// Callback for `cargo nds run`.
/// ///
/// This callback handles launching the application via `3dslink`. /// This callback handles launching the application via `dslink`.
fn callback(&self, config: &Option<CTRConfig>) { fn callback(&self, config: &Option<NDSConfig>) {
// Run the normal "build" callback // Run the normal "build" callback
self.build_args.callback(config); self.build_args.callback(config);
if !self.use_custom_runner() { if !self.use_custom_runner() {
if let Some(cfg) = config { if let Some(cfg) = config {
eprintln!("Running 3dslink"); eprintln!("Running dslink");
link(cfg, self, self.build_args.verbose); link(cfg, self, self.build_args.verbose);
} }
} }
} }
/// Returns whether the cargo environment has `target.armv6k-nintendo-3ds.runner` /// Returns whether the cargo environment has `target.armv6k-nintendo-nds.runner`
/// configured. This will only be checked once during the lifetime of the program, /// configured. This will only be checked once during the lifetime of the program,
/// and takes into account the usual ways Cargo looks for its /// and takes into account the usual ways Cargo looks for its
/// [configuration](https://doc.rust-lang.org/cargo/reference/config.html): /// [configuration](https://doc.rust-lang.org/cargo/reference/config.html):
@ -426,14 +393,16 @@ impl Run {
static HAS_RUNNER: OnceLock<bool> = OnceLock::new(); static HAS_RUNNER: OnceLock<bool> = OnceLock::new();
let &custom_runner_configured = HAS_RUNNER.get_or_init(|| { let &custom_runner_configured = HAS_RUNNER.get_or_init(|| {
let blocksds = env::var("BLOCKSDS").unwrap_or("/opt/wonderful/thirdparty/blocksds/core".to_owned());
env::set_var("RUSTFLAGS", format!("-C link-args=-specs={blocksds}/sys/crts/ds_arm9.specs"));
let mut cmd = cargo(&self.config); let mut cmd = cargo(&self.config);
cmd.args([ cmd.args([
// https://github.com/rust-lang/cargo/issues/9301 // https://github.com/rust-lang/cargo/issues/9301
"-Z", "-Z",
"unstable-options", "build-std=core,alloc",
"config", "--target",
"get", "./armv5te-nintendo-ds.json"
"target.armv6k-nintendo-3ds.runner",
]) ])
.stdout(Stdio::null()) .stdout(Stdio::null())
.stderr(Stdio::null()); .stderr(Stdio::null());
@ -458,10 +427,10 @@ impl Run {
} }
impl Test { impl Test {
/// Callback for `cargo 3ds test`. /// Callback for `cargo nds test`.
/// ///
/// This callback handles launching the application via `3dslink`. /// This callback handles launching the application via `ndslink`.
fn callback(&self, config: &Option<CTRConfig>) { fn callback(&self, config: &Option<NDSConfig>) {
if self.no_run { if self.no_run {
// If the tests don't have to run, use the "build" callback // If the tests don't have to run, use the "build" callback
self.run_args.build_args.callback(config); self.run_args.build_args.callback(config);
@ -479,7 +448,7 @@ impl Test {
fn cargo_args(&self) -> Vec<String> { fn cargo_args(&self) -> Vec<String> {
let mut cargo_args = self.run_args.build_args.passthrough.cargo_args(); let mut cargo_args = self.run_args.build_args.passthrough.cargo_args();
// We can't run 3DS executables on the host, but we want to respect // We can't run nds executables on the host, but we want to respect
// the user's "runner" configuration if set. // the user's "runner" configuration if set.
// //
// If doctests were requested, `--no-run` will be rejected on the // If doctests were requested, `--no-run` will be rejected on the
@ -512,9 +481,9 @@ impl Test {
} }
} }
const TOML_CHANGES: &str = r#"ctru-rs = { git = "https://github.com/rust3ds/ctru-rs" } const TOML_CHANGES: &str = r#"ctru-rs = { git = "https://github.com/rustnds/ctru-rs" }
[package.metadata.cargo-3ds] [package.metadata.cargo-nds]
romfs_dir = "romfs" romfs_dir = "romfs"
"#; "#;
@ -541,9 +510,9 @@ fn main() {
"#; "#;
impl New { impl New {
/// Callback for `cargo 3ds new`. /// Callback for `cargo nds new`.
/// ///
/// This callback handles the custom environment modifications when creating a new 3DS project. /// This callback handles the custom environment modifications when creating a new nds project.
fn callback(&self) { fn callback(&self) {
// Commmit changes to the project only if is meant to be a binary // Commmit changes to the project only if is meant to be a binary
if self.cargo_args.args.contains(&"--lib".to_string()) { if self.cargo_args.args.contains(&"--lib".to_string()) {
@ -672,7 +641,7 @@ mod tests {
expected_exe: &["bar"], expected_exe: &["bar"],
}, },
] { ] {
let input: Vec<&str> = ["cargo", "3ds", "run"] let input: Vec<&str> = ["cargo", "nds", "run"]
.iter() .iter()
.chain(param.input) .chain(param.input)
.copied() .copied()

View File

@ -21,7 +21,7 @@ use crate::graph::UnitGraph;
/// parsing and returning the messages from the spawned process. /// parsing and returning the messages from the spawned 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 nds files.
pub fn run_cargo(input: &Input, message_format: Option<String>) -> (ExitStatus, Vec<Message>) { pub fn run_cargo(input: &Input, message_format: Option<String>) -> (ExitStatus, Vec<Message>) {
let mut command = make_cargo_command(input, &message_format); let mut command = make_cargo_command(input, &message_format);
@ -57,7 +57,7 @@ pub fn run_cargo(input: &Input, message_format: Option<String>) -> (ExitStatus,
let buf_reader: &mut dyn BufRead = match (message_format, &input.cmd) { let buf_reader: &mut dyn BufRead = match (message_format, &input.cmd) {
// The user presumably cares about the message format if set, so we should // 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 // 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 ndstool etc.
(Some(_), _) | (Some(_), _) |
// Rustdoc unfortunately prints to stdout for compile errors, so // Rustdoc unfortunately prints to stdout for compile errors, so
// we also use a tee when building doc tests too. // we also use a tee when building doc tests too.
@ -104,14 +104,11 @@ fn should_use_ctru_debuginfo(cargo_cmd: &Command, verbose: bool) -> bool {
/// Create a cargo command based on the context. /// Create a cargo command based on the context.
/// ///
/// For "build" commands (which compile code, such as `cargo 3ds build` or `cargo 3ds clippy`), /// For "build" commands (which compile code, such as `cargo nds build` or `cargo nds clippy`),
/// if there is no pre-built std detected in the sysroot, `build-std` will be used instead. /// if there is no pre-built std detected in the sysroot, `build-std` will be used instead.
pub fn make_cargo_command(input: &Input, message_format: &Option<String>) -> Command { pub fn make_cargo_command(input: &Input, message_format: &Option<String>) -> Command {
let devkitpro = let blocksds = env::var("BLOCKSDS").unwrap_or("/opt/wonderful/thirdparty/blocksds/core".to_owned());
env::var("DEVKITPRO").expect("DEVKITPRO is not defined as an environment variable"); let rustflags = format!("-C link-args=-specs={blocksds}/sys/crts/ds_arm9.specs");
// TODO: should we actually prepend the user's RUSTFLAGS for linking order? not sure
let rustflags =
env::var("RUSTFLAGS").unwrap_or_default() + &format!(" -L{devkitpro}/libctru/lib");
let cargo_cmd = &input.cmd; let cargo_cmd = &input.cmd;
@ -125,21 +122,15 @@ pub fn make_cargo_command(input: &Input, message_format: &Option<String>) -> Com
if cargo_cmd.should_compile() { if cargo_cmd.should_compile() {
command command
.arg("--target") .arg("--target")
.arg("armv6k-nintendo-3ds") .arg("armv5te-nintendo-ds.json")
.arg("-Z")
.arg("build-std=core,alloc")
.arg("--message-format") .arg("--message-format")
.arg( .arg(
message_format message_format
.as_deref() .as_deref()
.unwrap_or(CargoCmd::DEFAULT_MESSAGE_FORMAT), .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");
// Always building the test crate is not ideal, but we don't know if the
// crate being built uses #![feature(test)], so we build it just in case.
command.arg("-Z").arg("build-std=std,test");
}
} }
if let CargoCmd::Test(test) = cargo_cmd { if let CargoCmd::Test(test) = cargo_cmd {
@ -212,10 +203,10 @@ pub fn check_rust_version() {
let rustc_version = rustc_version::version_meta().unwrap(); let rustc_version = rustc_version::version_meta().unwrap();
if rustc_version.channel > Channel::Nightly { if rustc_version.channel > Channel::Nightly {
eprintln!("cargo-3ds requires a nightly rustc version."); eprintln!("cargo-nds requires a nightly rustc version.");
eprintln!( eprintln!(
"Please run `rustup override set nightly` to use nightly in the \ "Please run `rustup override set nightly` to use nightly in the \
current directory, or use `cargo +nightly 3ds` to use it for a \ current directory, or use `cargo +nightly nds` to use it for a \
single invocation." single invocation."
); );
process::exit(1); process::exit(1);
@ -237,17 +228,17 @@ pub fn check_rust_version() {
}; };
if old_version || old_commit { if old_version || old_commit {
eprintln!("cargo-3ds requires rustc nightly version >= {MINIMUM_COMMIT_DATE}"); eprintln!("cargo-nds 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);
} }
} }
/// Parses messages returned by "build" cargo commands (such as `cargo 3ds build` or `cargo 3ds run`). /// Parses messages returned by "build" cargo commands (such as `cargo nds build` or `cargo nds run`).
/// The returned [`CTRConfig`] is then used for further building in and execution /// The returned [`CTRConfig`] is then used for further building in and execution
/// in [`build_smdh`], [`build_3dsx`], and [`link`]. /// in [`build_nds`], and [`link`].
pub fn get_metadata(messages: &[Message]) -> CTRConfig { pub fn get_metadata(messages: &[Message]) -> NDSConfig {
let metadata = MetadataCommand::new() let metadata = MetadataCommand::new()
.no_deps() .no_deps()
.exec() .exec()
@ -275,12 +266,12 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig {
let (package, artifact) = (package.unwrap(), artifact.unwrap()); let (package, artifact) = (package.unwrap(), artifact.unwrap());
let mut icon = String::from("./icon.png"); let mut icon = String::from("./icon.bmp");
if !Path::new(&icon).exists() { if !Path::new(&icon).exists() {
icon = format!( icon = format!(
"{}/libctru/default_icon.png", "{}/sys/icon.bmp",
env::var("DEVKITPRO").unwrap() env::var("BLOCKSDS").unwrap()
); );
} }
@ -300,63 +291,41 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig {
[] => String::from("Unspecified Author"), // as standard with the devkitPRO toolchain [] => String::from("Unspecified Author"), // as standard with the devkitPRO toolchain
}; };
CTRConfig { NDSConfig {
name, name,
author, author,
description: package description: package
.description .description
.clone() .clone()
.unwrap_or_else(|| String::from("Homebrew Application")), .unwrap_or_else(|| String::from("Homebrew Application")),
icon, icon : icon,
target_path: artifact.executable.unwrap().into(), target_path: artifact.executable.unwrap().into(),
cargo_manifest_path: package.manifest_path.into(), cargo_manifest_path: package.manifest_path.into(),
} }
} }
/// Builds the smdh using `smdhtool`. /// Builds the nds using `ndstool`.
/// This will fail if `smdhtool` is not within the running directory or in a directory found in $PATH /// This will fail if `ndstool` is not within the running directory or in a directory found in $PATH
pub fn build_smdh(config: &CTRConfig, verbose: bool) { pub fn build_nds(config: &NDSConfig, verbose: bool) {
let mut command = Command::new("smdhtool"); let mut command = Command::new("ndstool");
let banner_text = format!("\"{};{};{}\"", &config.name, &config.description, &config.author);
command command
.arg("--create") .arg("-c")
.arg(&config.name) .arg(config.path_nds())
.arg(&config.description) .arg("-9")
.arg(&config.author) .arg(config.path_arm9())
.arg("-7")
.arg(config.path_arm7())
.arg("-b")
.arg(&config.icon) .arg(&config.icon)
.arg(config.path_smdh()) .arg(banner_text);
.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) {
let mut command = Command::new("3dsxtool");
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 // If romfs directory exists, automatically include it
let (romfs_path, is_default_romfs) = get_romfs_path(config); let (romfs_path, is_default_romfs) = get_romfs_path(config);
if romfs_path.is_dir() { if romfs_path.is_dir() {
eprintln!("Adding RomFS from {}", romfs_path.display()); eprintln!("Adding RomFS from {}", romfs_path.display());
command.arg(format!("--romfs={}", romfs_path.to_string_lossy())); command.arg("-d")
.arg(&romfs_path);
} else if !is_default_romfs { } else if !is_default_romfs {
eprintln!( eprintln!(
"Could not find configured RomFS dir: {}", "Could not find configured RomFS dir: {}",
@ -374,7 +343,7 @@ pub fn build_3dsx(config: &CTRConfig, verbose: bool) {
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.stderr(Stdio::inherit()) .stderr(Stdio::inherit())
.spawn() .spawn()
.expect("3dsxtool command failed, most likely due to '3dsxtool' not being in $PATH"); .expect("ndstool command failed, most likely due to 'ndstool' not being in $PATH");
let status = process.wait().unwrap(); let status = process.wait().unwrap();
@ -383,13 +352,13 @@ pub fn build_3dsx(config: &CTRConfig, verbose: bool) {
} }
} }
/// Link the generated 3dsx to a 3ds to execute and test using `3dslink`. /// Link the generated nds to a ds to execute and test using `dslink`.
/// This will fail if `3dslink` is not within the running directory or in a directory found in $PATH /// This will fail if `dslink` is not within the running directory or in a directory found in $PATH
pub fn link(config: &CTRConfig, run_args: &Run, verbose: bool) { pub fn link(config: &NDSConfig, run_args: &Run, verbose: bool) {
let mut command = Command::new("3dslink"); let mut command = Command::new("dslink");
command command
.arg(config.path_3dsx()) .args(run_args.get_dslink_args())
.args(run_args.get_3dslink_args()) .arg(config.path_nds())
.stdin(Stdio::inherit()) .stdin(Stdio::inherit())
.stdout(Stdio::inherit()) .stdout(Stdio::inherit())
.stderr(Stdio::inherit()); .stderr(Stdio::inherit());
@ -407,7 +376,7 @@ 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. /// 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. /// The returned boolean is true when the default is used.
pub fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) { pub fn get_romfs_path(config: &NDSConfig) -> (PathBuf, bool) {
let manifest_path = &config.cargo_manifest_path; let manifest_path = &config.cargo_manifest_path;
let manifest_str = std::fs::read_to_string(manifest_path) let manifest_str = std::fs::read_to_string(manifest_path)
.unwrap_or_else(|e| panic!("Could not open {}: {e}", manifest_path.display())); .unwrap_or_else(|e| panic!("Could not open {}: {e}", manifest_path.display()));
@ -422,9 +391,9 @@ pub fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) {
.and_then(toml::Value::as_table) .and_then(toml::Value::as_table)
.and_then(|table| table.get("metadata")) .and_then(|table| table.get("metadata"))
.and_then(toml::Value::as_table) .and_then(toml::Value::as_table)
.and_then(|table| table.get("cargo-3ds")) .and_then(|table| table.get("nds"))
.and_then(toml::Value::as_table) .and_then(toml::Value::as_table)
.and_then(|table| table.get("romfs_dir")) .and_then(|table| table.get("romfs"))
.and_then(toml::Value::as_str) .and_then(toml::Value::as_str)
.unwrap_or_else(|| { .unwrap_or_else(|| {
is_default = true; is_default = true;
@ -437,8 +406,41 @@ pub fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) {
(romfs_path, is_default) (romfs_path, is_default)
} }
/// Read the `icon` 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_icon_path(config: &NDSConfig) -> (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 icon setting and compute the path
let mut is_default = false;
let icon_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("nds"))
.and_then(toml::Value::as_table)
.and_then(|table| table.get("icon"))
.and_then(toml::Value::as_str)
.unwrap_or_else(|| {
is_default = true;
"/opt/wonderful/thirdparty/blocksds/core/sys/icon.bmp"
});
let mut icon_path = manifest_path.clone();
icon_path.pop(); // Pop Cargo.toml
icon_path.push(icon_setting);
(icon_path, is_default)
}
#[derive(Default)] #[derive(Default)]
pub struct CTRConfig { pub struct NDSConfig {
name: String, name: String,
author: String, author: String,
description: String, description: String,
@ -447,13 +449,20 @@ pub struct CTRConfig {
cargo_manifest_path: PathBuf, cargo_manifest_path: PathBuf,
} }
impl CTRConfig { impl NDSConfig {
pub fn path_3dsx(&self) -> PathBuf { pub fn path_nds(&self) -> PathBuf {
self.target_path.with_extension("3dsx") self.target_path.with_extension("nds")
} }
pub fn path_arm9(&self) -> PathBuf {
pub fn path_smdh(&self) -> PathBuf { self.target_path.with_extension("arm9.elf")
self.target_path.with_extension("smdh") }
pub fn path_arm7(&self) -> PathBuf {
let arm7 =self.target_path.with_extension("arm7.elf");
if arm7.exists() {
return arm7;
}
let blocksds= env::var("BLOCKSDS").unwrap_or("/opt/wonderful/thirdparty/blocksds/core".to_owned());
PathBuf::from(format!("{}/sys/default_arm7/arm7.elf",blocksds))
} }
} }

View File

@ -1,12 +1,11 @@
use std::process; use std::{env, process};
use cargo_3ds::command::Cargo; use cargo_nds::command::Cargo;
use cargo_3ds::{check_rust_version, run_cargo}; use cargo_nds::{check_rust_version, run_cargo};
use clap::Parser; use clap::Parser;
fn main() { fn main() {
check_rust_version(); check_rust_version();
let Cargo::Input(mut input) = Cargo::parse(); let Cargo::Input(mut input) = Cargo::parse();
let message_format = match input.cmd.extract_message_format() { let message_format = match input.cmd.extract_message_format() {