Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
399
third-party/vendor/cc/src/tool.rs
vendored
Normal file
399
third-party/vendor/cc/src/tool.rs
vendored
Normal file
|
|
@ -0,0 +1,399 @@
|
|||
use std::{
|
||||
collections::HashMap,
|
||||
ffi::OsString,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use crate::command_helpers::{run_output, CargoOutput};
|
||||
|
||||
/// 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 new(
|
||||
path: PathBuf,
|
||||
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||
cargo_output: &CargoOutput,
|
||||
) -> Self {
|
||||
Self::with_features(path, None, false, cached_compiler_family, cargo_output)
|
||||
}
|
||||
|
||||
pub(crate) fn with_clang_driver(
|
||||
path: PathBuf,
|
||||
clang_driver: Option<&str>,
|
||||
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||
cargo_output: &CargoOutput,
|
||||
) -> Self {
|
||||
Self::with_features(
|
||||
path,
|
||||
clang_driver,
|
||||
false,
|
||||
cached_compiler_family,
|
||||
cargo_output,
|
||||
)
|
||||
}
|
||||
|
||||
/// 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,
|
||||
clang_driver: Option<&str>,
|
||||
cuda: bool,
|
||||
cached_compiler_family: &Mutex<HashMap<Box<Path>, ToolFamily>>,
|
||||
cargo_output: &CargoOutput,
|
||||
) -> Self {
|
||||
fn detect_family_inner(path: &Path, cargo_output: &CargoOutput) -> ToolFamily {
|
||||
let mut cmd = Command::new(path);
|
||||
cmd.arg("--version");
|
||||
|
||||
let stdout = match run_output(
|
||||
&mut cmd,
|
||||
&path.to_string_lossy(),
|
||||
// tool detection issues should always be shown as warnings
|
||||
cargo_output,
|
||||
)
|
||||
.ok()
|
||||
.and_then(|o| String::from_utf8(o).ok())
|
||||
{
|
||||
Some(s) => s,
|
||||
None => {
|
||||
// --version failed. fallback to gnu
|
||||
cargo_output.print_warning(&format_args!("Failed to run: {:?}", cmd));
|
||||
return ToolFamily::Gnu;
|
||||
}
|
||||
};
|
||||
if stdout.contains("clang") {
|
||||
ToolFamily::Clang
|
||||
} else if stdout.contains("GCC") {
|
||||
ToolFamily::Gnu
|
||||
} else {
|
||||
// --version doesn't include clang for GCC
|
||||
cargo_output.print_warning(&format_args!(
|
||||
"Compiler version doesn't include clang or GCC: {:?}",
|
||||
cmd
|
||||
));
|
||||
ToolFamily::Gnu
|
||||
}
|
||||
}
|
||||
let detect_family = |path: &Path| -> ToolFamily {
|
||||
if let Some(family) = cached_compiler_family.lock().unwrap().get(path) {
|
||||
return *family;
|
||||
}
|
||||
|
||||
let family = detect_family_inner(path, cargo_output);
|
||||
cached_compiler_family
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(path.into(), family);
|
||||
family
|
||||
};
|
||||
|
||||
// Try to detect family of the tool from its name, falling back to Gnu.
|
||||
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
|
||||
if fname.contains("clang-cl") {
|
||||
ToolFamily::Msvc { clang_cl: true }
|
||||
} else if fname.ends_with("cl") || fname == "cl.exe" {
|
||||
ToolFamily::Msvc { clang_cl: false }
|
||||
} else if fname.contains("clang") {
|
||||
match clang_driver {
|
||||
Some("cl") => ToolFamily::Msvc { clang_cl: true },
|
||||
_ => ToolFamily::Clang,
|
||||
}
|
||||
} else {
|
||||
detect_family(&path)
|
||||
}
|
||||
} else {
|
||||
detect_family(&path)
|
||||
};
|
||||
|
||||
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() {
|
||||
if 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) {
|
||||
println!("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);
|
||||
|
||||
let value = self
|
||||
.args
|
||||
.iter()
|
||||
.filter(|a| !self.removed_args.contains(a))
|
||||
.collect::<Vec<_>>();
|
||||
cmd.args(&value);
|
||||
|
||||
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 {
|
||||
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 {
|
||||
match self.family {
|
||||
ToolFamily::Msvc { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// 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 {
|
||||
*self == ToolFamily::Clang
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue