Skip to content

Commit

Permalink
Fix compiler family detection issue with clang-cl on macOS (#1328)
Browse files Browse the repository at this point in the history
  • Loading branch information
NobodyXu authored Jan 8, 2025
1 parent 41a0613 commit b886474
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 42 deletions.
8 changes: 7 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -725,6 +725,10 @@ impl Build {
},
);

if compiler.supports_path_delimiter() {
cmd.arg("--");
}

cmd.arg(&src);

// On MSVC skip the CRT by setting the entry point to `main`.
Expand Down Expand Up @@ -1795,14 +1799,16 @@ impl Build {
if is_asm {
cmd.args(self.asm_flags.iter().map(std::ops::Deref::deref));
}
if compiler.family == (ToolFamily::Msvc { clang_cl: true }) && !is_assembler_msvc {

if compiler.supports_path_delimiter() && !is_assembler_msvc {
// #513: For `clang-cl`, separate flags/options from the input file.
// When cross-compiling macOS -> Windows, this avoids interpreting
// common `/Users/...` paths as the `/U` flag and triggering
// `-Wslash-u-filename` warning.
cmd.arg("--");
}
cmd.arg(&obj.src);

if cfg!(target_os = "macos") {
self.fix_env_for_apple_os(&mut cmd)?;
}
Expand Down
107 changes: 66 additions & 41 deletions src/tool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,46 @@ impl Tool {
.unwrap_or_default()
}

fn guess_family_from_stdout(
stdout: &str,
path: &Path,
cargo_output: &CargoOutput,
) -> Result<ToolFamily, Error> {
cargo_output.print_debug(&stdout);

// https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271
// stdin is set to null to ensure that the help output is never paginated.
let accepts_cl_style_flags =
run(Command::new(path).arg("-?").stdin(Stdio::null()), path, &{
// the errors are not errors!
let mut cargo_output = cargo_output.clone();
cargo_output.warnings = cargo_output.debug;
cargo_output.output = OutputKind::Discard;
cargo_output
})
.is_ok();

let clang = stdout.contains(r#""clang""#);
let gcc = stdout.contains(r#""gcc""#);
let emscripten = stdout.contains(r#""emscripten""#);
let vxworks = stdout.contains(r#""VxWorks""#);

match (clang, accepts_cl_style_flags, gcc, emscripten, vxworks) {
(clang_cl, true, _, false, false) => Ok(ToolFamily::Msvc { clang_cl }),
(true, _, _, _, false) | (_, _, _, true, false) => Ok(ToolFamily::Clang {
zig_cc: is_zig_cc(path, cargo_output),
}),
(false, false, true, _, false) | (_, _, _, _, true) => Ok(ToolFamily::Gnu),
(false, false, false, false, false) => {
cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__`, `__EMSCRIPTEN__` or `__VXWORKS__`, also does not accept cl style flag `-?`, fallback to treating it as GNU");
Err(Error::new(
ErrorKind::ToolFamilyMacroNotFound,
"Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, `__VXWORKS__` or accepts cl style flag `-?`, but found none",
))
}
}
}

fn detect_family_inner(
path: &Path,
cargo_output: &CargoOutput,
Expand Down Expand Up @@ -140,53 +180,30 @@ impl Tool {
tmp_file.sync_data()?;
drop(tmp_file);

// When expanding the file, the compiler prints a lot of information to stderr
// that it is not an error, but related to expanding itself.
//
// cc would have to disable warning here to prevent generation of too many warnings.
let mut compiler_detect_output = cargo_output.clone();
compiler_detect_output.warnings = compiler_detect_output.debug;

let stdout = run_output(
Command::new(path).arg("-E").arg(tmp.path()),
path,
// When expanding the file, the compiler prints a lot of information to stderr
// that it is not an error, but related to expanding itself.
//
// cc would have to disable warning here to prevent generation of too many warnings.
&{
let mut cargo_output = cargo_output.clone();
cargo_output.warnings = cargo_output.debug;
cargo_output
},
&compiler_detect_output,
)?;
let stdout = String::from_utf8_lossy(&stdout);

cargo_output.print_debug(&stdout);

// https://gitlab.kitware.com/cmake/cmake/-/blob/69a2eeb9dff5b60f2f1e5b425002a0fd45b7cadb/Modules/CMakeDetermineCompilerId.cmake#L267-271
// stdin is set to null to ensure that the help output is never paginated.
let accepts_cl_style_flags =
run(Command::new(path).arg("-?").stdin(Stdio::null()), path, &{
// the errors are not errors!
let mut cargo_output = cargo_output.clone();
cargo_output.warnings = cargo_output.debug;
cargo_output.output = OutputKind::Discard;
cargo_output
})
.is_ok();

let clang = stdout.contains(r#""clang""#);
let gcc = stdout.contains(r#""gcc""#);
let emscripten = stdout.contains(r#""emscripten""#);
let vxworks = stdout.contains(r#""VxWorks""#);

match (clang, accepts_cl_style_flags, gcc, emscripten, vxworks) {
(clang_cl, true, _, false, false) => Ok(ToolFamily::Msvc { clang_cl }),
(true, _, _, _, false) | (_, _, _, true, false) => Ok(ToolFamily::Clang {
zig_cc: is_zig_cc(path, cargo_output),
}),
(false, false, true, _, false) | (_, _, _, _, true) => Ok(ToolFamily::Gnu),
(false, false, false, false, false) => {
cargo_output.print_warning(&"Compiler family detection failed since it does not define `__clang__`, `__GNUC__`, `__EMSCRIPTEN__` or `__VXWORKS__`, also does not accept cl style flag `-?`, fallback to treating it as GNU");
Err(Error::new(
ErrorKind::ToolFamilyMacroNotFound,
"Expects macro `__clang__`, `__GNUC__` or `__EMSCRIPTEN__`, `__VXWORKS__` or accepts cl style flag `-?`, but found none",
))
}
if stdout.contains("-Wslash-u-filename") {
let stdout = run_output(
Command::new(path).arg("-E").arg("--").arg(tmp.path()),
path,
&compiler_detect_output,
)?;
let stdout = String::from_utf8_lossy(&stdout);
guess_family_from_stdout(&stdout, path, cargo_output)
} else {
guess_family_from_stdout(&stdout, path, cargo_output)
}
}
let detect_family = |path: &Path| -> Result<ToolFamily, Error> {
Expand Down Expand Up @@ -403,6 +420,14 @@ impl Tool {
pub fn is_like_msvc(&self) -> bool {
matches!(self.family, ToolFamily::Msvc { .. })
}

/// Supports using `--` delimiter to separate arguments and path to source files.
pub(crate) fn supports_path_delimiter(&self) -> bool {
matches!(
self.family,
ToolFamily::Clang { .. } | ToolFamily::Msvc { clang_cl: true }
) && !self.cuda
}
}

/// Represents the family of tools this tool belongs to.
Expand Down
2 changes: 2 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ fn gnu_static() {
}

#[test]
// on macOS, cc/gcc is link to apple clang
#[cfg_attr(target_os = "macos", ignore)]
fn gnu_no_dash_dash() {
let test = Test::gnu();
test.gcc().file("foo.c").compile("foo");
Expand Down

0 comments on commit b886474

Please sign in to comment.