Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix compile family detection: Use C macros instead of $compiler -v #1000

Merged
merged 15 commits into from
Mar 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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");
NobodyXu marked this conversation as resolved.
Show resolved Hide resolved
}

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