forked from rust-lang/rust
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rollup merge of rust-lang#63175 - jsgf:argsfile, r=alexcrichton
rustc: implement argsfiles for command line Many tools, such as gcc and gnu-ld, support "args files" - that is, being able to specify @file on the command line. This causes `file` to be opened and parsed for command line options. They're separated with whitespace; whitespace can be quoted with double or single quotes, and everything can be \\-escaped. Args files may recursively include other args files via `@file2`. See https://sourceware.org/binutils/docs/ld/Options.html#Options for the documentation of gnu-ld's @file parameters. This is useful for very large command lines, or when command lines are being generated into files by other tooling.
- Loading branch information
Showing
11 changed files
with
307 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
use std::env; | ||
use std::error; | ||
use std::fmt; | ||
use std::fs; | ||
use std::io; | ||
use std::str; | ||
use std::sync::atomic::{AtomicBool, Ordering}; | ||
|
||
#[cfg(test)] | ||
mod tests; | ||
|
||
static USED_ARGSFILE_FEATURE: AtomicBool = AtomicBool::new(false); | ||
|
||
pub fn used_unstable_argsfile() -> bool { | ||
USED_ARGSFILE_FEATURE.load(Ordering::Relaxed) | ||
} | ||
|
||
pub struct ArgsIter { | ||
base: env::ArgsOs, | ||
file: std::vec::IntoIter<String>, | ||
} | ||
|
||
impl ArgsIter { | ||
pub fn new() -> Self { | ||
ArgsIter { base: env::args_os(), file: vec![].into_iter() } | ||
} | ||
} | ||
|
||
impl Iterator for ArgsIter { | ||
type Item = Result<String, Error>; | ||
|
||
fn next(&mut self) -> Option<Self::Item> { | ||
loop { | ||
if let Some(line) = self.file.next() { | ||
return Some(Ok(line)); | ||
} | ||
|
||
let arg = | ||
self.base.next().map(|arg| arg.into_string().map_err(|_| Error::Utf8Error(None))); | ||
match arg { | ||
Some(Err(err)) => return Some(Err(err)), | ||
Some(Ok(ref arg)) if arg.starts_with("@") => { | ||
let path = &arg[1..]; | ||
let file = match fs::read_to_string(path) { | ||
Ok(file) => { | ||
USED_ARGSFILE_FEATURE.store(true, Ordering::Relaxed); | ||
file | ||
} | ||
Err(ref err) if err.kind() == io::ErrorKind::InvalidData => { | ||
return Some(Err(Error::Utf8Error(Some(path.to_string())))); | ||
} | ||
Err(err) => return Some(Err(Error::IOError(path.to_string(), err))), | ||
}; | ||
self.file = | ||
file.lines().map(ToString::to_string).collect::<Vec<_>>().into_iter(); | ||
} | ||
Some(Ok(arg)) => return Some(Ok(arg)), | ||
None => return None, | ||
} | ||
} | ||
} | ||
} | ||
|
||
#[derive(Debug)] | ||
pub enum Error { | ||
Utf8Error(Option<String>), | ||
IOError(String, io::Error), | ||
} | ||
|
||
impl fmt::Display for Error { | ||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
match self { | ||
Error::Utf8Error(None) => write!(fmt, "Utf8 error"), | ||
Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {}", path), | ||
Error::IOError(path, err) => write!(fmt, "IO Error: {}: {}", path, err), | ||
} | ||
} | ||
} | ||
|
||
impl error::Error for Error { | ||
fn description(&self) -> &'static str { | ||
"argument error" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
use super::*; | ||
|
||
use std::str; | ||
|
||
fn want_args(v: impl IntoIterator<Item = &'static str>) -> Vec<String> { | ||
v.into_iter().map(String::from).collect() | ||
} | ||
|
||
fn got_args(file: &[u8]) -> Result<Vec<String>, Error> { | ||
let ret = str::from_utf8(file) | ||
.map_err(|_| Error::Utf8Error(None))? | ||
.lines() | ||
.map(ToString::to_string) | ||
.collect::<Vec<_>>(); | ||
Ok(ret) | ||
} | ||
|
||
#[test] | ||
fn nothing() { | ||
let file = b""; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec![])); | ||
} | ||
|
||
#[test] | ||
fn empty() { | ||
let file = b"\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec![""])); | ||
} | ||
|
||
#[test] | ||
fn simple() { | ||
let file = b"foo"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo"])); | ||
} | ||
|
||
#[test] | ||
fn simple_eol() { | ||
let file = b"foo\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo"])); | ||
} | ||
|
||
#[test] | ||
fn multi() { | ||
let file = b"foo\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_eol() { | ||
let file = b"foo\nbar\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty() { | ||
let file = b"foo\n\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_eol() { | ||
let file = b"foo\n\nbar\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_start() { | ||
let file = b"\nfoo\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["", "foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_end() { | ||
let file = b"foo\nbar\n\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar", ""])); | ||
} | ||
|
||
#[test] | ||
fn simple_eol_crlf() { | ||
let file = b"foo\r\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo"])); | ||
} | ||
|
||
#[test] | ||
fn multi_crlf() { | ||
let file = b"foo\r\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_eol_crlf() { | ||
let file = b"foo\r\nbar\r\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_crlf() { | ||
let file = b"foo\r\n\r\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_eol_crlf() { | ||
let file = b"foo\r\n\r\nbar\r\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_start_crlf() { | ||
let file = b"\r\nfoo\r\nbar"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["", "foo", "bar"])); | ||
} | ||
|
||
#[test] | ||
fn multi_empty_end_crlf() { | ||
let file = b"foo\r\nbar\r\n\r\n"; | ||
|
||
assert_eq!(got_args(file).unwrap(), want_args(vec!["foo", "bar", ""])); | ||
} | ||
|
||
#[test] | ||
fn bad_utf8() { | ||
let file = b"foo\x80foo"; | ||
|
||
match got_args(file).unwrap_err() { | ||
Error::Utf8Error(_) => (), | ||
bad => panic!("bad err: {:?}", bad), | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--cfg | ||
unbroken� |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
// Check to see if we can get parameters from an @argsfile file | ||
// | ||
// build-fail | ||
// normalize-stderr-test: "Argument \d+" -> "Argument $$N" | ||
// compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile-badutf8.args | ||
|
||
#[cfg(not(cmdline_set))] | ||
compile_error!("cmdline_set not set"); | ||
|
||
#[cfg(not(unbroken))] | ||
compile_error!("unbroken not set"); | ||
|
||
fn main() { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
error: Argument $N is not valid: Utf8 error in $DIR/commandline-argfile-badutf8.args | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Check to see if we can get parameters from an @argsfile file | ||
// | ||
// build-fail | ||
// normalize-stderr-test: "Argument \d+" -> "Argument $$N" | ||
// normalize-stderr-test: "os error \d+" -> "os error $$ERR" | ||
// compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile-missing.args | ||
|
||
#[cfg(not(cmdline_set))] | ||
compile_error!("cmdline_set not set"); | ||
|
||
#[cfg(not(unbroken))] | ||
compile_error!("unbroken not set"); | ||
|
||
fn main() { | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
error: Argument $N is not valid: IO Error: $DIR/commandline-argfile-missing.args: No such file or directory (os error $ERR) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
--cfg | ||
unbroken |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Check to see if we can get parameters from an @argsfile file | ||
// | ||
// build-pass | ||
// compile-flags: --cfg cmdline_set @{{src-base}}/commandline-argfile.args | ||
|
||
#[cfg(not(cmdline_set))] | ||
compile_error!("cmdline_set not set"); | ||
|
||
#[cfg(not(unbroken))] | ||
compile_error!("unbroken not set"); | ||
|
||
fn main() { | ||
} |