Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

430
vendor/cc/src/command_helpers.rs vendored Normal file
View File

@@ -0,0 +1,430 @@
//! Miscellaneous helpers for running commands
use std::{
borrow::Cow,
collections::hash_map,
ffi::OsString,
fmt::Display,
fs,
hash::Hasher,
io::{self, Read, Write},
path::Path,
process::{Child, ChildStderr, Command, Stdio},
sync::{
atomic::{AtomicBool, Ordering},
Arc,
},
};
use crate::{Error, ErrorKind, Object};
#[derive(Clone, Debug)]
pub(crate) struct CargoOutput {
pub(crate) metadata: bool,
pub(crate) warnings: bool,
pub(crate) debug: bool,
pub(crate) output: OutputKind,
checked_dbg_var: Arc<AtomicBool>,
}
/// Different strategies for handling compiler output (to stdout)
#[derive(Clone, Debug)]
pub(crate) enum OutputKind {
/// Forward the output to this process' stdout ([`Stdio::inherit()`])
Forward,
/// Discard the output ([`Stdio::null()`])
Discard,
/// Capture the result (`[Stdio::piped()`])
Capture,
}
impl CargoOutput {
pub(crate) fn new() -> Self {
#[allow(clippy::disallowed_methods)]
Self {
metadata: true,
warnings: true,
output: OutputKind::Forward,
debug: match std::env::var_os("CC_ENABLE_DEBUG_OUTPUT") {
Some(v) => v != "0" && v != "false" && !v.is_empty(),
None => false,
},
checked_dbg_var: Arc::new(AtomicBool::new(false)),
}
}
pub(crate) fn print_metadata(&self, s: &dyn Display) {
if self.metadata {
println!("{s}");
}
}
pub(crate) fn print_warning(&self, arg: &dyn Display) {
if self.warnings {
println!("cargo:warning={arg}");
}
}
pub(crate) fn print_debug(&self, arg: &dyn Display) {
if self.metadata
&& self
.checked_dbg_var
.compare_exchange(false, true, Ordering::Relaxed, Ordering::Relaxed)
.is_ok()
{
println!("cargo:rerun-if-env-changed=CC_ENABLE_DEBUG_OUTPUT");
}
if self.debug {
println!("{arg}");
}
}
fn stdio_for_warnings(&self) -> Stdio {
if self.warnings {
Stdio::piped()
} else {
Stdio::null()
}
}
fn stdio_for_output(&self) -> Stdio {
match self.output {
OutputKind::Capture => Stdio::piped(),
OutputKind::Forward => Stdio::inherit(),
OutputKind::Discard => Stdio::null(),
}
}
}
pub(crate) struct StderrForwarder {
inner: Option<(ChildStderr, Vec<u8>)>,
#[cfg(feature = "parallel")]
is_non_blocking: bool,
#[cfg(feature = "parallel")]
bytes_available_failed: bool,
/// number of bytes buffered in inner
bytes_buffered: usize,
}
const MIN_BUFFER_CAPACITY: usize = 100;
impl StderrForwarder {
pub(crate) fn new(child: &mut Child) -> Self {
Self {
inner: child
.stderr
.take()
.map(|stderr| (stderr, Vec::with_capacity(MIN_BUFFER_CAPACITY))),
bytes_buffered: 0,
#[cfg(feature = "parallel")]
is_non_blocking: false,
#[cfg(feature = "parallel")]
bytes_available_failed: false,
}
}
pub(crate) fn forward_available(&mut self) -> bool {
if let Some((stderr, buffer)) = self.inner.as_mut() {
loop {
// For non-blocking we check to see if there is data available, so we should try to
// read at least that much. For blocking, always read at least the minimum amount.
#[cfg(not(feature = "parallel"))]
let to_reserve = MIN_BUFFER_CAPACITY;
#[cfg(feature = "parallel")]
let to_reserve = if self.is_non_blocking && !self.bytes_available_failed {
match crate::parallel::stderr::bytes_available(stderr) {
#[cfg(windows)]
Ok(0) => break false,
#[cfg(unix)]
Ok(0) => {
// On Unix, depending on the implementation, we may sometimes get 0 in a
// loop (either there is data available or the pipe is broken), so
// continue with the non-blocking read anyway.
MIN_BUFFER_CAPACITY
}
#[cfg(windows)]
Err(_) => {
// On Windows, if we get an error then the pipe is broken, so flush
// the buffer and bail.
if !buffer.is_empty() {
write_warning(&buffer[..]);
}
self.inner = None;
break true;
}
#[cfg(unix)]
Err(_) => {
// On Unix, depending on the implementation, we may get spurious
// errors so make a note not to use bytes_available again and try
// the non-blocking read anyway.
self.bytes_available_failed = true;
MIN_BUFFER_CAPACITY
}
#[cfg(target_family = "wasm")]
Err(_) => panic!("bytes_available should always succeed on wasm"),
Ok(bytes_available) => MIN_BUFFER_CAPACITY.max(bytes_available),
}
} else {
MIN_BUFFER_CAPACITY
};
if self.bytes_buffered + to_reserve > buffer.len() {
buffer.resize(self.bytes_buffered + to_reserve, 0);
}
match stderr.read(&mut buffer[self.bytes_buffered..]) {
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
// No data currently, yield back.
break false;
}
Err(err) if err.kind() == std::io::ErrorKind::Interrupted => {
// Interrupted, try again.
continue;
}
Ok(bytes_read) if bytes_read != 0 => {
self.bytes_buffered += bytes_read;
let mut consumed = 0;
for line in buffer[..self.bytes_buffered].split_inclusive(|&b| b == b'\n') {
// Only forward complete lines, leave the rest in the buffer.
if let Some((b'\n', line)) = line.split_last() {
consumed += line.len() + 1;
write_warning(line);
}
}
if consumed > 0 && consumed < self.bytes_buffered {
// Remove the consumed bytes from buffer
buffer.copy_within(consumed.., 0);
}
self.bytes_buffered -= consumed;
}
res => {
// End of stream: flush remaining data and bail.
if self.bytes_buffered > 0 {
write_warning(&buffer[..self.bytes_buffered]);
}
if let Err(err) = res {
write_warning(
format!("Failed to read from child stderr: {err}").as_bytes(),
);
}
self.inner.take();
break true;
}
}
}
} else {
true
}
}
#[cfg(feature = "parallel")]
pub(crate) fn set_non_blocking(&mut self) -> Result<(), Error> {
assert!(!self.is_non_blocking);
#[cfg(unix)]
if let Some((stderr, _)) = self.inner.as_ref() {
crate::parallel::stderr::set_non_blocking(stderr)?;
}
self.is_non_blocking = true;
Ok(())
}
#[cfg(feature = "parallel")]
pub(crate) fn forward_all(&mut self) {
while !self.forward_available() {}
}
#[cfg(not(feature = "parallel"))]
fn forward_all(&mut self) {
let forward_result = self.forward_available();
assert!(forward_result, "Should have consumed all data");
}
}
fn write_warning(line: &[u8]) {
let stdout = io::stdout();
let mut stdout = stdout.lock();
stdout.write_all(b"cargo:warning=").unwrap();
stdout.write_all(line).unwrap();
stdout.write_all(b"\n").unwrap();
}
fn wait_on_child(
cmd: &Command,
child: &mut Child,
cargo_output: &CargoOutput,
) -> Result<(), Error> {
StderrForwarder::new(child).forward_all();
let status = match child.wait() {
Ok(s) => s,
Err(e) => {
return Err(Error::new(
ErrorKind::ToolExecError,
format!("failed to wait on spawned child process `{cmd:?}`: {e}"),
));
}
};
cargo_output.print_debug(&status);
if status.success() {
Ok(())
} else {
Err(Error::new(
ErrorKind::ToolExecError,
format!("command did not execute successfully (status code {status}): {cmd:?}"),
))
}
}
/// Find the destination object path for each file in the input source files,
/// and store them in the output Object.
pub(crate) fn objects_from_files(files: &[Arc<Path>], dst: &Path) -> Result<Vec<Object>, Error> {
let mut objects = Vec::with_capacity(files.len());
for file in files {
let basename = file
.file_name()
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidArgument,
"No file_name for object file path!",
)
})?
.to_string_lossy();
let dirname = file
.parent()
.ok_or_else(|| {
Error::new(
ErrorKind::InvalidArgument,
"No parent for object file path!",
)
})?
.to_string_lossy();
// Hash the dirname. This should prevent conflicts if we have multiple
// object files with the same filename in different subfolders.
let mut hasher = hash_map::DefaultHasher::new();
// Make the dirname relative (if possible) to avoid full system paths influencing the sha
// and making the output system-dependent
//
// NOTE: Here we allow using std::env::var (instead of Build::getenv) because
// CARGO_* variables always trigger a rebuild when changed
#[allow(clippy::disallowed_methods)]
let dirname = if let Some(root) = std::env::var_os("CARGO_MANIFEST_DIR") {
let root = root.to_string_lossy();
Cow::Borrowed(dirname.strip_prefix(&*root).unwrap_or(&dirname))
} else {
dirname
};
hasher.write(dirname.as_bytes());
if let Some(extension) = file.extension() {
hasher.write(extension.to_string_lossy().as_bytes());
}
let obj = dst
.join(format!("{:016x}-{}", hasher.finish(), basename))
.with_extension("o");
match obj.parent() {
Some(s) => fs::create_dir_all(s)?,
None => {
return Err(Error::new(
ErrorKind::InvalidArgument,
"dst is an invalid path with no parent",
));
}
};
objects.push(Object::new(file.to_path_buf(), obj));
}
Ok(objects)
}
pub(crate) fn run(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<(), Error> {
let mut child = spawn(cmd, cargo_output)?;
wait_on_child(cmd, &mut child, cargo_output)
}
pub(crate) fn run_output(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<Vec<u8>, Error> {
// We specifically need the output to be captured, so override default
let mut captured_cargo_output = cargo_output.clone();
captured_cargo_output.output = OutputKind::Capture;
let mut child = spawn(cmd, &captured_cargo_output)?;
let mut stdout = vec![];
child
.stdout
.take()
.unwrap()
.read_to_end(&mut stdout)
.unwrap();
// Don't care about this output, use the normal settings
wait_on_child(cmd, &mut child, cargo_output)?;
Ok(stdout)
}
pub(crate) fn spawn(cmd: &mut Command, cargo_output: &CargoOutput) -> Result<Child, Error> {
struct ResetStderr<'cmd>(&'cmd mut Command);
impl Drop for ResetStderr<'_> {
fn drop(&mut self) {
// Reset stderr to default to release pipe_writer so that print thread will
// not block forever.
self.0.stderr(Stdio::inherit());
}
}
cargo_output.print_debug(&format_args!("running: {cmd:?}"));
let cmd = ResetStderr(cmd);
let child = cmd
.0
.stderr(cargo_output.stdio_for_warnings())
.stdout(cargo_output.stdio_for_output())
.spawn();
match child {
Ok(child) => Ok(child),
Err(ref e) if e.kind() == io::ErrorKind::NotFound => {
let extra = if cfg!(windows) {
" (see https://docs.rs/cc/latest/cc/#compile-time-requirements for help)"
} else {
""
};
Err(Error::new(
ErrorKind::ToolNotFound,
format!("failed to find tool {:?}: {e}{extra}", cmd.0.get_program()),
))
}
Err(e) => Err(Error::new(
ErrorKind::ToolExecError,
format!("command `{:?}` failed to start: {e}", cmd.0),
)),
}
}
pub(crate) struct CmdAddOutputFileArgs {
pub(crate) cuda: bool,
pub(crate) is_assembler_msvc: bool,
pub(crate) msvc: bool,
pub(crate) clang: bool,
pub(crate) gnu: bool,
pub(crate) is_asm: bool,
pub(crate) is_arm: bool,
}
pub(crate) fn command_add_output_file(cmd: &mut Command, dst: &Path, args: CmdAddOutputFileArgs) {
if args.is_assembler_msvc
|| !(!args.msvc || args.clang || args.gnu || args.cuda || (args.is_asm && args.is_arm))
{
let mut s = OsString::from("-Fo");
s.push(dst);
cmd.arg(s);
} else {
cmd.arg("-o").arg(dst);
}
}

15
vendor/cc/src/detect_compiler_family.c vendored Normal file
View File

@@ -0,0 +1,15 @@
#ifdef __clang__
#pragma message "clang"
#endif
#ifdef __GNUC__
#pragma message "gcc"
#endif
#ifdef __EMSCRIPTEN__
#pragma message "emscripten"
#endif
#ifdef __VXWORKS__
#pragma message "VxWorks"
#endif

561
vendor/cc/src/flags.rs vendored Normal file
View File

