use super::types::{CacheableLink, LinkerFamily, ParsedLinkerInvocation};
use crate::core::NormalizedPath;
pub(super) fn parse_msvc_link(tool: &str, args: Vec<String>) -> ParsedLinkerInvocation {
if args.is_empty() {
return ParsedLinkerInvocation::NonCacheable {
reason: "no arguments".to_string(),
};
}
let mut is_dll = false;
let mut output_file: Option<NormalizedPath> = None;
let mut input_files: Vec<NormalizedPath> = Vec::new();
let mut cache_relevant_flags: Vec<String> = Vec::new();
let mut has_deterministic = false;
let mut secondary_outputs: Vec<NormalizedPath> = Vec::new();
for arg in &args {
let upper = arg.to_uppercase();
if upper == "/DLL" || upper == "-DLL" {
is_dll = true;
cache_relevant_flags.push(arg.clone());
continue;
}
if upper.starts_with("/OUT:") || upper.starts_with("-OUT:") {
output_file = Some(NormalizedPath::new(&arg[5..]));
continue;
}
if upper == "/DETERMINISTIC" || upper == "-DETERMINISTIC" {
has_deterministic = true;
cache_relevant_flags.push(arg.clone());
continue;
}
if upper.starts_with("/IMPLIB:") || upper.starts_with("-IMPLIB:") {
let implib_path = NormalizedPath::new(&arg[8..]);
let exp_path = NormalizedPath::new(implib_path.with_extension("exp"));
secondary_outputs.push(implib_path);
secondary_outputs.push(exp_path);
cache_relevant_flags.push(arg.clone());
continue;
}
if arg.starts_with('/') || arg.starts_with('-') {
cache_relevant_flags.push(arg.clone());
continue;
}
input_files.push(NormalizedPath::new(arg));
}
if input_files.is_empty() {
return ParsedLinkerInvocation::NonCacheable {
reason: "no input files specified".to_string(),
};
}
let output_file = output_file.unwrap_or_else(|| {
let first = &input_files[0];
let ext = if is_dll { "dll" } else { "exe" };
NormalizedPath::new(first.with_extension(ext))
});
ParsedLinkerInvocation::Cacheable(CacheableLink {
tool: NormalizedPath::new(tool),
family: LinkerFamily::MsvcLink,
input_files,
output_file,
secondary_outputs,
cache_relevant_flags,
original_args: args,
non_deterministic: !has_deterministic,
})
}