Skip to content

Commit

Permalink
Fix compile family detection: Use C macros instead of $compiler -v (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
NobodyXu authored Mar 17, 2024
1 parent 77a54a9 commit f36d6a7
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 85 deletions.
1 change: 1 addition & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ on: [push, pull_request]
env:
CARGO_INCREMENTAL: 0
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
CC_ENABLE_DEBUG_OUTPUT: true

jobs:
test:
Expand Down
10 changes: 10 additions & 0 deletions dev-tools/cc-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,15 @@ fn run_action_if_forked() -> bool {
true
}

fn disable_debug_output() {
// That env would break tests for warning/debug output,
// and it is set in the CI, to make debugging CI failure easier.
std::env::remove_var("CC_ENABLE_DEBUG_OUTPUT");
}

fn build_cargo_warnings(warnings: bool) {
disable_debug_output();

cc::Build::new()
.cargo_metadata(false)
.cargo_warnings(warnings)
Expand All @@ -166,6 +174,8 @@ fn build_cargo_warnings(warnings: bool) {
}

fn build_cargo_metadata(metadata: bool) {
disable_debug_output();

cc::Build::new()
.cargo_metadata(metadata)
.file("src/dummy.c")
Expand Down
2 changes: 2 additions & 0 deletions dev-tools/gen-windows-sys-binding/windows_sys.list
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Windows.Win32.System.Registry.REG_SZ

Windows.Win32.System.SystemInformation.IMAGE_FILE_MACHINE_AMD64

Windows.Win32.Storage.FileSystem.FILE_ATTRIBUTE_TEMPORARY

Windows.Win32.System.Threading.GetMachineTypeAttributes
Windows.Win32.System.Threading.ReleaseSemaphore
Windows.Win32.System.Threading.WaitForSingleObject
Expand Down
11 changes: 11 additions & 0 deletions src/detect_compiler_family.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#ifdef __clang__
#pragma message "clang"
#endif

#ifdef __GNUC__
#pragma message "gcc"
#endif

#ifdef _MSC_VER
#pragma message "msvc"
#endif
34 changes: 23 additions & 11 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,7 @@ pub use tool::Tool;
use tool::ToolFamily;

mod target_info;
mod tempfile;

/// A builder for compilation of a native library.
///
Expand Down Expand Up @@ -314,6 +315,8 @@ enum ErrorKind {
ToolNotFound,
/// One of the function arguments failed validation.
InvalidArgument,
/// No known macro is defined for the compiler when discovering tool family
ToolFamilyMacroNotFound,
/// Invalid target
InvalidTarget,
#[cfg(feature = "parallel")]
Expand Down Expand Up @@ -621,15 +624,15 @@ impl Build {
if compiler.family.verbose_stderr() {
compiler.remove_arg("-v".into());
}
if compiler.family == ToolFamily::Clang {
if compiler.is_like_clang() {
// Avoid reporting that the arg is unsupported just because the
// compiler complains that it wasn't used.
compiler.push_cc_arg("-Wno-unused-command-line-argument".into());
}

let mut cmd = compiler.to_command();
let is_arm = target.contains("aarch64") || target.contains("arm");
let clang = compiler.family == ToolFamily::Clang;
let clang = compiler.is_like_clang();
let gnu = compiler.family == ToolFamily::Gnu;
command_add_output_file(
&mut cmd,
Expand Down Expand Up @@ -1576,7 +1579,7 @@ impl Build {
let target = self.get_target()?;
let msvc = target.contains("msvc");
let compiler = self.try_get_compiler()?;
let clang = compiler.family == ToolFamily::Clang;
let clang = compiler.is_like_clang();
let gnu = compiler.family == ToolFamily::Gnu;

let is_assembler_msvc = msvc && asm_ext == Some(AsmFileExt::DotAsm);
Expand Down Expand Up @@ -1732,7 +1735,7 @@ impl Build {
if let Some(ref std) = self.std {
let separator = match cmd.family {
ToolFamily::Msvc { .. } => ':',
ToolFamily::Gnu | ToolFamily::Clang => '=',
ToolFamily::Gnu | ToolFamily::Clang { .. } => '=',
};
cmd.push_cc_arg(format!("-std{}{}", separator, std).into());
}
Expand Down Expand Up @@ -1825,23 +1828,23 @@ impl Build {
_ => {}
}
}
ToolFamily::Gnu | ToolFamily::Clang => {
ToolFamily::Gnu | ToolFamily::Clang { .. } => {
// arm-linux-androideabi-gcc 4.8 shipped with Android NDK does
// not support '-Oz'
if opt_level == "z" && cmd.family != ToolFamily::Clang {
if opt_level == "z" && !cmd.is_like_clang() {
cmd.push_opt_unless_duplicate("-Os".into());
} else {
cmd.push_opt_unless_duplicate(format!("-O{}", opt_level).into());
}

if cmd.family == ToolFamily::Clang && target.contains("windows") {
if cmd.is_like_clang() && target.contains("windows") {
// Disambiguate mingw and msvc on Windows. Problem is that
// depending on the origin clang can default to a mismatchig
// run-time.
cmd.push_cc_arg(format!("--target={}", target).into());
}

if cmd.family == ToolFamily::Clang && target.contains("android") {
if cmd.is_like_clang() && target.contains("android") {
// For compatibility with code that doesn't use pre-defined `__ANDROID__` macro.
// If compiler used via ndk-build or cmake (officially supported build methods)
// this macros is defined.
Expand Down Expand Up @@ -1899,7 +1902,7 @@ impl Build {

// Target flags
match cmd.family {
ToolFamily::Clang => {
ToolFamily::Clang { .. } => {
if !cmd.has_internal_target_arg
&& !(target.contains("android")
&& android_clang_compiler_uses_target_arg_internally(&cmd.path))
Expand Down Expand Up @@ -2290,7 +2293,7 @@ impl Build {
if self.cpp {
match (self.cpp_set_stdlib.as_ref(), cmd.family) {
(None, _) => {}
(Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang) => {
(Some(stdlib), ToolFamily::Gnu) | (Some(stdlib), ToolFamily::Clang { .. }) => {
cmd.push_cc_arg(format!("-stdlib=lib{}", stdlib).into());
}
_ => {
Expand Down Expand Up @@ -2666,11 +2669,15 @@ impl Build {
}

fn get_base_compiler(&self) -> Result<Tool, Error> {
let out_dir = self.get_out_dir().ok();
let out_dir = out_dir.as_deref();

if let Some(c) = &self.compiler {
return Ok(Tool::new(
(**c).to_owned(),
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
));
}
let host = self.get_host()?;
Expand Down Expand Up @@ -2712,6 +2719,7 @@ impl Build {
driver_mode,
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
);
if let Some(cc_wrapper) = wrapper {
t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
Expand All @@ -2730,6 +2738,7 @@ impl Build {
PathBuf::from("cmd"),
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
);
t.args.push("/c".into());
t.args.push(format!("{}.bat", tool).into());
Expand All @@ -2739,6 +2748,7 @@ impl Build {
PathBuf::from(tool),
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
))
}
} else {
Expand Down Expand Up @@ -2798,6 +2808,7 @@ impl Build {
PathBuf::from(compiler),
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
);
if let Some(cc_wrapper) = Self::rustc_wrapper_fallback() {
t.cc_wrapper_path = Some(PathBuf::from(cc_wrapper));
Expand All @@ -2821,6 +2832,7 @@ impl Build {
self.cuda,
&self.cached_compiler_family,
&self.cargo_output,
out_dir,
);
nvcc_tool
.args
Expand Down Expand Up @@ -3144,7 +3156,7 @@ impl Build {
// And even extend it to gcc targets by searching for "ar" instead
// of "llvm-ar"...
let compiler = self.get_base_compiler().ok()?;
if compiler.family == ToolFamily::Clang {
if compiler.is_like_clang() {
name = format!("llvm-{}", tool);
search_programs(&mut self.cmd(&compiler.path), &name, &self.cargo_output)
.map(|name| self.cmd(name))
Expand Down
84 changes: 84 additions & 0 deletions src/tempfile.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
use std::{
collections::hash_map::RandomState,
fs::{remove_file, File, OpenOptions},
hash::{BuildHasher, Hasher},
io, os,
path::{Path, PathBuf},
};

#[cfg(not(any(unix, target_os = "wasi", windows)))]
compile_error!("Your system is not supported since cc cannot create named tempfile");

fn rand() -> u64 {
RandomState::new().build_hasher().finish()
}

fn tmpname(suffix: &str) -> String {
format!("{}{}", rand(), suffix)
}

fn create_named(path: &Path) -> io::Result<File> {
let mut open_options = OpenOptions::new();

open_options.read(true).write(true).create_new(true);

#[cfg(all(unix, not(target_os = "wasi")))]
<OpenOptions as os::unix::fs::OpenOptionsExt>::mode(&mut open_options, 0o600);

#[cfg(windows)]
<OpenOptions as os::windows::fs::OpenOptionsExt>::custom_flags(
&mut open_options,
crate::windows::windows_sys::FILE_ATTRIBUTE_TEMPORARY,
);

open_options.open(path)
}

pub(super) struct NamedTempfile {
path: PathBuf,
file: Option<File>,
}

impl NamedTempfile {
pub(super) fn new(base: &Path, suffix: &str) -> io::Result<Self> {
for _ in 0..10 {
let path = base.join(tmpname(suffix));
match create_named(&path) {
Ok(file) => {
return Ok(Self {
file: Some(file),
path,
})
}
Err(e) if e.kind() == io::ErrorKind::AlreadyExists => continue,
Err(e) => return Err(e),
};
}

Err(io::Error::new(
io::ErrorKind::AlreadyExists,
format!(
"too many temporary files exist in base `{}` with suffix `{}`",
base.display(),
suffix
),
))
}

pub(super) fn path(&self) -> &Path {
&self.path
}

pub(super) fn file(&self) -> &File {
self.file.as_ref().unwrap()
}
}

impl Drop for NamedTempfile {
fn drop(&mut self) {
// On Windows you have to close all handle to it before
// removing the file.
self.file.take();
let _ = remove_file(&self.path);
}
}
Loading

0 comments on commit f36d6a7

Please sign in to comment.