@@ -0,0 +1,561 @@
use crate::target::TargetInfo;
use crate::{Build, Error, ErrorKind, Tool, ToolFamily};
use std::borrow::Cow;
use std::ffi::OsString;
#[derive(Debug, PartialEq, Default)]
pub(crate) struct RustcCodegenFlags<'a> {
branch_protection: Option<&'a str>,
code_model: Option<&'a str>,
no_vectorize_loops: bool,
no_vectorize_slp: bool,
profile_generate: Option<&'a str>,
profile_use: Option<&'a str>,
control_flow_guard: Option<&'a str>,
lto: Option<&'a str>,
relocation_model: Option<&'a str>,
embed_bitcode: Option<bool>,
force_frame_pointers: Option<bool>,
no_redzone: Option<bool>,
soft_float: Option<bool>,
dwarf_version: Option<u32>,
stack_protector: Option<&'a str>,
linker_plugin_lto: Option<bool>,
}
impl<'this> RustcCodegenFlags<'this> {
// Parse flags obtained from CARGO_ENCODED_RUSTFLAGS
pub(crate) fn parse(rustflags_env: &'this str) -> Result<Self, Error> {
fn is_flag_prefix(flag: &str) -> bool {
[
"-Z",
"-C",
"--codegen",
"-L",
"-l",
"-o",
"-W",
"--warn",
"-A",
"--allow",
"-D",
"--deny",
"-F",
"--forbid",
]
.contains(&flag)
}
fn handle_flag_prefix<'a>(prev: &'a str, curr: &'a str) -> (&'a str, &'a str) {
match prev {
"--codegen" | "-C" => ("-C", curr),
// Handle flags passed like --codegen=code-model=small
_ if curr.starts_with("--codegen=") => ("-C", &curr[10..]),
"-Z" => ("-Z", curr),
"-L" | "-l" | "-o" => (prev, curr),
// Handle lint flags
"-W" | "--warn" => ("-W", curr),
"-A" | "--allow" => ("-A", curr),
"-D" | "--deny" => ("-D", curr),
"-F" | "--forbid" => ("-F", curr),
_ => ("", curr),
}
}
let mut codegen_flags = Self::default();
let mut prev_prefix = None;
for curr in rustflags_env.split("\u{1f}") {
let prev = prev_prefix.take().unwrap_or("");
if prev.is_empty() && is_flag_prefix(curr) {
prev_prefix = Some(curr);
continue;
}
let (prefix, rustc_flag) = handle_flag_prefix(prev, curr);
codegen_flags.set_rustc_flag(prefix, rustc_flag)?;
}
Ok(codegen_flags)
}
fn set_rustc_flag(&mut self, prefix: &str, flag: &'this str) -> Result<(), Error> {
// Convert a textual representation of a bool-like rustc flag argument into an actual bool
fn arg_to_bool(arg: impl AsRef<str>) -> Option<bool> {
match arg.as_ref() {
"y" | "yes" | "on" | "true" => Some(true),
"n" | "no" | "off" | "false" => Some(false),
_ => None,
}
}
fn arg_to_u32(arg: impl AsRef<str>) -> Option<u32> {
arg.as_ref().parse().ok()
}
let (flag, value) = if let Some((flag, value)) = flag.split_once('=') {
(flag, Some(value))
} else {
(flag, None)
};
let flag = if prefix.is_empty() {
Cow::Borrowed(flag)
} else {
Cow::Owned(format!("{prefix}{flag}"))
};
let flag = flag.as_ref();
fn flag_not_empty_generic<T>(
flag: &str,
flag_value: Option<T>,
) -> Result<Option<T>, Error> {
if let Some(flag_value) = flag_value {
Ok(Some(flag_value))
} else {
Err(Error::new(
ErrorKind::InvalidFlag,
format!("{flag} must have a value"),
))
}
}
let flag_not_empty = |flag_value| flag_not_empty_generic(flag, flag_value);
match flag {
// https://doc.rust-lang.org/rustc/codegen-options/index.html#code-model
"-Ccode-model" => {
self.code_model = flag_not_empty(value)?;
}
// https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-loops
"-Cno-vectorize-loops" => self.no_vectorize_loops = true,
// https://doc.rust-lang.org/rustc/codegen-options/index.html#no-vectorize-slp
"-Cno-vectorize-slp" => self.no_vectorize_slp = true,
// https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-generate
"-Cprofile-generate" => {
self.profile_generate = flag_not_empty(value)?;
}
// https://doc.rust-lang.org/rustc/codegen-options/index.html#profile-use
"-Cprofile-use" => {
self.profile_use = flag_not_empty(value)?;
}
// https://doc.rust-lang.org/rustc/codegen-options/index.html#control-flow-guard
"-Ccontrol-flow-guard" => self.control_flow_guard = value.or(Some("true")),
// https://doc.rust-lang.org/rustc/codegen-options/index.html#lto
"-Clto" => self.lto = value.or(Some("true")),
// https://doc.rust-lang.org/rustc/linker-plugin-lto.html
"-Clinker-plugin-lto" => self.linker_plugin_lto = Some(true),
// https://doc.rust-lang.org/rustc/codegen-options/index.html#relocation-model
"-Crelocation-model" => {
self.relocation_model = flag_not_empty(value)?;
}
// https://doc.rust-lang.org/rustc/codegen-options/index.html#embed-bitcode
"-Cembed-bitcode" => self.embed_bitcode = value.map_or(Some(true), arg_to_bool),
// https://doc.rust-lang.org/rustc/codegen-options/index.html#force-frame-pointers
"-Cforce-frame-pointers" => {
self.force_frame_pointers = value.map_or(Some(true), arg_to_bool)
}
// https://doc.rust-lang.org/rustc/codegen-options/index.html#no-redzone
"-Cno-redzone" => self.no_redzone = value.map_or(Some(true), arg_to_bool),
// https://doc.rust-lang.org/rustc/codegen-options/index.html#soft-float
// Note: This flag is now deprecated in rustc.
"-Csoft-float" => self.soft_float = value.map_or(Some(true), arg_to_bool),
// https://doc.rust-lang.org/beta/unstable-book/compiler-flags/branch-protection.html
// FIXME: Drop the -Z variant and update the doc link once the option is stabilised
"-Zbranch-protection" | "-Cbranch-protection" => {
self.branch_protection = flag_not_empty(value)?;
}
// https://doc.rust-lang.org/beta/unstable-book/compiler-flags/dwarf-version.html
// FIXME: Drop the -Z variant and update the doc link once the option is stablized
"-Zdwarf-version" | "-Cdwarf-version" => {
self.dwarf_version = flag_not_empty_generic(flag, value.and_then(arg_to_u32))?;
}
// https://github.com/rust-lang/rust/issues/114903
// FIXME: Drop the -Z variant and update the doc link once the option is stabilized
"-Zstack-protector" | "-Cstack-protector" => {
self.stack_protector = flag_not_empty(value)?;
}
_ => {}
}
Ok(())
}
// Rust and clang/cc don't agree on what equivalent flags should look like.
pub(crate) fn cc_flags(&self, build: &Build, tool: &mut Tool, target: &TargetInfo<'_>) {
let family = tool.family;
// Push `flag` to `flags` if it is supported by the currently used CC
let mut push_if_supported = |flag: OsString| {
if build
.is_flag_supported_inner(&flag, tool, target)
.unwrap_or(false)
{
tool.args.push(flag);
} else {
build.cargo_output.print_warning(&format!(
"Inherited flag {flag:?} is not supported by the currently used CC"
));
}
};
let clang_or_gnu =
matches!(family, ToolFamily::Clang { .. }) || matches!(family, ToolFamily::Gnu);
// Flags shared between clang and gnu
if clang_or_gnu {
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mbranch-protection
// https://gcc.gnu.org/onlinedocs/gcc/AArch64-Options.html#index-mbranch-protection (Aarch64)
// https://gcc.gnu.org/onlinedocs/gcc/ARM-Options.html#index-mbranch-protection-1 (ARM)
// https://developer.arm.com/documentation/101754/0619/armclang-Reference/armclang-Command-line-Options/-mbranch-protection
if let Some(value) = self.branch_protection {
push_if_supported(
format!("-mbranch-protection={}", value.replace(",", "+")).into(),
);
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mcmodel
// https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mcmodel=`).
// FIXME(madsmtm): Parse the model, to make sure we pass the correct value (depending on arch).
if let Some(value) = self.code_model {
push_if_supported(format!("-mcmodel={value}").into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-vectorize
// https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html
if self.no_vectorize_loops {
push_if_supported("-fno-vectorize".into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-slp-vectorize
// https://gcc.gnu.org/onlinedocs/gnat_ugn/Vectorization-of-loops.html
if self.no_vectorize_slp {
push_if_supported("-fno-slp-vectorize".into());
}
if let Some(value) = self.relocation_model {
let cc_flag = match value {
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIC
// https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIC
"pic" => Some("-fPIC"),
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fPIE
// https://gcc.gnu.org/onlinedocs/gcc/Code-Gen-Options.html#index-fPIE
"pie" => Some("-fPIE"),
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mdynamic-no-pic
// https://gcc.gnu.org/onlinedocs/gcc/RS_002f6000-and-PowerPC-Options.html#index-mdynamic-no-pic
"dynamic-no-pic" => Some("-mdynamic-no-pic"),
_ => None,
};
if let Some(cc_flag) = cc_flag {
push_if_supported(cc_flag.into());
}
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fno-omit-frame-pointer
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fomit-frame-pointer
// https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#index-fomit-frame-pointer
if let Some(value) = self.force_frame_pointers {
let cc_flag = if value {
"-fno-omit-frame-pointer"
} else {
"-fomit-frame-pointer"
};
push_if_supported(cc_flag.into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mno-red-zone
// https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mno-red-zone
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mred-zone
// https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#index-mred-zone
if let Some(value) = self.no_redzone {
let cc_flag = if value { "-mno-red-zone" } else { "-mred-zone" };
push_if_supported(cc_flag.into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-msoft-float
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mhard-float
// https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-msoft-float`).
// https://gcc.gnu.org/onlinedocs/gcc/Option-Summary.html (several archs, search for `-mhard-float`).
if let Some(value) = self.soft_float {
let cc_flag = if value {
"-msoft-float"
} else {
// Do not use -mno-soft-float, that's basically just an alias for -mno-implicit-float.
"-mhard-float"
};
push_if_supported(cc_flag.into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-gdwarf-2
// https://gcc.gnu.org/onlinedocs/gcc/Debugging-Options.html#index-gdwarf
if let Some(value) = self.dwarf_version {
push_if_supported(format!("-gdwarf-{value}").into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fstack-protector
// https://gcc.gnu.org/onlinedocs/gcc/Instrumentation-Options.html#index-fstack-protector
if let Some(value) = self.stack_protector {
// don't need to propagate stack-protector on MSVC since /GS is already the default
// https://learn.microsoft.com/en-us/cpp/build/reference/gs-buffer-security-check?view=msvc-170
//
// Do NOT `stack-protector=none` since it weakens security for C code,
// and `-Zstack-protector=basic` is deprecated and will be removed soon.
let cc_flag = match value {
"strong" => Some("-fstack-protector-strong"),
"all" => Some("-fstack-protector-all"),
_ => None,
};
if let Some(cc_flag) = cc_flag {
push_if_supported(cc_flag.into());
}
}
}
// Compiler-exclusive flags
match family {
ToolFamily::Clang { .. } => {
// GNU and Clang compilers both support the same PGO flags, but they use different libraries and
// different formats for the profile files which are not compatible.
// clang and rustc both internally use llvm, so we want to inherit the PGO flags only for clang.
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-generate
if let Some(value) = self.profile_generate {
push_if_supported(format!("-fprofile-generate={value}").into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fprofile-use
if let Some(value) = self.profile_use {
push_if_supported(format!("-fprofile-use={value}").into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fembed-bitcode
if let Some(value) = self.embed_bitcode {
let cc_val = if value { "all" } else { "off" };
push_if_supported(format!("-fembed-bitcode={cc_val}").into());
}
// https://doc.rust-lang.org/rustc/linker-plugin-lto.html
if self.linker_plugin_lto.unwrap_or(false) {
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-flto
let cc_val = match self.lto {
Some("thin") => "thin",
_ => "full",
};
push_if_supported(format!("-flto={cc_val}").into());
}
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mguard
if let Some(value) = self.control_flow_guard {
let cc_val = match value {
"y" | "yes" | "on" | "true" | "checks" => Some("cf"),
"nochecks" => Some("cf-nochecks"),
"n" | "no" | "off" | "false" => Some("none"),
_ => None,
};
if let Some(cc_val) = cc_val {
push_if_supported(format!("-mguard={cc_val}").into());
}
}
}
ToolFamily::Gnu => {}
ToolFamily::Msvc { .. } => {
// https://learn.microsoft.com/en-us/cpp/build/reference/guard-enable-control-flow-guard
if let Some(value) = self.control_flow_guard {
let cc_val = match value {
"y" | "yes" | "on" | "true" | "checks" => Some("cf"),
"n" | "no" | "off" | "false" => Some("cf-"),
_ => None,
};
if let Some(cc_val) = cc_val {
push_if_supported(format!("/guard:{cc_val}").into());
}
}
// https://learn.microsoft.com/en-us/cpp/build/reference/oy-frame-pointer-omission
if let Some(value) = self.force_frame_pointers {
// Flag is unsupported on 64-bit arches
if !target.arch.contains("64") {
let cc_flag = if value { "/Oy-" } else { "/Oy" };
push_if_supported(cc_flag.into());
}
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[track_caller]
fn check(env: &str, expected: &RustcCodegenFlags) {
let actual = RustcCodegenFlags::parse(env).unwrap();
assert_eq!(actual, *expected);
}
#[test]
fn codegen_type() {
let expected = RustcCodegenFlags {
code_model: Some("tiny"),
..RustcCodegenFlags::default()
};
check("-Ccode-model=tiny", &expected);
check("-C\u{1f}code-model=tiny", &expected);
check("--codegen\u{1f}code-model=tiny", &expected);
check("--codegen=code-model=tiny", &expected);
}
#[test]
fn precedence() {
check(
"-ccode-model=tiny\u{1f}-Ccode-model=small",
&RustcCodegenFlags {
code_model: Some("small"),
..RustcCodegenFlags::default()
},
);
}
#[test]
fn two_valid_prefixes() {
let expected = RustcCodegenFlags::default();
check("-L\u{1f}-Clto", &expected);
}
#[test]
fn stack_protector() {
let expected = RustcCodegenFlags {
stack_protector: Some("strong"),
..RustcCodegenFlags::default()
};
check("-Zstack-protector=strong", &expected);
check("-Cstack-protector=strong", &expected);
}
#[test]
fn three_valid_prefixes() {
let expected = RustcCodegenFlags {
lto: Some("true"),
..RustcCodegenFlags::default()
};
check("-L\u{1f}-L\u{1f}-Clto", &expected);
}
#[test]
fn all_rustc_flags() {
// Throw all possible flags at the parser to catch false positives
let flags = [
// Set all the flags we recognise first
"-Ccode-model=tiny",
"-Ccontrol-flow-guard=yes",
"-Cembed-bitcode=no",
"-Cforce-frame-pointers=yes",
"-Clto=false",
"-Clink-dead-code=yes",
"-Cno-redzone=yes",
"-Cno-vectorize-loops",
"-Cno-vectorize-slp",
"-Cprofile-generate=fooprofile",
"-Cprofile-use=fooprofile",
"-Crelocation-model=pic",
"-Csoft-float=yes",
"-Zbranch-protection=bti,pac-ret,leaf",
"-Zdwarf-version=5",
"-Zstack-protector=strong",
// Set flags we don't recognise but rustc supports next
// rustc flags
"--cfg",
"a",
"--check-cfg 'cfg(verbose)",
"-L",
"/usr/lib/foo",
"-l",
"static:+whole-archive=mylib",
"--crate-type=dylib",
"--crate-name=foo",
"--edition=2021",
"--emit=asm",
"--print=crate-name",
"-g",
"-O",
"-o",
"foooutput",
"--out-dir",
"foooutdir",
"--target",
"aarch64-unknown-linux-gnu",
"-W",
"missing-docs",
"-D",
"unused-variables",
"--force-warn",
"dead-code",
"-A",
"unused",
"-F",
"unused",
"--cap-lints",
"warn",
"--version",
"--verbose",
"-v",
"--extern",
"foocrate",
"--sysroot",
"fooroot",
"--error-format",
"human",
"--color",
"auto",
"--diagnostic-width",
"80",
"--remap-path-prefix",
"foo=bar",
"--json=artifact",
// Codegen flags
"-Car",
"-Ccodegen-units=1",
"-Ccollapse-macro-debuginfo=yes",
"-Cdebug-assertions=yes",
"-Cdebuginfo=1",
"-Cdefault-linker-libraries=yes",
"-Cdlltool=foo",
"-Cextra-filename=foo",
"-Cforce-unwind-tables=yes",
"-Cincremental=foodir",
"-Cinline-threshold=6",
"-Cinstrument-coverage",
"-Clink-arg=-foo",
"-Clink-args=-foo",
"-Clink-self-contained=yes",
"-Clinker=lld",
"-Clinker-flavor=ld.lld",
"-Clinker-plugin-lto=/path",
"-Cllvm-args=foo",
"-Cmetadata=foo",
"-Cno-prepopulate-passes",
"-Cno-stack-check",
"-Copt-level=3",
"-Coverflow-checks=yes",
"-Cpanic=abort",
"-Cpasses=foopass",
"-Cprefer-dynamic=yes",
"-Crelro-level=partial",
"-Cremark=all",
"-Crpath=yes",
"-Csave-temps=yes",
"-Csplit-debuginfo=packed",
"-Cstrip=symbols",
"-Csymbol-mangling-version=v0",
"-Ctarget-cpu=native",
"-Ctarget-feature=+sve",
// Unstable options
"-Ztune-cpu=machine",
];
check(
&flags.join("\u{1f}"),
&RustcCodegenFlags {
code_model: Some("tiny"),
control_flow_guard: Some("yes"),
embed_bitcode: Some(false),
force_frame_pointers: Some(true),
lto: Some("false"),
no_redzone: Some(true),
no_vectorize_loops: true,
no_vectorize_slp: true,
profile_generate: Some("fooprofile"),
profile_use: Some("fooprofile"),
relocation_model: Some("pic"),
soft_float: Some(true),
branch_protection: Some("bti,pac-ret,leaf"),
dwarf_version: Some(5),
stack_protector: Some("strong"),
linker_plugin_lto: Some(true),
},
);
}
}

4397
vendor/cc/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

118
vendor/cc/src/parallel/async_executor.rs vendored Normal file
View File

@@ -0,0 +1,118 @@
use std::{
cell::Cell,
future::Future,
pin::Pin,
ptr,
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
thread,
time::Duration,
};
use crate::Error;
const NOOP_WAKER_VTABLE: RawWakerVTable = RawWakerVTable::new(
// Cloning just returns a new no-op raw waker
|_| NOOP_RAW_WAKER,
// `wake` does nothing
|_| {},
// `wake_by_ref` does nothing
|_| {},
// Dropping does nothing as we don't allocate anything
|_| {},
);
const NOOP_RAW_WAKER: RawWaker = RawWaker::new(ptr::null(), &NOOP_WAKER_VTABLE);
#[derive(Default)]
pub(crate) struct YieldOnce(bool);
impl Future for YieldOnce {
type Output = ();
fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
let flag = &mut std::pin::Pin::into_inner(self).0;
if !*flag {
*flag = true;
Poll::Pending
} else {
Poll::Ready(())
}
}
}
/// Execute the futures and return when they are all done.
///
/// Here we use our own homebrew async executor since cc is used in the build
/// script of many popular projects, pulling in additional dependencies would
/// significantly slow down its compilation.
pub(crate) fn block_on<Fut1, Fut2>(
mut fut1: Fut1,
mut fut2: Fut2,
has_made_progress: &Cell<bool>,
) -> Result<(), Error>
where
Fut1: Future<Output = Result<(), Error>>,
Fut2: Future<Output = Result<(), Error>>,
{
// Shadows the future so that it can never be moved and is guaranteed
// to be pinned.
//
// The same trick used in `pin!` macro.
//
// TODO: Once MSRV is bumped to 1.68, replace this with `std::pin::pin!`
let mut fut1 = Some(unsafe { Pin::new_unchecked(&mut fut1) });
let mut fut2 = Some(unsafe { Pin::new_unchecked(&mut fut2) });
// TODO: Once `Waker::noop` stablised and our MSRV is bumped to the version
// which it is stablised, replace this with `Waker::noop`.
let waker = unsafe { Waker::from_raw(NOOP_RAW_WAKER) };
let mut context = Context::from_waker(&waker);
let mut backoff_cnt = 0;
loop {
has_made_progress.set(false);
if let Some(fut) = fut2.as_mut() {
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
fut2 = None;
res?;
}
}
if let Some(fut) = fut1.as_mut() {
if let Poll::Ready(res) = fut.as_mut().poll(&mut context) {
fut1 = None;
res?;
}
}
if fut1.is_none() && fut2.is_none() {
return Ok(());
}
if !has_made_progress.get() {
if backoff_cnt > 3 {
// We have yielded at least three times without making'
// any progress, so we will sleep for a while.
let duration = Duration::from_millis(100 * (backoff_cnt - 3).min(10));
thread::sleep(duration);
} else {
// Given that we spawned a lot of compilation tasks, it is unlikely
// that OS cannot find other ready task to execute.
//
// If all of them are done, then we will yield them and spawn more,
// or simply return.
//
// Thus this will not be turned into a busy-wait loop and it will not
// waste CPU resource.
thread::yield_now();
}
}
backoff_cnt = if has_made_progress.get() {
0
} else {
backoff_cnt + 1
};
}
}

175
vendor/cc/src/parallel/command_runner.rs vendored Normal file
View File

@@ -0,0 +1,175 @@
use std::{
cell::Cell,
io::{self, Write as _},
process::{Child, Command},
};
use crate::{
parallel::{
async_executor::{block_on, YieldOnce},
job_token,
},
spawn, CargoOutput, Error, ErrorKind, StderrForwarder,
};
struct KillOnDrop(Child, StderrForwarder);
impl Drop for KillOnDrop {
fn drop(&mut self) {
let child = &mut self.0;
child.kill().ok();
}
}
fn cell_update<T, F>(cell: &Cell<T>, f: F)
where
T: Default,
F: FnOnce(T) -> T,
{
let old = cell.take();
let new = f(old);
cell.set(new);
}
fn try_wait_on_child(
cmd: &Command,
child: &mut Child,
mut stdout: impl io::Write,
stderr_forwarder: &mut StderrForwarder,
) -> Result<Option<()>, Error> {
stderr_forwarder.forward_available();
match child.try_wait() {
Ok(Some(status)) => {
stderr_forwarder.forward_all();
let _ = writeln!(stdout, "{}", status);
if status.success() {
Ok(Some(()))
} else {
Err(Error::new(
ErrorKind::ToolExecError,
format!("command did not execute successfully (status code {status}): {cmd:?}"),
))
}
}
Ok(None) => Ok(None),
Err(e) => {
stderr_forwarder.forward_all();
Err(Error::new(
ErrorKind::ToolExecError,
format!("failed to wait on spawned child process `{cmd:?}`: {e}"),
))
}
}
}
pub(crate) fn run_commands_in_parallel(
cargo_output: &CargoOutput,
cmds: &mut dyn Iterator<Item = Result<Command, Error>>,
) -> Result<(), Error> {
// Limit our parallelism globally with a jobserver.
let mut tokens = job_token::ActiveJobTokenServer::new();
// When compiling objects in parallel we do a few dirty tricks to speed
// things up:
//
// * First is that we use the `jobserver` crate to limit the parallelism
// of this build script. The `jobserver` crate will use a jobserver
// configured by Cargo for build scripts to ensure that parallelism is
// coordinated across C compilations and Rust compilations. Before we
// compile anything we make sure to wait until we acquire a token.
//
// Note that this jobserver is cached globally so we only used one per
// process and only worry about creating it once.
//
// * Next we use spawn the process to actually compile objects in
// parallel after we've acquired a token to perform some work
//
// With all that in mind we compile all objects in a loop here, after we
// acquire the appropriate tokens, Once all objects have been compiled
// we wait on all the processes and propagate the results of compilation.
let pendings = Cell::new(Vec::<(Command, KillOnDrop, job_token::JobToken)>::new());
let is_disconnected = Cell::new(false);
let has_made_progress = Cell::new(false);
let wait_future = async {
let mut error = None;
// Buffer the stdout
let mut stdout = io::BufWriter::with_capacity(128, io::stdout());
loop {
// If the other end of the pipe is already disconnected, then we're not gonna get any new jobs,
// so it doesn't make sense to reuse the tokens; in fact,
// releasing them as soon as possible (once we know that the other end is disconnected) is beneficial.
// Imagine that the last file built takes an hour to finish; in this scenario,
// by not releasing the tokens before that last file is done we would effectively block other processes from
// starting sooner - even though we only need one token for that last file, not N others that were acquired.
let mut pendings_is_empty = false;
cell_update(&pendings, |mut pendings| {
// Try waiting on them.
pendings.retain_mut(|(cmd, child, _token)| {
match try_wait_on_child(cmd, &mut child.0, &mut stdout, &mut child.1) {
Ok(Some(())) => {
// Task done, remove the entry
has_made_progress.set(true);
false
}
Ok(None) => true, // Task still not finished, keep the entry
Err(err) => {
// Task fail, remove the entry.
// Since we can only return one error, log the error to make
// sure users always see all the compilation failures.
has_made_progress.set(true);
if cargo_output.warnings {
let _ = writeln!(stdout, "cargo:warning={}", err);
}
error = Some(err);
false
}
}
});
pendings_is_empty = pendings.is_empty();
pendings
});
if pendings_is_empty && is_disconnected.get() {
break if let Some(err) = error {
Err(err)
} else {
Ok(())
};
}
YieldOnce::default().await;
}
};
let spawn_future = async {
for res in cmds {
let mut cmd = res?;
let token = tokens.acquire().await?;
let mut child = spawn(&mut cmd, cargo_output)?;
let mut stderr_forwarder = StderrForwarder::new(&mut child);
stderr_forwarder.set_non_blocking()?;
cell_update(&pendings, |mut pendings| {
pendings.push((cmd, KillOnDrop(child, stderr_forwarder), token));
pendings
});
has_made_progress.set(true);
}
is_disconnected.set(true);
Ok::<_, Error>(())
};
block_on(wait_future, spawn_future, &has_made_progress)
}

267
vendor/cc/src/parallel/job_token.rs vendored Normal file
View File

@@ -0,0 +1,267 @@
use std::marker::PhantomData;
use crate::{utilities::OnceLock, Error};
pub(crate) struct JobToken(PhantomData<()>);
impl JobToken {
fn new() -> Self {
Self(PhantomData)
}
}
impl Drop for JobToken {
fn drop(&mut self) {
match JobTokenServer::new() {
JobTokenServer::Inherited(jobserver) => jobserver.release_token_raw(),
JobTokenServer::InProcess(jobserver) => jobserver.release_token_raw(),
}
}
}
enum JobTokenServer {
Inherited(inherited_jobserver::JobServer),
InProcess(inprocess_jobserver::JobServer),
}
impl JobTokenServer {
/// This function returns a static reference to the jobserver because
/// - creating a jobserver from env is a bit fd-unsafe (e.g. the fd might
/// be closed by other jobserver users in the process) and better do it
/// at the start of the program.
/// - in case a jobserver cannot be created from env (e.g. it's not
/// present), we will create a global in-process only jobserver
/// that has to be static so that it will be shared by all cc
/// compilation.
fn new() -> &'static Self {
// TODO: Replace with a OnceLock once MSRV is 1.70
static JOBSERVER: OnceLock<JobTokenServer> = OnceLock::new();
JOBSERVER.get_or_init(|| {
unsafe { inherited_jobserver::JobServer::from_env() }
.map(Self::Inherited)
.unwrap_or_else(|| Self::InProcess(inprocess_jobserver::JobServer::new()))
})
}
}
pub(crate) enum ActiveJobTokenServer {
Inherited(inherited_jobserver::ActiveJobServer<'static>),
InProcess(&'static inprocess_jobserver::JobServer),
}
impl ActiveJobTokenServer {
pub(crate) fn new() -> Self {
match JobTokenServer::new() {
JobTokenServer::Inherited(inherited_jobserver) => {
Self::Inherited(inherited_jobserver.enter_active())
}
JobTokenServer::InProcess(inprocess_jobserver) => Self::InProcess(inprocess_jobserver),
}
}
pub(crate) async fn acquire(&mut self) -> Result<JobToken, Error> {
match self {
Self::Inherited(jobserver) => jobserver.acquire().await,
Self::InProcess(jobserver) => Ok(jobserver.acquire().await),
}
}
}
mod inherited_jobserver {
use super::JobToken;
use crate::{parallel::async_executor::YieldOnce, Error, ErrorKind};
use std::{
io, mem,
sync::{mpsc, Mutex, MutexGuard, PoisonError},
};
pub(super) struct JobServer {
/// Implicit token for this process which is obtained and will be
/// released in parent. Since `JobTokens` only give back what they got,
/// there should be at most one global implicit token in the wild.
///
/// Since Rust does not execute any `Drop` for global variables,
/// we can't just put it back to jobserver and then re-acquire it at
/// the end of the process.
///
/// Use `Mutex` to avoid race between acquire and release.
/// If an `AtomicBool` is used, then it's possible for:
/// - `release_token_raw`: Tries to set `global_implicit_token` to true, but it is already
/// set to `true`, continue to release it to jobserver
/// - `acquire` takes the global implicit token, set `global_implicit_token` to false
/// - `release_token_raw` now writes the token back into the jobserver, while
/// `global_implicit_token` is `false`
///
/// If the program exits here, then cc effectively increases parallelism by one, which is
/// incorrect, hence we use a `Mutex` here.
global_implicit_token: Mutex<bool>,
inner: jobserver::Client,
}
impl JobServer {
pub(super) unsafe fn from_env() -> Option<Self> {
jobserver::Client::from_env().map(|inner| Self {
inner,
global_implicit_token: Mutex::new(true),
})
}
fn get_global_implicit_token(&self) -> MutexGuard<'_, bool> {
self.global_implicit_token
.lock()
.unwrap_or_else(PoisonError::into_inner)
}
/// All tokens except for the global implicit token will be put back into the jobserver
/// immediately and they cannot be cached, since Rust does not call `Drop::drop` on
/// global variables.
pub(super) fn release_token_raw(&self) {
let mut global_implicit_token = self.get_global_implicit_token();
if *global_implicit_token {
// There's already a global implicit token, so this token must
// be released back into jobserver.
//
// `release_raw` should not block
let _ = self.inner.release_raw();
} else {
*global_implicit_token = true;
}
}
pub(super) fn enter_active(&self) -> ActiveJobServer<'_> {
ActiveJobServer {
jobserver: self,
helper_thread: None,
}
}
}
struct HelperThread {
inner: jobserver::HelperThread,
/// When rx is dropped, all the token stored within it will be dropped.
rx: mpsc::Receiver<io::Result<jobserver::Acquired>>,
}
impl HelperThread {
fn new(jobserver: &JobServer) -> Result<Self, Error> {
let (tx, rx) = mpsc::channel();
Ok(Self {
rx,
inner: jobserver.inner.clone().into_helper_thread(move |res| {
let _ = tx.send(res);
})?,
})
}
}
pub(crate) struct ActiveJobServer<'a> {
jobserver: &'a JobServer,
helper_thread: Option<HelperThread>,
}
impl ActiveJobServer<'_> {
pub(super) async fn acquire(&mut self) -> Result<JobToken, Error> {
let mut has_requested_token = false;
loop {
// Fast path
if mem::replace(&mut *self.jobserver.get_global_implicit_token(), false) {
break Ok(JobToken::new());
}
match self.jobserver.inner.try_acquire() {
Ok(Some(acquired)) => {
acquired.drop_without_releasing();
break Ok(JobToken::new());
}
Ok(None) => YieldOnce::default().await,
Err(err) if err.kind() == io::ErrorKind::Unsupported => {
// Fallback to creating a help thread with blocking acquire
let helper_thread = if let Some(thread) = self.helper_thread.as_ref() {
thread
} else {
self.helper_thread
.insert(HelperThread::new(self.jobserver)?)
};
match helper_thread.rx.try_recv() {
Ok(res) => {
let acquired = res?;
acquired.drop_without_releasing();
break Ok(JobToken::new());
}
Err(mpsc::TryRecvError::Disconnected) => break Err(Error::new(
ErrorKind::JobserverHelpThreadError,
"jobserver help thread has returned before ActiveJobServer is dropped",
)),
Err(mpsc::TryRecvError::Empty) => {
if !has_requested_token {
helper_thread.inner.request_token();
has_requested_token = true;
}
YieldOnce::default().await
}
}
}
Err(err) => break Err(err.into()),
}
}
}
}
}
mod inprocess_jobserver {
use super::JobToken;
use crate::parallel::async_executor::YieldOnce;
use std::{
env::var,
sync::atomic::{
AtomicU32,
Ordering::{AcqRel, Acquire},
},
};
pub(crate) struct JobServer(AtomicU32);
impl JobServer {
#[allow(clippy::disallowed_methods)]
pub(super) fn new() -> Self {
// Use `NUM_JOBS` if set (it's configured by Cargo) and otherwise
// just fall back to the number of cores on the local machine, or a reasonable
// default if that cannot be determined.
let parallelism = var("NUM_JOBS")
.ok()
.and_then(|j| j.parse::<u32>().ok())
.or_else(|| Some(std::thread::available_parallelism().ok()?.get() as u32))
.unwrap_or(4);
Self(AtomicU32::new(parallelism))
}
pub(super) async fn acquire(&self) -> JobToken {
loop {
let res = self
.0
.fetch_update(AcqRel, Acquire, |tokens| tokens.checked_sub(1));
if res.is_ok() {
break JobToken::new();
}
YieldOnce::default().await
}
}
pub(super) fn release_token_raw(&self) {
self.0.fetch_add(1, AcqRel);
}
}
}

6
vendor/cc/src/parallel/mod.rs vendored Normal file
View File

@@ -0,0 +1,6 @@
mod async_executor;
mod command_runner;
mod job_token;
pub(crate) mod stderr;
pub(crate) use command_runner::run_commands_in_parallel;

91
vendor/cc/src/parallel/stderr.rs vendored Normal file
View File

@@ -0,0 +1,91 @@
#![cfg_attr(target_family = "wasm", allow(unused))]
/// Helpers functions for [`ChildStderr`].
use std::{convert::TryInto, process::ChildStderr};
use crate::{Error, ErrorKind};
#[cfg(all(not(unix), not(windows), not(target_family = "wasm")))]
compile_error!("Only unix and windows support non-blocking pipes! For other OSes, disable the parallel feature.");
#[cfg(unix)]
fn get_flags(fd: std::os::unix::io::RawFd) -> Result<i32, Error> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL, 0) };
if flags == -1 {
Err(Error::new(
ErrorKind::IOError,
format!(
"Failed to get flags for pipe {}: {}",
fd,
std::io::Error::last_os_error()
),
))
} else {
Ok(flags)
}
}
#[cfg(unix)]
fn set_flags(fd: std::os::unix::io::RawFd, flags: std::os::raw::c_int) -> Result<(), Error> {
if unsafe { libc::fcntl(fd, libc::F_SETFL, flags) } == -1 {
Err(Error::new(
ErrorKind::IOError,
format!(
"Failed to set flags for pipe {}: {}",
fd,
std::io::Error::last_os_error()
),
))
} else {
Ok(())
}
}
#[cfg(unix)]
pub fn set_non_blocking(pipe: &impl std::os::unix::io::AsRawFd) -> Result<(), Error> {
// On Unix, switch the pipe to non-blocking mode.
// On Windows, we have a different way to be non-blocking.
let fd = pipe.as_raw_fd();
let flags = get_flags(fd)?;
set_flags(fd, flags | libc::O_NONBLOCK)
}
pub fn bytes_available(stderr: &mut ChildStderr) -> Result<usize, Error> {
let mut bytes_available = 0;
#[cfg(windows)]
{
use ::find_msvc_tools::windows_sys::PeekNamedPipe;
use std::os::windows::io::AsRawHandle;
use std::ptr::null_mut;
if unsafe {
PeekNamedPipe(
stderr.as_raw_handle(),
null_mut(),
0,
null_mut(),
&mut bytes_available,
null_mut(),
)
} == 0
{
return Err(Error::new(
ErrorKind::IOError,
format!(
"PeekNamedPipe failed with {}",
std::io::Error::last_os_error()
),
));
}
}
#[cfg(unix)]
{
use std::os::unix::io::AsRawFd;
if unsafe { libc::ioctl(stderr.as_raw_fd(), libc::FIONREAD, &mut bytes_available) } != 0 {
return Err(Error::new(
ErrorKind::IOError,
format!("ioctl failed with {}", std::io::Error::last_os_error()),
));
}
}
Ok(bytes_available.try_into().unwrap())
}

42
vendor/cc/src/target.rs vendored Normal file
View File

@@ -0,0 +1,42 @@
//! Parsing of `rustc` target names to match the values exposed to Cargo
//! build scripts (`CARGO_CFG_*`).
mod apple;
mod generated;
mod llvm;
mod parser;
pub(crate) use apple::*;
pub(crate) use parser::TargetInfoParser;
/// Information specific to a `rustc` target.
///
/// See <https://doc.rust-lang.org/cargo/appendix/glossary.html#target>.
#[derive(Debug, PartialEq, Clone)]
pub(crate) struct TargetInfo<'a> {
/// The full architecture, including the subarchitecture.
///
/// This differs from `cfg!(target_arch)`, which only specifies the
/// overall architecture, which is too coarse for certain cases.
pub full_arch: &'a str,
/// The overall target architecture.
///
/// This is the same as the value of `cfg!(target_arch)`.
pub arch: &'a str,
/// The target vendor.
///
/// This is the same as the value of `cfg!(target_vendor)`.
pub vendor: &'a str,
/// The operating system, or `none` on bare-metal targets.
///
/// This is the same as the value of `cfg!(target_os)`.
pub os: &'a str,
/// The environment on top of the operating system.
///
/// This is the same as the value of `cfg!(target_env)`.
pub env: &'a str,
/// The ABI on top of the operating system.
///
/// This is the same as the value of `cfg!(target_abi)`.
pub abi: &'a str,
}

64
vendor/cc/src/target/apple.rs vendored Normal file
View File

@@ -0,0 +1,64 @@
use super::TargetInfo;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub(crate) enum AppleEnv {
Simulator,
MacCatalyst,
}
pub(crate) use AppleEnv::*;
impl TargetInfo<'_> {
pub(crate) fn get_apple_env(&self) -> Option<AppleEnv> {
match (self.env, self.abi) {
("sim", _) | (_, "sim") => Some(Simulator),
("macabi", _) | (_, "macabi") => Some(MacCatalyst),
_ => None,
}
}
pub(crate) fn apple_sdk_name(&self) -> &'static str {
match (self.os, self.get_apple_env()) {
("macos", None) => "macosx",
("ios", None) => "iphoneos",
("ios", Some(Simulator)) => "iphonesimulator",
("ios", Some(MacCatalyst)) => "macosx",
("tvos", None) => "appletvos",
("tvos", Some(Simulator)) => "appletvsimulator",
("watchos", None) => "watchos",
("watchos", Some(Simulator)) => "watchsimulator",
("visionos", None) => "xros",
("visionos", Some(Simulator)) => "xrsimulator",
(os, _) => panic!("invalid Apple target OS {}", os),
}
}
pub(crate) fn apple_version_flag(&self, min_version: &str) -> String {
// There are many aliases for these, and `-mtargetos=` is preferred on Clang nowadays, but
// for compatibility with older Clang, we use the earliest supported name here.
//
// NOTE: GCC does not support `-miphoneos-version-min=` etc. (because it does not support
// iOS in general), but we specify them anyhow in case we actually have a Clang-like
// compiler disguised as a GNU-like compiler, or in case GCC adds support for these in the
// future.
//
// See also:
// https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-mmacos-version-min
// https://clang.llvm.org/docs/AttributeReference.html#availability
// https://gcc.gnu.org/onlinedocs/gcc/Darwin-Options.html#index-mmacosx-version-min
match (self.os, self.get_apple_env()) {
("macos", None) => format!("-mmacosx-version-min={min_version}"),
("ios", None) => format!("-miphoneos-version-min={min_version}"),
("ios", Some(Simulator)) => format!("-mios-simulator-version-min={min_version}"),
("ios", Some(MacCatalyst)) => format!("-mtargetos=ios{min_version}-macabi"),
("tvos", None) => format!("-mappletvos-version-min={min_version}"),
("tvos", Some(Simulator)) => format!("-mappletvsimulator-version-min={min_version}"),
("watchos", None) => format!("-mwatchos-version-min={min_version}"),
("watchos", Some(Simulator)) => format!("-mwatchsimulator-version-min={min_version}"),
// `-mxros-version-min` does not exist
// https://github.com/llvm/llvm-project/issues/88271
("visionos", None) => format!("-mtargetos=xros{min_version}"),
("visionos", Some(Simulator)) => format!("-mtargetos=xros{min_version}-simulator"),
(os, _) => panic!("invalid Apple target OS {}", os),
}
}
}

315
vendor/cc/src/target/generated.rs vendored Normal file
View File

@@ -0,0 +1,315 @@
//! This file is generated code. Please edit the generator in
//! dev-tools/gen-target-info if you need to make changes, or see
//! src/target/llvm.rs if you need to configure a specific LLVM triple.
#[rustfmt::skip]
pub(crate) const LLVM_TARGETS: &[(&str, &str)] = &[
("aarch64-apple-darwin", "arm64-apple-macosx"),
("aarch64-apple-ios", "arm64-apple-ios"),
("aarch64-apple-ios-macabi", "arm64-apple-ios-macabi"),
("aarch64-apple-ios-sim", "arm64-apple-ios-simulator"),
("aarch64-apple-tvos", "arm64-apple-tvos"),
("aarch64-apple-tvos-sim", "arm64-apple-tvos-simulator"),
("aarch64-apple-visionos", "arm64-apple-xros"),
("aarch64-apple-visionos-sim", "arm64-apple-xros-simulator"),
("aarch64-apple-watchos", "arm64-apple-watchos"),
("aarch64-apple-watchos-sim", "arm64-apple-watchos-simulator"),
("aarch64-fuchsia", "aarch64-fuchsia"),
("aarch64-kmc-solid_asp3", "aarch64-unknown-none"),
("aarch64-linux-android", "aarch64-linux-android"),
("aarch64-nintendo-switch-freestanding", "aarch64-unknown-none"),
("aarch64-pc-windows-gnullvm", "aarch64-pc-windows-gnu"),
("aarch64-pc-windows-msvc", "aarch64-pc-windows-msvc"),
("aarch64-unknown-freebsd", "aarch64-unknown-freebsd"),
("aarch64-unknown-fuchsia", "aarch64-unknown-fuchsia"),
("aarch64-unknown-hermit", "aarch64-unknown-hermit"),
("aarch64-unknown-illumos", "aarch64-unknown-solaris2.11"),
("aarch64-unknown-linux-gnu", "aarch64-unknown-linux-gnu"),
("aarch64-unknown-linux-gnu_ilp32", "aarch64-unknown-linux-gnu_ilp32"),
("aarch64-unknown-linux-musl", "aarch64-unknown-linux-musl"),
("aarch64-unknown-linux-ohos", "aarch64-unknown-linux-ohos"),
("aarch64-unknown-managarm-mlibc", "aarch64-unknown-managarm-mlibc"),
("aarch64-unknown-netbsd", "aarch64-unknown-netbsd"),
("aarch64-unknown-none", "aarch64-unknown-none"),
("aarch64-unknown-none-softfloat", "aarch64-unknown-none"),
("aarch64-unknown-nto-qnx700", "aarch64-unknown-unknown"),
("aarch64-unknown-nto-qnx710", "aarch64-unknown-unknown"),
("aarch64-unknown-nto-qnx710_iosock", "aarch64-unknown-unknown"),
("aarch64-unknown-nto-qnx800", "aarch64-unknown-unknown"),
("aarch64-unknown-nuttx", "aarch64-unknown-none"),
("aarch64-unknown-openbsd", "aarch64-unknown-openbsd"),
("aarch64-unknown-redox", "aarch64-unknown-redox"),
("aarch64-unknown-teeos", "aarch64-unknown-none"),
("aarch64-unknown-trusty", "aarch64-unknown-unknown-musl"),
("aarch64-unknown-uefi", "aarch64-unknown-windows"),
("aarch64-uwp-windows-msvc", "aarch64-pc-windows-msvc"),
("aarch64-wrs-vxworks", "aarch64-unknown-linux-gnu"),
("aarch64_be-unknown-hermit", "aarch64_be-unknown-hermit"),
("aarch64_be-unknown-linux-gnu", "aarch64_be-unknown-linux-gnu"),
("aarch64_be-unknown-linux-gnu_ilp32", "aarch64_be-unknown-linux-gnu_ilp32"),
("aarch64_be-unknown-linux-musl", "aarch64_be-unknown-linux-musl"),
("aarch64_be-unknown-netbsd", "aarch64_be-unknown-netbsd"),
("aarch64_be-unknown-none-softfloat", "aarch64_be-unknown-none"),
("amdgcn-amd-amdhsa", "amdgcn-amd-amdhsa"),
("arm-linux-androideabi", "arm-linux-androideabi"),
("arm-unknown-linux-gnueabi", "arm-unknown-linux-gnueabi"),
("arm-unknown-linux-gnueabihf", "arm-unknown-linux-gnueabihf"),
("arm-unknown-linux-musleabi", "arm-unknown-linux-musleabi"),
("arm-unknown-linux-musleabihf", "arm-unknown-linux-musleabihf"),
("arm64_32-apple-watchos", "arm64_32-apple-watchos"),
("arm64e-apple-darwin", "arm64e-apple-macosx"),
("arm64e-apple-ios", "arm64e-apple-ios"),
("arm64e-apple-tvos", "arm64e-apple-tvos"),
("arm64ec-pc-windows-msvc", "arm64ec-pc-windows-msvc"),
("armeb-unknown-linux-gnueabi", "armeb-unknown-linux-gnueabi"),
("armebv7r-none-eabi", "armebv7r-none-eabi"),
("armebv7r-none-eabihf", "armebv7r-none-eabihf"),
("armv4t-none-eabi", "armv4t-none-eabi"),
("armv4t-unknown-linux-gnueabi", "armv4t-unknown-linux-gnueabi"),
("armv5te-none-eabi", "armv5te-none-eabi"),
("armv5te-unknown-linux-gnueabi", "armv5te-unknown-linux-gnueabi"),
("armv5te-unknown-linux-musleabi", "armv5te-unknown-linux-musleabi"),
("armv5te-unknown-linux-uclibceabi", "armv5te-unknown-linux-gnueabi"),
("armv6-unknown-freebsd", "armv6-unknown-freebsd-gnueabihf"),
("armv6-unknown-netbsd-eabihf", "armv6-unknown-netbsdelf-eabihf"),
("armv6k-nintendo-3ds", "armv6k-none-eabihf"),
("armv7-apple-ios", "armv7-apple-ios7.0.0"),
("armv7-linux-androideabi", "armv7-none-linux-android"),
("armv7-rtems-eabihf", "armv7-unknown-none-eabihf"),
("armv7-sony-vita-newlibeabihf", "thumbv7a-sony-vita-eabihf"),
("armv7-unknown-freebsd", "armv7-unknown-freebsd-gnueabihf"),
("armv7-unknown-linux-gnueabi", "armv7-unknown-linux-gnueabi"),
("armv7-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"),
("armv7-unknown-linux-musleabi", "armv7-unknown-linux-musleabi"),
("armv7-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"),
("armv7-unknown-linux-ohos", "armv7-unknown-linux-ohos"),
("armv7-unknown-linux-uclibceabi", "armv7-unknown-linux-gnueabi"),
("armv7-unknown-linux-uclibceabihf", "armv7-unknown-linux-gnueabihf"),
("armv7-unknown-netbsd-eabihf", "armv7-unknown-netbsdelf-eabihf"),
("armv7-unknown-trusty", "armv7-unknown-unknown-gnueabi"),
("armv7-wrs-vxworks-eabihf", "armv7-unknown-linux-gnueabihf"),
("armv7a-kmc-solid_asp3-eabi", "armv7a-none-eabi"),
("armv7a-kmc-solid_asp3-eabihf", "armv7a-none-eabihf"),
("armv7a-none-eabi", "armv7a-none-eabi"),
("armv7a-none-eabihf", "armv7a-none-eabihf"),
("armv7a-nuttx-eabi", "armv7a-none-eabi"),
("armv7a-nuttx-eabihf", "armv7a-none-eabihf"),
("armv7a-vex-v5", "armv7a-none-eabihf"),
("armv7k-apple-watchos", "armv7k-apple-watchos"),
("armv7r-none-eabi", "armv7r-none-eabi"),
("armv7r-none-eabihf", "armv7r-none-eabihf"),
("armv7s-apple-ios", "armv7s-apple-ios"),
("armv8r-none-eabihf", "armv8r-none-eabihf"),
("asmjs-unknown-emscripten", "wasm32-unknown-emscripten"),
("avr-none", "avr-unknown-unknown"),
("avr-unknown-gnu-atmega328", "avr-unknown-unknown"),
("bpfeb-unknown-none", "bpfeb"),
("bpfel-unknown-none", "bpfel"),
("csky-unknown-linux-gnuabiv2", "csky-unknown-linux-gnuabiv2"),
("csky-unknown-linux-gnuabiv2hf", "csky-unknown-linux-gnuabiv2"),
("hexagon-unknown-linux-musl", "hexagon-unknown-linux-musl"),
("hexagon-unknown-none-elf", "hexagon-unknown-none-elf"),
("i386-apple-ios", "i386-apple-ios-simulator"),
("i586-pc-windows-msvc", "i586-pc-windows-msvc"),
("i586-unknown-linux-gnu", "i586-unknown-linux-gnu"),
("i586-unknown-linux-musl", "i586-unknown-linux-musl"),
("i586-unknown-netbsd", "i586-unknown-netbsdelf"),
("i586-unknown-redox", "i586-unknown-redox"),
("i686-apple-darwin", "i686-apple-macosx"),
("i686-linux-android", "i686-linux-android"),
("i686-pc-nto-qnx700", "i586-pc-unknown"),
("i686-pc-windows-gnu", "i686-pc-windows-gnu"),
("i686-pc-windows-gnullvm", "i686-pc-windows-gnu"),
("i686-pc-windows-msvc", "i686-pc-windows-msvc"),
("i686-unknown-freebsd", "i686-unknown-freebsd"),
("i686-unknown-haiku", "i686-unknown-haiku"),
("i686-unknown-hurd-gnu", "i686-unknown-hurd-gnu"),
("i686-unknown-linux-gnu", "i686-unknown-linux-gnu"),
("i686-unknown-linux-musl", "i686-unknown-linux-musl"),
("i686-unknown-netbsd", "i686-unknown-netbsdelf"),
("i686-unknown-openbsd", "i686-unknown-openbsd"),
("i686-unknown-uefi", "i686-unknown-windows-gnu"),
("i686-uwp-windows-gnu", "i686-pc-windows-gnu"),
("i686-uwp-windows-msvc", "i686-pc-windows-msvc"),
("i686-win7-windows-gnu", "i686-pc-windows-gnu"),
("i686-win7-windows-msvc", "i686-pc-windows-msvc"),
("i686-wrs-vxworks", "i686-unknown-linux-gnu"),
("loongarch32-unknown-none", "loongarch32-unknown-none"),
("loongarch32-unknown-none-softfloat", "loongarch32-unknown-none"),
("loongarch64-unknown-linux-gnu", "loongarch64-unknown-linux-gnu"),
("loongarch64-unknown-linux-musl", "loongarch64-unknown-linux-musl"),
("loongarch64-unknown-linux-ohos", "loongarch64-unknown-linux-ohos"),
("loongarch64-unknown-none", "loongarch64-unknown-none"),
("loongarch64-unknown-none-softfloat", "loongarch64-unknown-none"),
("m68k-unknown-linux-gnu", "m68k-unknown-linux-gnu"),
("m68k-unknown-none-elf", "m68k"),
("mips-mti-none-elf", "mips"),
("mips-unknown-linux-gnu", "mips-unknown-linux-gnu"),
("mips-unknown-linux-musl", "mips-unknown-linux-musl"),
("mips-unknown-linux-uclibc", "mips-unknown-linux-gnu"),
("mips64-openwrt-linux-musl", "mips64-unknown-linux-musl"),
("mips64-unknown-linux-gnuabi64", "mips64-unknown-linux-gnuabi64"),
("mips64-unknown-linux-muslabi64", "mips64-unknown-linux-musl"),
("mips64el-unknown-linux-gnuabi64", "mips64el-unknown-linux-gnuabi64"),
("mips64el-unknown-linux-muslabi64", "mips64el-unknown-linux-musl"),
("mipsel-mti-none-elf", "mipsel"),
("mipsel-sony-psp", "mipsel-sony-psp"),
("mipsel-sony-psx", "mipsel-sony-psx"),
("mipsel-unknown-linux-gnu", "mipsel-unknown-linux-gnu"),
("mipsel-unknown-linux-musl", "mipsel-unknown-linux-musl"),
("mipsel-unknown-linux-uclibc", "mipsel-unknown-linux-gnu"),
("mipsel-unknown-netbsd", "mipsel-unknown-netbsd"),
("mipsel-unknown-none", "mipsel-unknown-none"),
("mipsisa32r6-unknown-linux-gnu", "mipsisa32r6-unknown-linux-gnu"),
("mipsisa32r6el-unknown-linux-gnu", "mipsisa32r6el-unknown-linux-gnu"),
("mipsisa64r6-unknown-linux-gnuabi64", "mipsisa64r6-unknown-linux-gnuabi64"),
("mipsisa64r6el-unknown-linux-gnuabi64", "mipsisa64r6el-unknown-linux-gnuabi64"),
("msp430-none-elf", "msp430-none-elf"),
("nvptx64-nvidia-cuda", "nvptx64-nvidia-cuda"),
("powerpc-unknown-freebsd", "powerpc-unknown-freebsd13.0"),
("powerpc-unknown-linux-gnu", "powerpc-unknown-linux-gnu"),
("powerpc-unknown-linux-gnuspe", "powerpc-unknown-linux-gnuspe"),
("powerpc-unknown-linux-musl", "powerpc-unknown-linux-musl"),
("powerpc-unknown-linux-muslspe", "powerpc-unknown-linux-muslspe"),
("powerpc-unknown-netbsd", "powerpc-unknown-netbsd"),
("powerpc-unknown-openbsd", "powerpc-unknown-openbsd"),
("powerpc-wrs-vxworks", "powerpc-unknown-linux-gnu"),
("powerpc-wrs-vxworks-spe", "powerpc-unknown-linux-gnuspe"),
("powerpc64-ibm-aix", "powerpc64-ibm-aix"),
("powerpc64-unknown-freebsd", "powerpc64-unknown-freebsd"),
("powerpc64-unknown-linux-gnu", "powerpc64-unknown-linux-gnu"),
("powerpc64-unknown-linux-musl", "powerpc64-unknown-linux-musl"),
("powerpc64-unknown-openbsd", "powerpc64-unknown-openbsd"),
("powerpc64-wrs-vxworks", "powerpc64-unknown-linux-gnu"),
("powerpc64le-unknown-freebsd", "powerpc64le-unknown-freebsd"),
("powerpc64le-unknown-linux-gnu", "powerpc64le-unknown-linux-gnu"),
("powerpc64le-unknown-linux-musl", "powerpc64le-unknown-linux-musl"),
("riscv32-wrs-vxworks", "riscv32-unknown-linux-gnu"),
("riscv32e-unknown-none-elf", "riscv32"),
("riscv32em-unknown-none-elf", "riscv32"),
("riscv32emc-unknown-none-elf", "riscv32"),
("riscv32gc-unknown-linux-gnu", "riscv32-unknown-linux-gnu"),
("riscv32gc-unknown-linux-musl", "riscv32-unknown-linux-musl"),
("riscv32i-unknown-none-elf", "riscv32"),
("riscv32im-risc0-zkvm-elf", "riscv32"),
("riscv32im-unknown-none-elf", "riscv32"),
("riscv32ima-unknown-none-elf", "riscv32"),
("riscv32imac-esp-espidf", "riscv32"),
("riscv32imac-unknown-none-elf", "riscv32"),
("riscv32imac-unknown-nuttx-elf", "riscv32"),
("riscv32imac-unknown-xous-elf", "riscv32"),
("riscv32imafc-esp-espidf", "riscv32"),
("riscv32imafc-unknown-none-elf", "riscv32"),
("riscv32imafc-unknown-nuttx-elf", "riscv32"),
("riscv32imc-esp-espidf", "riscv32"),
("riscv32imc-unknown-none-elf", "riscv32"),
("riscv32imc-unknown-nuttx-elf", "riscv32"),
("riscv64-linux-android", "riscv64-linux-android"),
("riscv64-wrs-vxworks", "riscv64-unknown-linux-gnu"),
("riscv64a23-unknown-linux-gnu", "riscv64-unknown-linux-gnu"),
("riscv64gc-unknown-freebsd", "riscv64-unknown-freebsd"),
("riscv64gc-unknown-fuchsia", "riscv64-unknown-fuchsia"),
("riscv64gc-unknown-hermit", "riscv64-unknown-hermit"),
("riscv64gc-unknown-linux-gnu", "riscv64-unknown-linux-gnu"),
("riscv64gc-unknown-linux-musl", "riscv64-unknown-linux-musl"),
("riscv64gc-unknown-managarm-mlibc", "riscv64-unknown-managarm-mlibc"),
("riscv64gc-unknown-netbsd", "riscv64-unknown-netbsd"),
("riscv64gc-unknown-none-elf", "riscv64"),
("riscv64gc-unknown-nuttx-elf", "riscv64"),
("riscv64gc-unknown-openbsd", "riscv64-unknown-openbsd"),
("riscv64imac-unknown-none-elf", "riscv64"),
("riscv64imac-unknown-nuttx-elf", "riscv64"),
("s390x-unknown-linux-gnu", "s390x-unknown-linux-gnu"),
("s390x-unknown-linux-musl", "s390x-unknown-linux-musl"),
("sparc-unknown-linux-gnu", "sparc-unknown-linux-gnu"),
("sparc-unknown-none-elf", "sparc-unknown-none-elf"),
("sparc64-unknown-linux-gnu", "sparc64-unknown-linux-gnu"),
("sparc64-unknown-netbsd", "sparc64-unknown-netbsd"),
("sparc64-unknown-openbsd", "sparc64-unknown-openbsd"),
("sparcv9-sun-solaris", "sparcv9-sun-solaris"),
("thumbv4t-none-eabi", "thumbv4t-none-eabi"),
("thumbv5te-none-eabi", "thumbv5te-none-eabi"),
("thumbv6m-none-eabi", "thumbv6m-none-eabi"),
("thumbv6m-nuttx-eabi", "thumbv6m-none-eabi"),
("thumbv7a-nuttx-eabi", "thumbv7a-none-eabi"),
("thumbv7a-nuttx-eabihf", "thumbv7a-none-eabihf"),
("thumbv7a-pc-windows-msvc", "thumbv7a-pc-windows-msvc"),
("thumbv7a-uwp-windows-msvc", "thumbv7a-pc-windows-msvc"),
("thumbv7em-none-eabi", "thumbv7em-none-eabi"),
("thumbv7em-none-eabihf", "thumbv7em-none-eabihf"),
("thumbv7em-nuttx-eabi", "thumbv7em-none-eabi"),
("thumbv7em-nuttx-eabihf", "thumbv7em-none-eabihf"),
("thumbv7m-none-eabi", "thumbv7m-none-eabi"),
("thumbv7m-nuttx-eabi", "thumbv7m-none-eabi"),
("thumbv7neon-linux-androideabi", "armv7-none-linux-android"),
("thumbv7neon-unknown-linux-gnueabihf", "armv7-unknown-linux-gnueabihf"),
("thumbv7neon-unknown-linux-musleabihf", "armv7-unknown-linux-musleabihf"),
("thumbv8m.base-none-eabi", "thumbv8m.base-none-eabi"),
("thumbv8m.base-nuttx-eabi", "thumbv8m.base-none-eabi"),
("thumbv8m.main-none-eabi", "thumbv8m.main-none-eabi"),
("thumbv8m.main-none-eabihf", "thumbv8m.main-none-eabihf"),
("thumbv8m.main-nuttx-eabi", "thumbv8m.main-none-eabi"),
("thumbv8m.main-nuttx-eabihf", "thumbv8m.main-none-eabihf"),
("wasm32-unknown-emscripten", "wasm32-unknown-emscripten"),
("wasm32-unknown-unknown", "wasm32-unknown-unknown"),
("wasm32-wali-linux-musl", "wasm32-wasi"),
("wasm32-wasi", "wasm32-wasi"),
("wasm32-wasip1", "wasm32-wasip1"),
("wasm32-wasip1-threads", "wasm32-wasi"),
("wasm32-wasip2", "wasm32-wasip2"),
("wasm32v1-none", "wasm32-unknown-unknown"),
("wasm64-unknown-unknown", "wasm64-unknown-unknown"),
("x86_64-apple-darwin", "x86_64-apple-macosx"),
("x86_64-apple-ios", "x86_64-apple-ios-simulator"),
("x86_64-apple-ios-macabi", "x86_64-apple-ios-macabi"),
("x86_64-apple-tvos", "x86_64-apple-tvos-simulator"),
("x86_64-apple-watchos-sim", "x86_64-apple-watchos-simulator"),
("x86_64-fortanix-unknown-sgx", "x86_64-elf"),
("x86_64-fuchsia", "x86_64-fuchsia"),
("x86_64-linux-android", "x86_64-linux-android"),
("x86_64-lynx-lynxos178", "x86_64-unknown-unknown-gnu"),
("x86_64-pc-cygwin", "x86_64-pc-cygwin"),
("x86_64-pc-nto-qnx710", "x86_64-pc-unknown"),
("x86_64-pc-nto-qnx710_iosock", "x86_64-pc-unknown"),
("x86_64-pc-nto-qnx800", "x86_64-pc-unknown"),
("x86_64-pc-solaris", "x86_64-pc-solaris"),
("x86_64-pc-windows-gnu", "x86_64-pc-windows-gnu"),
("x86_64-pc-windows-gnullvm", "x86_64-pc-windows-gnu"),
("x86_64-pc-windows-msvc", "x86_64-pc-windows-msvc"),
("x86_64-sun-solaris", "x86_64-pc-solaris"),
("x86_64-unikraft-linux-musl", "x86_64-unknown-linux-musl"),
("x86_64-unknown-dragonfly", "x86_64-unknown-dragonfly"),
("x86_64-unknown-freebsd", "x86_64-unknown-freebsd"),
("x86_64-unknown-fuchsia", "x86_64-unknown-fuchsia"),
("x86_64-unknown-haiku", "x86_64-unknown-haiku"),
("x86_64-unknown-hermit", "x86_64-unknown-hermit"),
("x86_64-unknown-hurd-gnu", "x86_64-unknown-hurd-gnu"),
("x86_64-unknown-illumos", "x86_64-pc-solaris"),
("x86_64-unknown-l4re-uclibc", "x86_64-unknown-l4re-gnu"),
("x86_64-unknown-linux-gnu", "x86_64-unknown-linux-gnu"),
("x86_64-unknown-linux-gnux32", "x86_64-unknown-linux-gnux32"),
("x86_64-unknown-linux-musl", "x86_64-unknown-linux-musl"),
("x86_64-unknown-linux-none", "x86_64-unknown-linux-none"),
("x86_64-unknown-linux-ohos", "x86_64-unknown-linux-ohos"),
("x86_64-unknown-managarm-mlibc", "x86_64-unknown-managarm-mlibc"),
("x86_64-unknown-motor", "x86_64-unknown-none-elf"),
("x86_64-unknown-netbsd", "x86_64-unknown-netbsd"),
("x86_64-unknown-none", "x86_64-unknown-none-elf"),
("x86_64-unknown-none-linuxkernel", "x86_64-unknown-none-elf"),
("x86_64-unknown-openbsd", "x86_64-unknown-openbsd"),
("x86_64-unknown-redox", "x86_64-unknown-redox"),
("x86_64-unknown-trusty", "x86_64-unknown-unknown-musl"),
("x86_64-unknown-uefi", "x86_64-unknown-windows"),
("x86_64-uwp-windows-gnu", "x86_64-pc-windows-gnu"),
("x86_64-uwp-windows-msvc", "x86_64-pc-windows-msvc"),
("x86_64-win7-windows-gnu", "x86_64-pc-windows-gnu"),
("x86_64-win7-windows-msvc", "x86_64-pc-windows-msvc"),
("x86_64-wrs-vxworks", "x86_64-unknown-linux-gnu"),
("x86_64h-apple-darwin", "x86_64h-apple-macosx"),
("xtensa-esp32-espidf", "xtensa-none-elf"),
("xtensa-esp32-none-elf", "xtensa-none-elf"),
("xtensa-esp32s2-espidf", "xtensa-none-elf"),
("xtensa-esp32s2-none-elf", "xtensa-none-elf"),
("xtensa-esp32s3-espidf", "xtensa-none-elf"),
("xtensa-esp32s3-none-elf", "xtensa-none-elf"),
];

322
vendor/cc/src/target/llvm.rs vendored Normal file
View File

@@ -0,0 +1,322 @@
use std::borrow::Cow;
use super::{generated, TargetInfo};
impl TargetInfo<'_> {
/// The LLVM/Clang target triple.
///
/// See <https://clang.llvm.org/docs/CrossCompilation.html#target-triple>.
///
/// Rust and Clang don't really agree on target naming, so we first try to
/// find the matching trible based on `rustc`'s output, but if no such
/// triple exists, we attempt to construct the triple from scratch.
///
/// NOTE: You should never need to match on this explicitly, use the
/// fields on [`TargetInfo`] instead.
pub(crate) fn llvm_target(
&self,
rustc_target: &str,
version: Option<&str>,
) -> Cow<'static, str> {
if rustc_target == "armv7-apple-ios" {
// FIXME(madsmtm): Unnecessary once we bump MSRV to Rust 1.74
return Cow::Borrowed("armv7-apple-ios");
} else if self.os == "uefi" {
// Override the UEFI LLVM targets.
//
// The rustc mappings (as of 1.82) for the UEFI targets are:
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows
// * aarch64-unknown-uefi -> aarch64-unknown-windows
//
// However, in cc-rs all the UEFI targets use
// -windows-gnu. This has been the case since 2021 [1].
// * i686-unknown-uefi -> i686-unknown-windows-gnu
// * x86_64-unknown-uefi -> x86_64-unknown-windows-gnu
// * aarch64-unknown-uefi -> aarch64-unknown-windows-gnu
//
// For now, override the UEFI mapping to keep the behavior
// of cc-rs unchanged.
//
// TODO: as discussed in [2], it may be possible to switch
// to new UEFI targets added to clang, and regardless it
// would be good to have consistency between rustc and
// cc-rs.
//
// [1]: https://github.com/rust-lang/cc-rs/pull/623
// [2]: https://github.com/rust-lang/cc-rs/pull/1264
return Cow::Owned(format!("{}-unknown-windows-gnu", self.full_arch));
}
// If no version is requested, let's take the triple directly from
// `rustc` (the logic below is not yet good enough for most targets).
//
// FIXME(madsmtm): This should ideally be removed.
if version.is_none() {
if let Ok(index) = generated::LLVM_TARGETS
.binary_search_by_key(&rustc_target, |(rustc_target, _)| rustc_target)
{
let (_, llvm_target) = &generated::LLVM_TARGETS[index];
return Cow::Borrowed(llvm_target);
}
}
// Otherwise, attempt to construct the triple from the target info.
let arch = match self.full_arch {
riscv32 if riscv32.starts_with("riscv32") => "riscv32",
riscv64 if riscv64.starts_with("riscv64") => "riscv64",
"aarch64" if self.vendor == "apple" => "arm64",
"armv7" if self.vendor == "sony" => "thumbv7a", // FIXME
arch => arch,
};
let vendor = match self.vendor {
"kmc" | "nintendo" => "unknown",
"unknown" if self.os == "android" => "linux",
"uwp" => "pc",
"espressif" => "",
_ if self.arch == "msp430" => "",
vendor => vendor,
};
let os = match self.os {
"macos" => "macosx",
"visionos" => "xros",
"uefi" => "windows",
"solid_asp3" | "horizon" | "teeos" | "nuttx" | "espidf" => "none",
"nto" => "unknown", // FIXME
"trusty" => "unknown", // FIXME
os => os,
};
let version = version.unwrap_or("");
let env = match self.env {
"newlib" | "nto70" | "nto71" | "nto71_iosock" | "p1" | "p2" | "relibc" | "sgx"
| "uclibc" => "",
"sim" => "simulator",
env => env,
};
let abi = match self.abi {
"sim" => {
if env != "simulator" {
"simulator"
} else {
""
}
}
"llvm" | "softfloat" | "uwp" | "vec-extabi" => "",
"ilp32" => "_ilp32",
"abi64" => "",
abi => abi,
};
Cow::Owned(match (vendor, env, abi) {
("", "", "") => format!("{arch}-{os}{version}"),
("", env, abi) => format!("{arch}-{os}{version}-{env}{abi}"),
(vendor, "", "") => format!("{arch}-{vendor}-{os}{version}"),
(vendor, env, abi) => format!("{arch}-{vendor}-{os}{version}-{env}{abi}"),
})
}
}
#[cfg(test)]
mod tests {
use std::process::Command;
use crate::TargetInfo;
#[test]
fn test_old_ios_target() {
assert_eq!(
TargetInfo {
full_arch: "armv7",
arch: "armv7",
vendor: "apple",
os: "ios",
env: "",
abi: "",
}
.llvm_target("armv7-apple-ios", None),
"armv7-apple-ios"
);
}
#[test]
fn basic_llvm_triple_guessing() {
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "unknown",
os: "linux",
env: "",
abi: "",
}
.llvm_target("invalid", None),
"aarch64-unknown-linux"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "gnu",
abi: "",
}
.llvm_target("invalid", None),
"x86_64-unknown-linux-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "gnu",
abi: "eabi",
}
.llvm_target("invalid", None),
"x86_64-unknown-linux-gnueabi"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "apple",
os: "macos",
env: "",
abi: "",
}
.llvm_target("invalid", None),
"x86_64-apple-macosx"
);
}
#[test]
fn llvm_version() {
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "ios",
env: "",
abi: "sim",
}
.llvm_target("aarch64-apple-ios-sim", Some("14.0")),
"arm64-apple-ios14.0-simulator"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "visionos",
env: "",
abi: "",
}
.llvm_target("aarch64-apple-visionos", Some("2.0")),
"arm64-apple-xros2.0"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "apple",
os: "ios",
env: "",
abi: "macabi",
}
.llvm_target("aarch64-apple-ios-macabi", Some("13.1")),
"arm64-apple-ios13.1-macabi"
);
}
#[test]
fn uefi() {
assert_eq!(
TargetInfo {
full_arch: "i686",
arch: "x86",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("i686-unknown-uefi", None),
"i686-unknown-windows-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("x86_64-unknown-uefi", None),
"x86_64-unknown-windows-gnu"
);
assert_eq!(
TargetInfo {
full_arch: "aarch64",
arch: "aarch64",
vendor: "unknown",
os: "uefi",
env: "",
abi: "",
}
.llvm_target("aarch64-unknown-uefi", None),
"aarch64-unknown-windows-gnu"
);
}
#[test]
#[ignore = "not yet done"]
fn llvm_for_all_rustc_targets() {
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
let target_list = Command::new(&rustc)
.arg("--print=target-list")
.output()
.unwrap()
.stdout;
let target_list = String::from_utf8(target_list).unwrap();
let mut has_failure = false;
for target in target_list.lines() {
let spec_json = Command::new(&rustc)
.arg("--target")
.arg(target)
.arg("-Zunstable-options")
.arg("--print=target-spec-json")
.env("RUSTC_BOOTSTRAP", "1") // Crimes
.output()
.unwrap()
.stdout;
let spec_json = String::from_utf8(spec_json).unwrap();
// JSON crimes
let expected = spec_json
.split_once("llvm-target\": \"")
.unwrap()
.1
.split_once("\"")
.unwrap()
.0;
let actual = TargetInfo::from_rustc_target(target)
.map(|target| target.llvm_target("invalid", None));
if Some(expected) != actual.as_deref().ok() {
eprintln!("failed comparing {target}:");
eprintln!(" expected: Ok({expected:?})");
eprintln!(" actual: {actual:?}");
eprintln!();
has_failure = true;
}
}
if has_failure {
panic!("failed comparing targets");
}
}
}

