use crate::compiler::args::*;
use crate::compiler::c::{
ArtifactDescriptor, CCompilerImpl, CCompilerKind, Language, ParsedArguments,
};
use crate::compiler::{
clang, gcc, write_temp_file, Cacheable, ColorMode, CompileCommand, CompilerArguments,
};
use crate::mock_command::{CommandCreatorSync, RunCommand};
use crate::util::run_input_output;
use crate::{counted_array, dist};
use log::Level::Debug;
use std::collections::{HashMap, HashSet};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{self, BufWriter, Write};
use std::path::{Path, PathBuf};
use std::process::{self, Stdio};
use crate::errors::*;
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Msvc {
pub includes_prefix: String,
pub is_clang: bool,
pub version: Option<String>,
}
#[async_trait]
impl CCompilerImpl for Msvc {
fn kind(&self) -> CCompilerKind {
CCompilerKind::Msvc
}
fn plusplus(&self) -> bool {
false
}
fn version(&self) -> Option<String> {
self.version.clone()
}
fn parse_arguments(
&self,
arguments: &[OsString],
cwd: &Path,
) -> CompilerArguments<ParsedArguments> {
parse_arguments(arguments, cwd, self.is_clang)
}
#[allow(clippy::too_many_arguments)]
async fn preprocess<T>(
&self,
creator: &T,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
may_dist: bool,
rewrite_includes_only: bool,
) -> Result<process::Output>
where
T: CommandCreatorSync,
{
preprocess(
creator,
executable,
parsed_args,
cwd,
env_vars,
may_dist,
&self.includes_prefix,
rewrite_includes_only,
self.is_clang,
)
.await
}
fn generate_compile_commands(
&self,
path_transformer: &mut dist::PathTransformer,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
_rewrite_includes_only: bool,
) -> Result<(CompileCommand, Option<dist::CompileCommand>, Cacheable)> {
generate_compile_commands(path_transformer, executable, parsed_args, cwd, env_vars)
}
}
#[cfg(not(windows))]
fn from_local_codepage(multi_byte_str: &[u8]) -> io::Result<String> {
String::from_utf8(multi_byte_str.to_vec())
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e))
}
#[cfg(windows)]
pub fn from_local_codepage(multi_byte_str: &[u8]) -> io::Result<String> {
let codepage = winapi::um::winnls::CP_OEMCP;
let flags = winapi::um::winnls::MB_ERR_INVALID_CHARS;
if multi_byte_str.is_empty() {
return Ok(String::new());
}
unsafe {
let len = winapi::um::stringapiset::MultiByteToWideChar(
codepage,
flags,
multi_byte_str.as_ptr() as _,
multi_byte_str.len() as i32,
std::ptr::null_mut(),
0,
);
if len > 0 {
let mut wstr: Vec<u16> = Vec::with_capacity(len as usize);
let len = winapi::um::stringapiset::MultiByteToWideChar(
codepage,
flags,
multi_byte_str.as_ptr() as _,
multi_byte_str.len() as i32,
wstr.as_mut_ptr() as _,
len,
);
if len > 0 {
wstr.set_len(len as usize);
return String::from_utf16(&wstr[0..(len as usize)])
.map_err(|e| io::Error::new(io::ErrorKind::InvalidInput, e));
}
}
Err(io::Error::last_os_error())
}
}
pub async fn detect_showincludes_prefix<T>(
creator: &T,
exe: &OsStr,
is_clang: bool,
env: Vec<(OsString, OsString)>,
pool: &tokio::runtime::Handle,
) -> Result<String>
where
T: CommandCreatorSync,
{
let (tempdir, input) =
write_temp_file(pool, "test.c".as_ref(), b"#include \"test.h\"\n".to_vec()).await?;
let exe = exe.to_os_string();
let mut creator = creator.clone();
let pool = pool.clone();
let header = tempdir.path().join("test.h");
let tempdir = pool
.spawn_blocking(move || {
let mut file = File::create(&header)?;
file.write_all(b"/* empty */\n")?;
Ok::<_, std::io::Error>(tempdir)
})
.await?
.context("Failed to write temporary file")?;
let mut cmd = creator.new_command_sync(&exe);
if is_clang {
cmd.arg("--driver-mode=cl");
}
cmd.args(&["-nologo", "-showIncludes", "-c", "-Fonul", "-I."])
.arg(&input)
.current_dir(&tempdir.path())
.stdout(Stdio::piped())
.stderr(Stdio::null());
for (k, v) in env {
cmd.env(k, v);
}
trace!("detect_showincludes_prefix: {:?}", cmd);
let output = run_input_output(cmd, None).await?;
if !output.status.success() {
bail!("Failed to detect showIncludes prefix")
}
let process::Output {
stdout: stdout_bytes,
..
} = output;
let stdout = from_local_codepage(&stdout_bytes)
.context("Failed to convert compiler stdout while detecting showIncludes prefix")?;
for line in stdout.lines() {
if !line.ends_with("test.h") {
continue;
}
for (i, c) in line.char_indices().rev() {
if c != ' ' {
continue;
}
let path = tempdir.path().join(&line[i + 1..]);
if path.exists() {
return Ok(line[..=i].to_owned());
}
}
}
drop(tempdir);
debug!(
"failed to detect showIncludes prefix with output: {}",
stdout
);
bail!("Failed to detect showIncludes prefix")
}
#[cfg(unix)]
fn encode_path(dst: &mut dyn Write, path: &Path) -> io::Result<()> {
use std::os::unix::prelude::*;
let bytes = path.as_os_str().as_bytes();
dst.write_all(bytes)
}
#[cfg(windows)]
fn encode_path(dst: &mut dyn Write, path: &Path) -> io::Result<()> {
use std::os::windows::prelude::*;
let points = path.as_os_str().encode_wide().collect::<Vec<_>>();
let bytes = wide_char_to_multi_byte(&points)?; dst.write_all(&bytes)
}
#[cfg(windows)]
pub fn wide_char_to_multi_byte(wide_char_str: &[u16]) -> io::Result<Vec<u8>> {
let codepage = winapi::um::winnls::CP_OEMCP;
let flags = 0;
if wide_char_str.is_empty() {
return Ok(Vec::new());
}
unsafe {
let len = winapi::um::stringapiset::WideCharToMultiByte(
codepage,
flags,
wide_char_str.as_ptr(),
wide_char_str.len() as i32,
std::ptr::null_mut(),
0,
std::ptr::null(),
std::ptr::null_mut(),
);
if len > 0 {
let mut astr: Vec<u8> = Vec::with_capacity(len as usize);
let len = winapi::um::stringapiset::WideCharToMultiByte(
codepage,
flags,
wide_char_str.as_ptr(),
wide_char_str.len() as i32,
astr.as_mut_ptr() as _,
len,
std::ptr::null(),
std::ptr::null_mut(),
);
if len > 0 {
astr.set_len(len as usize);
if (len as usize) == astr.len() {
return Ok(astr);
} else {
return Ok(astr[0..(len as usize)].to_vec());
}
}
}
Err(io::Error::last_os_error())
}
}
ArgData! {
TooHardFlag,
TooHard(OsString),
TooHardPath(PathBuf),
PreprocessorArgument(OsString),
PreprocessorArgumentPath(PathBuf),
SuppressCompilation,
DoCompilation,
ShowIncludes,
Output(PathBuf),
DepFile(PathBuf),
ProgramDatabase(PathBuf),
DebugInfo,
PassThrough, PassThroughWithPath(PathBuf), PassThroughWithSuffix(OsString), Ignore, IgnoreWithSuffix(OsString), ExtraHashFile(PathBuf),
XClang(OsString), Clang(OsString), ExternalIncludePath(PathBuf),
}
use self::ArgData::*;
macro_rules! msvc_args {
(static ARGS: [$t:ty; _] = [$($macro:ident ! ($($v:tt)*),)*]) => {
counted_array!(static ARGS: [$t; _] = [$(msvc_args!(@one "-", $macro!($($v)*)),)*]);
counted_array!(static SLASH_ARGS: [$t; _] = [$(msvc_args!(@one "/", $macro!($($v)*)),)*]);
};
(@one $prefix:expr, msvc_take_arg!($s:expr, $($t:tt)*)) => {
take_arg!(concat!($prefix, $s), $($t)+)
};
(@one $prefix:expr, msvc_flag!($s:expr, $($t:tt)+)) => {
flag!(concat!($prefix, $s), $($t)+)
};
(@one $prefix:expr, $other:expr) => { $other };
}
msvc_args!(static ARGS: [ArgInfo<ArgData>; _] = [
msvc_flag!("?", SuppressCompilation),
msvc_flag!("Brepro", PassThrough),
msvc_flag!("C", PassThrough), msvc_take_arg!("D", OsString, CanBeSeparated, PreprocessorArgument),
msvc_flag!("E", SuppressCompilation),
msvc_take_arg!("EH", OsString, Concatenated, PassThroughWithSuffix), msvc_flag!("EP", SuppressCompilation),
msvc_take_arg!("F", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("FA", OsString, Concatenated, TooHard),
msvc_flag!("FC", TooHardFlag), msvc_take_arg!("FI", PathBuf, CanBeSeparated, PreprocessorArgumentPath),
msvc_take_arg!("FR", PathBuf, Concatenated, TooHardPath),
msvc_flag!("FS", Ignore),
msvc_take_arg!("FU", PathBuf, CanBeSeparated, TooHardPath),
msvc_take_arg!("Fa", PathBuf, Concatenated, TooHardPath),
msvc_take_arg!("Fd", PathBuf, Concatenated, ProgramDatabase),
msvc_take_arg!("Fe", PathBuf, Concatenated, TooHardPath),
msvc_take_arg!("Fi", PathBuf, Concatenated, TooHardPath),
msvc_take_arg!("Fm", PathBuf, Concatenated, PassThroughWithPath), msvc_take_arg!("Fo", PathBuf, Concatenated, Output),
msvc_take_arg!("Fp", PathBuf, Concatenated, TooHardPath),
msvc_take_arg!("Fr", PathBuf, Concatenated, TooHardPath),
msvc_flag!("Fx", TooHardFlag),
msvc_flag!("GA", PassThrough),
msvc_flag!("GF", PassThrough),
msvc_flag!("GH", PassThrough),
msvc_flag!("GL", PassThrough),
msvc_flag!("GL-", PassThrough),
msvc_flag!("GR", PassThrough),
msvc_flag!("GR-", PassThrough),
msvc_flag!("GS", PassThrough),
msvc_flag!("GS-", PassThrough),
msvc_flag!("GT", PassThrough),
msvc_flag!("GX", PassThrough),
msvc_flag!("GZ", PassThrough),
msvc_flag!("Gd", PassThrough),
msvc_flag!("Ge", PassThrough),
msvc_flag!("Gh", PassThrough),
msvc_flag!("Gm", TooHardFlag),
msvc_flag!("Gr", PassThrough),
msvc_take_arg!("Gs", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("Gv", PassThrough),
msvc_flag!("Gw", PassThrough),
msvc_flag!("Gw-", PassThrough),
msvc_flag!("Gy", PassThrough),
msvc_flag!("Gy-", PassThrough),
msvc_flag!("Gz", PassThrough),
msvc_take_arg!("H", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("HELP", SuppressCompilation),
msvc_take_arg!("I", PathBuf, CanBeSeparated, PreprocessorArgumentPath),
msvc_flag!("J", PassThrough),
msvc_flag!("JMC", PassThrough),
msvc_flag!("JMC-", PassThrough),
msvc_flag!("LD", PassThrough),
msvc_flag!("LDd", PassThrough),
msvc_flag!("MD", PassThrough),
msvc_flag!("MDd", PassThrough),
msvc_take_arg!("MP", OsString, Concatenated, IgnoreWithSuffix),
msvc_flag!("MT", PassThrough),
msvc_flag!("MTd", PassThrough),
msvc_flag!("O1", PassThrough),
msvc_flag!("O2", PassThrough),
msvc_flag!("Ob0", PassThrough),
msvc_flag!("Ob1", PassThrough),
msvc_flag!("Ob2", PassThrough),
msvc_flag!("Ob3", PassThrough),
msvc_flag!("Od", PassThrough),
msvc_flag!("Og", PassThrough),
msvc_flag!("Oi", PassThrough),
msvc_flag!("Oi-", PassThrough),
msvc_flag!("Os", PassThrough),
msvc_flag!("Ot", PassThrough),
msvc_flag!("Ox", PassThrough),
msvc_flag!("Oy", PassThrough),
msvc_flag!("Oy-", PassThrough),
msvc_flag!("P", SuppressCompilation),
msvc_flag!("QIfist", PassThrough),
msvc_flag!("QIntel-jcc-erratum", PassThrough),
msvc_flag!("Qfast_transcendentals", PassThrough),
msvc_flag!("Qimprecise_fwaits", PassThrough),
msvc_flag!("Qpar", PassThrough),
msvc_flag!("Qsafe_fp_loads", PassThrough),
msvc_flag!("Qspectre", PassThrough),
msvc_flag!("Qspectre-load", PassThrough),
msvc_flag!("Qspectre-load-cf", PassThrough),
msvc_flag!("Qvec-report:1", PassThrough),
msvc_flag!("Qvec-report:2", PassThrough),
msvc_take_arg!("RTC", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("TC", PassThrough), msvc_flag!("TP", PassThrough), msvc_take_arg!("U", OsString, Concatenated, PreprocessorArgument),
msvc_take_arg!("V", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("W0", PassThrough),
msvc_flag!("W1", PassThrough),
msvc_flag!("W2", PassThrough),
msvc_flag!("W3", PassThrough),
msvc_flag!("W4", PassThrough),
msvc_flag!("WL", PassThrough),
msvc_flag!("WX", PassThrough),
msvc_flag!("Wall", PassThrough),
msvc_take_arg!("Wv:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("X", PassThrough),
msvc_take_arg!("Xclang", OsString, Separated, XClang),
msvc_flag!("Yd", PassThrough),
msvc_flag!("Z7", PassThrough), msvc_take_arg!("ZH:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("ZI", DebugInfo), msvc_flag!("ZW", PassThrough),
msvc_flag!("Za", PassThrough),
msvc_take_arg!("Zc:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("Ze", PassThrough),
msvc_flag!("Zi", DebugInfo),
msvc_flag!("Zo", PassThrough),
msvc_flag!("Zo-", PassThrough),
msvc_flag!("Zp1", PassThrough),
msvc_flag!("Zp16", PassThrough),
msvc_flag!("Zp2", PassThrough),
msvc_flag!("Zp4", PassThrough),
msvc_flag!("Zp8", PassThrough),
msvc_flag!("Zs", SuppressCompilation),
msvc_flag!("analyze", PassThrough),
msvc_flag!("analyze-", PassThrough),
msvc_take_arg!("analyze:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("arch:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("await", PassThrough),
msvc_flag!("bigobj", PassThrough),
msvc_flag!("c", DoCompilation),
msvc_take_arg!("cgthreads", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("clang:", OsString, Concatenated, Clang),
msvc_flag!("clr", PassThrough),
msvc_take_arg!("clr:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("constexpr:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("deps", PathBuf, Concatenated, DepFile),
msvc_take_arg!("diagnostics:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("doc", PathBuf, Concatenated, TooHardPath), msvc_take_arg!("errorReport:", OsString, Concatenated, PassThroughWithSuffix), msvc_take_arg!("execution-charset:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("experimental:deterministic", PassThrough),
msvc_flag!("experimental:external", PassThrough),
msvc_flag!("experimental:module", TooHardFlag),
msvc_flag!("experimental:module-", PassThrough), msvc_take_arg!("experimental:preprocessor", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("external:I", PathBuf, CanBeSeparated, ExternalIncludePath),
msvc_flag!("external:W0", PassThrough),
msvc_flag!("external:W1", PassThrough),
msvc_flag!("external:W2", PassThrough),
msvc_flag!("external:W3", PassThrough),
msvc_flag!("external:W4", PassThrough),
msvc_take_arg!("favor:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("fp:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("fsanitize-blacklist", PathBuf, Concatenated('='), ExtraHashFile),
msvc_flag!("fsyntax-only", SuppressCompilation),
msvc_take_arg!("guard:cf", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("homeparams", PassThrough),
msvc_flag!("hotpatch", PassThrough),
msvc_take_arg!("imsvc", PathBuf, CanBeSeparated, PreprocessorArgumentPath),
msvc_flag!("kernel", PassThrough),
msvc_flag!("kernel-", PassThrough),
msvc_flag!("nologo", PassThrough),
msvc_take_arg!("o", PathBuf, Separated, Output), msvc_flag!("openmp", PassThrough),
msvc_flag!("openmp:experimental", PassThrough),
msvc_flag!("permissive-", PassThrough),
msvc_flag!("sdl", PassThrough),
msvc_flag!("sdl-", PassThrough),
msvc_flag!("showIncludes", ShowIncludes),
msvc_take_arg!("source-charset:", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("std:", OsString, Concatenated, PassThroughWithSuffix),
msvc_flag!("u", PassThrough),
msvc_flag!("utf-8", PassThrough),
msvc_flag!("validate-charset", PassThrough),
msvc_flag!("validate-charset-", PassThrough),
msvc_flag!("vd0", PassThrough),
msvc_flag!("vd1", PassThrough),
msvc_flag!("vd2", PassThrough),
msvc_flag!("vmb", PassThrough),
msvc_flag!("vmg", PassThrough),
msvc_flag!("vmm", PassThrough),
msvc_flag!("vms", PassThrough),
msvc_flag!("vmv", PassThrough),
msvc_flag!("volatile:iso", PassThrough),
msvc_flag!("volatile:ms", PassThrough),
msvc_flag!("w", PassThrough),
msvc_take_arg!("w1", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("w2", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("w3", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("w4", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("wd", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("we", OsString, Concatenated, PassThroughWithSuffix),
msvc_take_arg!("winsysroot", PathBuf, CanBeSeparated, PassThroughWithPath),
msvc_take_arg!("wo", OsString, Concatenated, PassThroughWithSuffix),
take_arg!("@", PathBuf, Concatenated, TooHardPath),
]);
pub fn parse_arguments(
arguments: &[OsString],
cwd: &Path,
is_clang: bool,
) -> CompilerArguments<ParsedArguments> {
let mut output_arg = None;
let mut input_arg = None;
let mut common_args = vec![];
let mut preprocessor_args = vec![];
let mut dependency_args = vec![];
let mut extra_hash_files = vec![];
let mut compilation = false;
let mut compilation_flag = OsString::new();
let mut debug_info = false;
let mut pdb = None;
let mut depfile = None;
let mut show_includes = false;
let mut xclangs: Vec<OsString> = vec![];
let mut clangs: Vec<OsString> = vec![];
let mut profile_generate = false;
let mut multiple_input = false;
let mut multiple_input_files = Vec::new();
for arg in ArgsIter::new(arguments.iter().cloned(), (&ARGS[..], &SLASH_ARGS[..])) {
let arg = try_or_cannot_cache!(arg, "argument parse");
match arg.get_data() {
Some(PassThrough) | Some(PassThroughWithPath(_)) | Some(PassThroughWithSuffix(_)) => {}
Some(TooHardFlag) | Some(TooHard(_)) | Some(TooHardPath(_)) => {
cannot_cache!(arg.flag_str().expect("Can't be Argument::Raw/UnknownFlag",))
}
Some(DoCompilation) => {
compilation = true;
compilation_flag =
OsString::from(arg.flag_str().expect("Compilation flag expected"));
}
Some(ShowIncludes) => {
show_includes = true;
dependency_args.push(arg.to_os_string());
}
Some(Output(out)) => {
output_arg = Some(out.clone());
if out.as_os_str() == "nul" {
cannot_cache!("output to nul")
}
}
Some(DepFile(p)) => depfile = Some(p.clone()),
Some(ProgramDatabase(p)) => pdb = Some(p.clone()),
Some(DebugInfo) => debug_info = true,
Some(PreprocessorArgument(_))
| Some(PreprocessorArgumentPath(_))
| Some(ExtraHashFile(_))
| Some(Ignore)
| Some(IgnoreWithSuffix(_))
| Some(ExternalIncludePath(_)) => {}
Some(SuppressCompilation) => {
return CompilerArguments::NotCompilation;
}
Some(XClang(s)) => xclangs.push(s.clone()),
Some(Clang(s)) => clangs.push(s.clone()),
None => {
match arg {
Argument::Raw(ref val) => {
if input_arg.is_some() {
multiple_input = true;
multiple_input_files.push(val.clone());
}
input_arg = Some(val.clone());
}
Argument::UnknownFlag(ref flag) => common_args.push(flag.clone()),
_ => unreachable!(),
}
}
}
match arg.get_data() {
Some(PreprocessorArgument(_)) | Some(PreprocessorArgumentPath(_)) => preprocessor_args
.extend(
arg.normalize(NormalizedDisposition::Concatenated)
.iter_os_strings(),
),
Some(ProgramDatabase(_))
| Some(DebugInfo)
| Some(PassThrough)
| Some(PassThroughWithPath(_))
| Some(PassThroughWithSuffix(_)) => common_args.extend(
arg.normalize(NormalizedDisposition::Concatenated)
.iter_os_strings(),
),
Some(ExtraHashFile(path)) => {
extra_hash_files.push(cwd.join(path));
common_args.extend(
arg.normalize(NormalizedDisposition::Concatenated)
.iter_os_strings(),
)
}
Some(ExternalIncludePath(_)) => common_args.extend(
arg.normalize(NormalizedDisposition::Separated)
.iter_os_strings(),
),
Some(Ignore) | Some(IgnoreWithSuffix(_)) => {}
_ => {}
}
}
fn xclang_append(arg: OsString, args: &mut Vec<OsString>) {
args.push("-Xclang".into());
args.push(arg);
}
fn dash_clang_append(arg: OsString, args: &mut Vec<OsString>) {
let mut a = OsString::from("-clang:");
a.push(arg);
args.push(a);
}
for (args, append_fn) in Iterator::zip(
[xclangs, clangs].iter(),
&[xclang_append, dash_clang_append],
) {
let it = gcc::ExpandIncludeFile::new(cwd, args);
for arg in ArgsIter::new(it, (&gcc::ARGS[..], &clang::ARGS[..])) {
let arg = try_or_cannot_cache!(arg, "argument parse");
use crate::compiler::gcc::ArgData::*;
let args = match arg.get_data() {
Some(SplitDwarf) | Some(TestCoverage) | Some(Coverage) | Some(DoCompilation)
| Some(Language(_)) | Some(Output(_)) | Some(TooHardFlag) | Some(XClang(_))
| Some(TooHard(_)) => cannot_cache!(arg
.flag_str()
.unwrap_or("Can't handle complex arguments through clang",)),
None => match arg {
Argument::Raw(_) | Argument::UnknownFlag(_) => &mut common_args,
_ => unreachable!(),
},
Some(DiagnosticsColor(_))
| Some(DiagnosticsColorFlag)
| Some(NoDiagnosticsColorFlag)
| Some(Arch(_))
| Some(PassThrough(_))
| Some(PassThroughPath(_))
| Some(PedanticFlag)
| Some(Standard(_)) => &mut common_args,
Some(ProfileGenerate) => {
profile_generate = true;
&mut common_args
}
Some(ClangProfileUse(path)) => {
extra_hash_files.push(clang::resolve_profile_use_path(path, cwd));
&mut common_args
}
Some(ExtraHashFile(path)) => {
extra_hash_files.push(cwd.join(path));
&mut common_args
}
Some(PreprocessorArgumentFlag)
| Some(PreprocessorArgument(_))
| Some(PreprocessorArgumentPath(_)) => &mut preprocessor_args,
Some(DepArgumentPath(_)) | Some(DepTarget(_)) | Some(NeedDepTarget) => {
&mut dependency_args
}
};
let norm = match arg.flag_str() {
Some(s) if s.len() == 2 => NormalizedDisposition::Concatenated,
_ => NormalizedDisposition::Separated,
};
for arg in arg.normalize(norm).iter_os_strings() {
append_fn(arg, args);
}
}
}
if !compilation {
return CompilerArguments::NotCompilation;
}
if multiple_input {
cannot_cache!(
"multiple input files",
format!("{:?}", multiple_input_files)
);
}
let (input, language) = match input_arg {
Some(i) => match Language::from_file_name(Path::new(&i)) {
Some(l) => (i.to_owned(), l),
None => cannot_cache!("unknown source language"),
},
None => cannot_cache!("no input file"),
};
let mut outputs = HashMap::new();
match output_arg {
None => {
outputs.insert(
"obj",
ArtifactDescriptor {
path: Path::new(&input).with_extension("obj"),
optional: false,
},
);
}
Some(o) => {
if o.extension().is_none() && compilation {
outputs.insert(
"obj",
ArtifactDescriptor {
path: o.with_extension("obj"),
optional: false,
},
);
} else {
outputs.insert(
"obj",
ArtifactDescriptor {
path: o,
optional: false,
},
);
}
}
}
if debug_info && !is_clang {
match pdb {
Some(p) => outputs.insert(
"pdb",
ArtifactDescriptor {
path: p,
optional: false,
},
),
None => {
cannot_cache!("shared pdb");
}
};
}
CompilerArguments::Ok(ParsedArguments {
input: input.into(),
language,
compilation_flag,
depfile,
outputs,
dependency_args,
preprocessor_args,
common_args,
extra_hash_files,
msvc_show_includes: show_includes,
profile_generate,
color_mode: ColorMode::Auto,
suppress_rewrite_includes_only: false,
})
}
#[cfg(windows)]
fn normpath(path: &str) -> String {
use std::os::windows::ffi::OsStringExt;
use std::os::windows::io::AsRawHandle;
use std::ptr;
use winapi::um::fileapi::GetFinalPathNameByHandleW;
File::open(path)
.and_then(|f| {
let handle = f.as_raw_handle();
let size = unsafe { GetFinalPathNameByHandleW(handle, ptr::null_mut(), 0, 0) };
if size == 0 {
return Err(io::Error::last_os_error());
}
let mut wchars = vec![0; size as usize];
if unsafe {
GetFinalPathNameByHandleW(handle, wchars.as_mut_ptr(), wchars.len() as u32, 0)
} == 0
{
return Err(io::Error::last_os_error());
}
let o = OsString::from_wide(&wchars[4..wchars.len() - 1]);
o.into_string()
.map(|s| s.replace('\\', "/"))
.map_err(|_| io::Error::new(io::ErrorKind::Other, "Error converting string"))
})
.unwrap_or_else(|_| path.replace('\\', "/"))
}
#[cfg(not(windows))]
fn normpath(path: &str) -> String {
path.to_owned()
}
#[allow(clippy::too_many_arguments)]
pub async fn preprocess<T>(
creator: &T,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
may_dist: bool,
includes_prefix: &str,
rewrite_includes_only: bool,
is_clang: bool,
) -> Result<process::Output>
where
T: CommandCreatorSync,
{
let mut cmd = creator.clone().new_command_sync(executable);
if may_dist || parsed_args.profile_generate {
cmd.arg("-E");
} else {
cmd.arg("-EP");
}
cmd.arg(&parsed_args.input)
.arg("-nologo")
.args(&parsed_args.preprocessor_args)
.args(&parsed_args.dependency_args)
.args(&parsed_args.common_args)
.env_clear()
.envs(env_vars.iter().map(|&(ref k, ref v)| (k, v)))
.current_dir(&cwd);
if parsed_args.depfile.is_some() && !parsed_args.msvc_show_includes {
cmd.arg("-showIncludes");
}
if rewrite_includes_only && is_clang {
cmd.arg("-clang:-frewrite-includes");
}
if log_enabled!(Debug) {
debug!("preprocess: {:?}", cmd);
}
let parsed_args = parsed_args.clone();
let includes_prefix = includes_prefix.to_string();
let cwd = cwd.to_owned();
let output = run_input_output(cmd, None).await?;
let parsed_args = &parsed_args;
if let (Some(obj), &Some(ref depfile)) = (parsed_args.outputs.get("obj"), &parsed_args.depfile)
{
let objfile = &obj.path;
let f = File::create(cwd.join(depfile))?;
let mut f = BufWriter::new(f);
encode_path(&mut f, objfile)
.with_context(|| format!("Couldn't encode objfile filename: '{:?}'", objfile))?;
write!(f, ": ")?;
encode_path(&mut f, &parsed_args.input)
.with_context(|| format!("Couldn't encode input filename: '{:?}'", objfile))?;
write!(f, " ")?;
let process::Output {
status,
stdout,
stderr: stderr_bytes,
} = output;
let stderr =
from_local_codepage(&stderr_bytes).context("Failed to convert preprocessor stderr")?;
let mut deps = HashSet::new();
let mut stderr_bytes = vec![];
for line in stderr.lines() {
if line.starts_with(&includes_prefix) {
let dep = normpath(line[includes_prefix.len()..].trim());
trace!("included: {}", dep);
if deps.insert(dep.clone()) && !dep.contains(' ') {
write!(f, "{} ", dep)?;
}
if !parsed_args.msvc_show_includes {
continue;
}
}
stderr_bytes.extend_from_slice(line.as_bytes());
stderr_bytes.push(b'\n');
}
writeln!(f)?;
encode_path(&mut f, &parsed_args.input)
.with_context(|| format!("Couldn't encode filename: '{:?}'", parsed_args.input))?;
writeln!(f, ":")?;
let mut sorted = deps.into_iter().collect::<Vec<_>>();
sorted.sort();
for dep in sorted {
if !dep.contains(' ') {
writeln!(f, "{}:", dep)?;
}
}
Ok(process::Output {
status,
stdout,
stderr: stderr_bytes,
})
} else {
Ok(output)
}
}
fn generate_compile_commands(
path_transformer: &mut dist::PathTransformer,
executable: &Path,
parsed_args: &ParsedArguments,
cwd: &Path,
env_vars: &[(OsString, OsString)],
) -> Result<(CompileCommand, Option<dist::CompileCommand>, Cacheable)> {
#[cfg(not(feature = "dist-client"))]
let _ = path_transformer;
trace!("compile");
let out_file = match parsed_args.outputs.get("obj") {
Some(obj) => &obj.path,
None => bail!("Missing object file output"),
};
let cacheable = parsed_args
.outputs
.get("pdb")
.map_or(Cacheable::Yes, |pdb| {
if Path::new(&cwd).join(pdb.path.clone()).exists() {
Cacheable::No
} else {
Cacheable::Yes
}
});
let mut fo = OsString::from("-Fo");
fo.push(&out_file);
let mut arguments: Vec<OsString> = vec![
parsed_args.compilation_flag.clone(),
parsed_args.input.clone().into(),
fo,
];
arguments.extend(parsed_args.preprocessor_args.clone());
arguments.extend(parsed_args.dependency_args.clone());
arguments.extend(parsed_args.common_args.clone());
let command = CompileCommand {
executable: executable.to_owned(),
arguments,
env_vars: env_vars.to_owned(),
cwd: cwd.to_owned(),
};
#[cfg(not(feature = "dist-client"))]
let dist_command = None;
#[cfg(feature = "dist-client")]
let dist_command = (|| {
let mut fo = String::from("-Fo");
fo.push_str(&path_transformer.as_dist(out_file)?);
let mut arguments: Vec<String> = vec![
parsed_args.compilation_flag.clone().into_string().ok()?,
path_transformer.as_dist(&parsed_args.input)?,
fo,
];
arguments.extend(dist::osstrings_to_strings(&parsed_args.common_args)?);
Some(dist::CompileCommand {
executable: path_transformer.as_dist(executable)?,
arguments,
env_vars: dist::osstring_tuples_to_strings(env_vars)?,
cwd: path_transformer.as_dist(cwd)?,
})
})();
Ok((command, dist_command, cacheable))
}
#[cfg(test)]
mod test {
use super::*;
use crate::compiler::*;
use crate::mock_command::*;
use crate::test::utils::*;
fn parse_arguments(arguments: Vec<OsString>) -> CompilerArguments<ParsedArguments> {
super::parse_arguments(&arguments, &std::env::current_dir().unwrap(), false)
}
#[test]
fn test_detect_showincludes_prefix() {
drop(env_logger::try_init());
let creator = new_creator();
let runtime = single_threaded_runtime();
let pool = runtime.handle().clone();
let f = TestFixture::new();
let srcfile = f.touch("test.h").unwrap();
let mut s = srcfile.to_str().unwrap();
if s.starts_with("\\\\?\\") {
s = &s[4..];
}
let stdout = format!("blah: {}\r\n", s);
let stderr = String::from("some\r\nstderr\r\n");
next_command(
&creator,
Ok(MockChild::new(exit_status(0), &stdout, &stderr)),
);
assert_eq!(
"blah: ",
detect_showincludes_prefix(&creator, "cl.exe".as_ref(), false, Vec::new(), &pool)
.wait()
.unwrap()
);
}
#[test]
fn test_parse_arguments_simple() {
let args = ovec!["-c", "foo.c", "-Fofoo.obj"];
let ParsedArguments {
input,
language,
compilation_flag,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_eq!(Some("-c"), compilation_flag.to_str());
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert!(common_args.is_empty());
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_compile_flag() {
let args = ovec!["/c", "foo.c", "-Fofoo.obj"];
let ParsedArguments {
input,
language,
compilation_flag,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_eq!(Some("/c"), compilation_flag.to_str());
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert!(common_args.is_empty());
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_arguments_default_name() {
let args = ovec!["-c", "foo.c"];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert!(common_args.is_empty());
assert!(!msvc_show_includes);
}
#[test]
fn parse_argument_slashes() {
let args = ovec!["-c", "foo.c", "/Fofoo.obj"];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert!(common_args.is_empty());
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_arguments_clang_passthrough() {
let args = ovec![
"-Fohost_dictionary.obj",
"-c",
"-Xclang",
"-MP",
"-Xclang",
"-dependency-file",
"-Xclang",
".deps/host_dictionary.obj.pp",
"-Xclang",
"-MT",
"-Xclang",
"host_dictionary.obj",
"-clang:-fprofile-generate",
"-clang:-fprofile-use=xyz.profdata",
"dictionary.c"
];
let ParsedArguments {
dependency_args,
preprocessor_args,
common_args,
profile_generate,
extra_hash_files,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert!(profile_generate);
assert!(preprocessor_args.is_empty());
assert_eq!(
dependency_args,
ovec!(
"-Xclang",
"-MP",
"-Xclang",
"-dependency-file",
"-Xclang",
".deps/host_dictionary.obj.pp",
"-Xclang",
"-MT",
"-Xclang",
"host_dictionary.obj"
)
);
assert_eq!(
common_args,
ovec!(
"-clang:-fprofile-generate",
"-clang:-fprofile-use=xyz.profdata"
)
);
assert_eq!(
extra_hash_files,
ovec!(std::env::current_dir().unwrap().join("xyz.profdata"))
);
}
#[test]
fn test_parse_arguments_extra() {
let args = ovec!["-c", "foo.c", "-foo", "-Fofoo.obj", "-bar"];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert_eq!(common_args, ovec!["-foo", "-bar"]);
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_arguments_values() {
let args = ovec![
"-c",
"foo.c",
"-FI",
"file",
"-imsvc",
"/a/b/c",
"-Fofoo.obj",
"/showIncludes",
"/winsysroot../../some/dir"
];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
dependency_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert_eq!(preprocessor_args, ovec!["-FIfile", "-imsvc/a/b/c"]);
assert_eq!(dependency_args, ovec!["/showIncludes"]);
assert_eq!(common_args, ovec!["/winsysroot../../some/dir"]);
assert!(msvc_show_includes);
}
#[test]
fn test_parse_arguments_pdb() {
let args = ovec!["-c", "foo.c", "-Zi", "-Fdfoo.pdb", "-Fofoo.obj"];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
),
(
"pdb",
ArtifactDescriptor {
path: PathBuf::from("foo.pdb"),
optional: false
}
)
);
assert!(preprocessor_args.is_empty());
assert_eq!(common_args, ovec!["-Zi", "-Fdfoo.pdb"]);
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_arguments_external_include() {
let args = ovec![
"-c",
"foo.c",
"-Fofoo.obj",
"-experimental:external",
"-external:templates-",
"-external:I",
"path/to/system/includes"
];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert_eq!(1, outputs.len());
assert!(preprocessor_args.is_empty());
assert_eq!(
common_args,
ovec![
"-experimental:external",
"-external:templates-",
"-external:I",
"path/to/system/includes"
]
);
assert!(!msvc_show_includes);
}
#[test]
fn test_parse_arguments_external_warning_suppression_forward_slashes() {
for n in 0..5 {
let args = ovec![
"-c",
"foo.c",
"/Fofoo.obj",
"/experimental:external",
format!("/external:W{}", n)
];
let ParsedArguments {
input,
language,
outputs,
preprocessor_args,
msvc_show_includes,
common_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("foo.c"), input.to_str());
assert_eq!(Language::C, language);
assert_map_contains!(
outputs,
(
"obj",
ArtifactDescriptor {
path: PathBuf::from("foo.obj"),
optional: false
}
)
);
assert_eq!(1, outputs.len());
assert!(preprocessor_args.is_empty());
assert_eq!(
common_args,
ovec!["/experimental:external", format!("/external:W{}", n)]
);
assert!(!msvc_show_includes);
}
}
#[test]
fn test_parse_arguments_empty_args() {
assert_eq!(CompilerArguments::NotCompilation, parse_arguments(vec!()));
}
#[test]
fn test_parse_arguments_not_compile() {
assert_eq!(
CompilerArguments::NotCompilation,
parse_arguments(ovec!["-Fofoo", "foo.c"])
);
}
#[test]
fn test_parse_arguments_passthrough() {
let args = ovec![
"-Oy",
"-Gw",
"-EHa",
"-Fmdictionary-map",
"-c",
"-Fohost_dictionary.obj",
"dictionary.c"
];
let ParsedArguments {
input,
common_args,
dependency_args,
preprocessor_args,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(Some("dictionary.c"), input.to_str());
assert!(preprocessor_args.is_empty());
assert!(dependency_args.is_empty());
assert!(!common_args.is_empty());
assert_eq!(
common_args,
ovec!("-Oy", "-Gw", "-EHa", "-Fmdictionary-map")
);
}
#[test]
fn test_parse_arguments_too_many_inputs_single() {
assert_eq!(
CompilerArguments::CannotCache("multiple input files", Some("[\"bar.c\"]".to_string())),
parse_arguments(ovec!["-c", "foo.c", "-Fofoo.obj", "bar.c"])
);
}
#[test]
fn test_parse_arguments_too_many_inputs_multiple() {
assert_eq!(
CompilerArguments::CannotCache(
"multiple input files",
Some("[\"bar.c\", \"baz.c\"]".to_string())
),
parse_arguments(ovec!["-c", "foo.c", "-Fofoo.obj", "bar.c", "baz.c"])
);
}
#[test]
fn test_parse_arguments_unsupported() {
assert_eq!(
CompilerArguments::CannotCache("-FA", None),
parse_arguments(ovec!["-c", "foo.c", "-Fofoo.obj", "-FA"])
);
assert_eq!(
CompilerArguments::CannotCache("-Fa", None),
parse_arguments(ovec!["-Fa", "-c", "foo.c", "-Fofoo.obj"])
);
assert_eq!(
CompilerArguments::CannotCache("-FR", None),
parse_arguments(ovec!["-c", "foo.c", "-FR", "-Fofoo.obj"])
);
}
#[test]
fn test_parse_arguments_response_file() {
assert_eq!(
CompilerArguments::CannotCache("@", None),
parse_arguments(ovec!["-c", "foo.c", "@foo", "-Fofoo.obj"])
);
}
#[test]
fn test_parse_arguments_missing_pdb() {
assert_eq!(
CompilerArguments::CannotCache("shared pdb", None),
parse_arguments(ovec!["-c", "foo.c", "-Zi", "-Fofoo.obj"])
);
}
#[test]
fn test_parse_arguments_missing_edit_and_continue_pdb() {
assert_eq!(
CompilerArguments::CannotCache("shared pdb", None),
parse_arguments(ovec!["-c", "foo.c", "-ZI", "-Fofoo.obj"])
);
}
#[test]
fn test_compile_simple() {
let creator = new_creator();
let f = TestFixture::new();
let parsed_args = ParsedArguments {
input: "foo.c".into(),
language: Language::C,
compilation_flag: "-c".into(),
depfile: None,
outputs: vec![(
"obj",
ArtifactDescriptor {
path: "foo.obj".into(),
optional: false,
},
)]
.into_iter()
.collect(),
dependency_args: vec![],
preprocessor_args: vec![],
common_args: vec![],
extra_hash_files: vec![],
msvc_show_includes: false,
profile_generate: false,
color_mode: ColorMode::Auto,
suppress_rewrite_includes_only: false,
};
let compiler = &f.bins[0];
next_command(&creator, Ok(MockChild::new(exit_status(0), "", "")));
let mut path_transformer = dist::PathTransformer::default();
let (command, dist_command, cacheable) = generate_compile_commands(
&mut path_transformer,
compiler,
&parsed_args,
f.tempdir.path(),
&[],
)
.unwrap();
#[cfg(feature = "dist-client")]
assert!(dist_command.is_some());
#[cfg(not(feature = "dist-client"))]
assert!(dist_command.is_none());
let _ = command.execute(&creator).wait();
assert_eq!(Cacheable::Yes, cacheable);
assert_eq!(0, creator.lock().unwrap().children.len());
}
#[test]
fn test_compile_not_cacheable_pdb() {
let creator = new_creator();
let f = TestFixture::new();
let pdb = f.touch("foo.pdb").unwrap();
let parsed_args = ParsedArguments {
input: "foo.c".into(),
language: Language::C,
compilation_flag: "/c".into(),
depfile: None,
outputs: vec![
(
"obj",
ArtifactDescriptor {
path: "foo.obj".into(),
optional: false,
},
),
(
"pdb",
ArtifactDescriptor {
path: pdb,
optional: false,
},
),
]
.into_iter()
.collect(),
dependency_args: vec![],
preprocessor_args: vec![],
common_args: vec![],
extra_hash_files: vec![],
msvc_show_includes: false,
profile_generate: false,
color_mode: ColorMode::Auto,
suppress_rewrite_includes_only: false,
};
let compiler = &f.bins[0];
next_command(&creator, Ok(MockChild::new(exit_status(0), "", "")));
let mut path_transformer = dist::PathTransformer::default();
let (command, dist_command, cacheable) = generate_compile_commands(
&mut path_transformer,
compiler,
&parsed_args,
f.tempdir.path(),
&[],
)
.unwrap();
#[cfg(feature = "dist-client")]
assert!(dist_command.is_some());
#[cfg(not(feature = "dist-client"))]
assert!(dist_command.is_none());
let _ = command.execute(&creator).wait();
assert_eq!(Cacheable::No, cacheable);
assert_eq!(0, creator.lock().unwrap().children.len());
}
#[test]
fn test_parse_fsanitize_blacklist() {
let args = ovec![
"-c",
"foo.c",
"-o",
"foo.o",
"-fsanitize-blacklist=list.txt"
];
let ParsedArguments {
common_args,
extra_hash_files,
..
} = match parse_arguments(args) {
CompilerArguments::Ok(args) => args,
o => panic!("Got unexpected parse result: {:?}", o),
};
assert_eq!(ovec!["-fsanitize-blacklist=list.txt"], common_args);
assert_eq!(
ovec![std::env::current_dir().unwrap().join("list.txt")],
extra_hash_files
);
}
#[test]
#[cfg(windows)]
fn local_oem_codepage_conversions() {
use winapi::um::winnls::GetOEMCP;
let current_oemcp = unsafe { GetOEMCP() };
if current_oemcp == 437 || current_oemcp == 850 || current_oemcp == 858 {
const INPUT_STRING: &str = "ÇüéâäàåçêëèïîìÄÅ";
const INPUT_BYTES: [u8; 16] = [
128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143,
];
assert_eq!(from_local_codepage(&INPUT_BYTES).unwrap(), INPUT_STRING);
const INPUT_WORDS: [u16; 16] = [
199, 252, 233, 226, 228, 224, 229, 231, 234, 235, 232, 239, 238, 236, 196, 197,
];
assert_eq!(wide_char_to_multi_byte(&INPUT_WORDS).unwrap(), INPUT_BYTES);
}
}
}