diff options
| author | Miquel Sabaté Solà <mikisabate@gmail.com> | 2025-01-10 16:35:12 +0100 |
|---|---|---|
| committer | Miquel Sabaté Solà <mikisabate@gmail.com> | 2025-01-10 16:35:12 +0100 |
| commit | 7c09adae9e2710aefccd25b2920ca684c714ef49 (patch) | |
| tree | 45870865c7700e28d092469ee9d850cc70f3f292 /crates/xa65/src | |
| parent | 343b2a41a36286a5f950116c183cd2ae54aad1bd (diff) | |
| download | tools.nes-7c09adae9e2710aefccd25b2920ca684c714ef49.tar.gz tools.nes-7c09adae9e2710aefccd25b2920ca684c714ef49.zip | |
Introduce xa65
This is a bridge between 'nasm' and 'cl65'. That is, it runs the same
command on both 'nasm' and 'cl65', compares the resulting binaries, and
gives back the binary from 'cl65'.
Signed-off-by: Miquel Sabaté Solà <mikisabate@gmail.com>
Diffstat (limited to 'crates/xa65/src')
| -rw-r--r-- | crates/xa65/src/main.rs | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/crates/xa65/src/main.rs b/crates/xa65/src/main.rs new file mode 100644 index 0000000..9cb8a70 --- /dev/null +++ b/crates/xa65/src/main.rs @@ -0,0 +1,124 @@ +use anyhow::{bail, Context, Result}; +use clap::Parser as ClapParser; +use rand::distributions::{Alphanumeric, DistString}; +use std::path::PathBuf; +use std::process::Command; + +/// Bridge between 'nasm' and 'ca65'. +#[derive(ClapParser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// Assemble the instructions given on this file. + file: String, + + /// Linker configuration to be used. This configuration can be an identifier + /// for the configurations already baked in into this application, or it can + /// be a file path to a configuration of your choosing. See the + /// documentation for more information on this format. Defaults to 'nrom'. + #[arg(short = 'c', long)] + config: Option<String>, + + /// Place the output into the given <OUT> file. + #[arg(short = 'o', long)] + out: String, +} + +// Find the binary by `name` in "PATH". Implementation taken from: +// https://stackoverflow.com/a/37499032. +fn find_binary(name: &str) -> Option<PathBuf> { + std::env::var_os("PATH").and_then(|paths| { + std::env::split_paths(&paths) + .filter_map(|dir| { + let full_path = dir.join(name); + if full_path.is_file() { + Some(full_path) + } else { + None + } + }) + .next() + }) +} + +// Returns the path for the binaries for 'nasm' and 'cl65'. +fn get_binaries() -> Result<(PathBuf, PathBuf)> { + let nasm = match find_binary("nasm") { + Some(nasm) => nasm, + None => bail!("could not find 'nasm'".to_string()), + }; + let cl65 = match find_binary("cl65") { + Some(cl65) => cl65, + None => bail!("could not find 'cl65'".to_string()), + }; + + Ok((nasm, cl65)) +} + +fn main() -> Result<()> { + // Make sure that the binaries are there. + let (nasm, cl65) = get_binaries()?; + let args = Args::parse(); + + // Generate a temporary directory in which both binary files will be placed + // as an intermediate step. + let random_string = &Alphanumeric.sample_string(&mut rand::thread_rng(), 16); + let dir = std::env::temp_dir().join(random_string); + std::fs::create_dir(&dir)?; + + // Run 'nasm' with the given arguments. Note that we don't care whether + // 'nasm' itself errors out. + let _ = Command::new(nasm) + .arg(&args.file) + .arg("-o") + .arg(dir.join("nasm.nes")) + .arg("-c") + .arg(args.config.clone().unwrap_or("nrom65".to_string())) + .status() + .with_context(|| "could not execute 'nasm'")?; + + // Run 'cl65' with the given arguments. + let mut cl65_command = Command::new(cl65); + cl65_command + .arg("--target") + .arg("nes") + .arg(&args.file) + .arg("-o") + .arg(dir.join("cl65.nes")); + if let Some(config) = &args.config { + cl65_command.arg("-c").arg(config); + } + + // Here, and in contrast with the 'nasm' execution, we do care about the + // exit code of 'cl65'. + let out = cl65_command + .status() + .with_context(|| "could not execute 'cl65'")?; + if !out.success() { + std::process::exit(1); + } + + // Everything went fine, we should have both binaries available to be + // compared. For 'diff' actually capture the output so it does not pollute + // the shell. + let diff = Command::new("diff") + .arg(dir.join("nasm.nes")) + .arg(dir.join("cl65.nes")) + .output() + .with_context(|| "failed to run diff")?; + + // If 'diff' failed, show it but don't error out. + if !diff.status.success() { + println!( + "xa65 (error): 'nasm' and 'ca65' have a mismatch. Check the results at {}", + dir.display() + ); + } + + // And just copy one of the binaries to where it was originally requested. + // Note that the binary is the one from 'cl65' just in case 'diff' failed + // (we take 'cl65' as the source of truth). + std::fs::copy(dir.join("cl65.nes"), args.out) + .with_context(|| "could not copy the resulting binary")?; + + Ok(()) +} |
