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]]
name = "cargo-3ds"
name = "cargo-nds"
version = "0.1.2"
dependencies = [
"cargo_metadata",
@ -91,15 +91,16 @@ dependencies = [
[[package]]
name = "cargo_metadata"
version = "0.14.2"
version = "0.18.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4acbb09d9ee8e23699b9634375c72795d095bf268439da88562cf9b501f181fa"
checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037"
dependencies = [
"camino",
"cargo-platform",
"semver",
"serde",
"serde_json",
"thiserror",
]
[[package]]
@ -149,6 +150,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7"
[[package]]
name = "equivalent"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
[[package]]
name = "errno"
version = "0.3.8"
@ -159,12 +166,28 @@ dependencies = [
"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]]
name = "heck"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]]
name = "itoa"
version = "1.0.9"
@ -184,19 +207,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "969488b55f8ac402214f3f5fd243ebb7206cf82de60d3172994707a4bcc2b829"
[[package]]
name = "proc-macro2"
version = "1.0.70"
name = "memchr"
version = "2.7.2"
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 = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.33"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
dependencies = [
"proc-macro2",
]
@ -269,6 +298,15 @@ dependencies = [
"serde",
]
[[package]]
name = "serde_spanned"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1"
dependencies = [
"serde",
]
[[package]]
name = "shlex"
version = "1.2.0"
@ -283,9 +321,9 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "2.0.39"
version = "2.0.60"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a"
checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3"
dependencies = [
"proc-macro2",
"quote",
@ -309,12 +347,57 @@ dependencies = [
]
[[package]]
name = "toml"
version = "0.5.11"
name = "thiserror"
version = "1.0.59"
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 = [
"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]]
@ -460,3 +543,12 @@ name = "windows_x86_64_msvc"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
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]
name = "cargo-3ds"
name = "cargo-nds"
version = "0.1.2"
authors = ["Rust3DS Org", "Andrea Ciliberti <meziu210@icloud.com>"]
description = "Cargo wrapper for developing Nintendo 3DS homebrew apps"
repository = "https://github.com/rust3ds/cargo-3ds"
keywords = ["3ds", "homebrew"]
authors = ["Rustnds Org", "Andrea Ciliberti <meziu210@icloud.com>"]
description = "Cargo wrapper for developing Nintendo nds homebrew apps"
repository = "https://github.com/rustnds/cargo-nds"
keywords = ["nds", "homebrew"]
categories = ["command-line-utilities", "development-tools::cargo-plugins"]
exclude = [".github"]
license = "MIT OR Apache-2.0"
edition = "2021"
[dependencies]
cargo_metadata = "0.14.0"
cargo_metadata = "0.18.1"
rustc_version = "0.4.0"
semver = "1.0.10"
serde = { version = "1.0.139", features = ["derive"] }
tee = "0.1.0"
toml = "0.5.6"
toml = "0.8.12"
clap = { version = "4.0.15", features = ["derive", "wrap_help"] }
shlex = "1.1.0"
serde_json = "1.0.108"

View File

@ -1,4 +1,4 @@
use std::fs;
use std::{env, fs};
use std::io::Read;
use std::process::Stdio;
use std::sync::OnceLock;
@ -6,12 +6,12 @@ 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_nds, cargo, get_metadata, link, print_command, NDSConfig};
#[derive(Parser, Debug)]
#[command(name = "cargo", bin_name = "cargo")]
pub enum Cargo {
#[command(name = "3ds")]
#[command(name = "nds")]
Input(Input),
}
@ -21,7 +21,7 @@ pub struct Input {
#[command(subcommand)]
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
/// `-- -v` to the end of the command line.
#[arg(long, short = 'v', global = true)]
@ -34,32 +34,32 @@ pub struct Input {
}
/// 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
/// 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)]
#[command(allow_external_subcommands = true)]
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),
/// Builds an executable and sends it to a device with `3dslink`.
/// Builds an executable and sends it to a device with `dslink`.
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
/// unit tests (which require a custom test runner).
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),
// 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
// 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")]
Passthrough(Vec<String>),
}
@ -73,7 +73,7 @@ pub struct RemainingArgs {
///
/// To pass arguments to an executable being run, a *second* `--` must be
/// 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`.
#[arg(
trailing_var_arg = true,
@ -97,23 +97,23 @@ pub struct Build {
pub struct Run {
/// 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.
#[arg(long, short = 'a')]
pub address: Option<std::net::Ipv4Addr>,
/// Set the 0th argument of the executable when running it. Corresponds to
/// 3dslink's `--argv0` argument.
/// ndslink's `--argv0` argument.
#[arg(long, short = '0')]
pub argv0: Option<String>,
/// Start the 3dslink server after sending the executable. Corresponds to
/// 3dslink's `--server` argument.
/// Start the ndslink server after sending the executable. Corresponds to
/// ndslink's `--server` argument.
#[arg(long, short = 's', default_value_t = false)]
pub server: bool,
/// 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`
#[arg(long)]
pub retries: Option<usize>,
@ -133,7 +133,7 @@ pub struct Test {
pub no_run: bool,
/// 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.
#[arg(long)]
pub doc: bool,
@ -158,7 +158,7 @@ impl CargoCmd {
/// Returns the additional arguments run by the "official" cargo subcommand.
pub fn cargo_args(&self) -> Vec<String> {
match self {
CargoCmd::Build(build) => build.passthrough.cargo_args(),
CargoCmd::Build(build) =>build.passthrough.cargo_args(),
CargoCmd::Run(run) => run.build_args.passthrough.cargo_args(),
CargoCmd::Test(test) => test.cargo_args(),
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
///
@ -203,13 +203,13 @@ impl CargoCmd {
)
}
/// Whether or not this command should build a 3DSX executable file.
pub fn should_build_3dsx(&self) -> bool {
/// Whether or not this command should build a ndsX executable file.
pub fn should_build_ndsx(&self) -> bool {
match self {
Self::Build(_) | CargoCmd::Run(_) => true,
&Self::Test(Test { doc, .. }) => {
if doc {
eprintln!("Documentation tests requested, no 3dsx will be built");
eprintln!("Documentation tests requested, no ndsx will be built");
false
} else {
true
@ -219,8 +219,8 @@ impl CargoCmd {
}
}
/// Whether or not the resulting executable should be sent to the 3DS with
/// `3dslink`.
/// Whether or not the resulting executable should be sent to the nds with
/// `ndslink`.
pub fn should_link_to_device(&self) -> bool {
match self {
Self::Test(Test { no_run: true, .. }) => false,
@ -247,7 +247,7 @@ impl CargoCmd {
if let Self::Test(Test { doc: true, .. }) = self {
// We don't care about JSON output for doctests since we're not
// building any 3dsx etc. Just use the default output as it's more
// building any ndsx etc. Just use the default output as it's more
// readable compared to DEFAULT_MESSAGE_FORMAT
Ok(Some(String::from("human")))
} else {
@ -293,11 +293,11 @@ impl CargoCmd {
///
/// # Examples
///
/// - `cargo 3ds build` and other "build" commands will use their callbacks to build the final `.3dsx` file and link it.
/// - `cargo 3ds new` and other generic commands will use their callbacks to make 3ds-specific changes to the environment.
/// - `cargo nds build` and other "build" commands will use their callbacks to build the final `.ndsx` file and link it.
/// - `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]) {
// 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");
Some(get_metadata(messages))
@ -343,78 +343,45 @@ impl RemainingArgs {
}
impl Build {
/// Callback for `cargo 3ds build`.
/// Callback for `cargo nds build`.
///
/// This callback handles building the application as a `.3dsx` file.
fn callback(&self, config: &Option<CTRConfig>) {
/// This callback handles building the application as a `.ndsx` file.
fn callback(&self, config: &Option<NDSConfig>) {
if let Some(config) = config {
eprintln!("Building smdh: {}", config.path_smdh().display());
build_smdh(config, self.verbose);
eprintln!("Building 3dsx: {}", config.path_3dsx().display());
build_3dsx(config, self.verbose);
eprintln!("Building nds: {}", config.path_nds().display());
build_nds(config, self.verbose);
}
}
}
impl Run {
/// Get the args to pass to `3dslink` based on these options.
pub fn get_3dslink_args(&self) -> Vec<String> {
/// Get the args to pass to `ndslink` based on these options.
pub fn get_dslink_args(&self) -> Vec<String> {
let mut args = Vec::new();
if let Some(address) = self.address {
args.extend(["--address".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.extend(["-a".to_string(), address.to_string()]);
}
args
}
/// Callback for `cargo 3ds run`.
/// Callback for `cargo nds run`.
///
/// This callback handles launching the application via `3dslink`.
fn callback(&self, config: &Option<CTRConfig>) {
/// This callback handles launching the application via `dslink`.
fn callback(&self, config: &Option<NDSConfig>) {
// Run the normal "build" callback
self.build_args.callback(config);
if !self.use_custom_runner() {
if let Some(cfg) = config {
eprintln!("Running 3dslink");
eprintln!("Running dslink");
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,
/// and takes into account the usual ways Cargo looks for its
/// [configuration](https://doc.rust-lang.org/cargo/reference/config.html):
@ -426,14 +393,16 @@ impl Run {
static HAS_RUNNER: OnceLock<bool> = OnceLock::new();
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);
cmd.args([
// https://github.com/rust-lang/cargo/issues/9301
"-Z",
"unstable-options",
"config",
"get",
"target.armv6k-nintendo-3ds.runner",
"build-std=core,alloc",
"--target",
"./armv5te-nintendo-ds.json"
])
.stdout(Stdio::null())
.stderr(Stdio::null());
@ -458,10 +427,10 @@ impl Run {
}
impl Test {
/// Callback for `cargo 3ds test`.
/// Callback for `cargo nds test`.
///
/// This callback handles launching the application via `3dslink`.
fn callback(&self, config: &Option<CTRConfig>) {
/// This callback handles launching the application via `ndslink`.
fn callback(&self, config: &Option<NDSConfig>) {
if self.no_run {
// If the tests don't have to run, use the "build" callback
self.run_args.build_args.callback(config);
@ -479,7 +448,7 @@ impl Test {
fn cargo_args(&self) -> Vec<String> {
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.
//
// 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"
"#;
@ -541,9 +510,9 @@ fn main() {
"#;
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) {
// Commmit changes to the project only if is meant to be a binary
if self.cargo_args.args.contains(&"--lib".to_string()) {
@ -672,7 +641,7 @@ mod tests {
expected_exe: &["bar"],
},
] {
let input: Vec<&str> = ["cargo", "3ds", "run"]
let input: Vec<&str> = ["cargo", "nds", "run"]
.iter()
.chain(param.input)
.copied()

View File

@ -21,7 +21,7 @@ use crate::graph::UnitGraph;
/// parsing and returning the messages from the spawned process.
///
/// 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>) {
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) {
// 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
// information out of it that we need for 3dsxtool etc.
// information out of it that we need for ndstool etc.
(Some(_), _) |
// Rustdoc unfortunately prints to stdout for compile errors, so
// 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.
///
/// 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.
pub fn make_cargo_command(input: &Input, message_format: &Option<String>) -> Command {
let devkitpro =
env::var("DEVKITPRO").expect("DEVKITPRO is not defined as an environment variable");
// 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 blocksds = env::var("BLOCKSDS").unwrap_or("/opt/wonderful/thirdparty/blocksds/core".to_owned());
let rustflags = format!("-C link-args=-specs={blocksds}/sys/crts/ds_arm9.specs");
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() {
command
.arg("--target")
.arg("armv6k-nintendo-3ds")
.arg("armv5te-nintendo-ds.json")
.arg("-Z")
.arg("build-std=core,alloc")
.arg("--message-format")
.arg(
message_format
.as_deref()
.unwrap_or(CargoCmd::DEFAULT_MESSAGE_FORMAT),
);
let sysroot = find_sysroot();
if !sysroot.join("lib/rustlib/armv6k-nintendo-3ds").exists() {
eprintln!("No pre-build std found, using build-std");
// 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 {
@ -212,10 +203,10 @@ 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!("cargo-nds requires a nightly rustc version.");
eprintln!(
"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."
);
process::exit(1);
@ -237,17 +228,17 @@ pub fn check_rust_version() {
};
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");
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
/// in [`build_smdh`], [`build_3dsx`], and [`link`].
pub fn get_metadata(messages: &[Message]) -> CTRConfig {
/// in [`build_nds`], and [`link`].
pub fn get_metadata(messages: &[Message]) -> NDSConfig {
let metadata = MetadataCommand::new()
.no_deps()
.exec()
@ -275,12 +266,12 @@ pub fn get_metadata(messages: &[Message]) -> CTRConfig {
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() {
icon = format!(
"{}/libctru/default_icon.png",
env::var("DEVKITPRO").unwrap()
"{}/sys/icon.bmp",
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
};
CTRConfig {
NDSConfig {
name,
author,
description: package
.description
.clone()
.unwrap_or_else(|| String::from("Homebrew Application")),
icon,
icon : icon,
target_path: artifact.executable.unwrap().into(),
cargo_manifest_path: package.manifest_path.into(),
}
}
/// 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, verbose: bool) {
let mut command = Command::new("smdhtool");
/// Builds the nds using `ndstool`.
/// This will fail if `ndstool` is not within the running directory or in a directory found in $PATH
pub fn build_nds(config: &NDSConfig, verbose: bool) {
let mut command = Command::new("ndstool");
let banner_text = format!("\"{};{};{}\"", &config.name, &config.description, &config.author);
command
.arg("--create")
.arg(&config.name)
.arg(&config.description)
.arg(&config.author)
.arg("-c")
.arg(config.path_nds())
.arg("-9")
.arg(config.path_arm9())
.arg("-7")
.arg(config.path_arm7())
.arg("-b")
.arg(&config.icon)
.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) {
let mut command = Command::new("3dsxtool");
command
.arg(&config.target_path)
.arg(config.path_3dsx())
.arg(format!("--smdh={}", config.path_smdh().to_string_lossy()));
.arg(banner_text);
// 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()));
command.arg("-d")
.arg(&romfs_path);
} else if !is_default_romfs {
eprintln!(
"Could not find configured RomFS dir: {}",
@ -374,7 +343,7 @@ pub fn build_3dsx(config: &CTRConfig, verbose: bool) {
.stdout(Stdio::inherit())
.stderr(Stdio::inherit())
.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();
@ -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`.
/// This will fail if `3dslink` is not within the running directory or in a directory found in $PATH
pub fn link(config: &CTRConfig, run_args: &Run, verbose: bool) {
let mut command = Command::new("3dslink");
/// Link the generated nds to a ds to execute and test using `dslink`.
/// This will fail if `dslink` is not within the running directory or in a directory found in $PATH
pub fn link(config: &NDSConfig, run_args: &Run, verbose: bool) {
let mut command = Command::new("dslink");
command
.arg(config.path_3dsx())
.args(run_args.get_3dslink_args())
.args(run_args.get_dslink_args())
.arg(config.path_nds())
.stdin(Stdio::inherit())
.stdout(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.
/// 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_str = std::fs::read_to_string(manifest_path)
.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(|table| table.get("metadata"))
.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(|table| table.get("romfs_dir"))
.and_then(|table| table.get("romfs"))
.and_then(toml::Value::as_str)
.unwrap_or_else(|| {
is_default = true;
@ -437,8 +406,41 @@ pub fn get_romfs_path(config: &CTRConfig) -> (PathBuf, bool) {
(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)]
pub struct CTRConfig {
pub struct NDSConfig {
name: String,
author: String,
description: String,
@ -447,13 +449,20 @@ pub struct CTRConfig {
cargo_manifest_path: PathBuf,
}
impl CTRConfig {
pub fn path_3dsx(&self) -> PathBuf {
self.target_path.with_extension("3dsx")
impl NDSConfig {
pub fn path_nds(&self) -> PathBuf {
self.target_path.with_extension("nds")
}
pub fn path_smdh(&self) -> PathBuf {
self.target_path.with_extension("smdh")
pub fn path_arm9(&self) -> PathBuf {
self.target_path.with_extension("arm9.elf")
}
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_3ds::{check_rust_version, run_cargo};
use cargo_nds::command::Cargo;
use cargo_nds::{check_rust_version, run_cargo};
use clap::Parser;
fn main() {
check_rust_version();
let Cargo::Input(mut input) = Cargo::parse();
let message_format = match input.cmd.extract_message_format() {