600
vendor/cc/src/target/parser.rs vendored Normal file
View File

@@ -0,0 +1,600 @@
use std::env;
use crate::{target::TargetInfo, utilities::OnceLock, Error, ErrorKind};
#[derive(Debug)]
struct TargetInfoParserInner {
full_arch: Box<str>,
arch: Box<str>,
vendor: Box<str>,
os: Box<str>,
env: Box<str>,
abi: Box<str>,
}
impl TargetInfoParserInner {
fn from_cargo_environment_variables() -> Result<Self, Error> {
// `TARGET` must be present.
//
// No need to emit `rerun-if-env-changed` for this,
// as it is controlled by Cargo itself.
#[allow(clippy::disallowed_methods)]
let target_name = env::var("TARGET").map_err(|err| {
Error::new(
ErrorKind::EnvVarNotFound,
format!("failed reading TARGET: {err}"),
)
})?;
// Parse the full architecture name from the target name.
let (full_arch, _rest) = target_name.split_once('-').ok_or(Error::new(
ErrorKind::InvalidTarget,
format!("target `{target_name}` only had a single component (at least two required)"),
))?;
let cargo_env = |name, fallback: Option<&str>| -> Result<Box<str>, Error> {
// No need to emit `rerun-if-env-changed` for these,
// as they are controlled by Cargo itself.
#[allow(clippy::disallowed_methods)]
match env::var(name) {
Ok(var) => Ok(var.into_boxed_str()),
Err(err) => match fallback {
Some(fallback) => Ok(fallback.into()),
None => Err(Error::new(
ErrorKind::EnvVarNotFound,
format!("did not find fallback information for target `{target_name}`, and failed reading {name}: {err}"),
)),
},
}
};
// Prefer to use `CARGO_ENV_*` if set, since these contain the most
// correct information relative to the current `rustc`, and makes it
// possible to support custom target JSON specs unknown to `rustc`.
//
// NOTE: If the user is using an older `rustc`, that data may be older
// than our pre-generated data, but we still prefer Cargo's view of
// the world, since at least `cc` won't differ from `rustc` in that
// case.
//
// These may not be set in case the user depended on being able to
// just set `TARGET` outside of build scripts; in those cases, fall
// back back to data from the known set of target names instead.
//
// See discussion in #1225 for further details.
let fallback_target = TargetInfo::from_rustc_target(&target_name).ok();
let ft = fallback_target.as_ref();
let arch = cargo_env("CARGO_CFG_TARGET_ARCH", ft.map(|t| t.arch))?;
let vendor = cargo_env("CARGO_CFG_TARGET_VENDOR", ft.map(|t| t.vendor))?;
let os = cargo_env("CARGO_CFG_TARGET_OS", ft.map(|t| t.os))?;
let env = cargo_env("CARGO_CFG_TARGET_ENV", ft.map(|t| t.env))?;
// `target_abi` was stabilized in Rust 1.78, which is higher than our
// MSRV, so it may not always be available; In that case, fall back to
// `""`, which is _probably_ correct for unknown target names.
let abi = cargo_env("CARGO_CFG_TARGET_ABI", ft.map(|t| t.abi))
.unwrap_or_else(|_| String::default().into_boxed_str());
Ok(Self {
full_arch: full_arch.to_string().into_boxed_str(),
arch,
vendor,
os,
env,
abi,
})
}
}
/// Parser for [`TargetInfo`], contains cached information.
#[derive(Default, Debug)]
pub(crate) struct TargetInfoParser(OnceLock<Result<TargetInfoParserInner, Error>>);
impl TargetInfoParser {
pub fn parse_from_cargo_environment_variables(&self) -> Result<TargetInfo<'_>, Error> {
match self
.0
.get_or_init(TargetInfoParserInner::from_cargo_environment_variables)
{
Ok(TargetInfoParserInner {
full_arch,
arch,
vendor,
os,
env,
abi,
}) => Ok(TargetInfo {
full_arch,
arch,
vendor,
os,
env,
abi,
}),
Err(e) => Err(e.clone()),
}
}
}
/// Parse the full architecture in the target name into the simpler
/// `cfg(target_arch = "...")` that `rustc` exposes.
fn parse_arch(full_arch: &str) -> Option<&str> {
// NOTE: Some of these don't necessarily match an existing target in
// `rustc`. They're parsed anyhow to be as forward-compatible as possible,
// while still being correct.
//
// See also:
// https://docs.rs/cfg-expr/0.18.0/cfg_expr/targets/index.html
// https://docs.rs/target-lexicon/0.13.2/target_lexicon/enum.Architecture.html
// https://gcc.gnu.org/onlinedocs/gcc/Submodel-Options.html
// `clang -print-targets`
Some(match full_arch {
arch if arch.starts_with("mipsisa32r6") => "mips32r6", // mipsisa32r6 | mipsisa32r6el
arch if arch.starts_with("mipsisa64r6") => "mips64r6", // mipsisa64r6 | mipsisa64r6el
arch if arch.starts_with("mips64") => "mips64", // mips64 | mips64el
arch if arch.starts_with("mips") => "mips", // mips | mipsel
arch if arch.starts_with("loongarch64") => "loongarch64",
arch if arch.starts_with("loongarch32") => "loongarch32",
arch if arch.starts_with("powerpc64") => "powerpc64", // powerpc64 | powerpc64le
arch if arch.starts_with("powerpc") => "powerpc",
arch if arch.starts_with("ppc64") => "powerpc64",
arch if arch.starts_with("ppc") => "powerpc",
arch if arch.starts_with("x86_64") => "x86_64", // x86_64 | x86_64h
arch if arch.starts_with("i") && arch.ends_with("86") => "x86", // i386 | i586 | i686
"arm64ec" => "arm64ec", // https://github.com/rust-lang/rust/issues/131172
arch if arch.starts_with("aarch64") => "aarch64", // arm64e | arm64_32
arch if arch.starts_with("arm64") => "aarch64", // aarch64 | aarch64_be
arch if arch.starts_with("arm") => "arm", // arm | armv7s | armeb | ...
arch if arch.starts_with("thumb") => "arm", // thumbv4t | thumbv7a | thumbv8m | ...
arch if arch.starts_with("riscv64") => "riscv64",
arch if arch.starts_with("riscv32") => "riscv32",
arch if arch.starts_with("wasm64") => "wasm64",
arch if arch.starts_with("wasm32") => "wasm32", // wasm32 | wasm32v1
"asmjs" => "wasm32",
arch if arch.starts_with("nvptx64") => "nvptx64",
arch if arch.starts_with("nvptx") => "nvptx",
arch if arch.starts_with("bpf") => "bpf", // bpfeb | bpfel
// https://github.com/bytecodealliance/wasmtime/tree/v30.0.1/pulley
arch if arch.starts_with("pulley64") => "pulley64",
arch if arch.starts_with("pulley32") => "pulley32",
// https://github.com/Clever-ISA/Clever-ISA
arch if arch.starts_with("clever") => "clever",
"sparc" | "sparcv7" | "sparcv8" => "sparc",
"sparc64" | "sparcv9" => "sparc64",
"amdgcn" => "amdgpu",
"avr" => "avr",
"csky" => "csky",
"hexagon" => "hexagon",
"m68k" => "m68k",
"msp430" => "msp430",
"r600" => "r600",
"s390x" => "s390x",
"xtensa" => "xtensa",
// Arches supported by gcc, but not LLVM.
arch if arch.starts_with("alpha") => "alpha", // DEC Alpha
"hppa" => "hppa", // https://en.wikipedia.org/wiki/PA-RISC, also known as HPPA
arch if arch.starts_with("sh") => "sh", // SuperH
_ => return None,
})
}
/// Parse environment and ABI from the last component of the target name.
fn parse_envabi(last_component: &str) -> Option<(&str, &str)> {
let (env, abi) = match last_component {
// Combined environment and ABI
// gnullvm | gnueabi | gnueabihf | gnuabiv2 | gnuabi64 | gnuspe | gnux32 | gnu_ilp32
env_and_abi if env_and_abi.starts_with("gnu") => {
let abi = env_and_abi.strip_prefix("gnu").unwrap();
let abi = abi.strip_prefix("_").unwrap_or(abi);
("gnu", abi)
}
// musl | musleabi | musleabihf | muslabi64 | muslspe
env_and_abi if env_and_abi.starts_with("musl") => {
("musl", env_and_abi.strip_prefix("musl").unwrap())
}
// uclibc | uclibceabi | uclibceabihf
env_and_abi if env_and_abi.starts_with("uclibc") => {
("uclibc", env_and_abi.strip_prefix("uclibc").unwrap())
}
// newlib | newlibeabihf
env_and_abi if env_and_abi.starts_with("newlib") => {
("newlib", env_and_abi.strip_prefix("newlib").unwrap())
}
// Environments
"msvc" => ("msvc", ""),
"ohos" => ("ohos", ""),
"qnx700" => ("nto70", ""),
"qnx710_iosock" => ("nto71_iosock", ""),
"qnx710" => ("nto71", ""),
"qnx800" => ("nto80", ""),
"sgx" => ("sgx", ""),
"threads" => ("threads", ""),
"mlibc" => ("mlibc", ""),
// ABIs
"abi64" => ("", "abi64"),
"abiv2" => ("", "spe"),
"eabi" => ("", "eabi"),
"eabihf" => ("", "eabihf"),
"macabi" => ("macabi", ""),
"sim" => ("sim", ""),
"softfloat" => ("", "softfloat"),
"spe" => ("", "spe"),
"x32" => ("", "x32"),
// Badly named targets, ELF is already known from target OS.
// Probably too late to fix now though.
"elf" => ("", ""),
// Undesirable to expose to user code (yet):
// https://github.com/rust-lang/rust/pull/131166#issuecomment-2389541917
"freestanding" => ("", ""),
_ => return None,
};
Some((env, abi))
}
impl<'a> TargetInfo<'a> {
pub(crate) fn from_rustc_target(target: &'a str) -> Result<Self, Error> {
// FIXME(madsmtm): This target should be renamed, cannot be parsed
// with the means we do below (since `none` must not be interpreted
// as an env/ABI).
if target == "x86_64-unknown-linux-none" {
return Ok(Self {
full_arch: "x86_64",
arch: "x86_64",
vendor: "unknown",
os: "linux",
env: "",
abi: "",
});
}
if target == "armv7a-vex-v5" {
return Ok(Self {
full_arch: "armv7a",
arch: "arm",
vendor: "vex",
os: "vexos",
env: "v5",
abi: "eabihf",
});
}
let mut components = target.split('-');
// Insist that the target name contains at least a valid architecture.
let full_arch = components.next().ok_or(Error::new(
ErrorKind::InvalidTarget,
"target was empty".to_string(),
))?;
let arch = parse_arch(full_arch).ok_or_else(|| {
Error::new(
ErrorKind::UnknownTarget,
format!("target `{target}` had an unknown architecture"),
)
})?;
// Newer target names have begun omitting the vendor, so the only
// component we know must be there is the OS name.
let components: Vec<_> = components.collect();
let (vendor, os, mut env, mut abi) = match &*components {
[] => {
return Err(Error::new(
ErrorKind::InvalidTarget,
format!("target `{target}` must have at least two components"),
))
}
// Two components; format is `arch-os`.
[os] => ("unknown", *os, "", ""),
// The three-component case is a bit tricky to handle, it could
// either have the format `arch-vendor-os` or `arch-os-env+abi`.
[vendor_or_os, os_or_envabi] => {
// We differentiate between these by checking if the last
// component is an env/ABI; if it isn't, then it's probably
// an OS instead.
if let Some((env, abi)) = parse_envabi(os_or_envabi) {
("unknown", *vendor_or_os, env, abi)
} else {
(*vendor_or_os, *os_or_envabi, "", "")
}
}
// Four components; format is `arch-vendor-os-env+abi`.
[vendor, os, envabi] => {
let (env, abi) = parse_envabi(envabi).ok_or_else(|| {
Error::new(
ErrorKind::UnknownTarget,
format!("unknown environment/ABI `{envabi}` in target `{target}`"),
)
})?;
(*vendor, *os, env, abi)
}
_ => {
return Err(Error::new(
ErrorKind::InvalidTarget,
format!("too many components in target `{target}`"),
))
}
};
// Part of the architecture name is carried over into the ABI.
match full_arch {
// https://github.com/rust-lang/compiler-team/issues/830
arch if arch.starts_with("riscv32e") => {
abi = "ilp32e";
}
_ => {}
}
// Various environment/ABIs are determined based on OS name.
match os {
"3ds" | "rtems" | "espidf" => env = "newlib",
"vxworks" => env = "gnu",
"redox" => env = "relibc",
"aix" => abi = "vec-extabi",
_ => {}
}
// Extra overrides for badly named targets.
match target {
// Actually simulator targets.
"i386-apple-ios" | "x86_64-apple-ios" | "x86_64-apple-tvos" => {
env = "sim";
}
// Name should've contained `muslabi64`.
"mips64-openwrt-linux-musl" => {
abi = "abi64";
}
// Specifies abi even though not in name.
"armv6-unknown-freebsd" | "armv6k-nintendo-3ds" | "armv7-unknown-freebsd" => {
abi = "eabihf";
}
// Specifies abi even though not in name.
"armv7-unknown-linux-ohos" | "armv7-unknown-trusty" => {
abi = "eabi";
}
_ => {}
}
let os = match os {
// Horizon is the common/internal OS name for 3DS and the Switch.
"3ds" | "switch" => "horizon",
// FIXME(madsmtm): macOS targets are badly named.
"darwin" => "macos",
// WASI targets contain the preview version in them too. Should've
// been `wasi-p1`/`wasi-p2`, but that's probably too late now.
os if os.starts_with("wasi") => {
env = os.strip_prefix("wasi").unwrap();
"wasi"
}
// FIXME(madsmtm): Badly named targets `*-linux-androideabi`,
// should be `*-android-eabi`.
"androideabi" => {
abi = "eabi";
"android"
}
os => os,
};
let vendor = match vendor {
// esp, esp32, esp32s2 etc.
vendor if vendor.starts_with("esp") => "espressif",
// FIXME(madsmtm): Badly named targets `*-linux-android*`,
// "linux" makes no sense as the vendor name.
"linux" if os == "android" || os == "androideabi" => "unknown",
// FIXME(madsmtm): Fix in `rustc` after
// https://github.com/rust-lang/compiler-team/issues/850.
"wali" => "unknown",
"lynx" => "unknown",
// Some Linux distributions set their name as the target vendor,
// so we have to assume that it can be an arbitary string.
vendor => vendor,
};
// Intentionally also marked as an ABI:
// https://github.com/rust-lang/rust/pull/86922
if vendor == "fortanix" {
abi = "fortanix";
}
if vendor == "uwp" {
abi = "uwp";
}
if ["powerpc64-unknown-linux-gnu", "powerpc64-wrs-vxworks"].contains(&target) {
abi = "elfv1";
}
if [
"powerpc64-unknown-freebsd",
"powerpc64-unknown-linux-musl",
"powerpc64-unknown-openbsd",
"powerpc64le-unknown-freebsd",
"powerpc64le-unknown-linux-gnu",
"powerpc64le-unknown-linux-musl",
]
.contains(&target)
{
abi = "elfv2";
}
Ok(Self {
full_arch,
arch,
vendor,
os,
env,
abi,
})
}
}
#[cfg(test)]
#[allow(unexpected_cfgs)]
mod tests {
use std::process::Command;
use super::TargetInfo;
use crate::ErrorKind;
// Test tier 1 targets.
#[test]
fn tier1() {
let targets = [
"aarch64-unknown-linux-gnu",
"aarch64-apple-darwin",
"i686-pc-windows-gnu",
"i686-pc-windows-msvc",
"i686-unknown-linux-gnu",
"x86_64-apple-darwin",
"x86_64-pc-windows-gnu",
"x86_64-pc-windows-msvc",
"x86_64-unknown-linux-gnu",
];
for target in targets {
// Check that they parse.
let _ = TargetInfo::from_rustc_target(target).unwrap();
}
}
// Various custom target names not (or no longer) known by `rustc`.
#[test]
fn parse_extra() {
let targets = [
"aarch64-unknown-none-gnu",
"aarch64-uwp-windows-gnu",
"arm-frc-linux-gnueabi",
"arm-unknown-netbsd-eabi",
"armv7neon-unknown-linux-gnueabihf",
"armv7neon-unknown-linux-musleabihf",
"thumbv7-unknown-linux-gnueabihf",
"thumbv7-unknown-linux-musleabihf",
"armv7-apple-ios",
"wasm32-wasi",
"x86_64-rumprun-netbsd",
"x86_64-unknown-linux",
"x86_64-alpine-linux-musl",
"x86_64-chimera-linux-musl",
"x86_64-foxkit-linux-musl",
"arm-poky-linux-gnueabi",
"x86_64-unknown-moturus",
"x86_64-unknown-managarm-mlibc",
];
for target in targets {
// Check that they parse.
let _ = TargetInfo::from_rustc_target(target).unwrap();
}
}
fn target_from_rustc_cfgs<'a>(target: &'a str, cfgs: &'a str) -> TargetInfo<'a> {
// Cannot determine full architecture from cfgs.
let (full_arch, _rest) = target.split_once('-').expect("target to have arch");
let mut target = TargetInfo {
full_arch,
arch: "invalid-none-set",
vendor: "invalid-none-set",
os: "invalid-none-set",
env: "invalid-none-set",
// Not set in older Rust versions
abi: "",
};
for cfg in cfgs.lines() {
if let Some((name, value)) = cfg.split_once('=') {
// Remove whitespace, if `rustc` decided to insert any.
let name = name.trim();
let value = value.trim();
// Remove quotes around value.
let value = value.strip_prefix('"').unwrap_or(value);
let value = value.strip_suffix('"').unwrap_or(value);
match name {
"target_arch" => target.arch = value,
"target_vendor" => target.vendor = value,
"target_os" => target.os = value,
"target_env" => target.env = value,
"target_abi" => target.abi = value,
_ => {}
}
} else {
// Skip cfgs like `debug_assertions` and `unix`.
}
}
if matches!(target.abi, "macabi" | "sim") {
assert_eq!(target.env, target.abi);
target.abi = "";
}
target
}
#[test]
fn unknown_env_determined_as_unknown() {
let err = TargetInfo::from_rustc_target("aarch64-unknown-linux-bogus").unwrap_err();
assert!(matches!(err.kind, ErrorKind::UnknownTarget));
}
// Used in .github/workflows/test-rustc-targets.yml
#[test]
#[cfg_attr(
not(rustc_target_test),
ignore = "must enable explicitly with --cfg=rustc_target_test"
)]
fn parse_rustc_targets() {
let rustc = std::env::var("RUSTC").unwrap_or_else(|_| "rustc".to_string());
let target_list = Command::new(&rustc)
.arg("--print=target-list")
.output()
.unwrap()
.stdout;
let target_list = String::from_utf8(target_list).unwrap();
let mut has_failure = false;
for target in target_list.lines() {
let cfgs = Command::new(&rustc)
.arg("--target")
.arg(target)
.arg("--print=cfg")
.output()
.unwrap()
.stdout;
let cfgs = String::from_utf8(cfgs).unwrap();
let expected = target_from_rustc_cfgs(target, &cfgs);
let actual = TargetInfo::from_rustc_target(target);
if Some(&expected) != actual.as_ref().ok() {
eprintln!("failed comparing {target}:");
eprintln!(" expected: Ok({expected:?})");
eprintln!(" actual: {actual:?}");
eprintln!();
has_failure = true;
}
}
if has_failure {
panic!("failed comparing targets");
}
}
}

