in src/compiler/gcc.rs [975:1021]
fn next(&mut self) -> Option<OsString> {
loop {
let arg = self.stack.pop()?;
let file = match arg.split_prefix("@") {
Some(arg) => self.cwd.join(arg),
None => return Some(arg),
};
// According to gcc [1], @file means:
//
// Read command-line options from file. The options read are
// inserted in place of the original @file option. If file does
// not exist, or cannot be read, then the option will be
// treated literally, and not removed.
//
// Options in file are separated by whitespace. A
// whitespace character may be included in an option by
// surrounding the entire option in either single or double
// quotes. Any character (including a backslash) may be
// included by prefixing the character to be included with
// a backslash. The file may itself contain additional
// @file options; any such options will be processed
// recursively.
//
// So here we interpret any I/O errors as "just return this
// argument". Currently we don't implement handling of arguments
// with quotes, so if those are encountered we just pass the option
// through literally anyway.
//
// At this time we interpret all `@` arguments above as non
// cacheable, so if we fail to interpret this we'll just call the
// compiler anyway.
//
// [1]: https://gcc.gnu.org/onlinedocs/gcc/Overall-Options.html#Overall-Options
let mut contents = String::new();
let res = File::open(&file).and_then(|mut f| f.read_to_string(&mut contents));
if let Err(e) = res {
debug!("failed to read @-file `{}`: {}", file.display(), e);
return Some(arg);
}
if contents.contains('"') || contents.contains('\'') {
return Some(arg);
}
let new_args = contents.split_whitespace().collect::<Vec<_>>();
self.stack.extend(new_args.iter().rev().map(|s| s.into()));
}
}