in src/compiler/rust.rs [1065:1305]
fn parse_arguments(arguments: &[OsString], cwd: &Path) -> CompilerArguments<ParsedArguments> {
let mut args = vec![];
let mut emit: Option<HashSet<String>> = None;
let mut input = None;
let mut output_dir = None;
let mut crate_name = None;
let mut crate_types = CrateTypes {
rlib: false,
staticlib: false,
};
let mut extra_filename = None;
let mut externs = vec![];
let mut crate_link_paths = vec![];
let mut static_lib_names = vec![];
let mut static_link_paths: Vec<PathBuf> = vec![];
let mut color_mode = ColorMode::Auto;
let mut has_json = false;
let mut profile = None;
let mut gcno = false;
let mut target_json = None;
for arg in ArgsIter::new(arguments.iter().cloned(), &ARGS[..]) {
let arg = try_or_cannot_cache!(arg, "argument parse");
match arg.get_data() {
Some(TooHardFlag) | Some(TooHardPath(_)) => {
cannot_cache!(arg.flag_str().expect("Can't be Argument::Raw/UnknownFlag",))
}
Some(NotCompilationFlag) | Some(NotCompilation(_)) => {
return CompilerArguments::NotCompilation
}
Some(LinkLibrary(ArgLinkLibrary { kind, name })) => {
if kind == "static" {
static_lib_names.push(name.to_owned())
}
}
Some(LinkPath(ArgLinkPath { kind, path })) => {
// "crate" is not typically necessary as cargo will normally
// emit explicit --extern arguments
if kind == "crate" || kind == "dependency" || kind == "all" {
crate_link_paths.push(cwd.join(path))
}
if kind == "native" || kind == "all" {
static_link_paths.push(cwd.join(path))
}
}
Some(Emit(value)) => {
if emit.is_some() {
// We don't support passing --emit more than once.
cannot_cache!("more than one --emit");
}
emit = Some(value.split(',').map(str::to_owned).collect())
}
Some(CrateType(ArgCrateTypes {
rlib,
staticlib,
others,
})) => {
// We can't cache non-rlib/staticlib crates, because rustc invokes the
// system linker to link them, and we don't know about all the linker inputs.
if !others.is_empty() {
let others: Vec<&str> = others.iter().map(String::as_str).collect();
let others_string = others.join(",");
cannot_cache!("crate-type", others_string)
}
crate_types.rlib |= rlib;
crate_types.staticlib |= staticlib;
}
Some(CrateName(value)) => crate_name = Some(value.clone()),
Some(OutDir(value)) => output_dir = Some(value.clone()),
Some(Extern(ArgExtern { path, .. })) => externs.push(path.clone()),
Some(CodeGen(ArgCodegen { opt, value })) => {
match (opt.as_ref(), value) {
("extra-filename", Some(value)) => extra_filename = Some(value.to_owned()),
("extra-filename", None) => cannot_cache!("extra-filename"),
("profile-use", Some(v)) => profile = Some(v.to_string()),
// Incremental compilation makes a mess of sccache's entire world
// view. It produces additional compiler outputs that we don't cache,
// and just letting rustc do its work in incremental mode is likely
// to be faster than trying to fetch a result from cache anyway, so
// don't bother caching compiles where it's enabled currently.
// Longer-term we would like to figure out better integration between
// sccache and rustc in the incremental scenario:
// https://github.com/mozilla/sccache/issues/236
("incremental", _) => cannot_cache!("incremental"),
(_, _) => (),
}
}
Some(Unstable(ArgUnstable { opt, value })) => match value.as_deref() {
Some("y") | Some("yes") | Some("on") | None if opt == "profile" => {
gcno = true;
}
_ => (),
},
Some(Color(value)) => {
// We'll just assume the last specified value wins.
color_mode = match value.as_ref() {
"always" => ColorMode::On,
"never" => ColorMode::Off,
_ => ColorMode::Auto,
};
}
Some(Json(_)) => {
has_json = true;
}
Some(PassThrough(_)) => (),
Some(Target(target)) => match target {
ArgTarget::Path(json_path) => target_json = Some(json_path.to_owned()),
ArgTarget::Unsure(_) => cannot_cache!("target unsure"),
ArgTarget::Name(_) => (),
},
None => {
match arg {
Argument::Raw(ref val) => {
if input.is_some() {
// Can't cache compilations with multiple inputs.
cannot_cache!("multiple input files");
}
input = Some(val.clone());
}
Argument::UnknownFlag(_) => {}
_ => unreachable!(),
}
}
}
// We'll drop --color arguments, we're going to pass --color=always and the client will
// strip colors if necessary.
match arg.get_data() {
Some(Color(_)) => {}
_ => args.push(arg.normalize(NormalizedDisposition::Separated)),
}
}
// Unwrap required values.
macro_rules! req {
($x:ident) => {
let $x = if let Some($x) = $x {
$x
} else {
debug!("Can't cache compilation, missing `{}`", stringify!($x));
cannot_cache!(concat!("missing ", stringify!($x)));
};
};
}
// We don't actually save the input value, but there needs to be one.
req!(input);
drop(input);
req!(output_dir);
req!(emit);
req!(crate_name);
// We won't cache invocations that are not producing
// binary output.
if !emit.is_empty() && !emit.contains("link") && !emit.contains("metadata") {
return CompilerArguments::NotCompilation;
}
// If it's not an rlib and not a staticlib then crate-type wasn't passed,
// so it will usually be inferred as a binary, though the `#![crate_type`
// annotation may dictate otherwise - either way, we don't know what to do.
if let CrateTypes {
rlib: false,
staticlib: false,
} = crate_types
{
cannot_cache!("crate-type", "No crate-type passed".to_owned())
}
// We won't cache invocations that are outputting anything but
// linker output and dep-info.
if emit.iter().any(|e| !ALLOWED_EMIT.contains(e.as_str())) {
cannot_cache!("unsupported --emit");
}
// Figure out the dep-info filename, if emitting dep-info.
let dep_info = if emit.contains("dep-info") {
let mut dep_info = crate_name.clone();
if let Some(extra_filename) = extra_filename.clone() {
dep_info.push_str(&extra_filename[..]);
}
dep_info.push_str(".d");
Some(dep_info)
} else {
None
};
// Ignore profile is `link` is not in emit which means we are running `cargo check`.
let profile = if emit.contains("link") { profile } else { None };
// Figure out the gcno filename, if producing gcno files with `-Zprofile`.
let gcno = if gcno && emit.contains("link") {
let mut gcno = crate_name.clone();
if let Some(extra_filename) = extra_filename {
gcno.push_str(&extra_filename[..]);
}
gcno.push_str(".gcno");
Some(gcno)
} else {
None
};
// Locate all static libs specified on the commandline.
let staticlibs = static_lib_names
.into_iter()
.filter_map(|name| {
for path in static_link_paths.iter() {
for f in &[
format_args!("lib{}.a", name),
format_args!("{}.lib", name),
format_args!("{}.a", name),
] {
let lib_path = path.join(fmt::format(*f));
if lib_path.exists() {
return Some(lib_path);
}
}
}
// rustc will just error if there's a missing static library, so don't worry about
// it too much.
None
})
.collect();
// We'll figure out the source files and outputs later in
// `generate_hash_key` where we can run rustc.
// Cargo doesn't deterministically order --externs, and we need the hash inputs in a
// deterministic order.
externs.sort();
CompilerArguments::Ok(ParsedArguments {
arguments: args,
output_dir,
crate_types,
externs,
crate_link_paths,
staticlibs,
crate_name,
dep_info: dep_info.map(|s| s.into()),
profile: profile.map(|s| s.into()),
gcno: gcno.map(|s| s.into()),
emit,
color_mode,
has_json,
target_json,
})
}