86
vendor/cc/src/tempfile.rs vendored Normal file
View File

@@ -0,0 +1,86 @@
#![cfg_attr(target_family = "wasm", allow(unused))]
use std::{
collections::hash_map::RandomState,
fs::{remove_file, File, OpenOptions},
hash::{BuildHasher, Hasher},
io, os,
path::{Path, PathBuf},
};
#[cfg(not(any(unix, target_family = "wasm", 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,
::find_msvc_tools::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 take_file(&mut self) -> Option<File> {
self.file.take()
}
}
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);
}
}

564
vendor/cc/src/tool.rs vendored Normal file
View File

@@ -0,0 +1,564 @@
use crate::{
command_helpers::{run_output, spawn, CargoOutput},
run,
tempfile::NamedTempfile,
Error, ErrorKind, OutputKind,
};
use std::io::Read;
use std::{
borrow::Cow,
collections::HashMap,
env,
ffi::{OsStr, OsString},
io::Write,
path::{Path, PathBuf},
process::{Command, Stdio},
sync::RwLock,
};
pub(crate) type CompilerFamilyLookupCache = HashMap<Box<[Box<OsStr>]>, ToolFamily>;
/// Configuration used to represent an invocation of a C compiler.
///
/// This can be used to figure out what compiler is in use, what the arguments
/// to it are, and what the environment variables look like for the compiler.
/// This can be used to further configure other build systems (e.g. forward
/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
/// compiler itself.
#[derive(Clone, Debug)]
#[allow(missing_docs)]
pub struct Tool {
pub(crate) path: PathBuf,
pub(crate) cc_wrapper_path: Option<PathBuf>,
pub(crate) cc_wrapper_args: Vec<OsString>,
pub(crate) args: Vec<OsString>,
pub(crate) env: Vec<(OsString, OsString)>,
pub(crate) family: ToolFamily,
pub(crate) cuda: bool,
pub(crate) removed_args: Vec<OsString>,
pub(crate) has_internal_target_arg: bool,
}
impl Tool {
pub(crate) fn from_find_msvc_tools(tool: ::find_msvc_tools::Tool) -> Self {
let mut cc_tool = Self::with_family(
tool.path().into(),
ToolFamily::Msvc {
clang_cl: tool.is_clang_cl(),
},
);
cc_tool.env = tool
.env()
.into_iter()
.map(|(k, v)| (k.clone(), v.clone()))
.collect();
cc_tool
}
pub(crate) fn new(
path: PathBuf,
cached_compiler_family: &RwLock<CompilerFamilyLookupCache>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
Self::with_features(
path,
vec![],
false,
cached_compiler_family,
cargo_output,
out_dir,
)
}
pub(crate) fn with_args(
path: PathBuf,
args: Vec<String>,
cached_compiler_family: &RwLock<CompilerFamilyLookupCache>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
Self::with_features(
path,
args,
false,
cached_compiler_family,
cargo_output,
out_dir,
)
}
/// Explicitly set the `ToolFamily`, skipping name-based detection.
pub(crate) fn with_family(path: PathBuf, family: ToolFamily) -> Self {
Self {
path,
cc_wrapper_path: None,
cc_wrapper_args: Vec::new(),
args: Vec::new(),
env: Vec::new(),
family,
cuda: false,
removed_args: Vec::new(),
has_internal_target_arg: false,
}
}
pub(crate) fn with_features(
path: PathBuf,
args: Vec<String>,
cuda: bool,
cached_compiler_family: &RwLock<CompilerFamilyLookupCache>,
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Self {
fn is_zig_cc(path: &Path, cargo_output: &CargoOutput) -> bool {
run_output(
Command::new(path).arg("--version"),
// tool detection issues should always be shown as warnings
cargo_output,
)
.map(|o| String::from_utf8_lossy(&o).contains("ziglang"))
.unwrap_or_default()
|| {
match path.file_name().map(OsStr::to_string_lossy) {
Some(fname) => fname.contains("zig"),
_ => false,
}
}
}
fn guess_family_from_stdout(
stdout: &str,
path: &Path,
args: &[String],
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).args(args).arg("-?").stdin(Stdio::null()),
&{
// 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,
args: &[String],
cargo_output: &CargoOutput,
out_dir: Option<&Path>,
) -> Result<ToolFamily, Error> {
let out_dir = out_dir
.map(Cow::Borrowed)
.unwrap_or_else(|| Cow::Owned(env::temp_dir()));
// Ensure all the parent directories exist otherwise temp file creation
// will fail
std::fs::create_dir_all(&out_dir).map_err(|err| Error {
kind: ErrorKind::IOError,
message: format!("failed to create OUT_DIR '{}': {}", out_dir.display(), err)
.into(),
})?;
let mut tmp =
NamedTempfile::new(&out_dir, "detect_compiler_family.c").map_err(|err| Error {
kind: ErrorKind::IOError,
message: format!(
"failed to create detect_compiler_family.c temp file in '{}': {}",
out_dir.display(),
err
)
.into(),
})?;
let mut tmp_file = tmp.take_file().unwrap();
tmp_file.write_all(include_bytes!("detect_compiler_family.c"))?;
// Close the file handle *now*, otherwise the compiler may fail to open it on Windows
// (#1082). The file stays on disk and its path remains valid until `tmp` is dropped.
tmp_file.flush()?;
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 mut cmd = Command::new(path);
cmd.arg("-E").arg(tmp.path());
// The -Wslash-u-filename warning is normally part of stdout.
// But with clang-cl it can be part of stderr instead and exit with a
// non-zero exit code.
let mut captured_cargo_output = compiler_detect_output.clone();
captured_cargo_output.output = OutputKind::Capture;
captured_cargo_output.warnings = true;
let mut child = spawn(&mut cmd, &captured_cargo_output)?;
let mut out = vec![];
let mut err = vec![];
child.stdout.take().unwrap().read_to_end(&mut out)?;
child.stderr.take().unwrap().read_to_end(&mut err)?;
let status = child.wait()?;
let stdout = if [&out, &err]
.iter()
.any(|o| String::from_utf8_lossy(o).contains("-Wslash-u-filename"))
{
run_output(
Command::new(path).arg("-E").arg("--").arg(tmp.path()),
&compiler_detect_output,
)?
} else {
if !status.success() {
return Err(Error::new(
ErrorKind::ToolExecError,
format!(
"command did not execute successfully (status code {status}): {cmd:?}"
),
));
}
out
};
let stdout = String::from_utf8_lossy(&stdout);
guess_family_from_stdout(&stdout, path, args, cargo_output)
}
let detect_family = |path: &Path, args: &[String]| -> Result<ToolFamily, Error> {
let cache_key = [path.as_os_str()]
.iter()
.cloned()
.chain(args.iter().map(OsStr::new))
.map(Into::into)
.collect();
if let Some(family) = cached_compiler_family.read().unwrap().get(&cache_key) {
return Ok(*family);
}
let family = detect_family_inner(path, args, cargo_output, out_dir)?;
cached_compiler_family
.write()
.unwrap()
.insert(cache_key, family);
Ok(family)
};
let family = detect_family(&path, &args).unwrap_or_else(|e| {
cargo_output.print_warning(&format_args!(
"Compiler family detection failed due to error: {e}"
));
match path.file_name().map(OsStr::to_string_lossy) {
Some(fname) if fname.contains("clang-cl") => ToolFamily::Msvc { clang_cl: true },
Some(fname) if fname.ends_with("cl") || fname == "cl.exe" => {
ToolFamily::Msvc { clang_cl: false }
}
Some(fname) if fname.contains("clang") => {
let is_clang_cl = args
.iter()
.any(|a| a.strip_prefix("--driver-mode=") == Some("cl"));
if is_clang_cl {
ToolFamily::Msvc { clang_cl: true }
} else {
ToolFamily::Clang {
zig_cc: is_zig_cc(&path, cargo_output),
}
}
}
Some(fname) if fname.contains("zig") => ToolFamily::Clang { zig_cc: true },
_ => ToolFamily::Gnu,
}
});
Tool {
path,
cc_wrapper_path: None,
cc_wrapper_args: Vec::new(),
args: Vec::new(),
env: Vec::new(),
family,
cuda,
removed_args: Vec::new(),
has_internal_target_arg: false,
}
}
/// Add an argument to be stripped from the final command arguments.
pub(crate) fn remove_arg(&mut self, flag: OsString) {
self.removed_args.push(flag);
}
/// Push an "exotic" flag to the end of the compiler's arguments list.
///
/// Nvidia compiler accepts only the most common compiler flags like `-D`,
/// `-I`, `-c`, etc. Options meant specifically for the underlying
/// host C++ compiler have to be prefixed with `-Xcompiler`.
/// [Another possible future application for this function is passing
/// clang-specific flags to clang-cl, which otherwise accepts only
/// MSVC-specific options.]
pub(crate) fn push_cc_arg(&mut self, flag: OsString) {
if self.cuda {
self.args.push("-Xcompiler".into());
}
self.args.push(flag);
}
/// Checks if an argument or flag has already been specified or conflicts.
///
/// Currently only checks optimization flags.
pub(crate) fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
let flag = flag.to_str().unwrap();
let mut chars = flag.chars();
// Only duplicate check compiler flags
if self.is_like_msvc() {
if chars.next() != Some('/') {
return false;
}
} else if (self.is_like_gnu() || self.is_like_clang()) && chars.next() != Some('-') {
return false;
}
// Check for existing optimization flags (-O, /O)
if chars.next() == Some('O') {
return self
.args()
.iter()
.any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
}
// TODO Check for existing -m..., -m...=..., /arch:... flags
false
}
/// Don't push optimization arg if it conflicts with existing args.
pub(crate) fn push_opt_unless_duplicate(&mut self, flag: OsString) {
if self.is_duplicate_opt_arg(&flag) {
eprintln!("Info: Ignoring duplicate arg {:?}", &flag);
} else {
self.push_cc_arg(flag);
}
}
/// Converts this compiler into a `Command` that's ready to be run.
///
/// This is useful for when the compiler needs to be executed and the
/// command returned will already have the initial arguments and environment
/// variables configured.
pub fn to_command(&self) -> Command {
let mut cmd = match self.cc_wrapper_path {
Some(ref cc_wrapper_path) => {
let mut cmd = Command::new(cc_wrapper_path);
cmd.arg(&self.path);
cmd
}
None => Command::new(&self.path),
};
cmd.args(&self.cc_wrapper_args);
cmd.args(self.args.iter().filter(|a| !self.removed_args.contains(a)));
for (k, v) in self.env.iter() {
cmd.env(k, v);
}
cmd
}
/// Returns the path for this compiler.
///
/// Note that this may not be a path to a file on the filesystem, e.g. "cc",
/// but rather something which will be resolved when a process is spawned.
pub fn path(&self) -> &Path {
&self.path
}
/// Returns the default set of arguments to the compiler needed to produce
/// executables for the target this compiler generates.
pub fn args(&self) -> &[OsString] {
&self.args
}
/// Returns the set of environment variables needed for this compiler to
/// operate.
///
/// This is typically only used for MSVC compilers currently.
pub fn env(&self) -> &[(OsString, OsString)] {
&self.env
}
/// Returns the compiler command in format of CC environment variable.
/// Or empty string if CC env was not present
///
/// This is typically used by configure script
pub fn cc_env(&self) -> OsString {
match self.cc_wrapper_path {
Some(ref cc_wrapper_path) => {
let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
cc_env.push(" ");
cc_env.push(self.path.to_path_buf().into_os_string());
for arg in self.cc_wrapper_args.iter() {
cc_env.push(" ");
cc_env.push(arg);
}
cc_env
}
None => OsString::from(""),
}
}
/// Returns the compiler flags in format of CFLAGS environment variable.
/// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
/// This is typically used by configure script
pub fn cflags_env(&self) -> OsString {
let mut flags = OsString::new();
for (i, arg) in self.args.iter().enumerate() {
if i > 0 {
flags.push(" ");
}
flags.push(arg);
}
flags
}
/// Whether the tool is GNU Compiler Collection-like.
pub fn is_like_gnu(&self) -> bool {
self.family == ToolFamily::Gnu
}
/// Whether the tool is Clang-like.
pub fn is_like_clang(&self) -> bool {
matches!(self.family, ToolFamily::Clang { .. })
}
/// Whether the tool is AppleClang under .xctoolchain
#[cfg(target_vendor = "apple")]
pub(crate) fn is_xctoolchain_clang(&self) -> bool {
let path = self.path.to_string_lossy();
path.contains(".xctoolchain/")
}
#[cfg(not(target_vendor = "apple"))]
pub(crate) fn is_xctoolchain_clang(&self) -> bool {
false
}
/// Whether the tool is MSVC-like.
pub fn is_like_msvc(&self) -> bool {
matches!(self.family, ToolFamily::Msvc { .. })
}
/// Whether the tool is `clang-cl`-based MSVC-like.
pub fn is_like_clang_cl(&self) -> bool {
matches!(self.family, ToolFamily::Msvc { clang_cl: true })
}
/// Supports using `--` delimiter to separate arguments and path to source files.
pub(crate) fn supports_path_delimiter(&self) -> bool {
// homebrew clang and zig-cc does not support this while stock version does
matches!(self.family, ToolFamily::Msvc { clang_cl: true }) && !self.cuda
}
}
/// Represents the family of tools this tool belongs to.
///
/// Each family of tools differs in how and what arguments they accept.
///
/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ToolFamily {
/// Tool is GNU Compiler Collection-like.
Gnu,
/// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
/// and its cross-compilation approach is different.
Clang { zig_cc: bool },
/// Tool is the MSVC cl.exe.
Msvc { clang_cl: bool },
}
impl ToolFamily {
/// What the flag to request debug info for this family of tools look like
pub(crate) fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option<u32>) {
match *self {
ToolFamily::Msvc { .. } => {
cmd.push_cc_arg("-Z7".into());
}
ToolFamily::Gnu | ToolFamily::Clang { .. } => {
cmd.push_cc_arg(
dwarf_version
.map_or_else(|| "-g".into(), |v| format!("-gdwarf-{v}"))
.into(),
);
}
}
}
/// What the flag to force frame pointers.
pub(crate) fn add_force_frame_pointer(&self, cmd: &mut Tool) {
match *self {
ToolFamily::Gnu | ToolFamily::Clang { .. } => {
cmd.push_cc_arg("-fno-omit-frame-pointer".into());
}
_ => (),
}
}
/// What the flags to enable all warnings
pub(crate) fn warnings_flags(&self) -> &'static str {
match *self {
ToolFamily::Msvc { .. } => "-W4",
ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Wall",
}
}
/// What the flags to enable extra warnings
pub(crate) fn extra_warnings_flags(&self) -> Option<&'static str> {
match *self {
ToolFamily::Msvc { .. } => None,
ToolFamily::Gnu | ToolFamily::Clang { .. } => Some("-Wextra"),
}
}
/// What the flag to turn warning into errors
pub(crate) fn warnings_to_errors_flag(&self) -> &'static str {
match *self {
ToolFamily::Msvc { .. } => "-WX",
ToolFamily::Gnu | ToolFamily::Clang { .. } => "-Werror",
}
}
pub(crate) fn verbose_stderr(&self) -> bool {
matches!(*self, ToolFamily::Clang { .. })
}
}

