diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cee5de..bef2e82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,12 +7,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] +- [#96] Add support for @file args - [#94] Refactor tests - [#93] Remove TryInto import (in prelude since 2021 edition) - [#92] Recommend `target.'cfg(..)'.linker` for recent Cargo versions - [#90] Configure release-plz - [#88] Setup release-plz +[#96]: https://github.com/knurling-rs/flip-link/pull/96 [#94]: https://github.com/knurling-rs/flip-link/pull/94 [#93]: https://github.com/knurling-rs/flip-link/pull/93 [#92]: https://github.com/knurling-rs/flip-link/pull/92 diff --git a/src/argument_parser.rs b/src/argument_parser.rs index 14f13c2..be5661a 100644 --- a/src/argument_parser.rs +++ b/src/argument_parser.rs @@ -1,4 +1,9 @@ -use std::{borrow::Cow, path::PathBuf}; +use std::{ + borrow::Cow, + fs::File, + io::{BufRead, BufReader}, + path::PathBuf, +}; /// Get `output_path`, specified by `-o` pub fn get_output_path(args: &[String]) -> crate::Result<&String> { @@ -22,3 +27,36 @@ pub fn get_search_targets(args: &[String]) -> Vec> { .filter_map(|arg| arg.strip_prefix("-T").map(Cow::Borrowed)) .collect() } + +/// Exapnds @file arguments into the file's contents +pub fn expand_files(args: &[String]) -> Vec { + let mut expanded = Vec::with_capacity(args.len()); + + for arg in args { + if let Some(arg) = arg.strip_prefix('@') { + // The normal linker was able to open the file, so this *should* never panic + let file = File::open(arg).unwrap_or_else(|e| { + panic!("Unable to open {arg}, this should never happen and should be reported: {e}") + }); + let reader = BufReader::new(file); + for line in reader.lines() { + // Same as above, normal linker succeeded so we should too + let line = line.unwrap_or_else(|e| { + panic!( + "Invalid file {arg}, this should never happen and should be reported: {e}" + ) + }); + // Remove quotes if they exist + if line.starts_with('"') && line.ends_with('"') { + expanded.push(line[1..line.len() - 1].to_owned()); + } else { + expanded.push(line.to_owned()); + } + } + } else { + expanded.push(arg.clone()); + } + } + + expanded +} diff --git a/src/main.rs b/src/main.rs index 3125bba..eac095d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -27,22 +27,23 @@ fn notmain() -> Result { env_logger::init(); // NOTE `skip` the name/path of the binary (first argument) - let args = env::args().skip(1).collect::>(); + let raw_args = env::args().skip(1).collect::>(); { - let exit_status = linking::link_normally(&args)?; + let exit_status = linking::link_normally(&raw_args)?; if !exit_status.success() { eprintln!( "\nflip-link: the native linker failed to link the program normally; \ - please check your project configuration and linker scripts" + please check your project configuration and linker scripts" ); return Ok(exit_status.code().unwrap_or(EXIT_CODE_FAILURE)); } // if linking succeeds then linker scripts are well-formed; we'll rely on that in the parser } + let expanded_args = argument_parser::expand_files(&raw_args); let current_dir = env::current_dir()?; - let linker_scripts = get_linker_scripts(&args, ¤t_dir)?; + let linker_scripts = get_linker_scripts(&expanded_args, ¤t_dir)?; // here we assume that we'll end with the same linker script as LLD // I'm unsure about how LLD picks a linker script when there are multiple candidates in the @@ -59,7 +60,7 @@ fn notmain() -> Result { let (ram_linker_script, ram_entry) = ram_path_entry.ok_or("MEMORY.RAM not found after scanning linker scripts")?; - let output_path = argument_parser::get_output_path(&args)?; + let output_path = argument_parser::get_output_path(&expanded_args)?; let elf = fs::read(output_path)?; let object = object::File::parse(elf.as_slice())?; @@ -99,7 +100,7 @@ fn notmain() -> Result { } new_linker_script.flush()?; - let exit_status = linking::link_modified(&args, ¤t_dir, tempdir, new_origin)?; + let exit_status = linking::link_modified(&raw_args, ¤t_dir, tempdir, new_origin)?; Ok(exit_status) })?;