130
vendor/cc/src/utilities.rs vendored Normal file
View File

@@ -0,0 +1,130 @@
use std::{
cell::UnsafeCell,
ffi::OsStr,
fmt::{self, Write},
marker::PhantomData,
mem::MaybeUninit,
panic::{RefUnwindSafe, UnwindSafe},
path::Path,
sync::Once,
};
pub(super) struct JoinOsStrs<'a, T> {
pub(super) slice: &'a [T],
pub(super) delimiter: char,
}
impl<T> fmt::Display for JoinOsStrs<'_, T>
where
T: AsRef<OsStr>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let len = self.slice.len();
for (index, os_str) in self.slice.iter().enumerate() {
// TODO: Use OsStr::display once it is stablised,
// Path and OsStr has the same `Display` impl
write!(f, "{}", Path::new(os_str).display())?;
if index + 1 < len {
f.write_char(self.delimiter)?;
}
}
Ok(())
}
}
pub(super) struct OptionOsStrDisplay<T>(pub(super) Option<T>);
impl<T> fmt::Display for OptionOsStrDisplay<T>
where
T: AsRef<OsStr>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// TODO: Use OsStr::display once it is stablised
// Path and OsStr has the same `Display` impl
if let Some(os_str) = self.0.as_ref() {
write!(f, "Some({})", Path::new(os_str).display())
} else {
f.write_str("None")
}
}
}
pub(crate) struct OnceLock<T> {
once: Once,
value: UnsafeCell<MaybeUninit<T>>,
_marker: PhantomData<T>,
}
impl<T> Default for OnceLock<T> {
fn default() -> Self {
Self::new()
}
}
impl<T> OnceLock<T> {
pub(crate) const fn new() -> Self {
Self {
once: Once::new(),
value: UnsafeCell::new(MaybeUninit::uninit()),
_marker: PhantomData,
}
}
#[inline]
fn is_initialized(&self) -> bool {
self.once.is_completed()
}
unsafe fn get_unchecked(&self) -> &T {
debug_assert!(self.is_initialized());
#[allow(clippy::needless_borrow)]
#[allow(unused_unsafe)]
unsafe {
(&*self.value.get()).assume_init_ref()
}
}
pub(crate) fn get_or_init(&self, f: impl FnOnce() -> T) -> &T {
self.once.call_once(|| {
unsafe { &mut *self.value.get() }.write(f());
});
unsafe { self.get_unchecked() }
}
pub(crate) fn get(&self) -> Option<&T> {
if self.is_initialized() {
// Safe b/c checked is_initialized
Some(unsafe { self.get_unchecked() })
} else {
None
}
}
}
impl<T: fmt::Debug> fmt::Debug for OnceLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_tuple("OnceLock");
match self.get() {
Some(v) => d.field(v),
None => d.field(&format_args!("<uninit>")),
};
d.finish()
}
}
unsafe impl<T: Sync + Send> Sync for OnceLock<T> {}
unsafe impl<T: Send> Send for OnceLock<T> {}
impl<T: RefUnwindSafe + UnwindSafe> RefUnwindSafe for OnceLock<T> {}
impl<T: UnwindSafe> UnwindSafe for OnceLock<T> {}
impl<T> Drop for OnceLock<T> {
#[inline]
fn drop(&mut self) {
if self.once.is_completed() {
// SAFETY: The cell is initialized and being dropped, so it can't
// be accessed again.
unsafe { self.value.get_mut().assume_init_drop() };
}
}
}