Skip to content

Commit 50c0e7f

Browse files
committed
split Tool and ToolFamily to a new tool module
1 parent bb48f58 commit 50c0e7f

File tree

2 files changed

+378
-366
lines changed

2 files changed

+378
-366
lines changed

src/lib.rs

+4-366
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,10 @@ pub mod windows_registry;
9090
mod command_helpers;
9191
use command_helpers::*;
9292

93+
mod tool;
94+
pub use tool::Tool;
95+
use tool::ToolFamily;
96+
9397
/// A builder for compilation of a native library.
9498
///
9599
/// A `Build` is the main type of the `cc` crate and is used to control all the
@@ -187,98 +191,6 @@ impl Display for Error {
187191

188192
impl std::error::Error for Error {}
189193

190-
/// Configuration used to represent an invocation of a C compiler.
191-
///
192-
/// This can be used to figure out what compiler is in use, what the arguments
193-
/// to it are, and what the environment variables look like for the compiler.
194-
/// This can be used to further configure other build systems (e.g. forward
195-
/// along CC and/or CFLAGS) or the `to_command` method can be used to run the
196-
/// compiler itself.
197-
#[derive(Clone, Debug)]
198-
pub struct Tool {
199-
path: PathBuf,
200-
cc_wrapper_path: Option<PathBuf>,
201-
cc_wrapper_args: Vec<OsString>,
202-
args: Vec<OsString>,
203-
env: Vec<(OsString, OsString)>,
204-
family: ToolFamily,
205-
cuda: bool,
206-
removed_args: Vec<OsString>,
207-
has_internal_target_arg: bool,
208-
}
209-
210-
/// Represents the family of tools this tool belongs to.
211-
///
212-
/// Each family of tools differs in how and what arguments they accept.
213-
///
214-
/// Detection of a family is done on best-effort basis and may not accurately reflect the tool.
215-
#[derive(Copy, Clone, Debug, PartialEq)]
216-
enum ToolFamily {
217-
/// Tool is GNU Compiler Collection-like.
218-
Gnu,
219-
/// Tool is Clang-like. It differs from the GCC in a sense that it accepts superset of flags
220-
/// and its cross-compilation approach is different.
221-
Clang,
222-
/// Tool is the MSVC cl.exe.
223-
Msvc { clang_cl: bool },
224-
}
225-
226-
impl ToolFamily {
227-
/// What the flag to request debug info for this family of tools look like
228-
fn add_debug_flags(&self, cmd: &mut Tool, dwarf_version: Option<u32>) {
229-
match *self {
230-
ToolFamily::Msvc { .. } => {
231-
cmd.push_cc_arg("-Z7".into());
232-
}
233-
ToolFamily::Gnu | ToolFamily::Clang => {
234-
cmd.push_cc_arg(
235-
dwarf_version
236-
.map_or_else(|| "-g".into(), |v| format!("-gdwarf-{}", v))
237-
.into(),
238-
);
239-
}
240-
}
241-
}
242-
243-
/// What the flag to force frame pointers.
244-
fn add_force_frame_pointer(&self, cmd: &mut Tool) {
245-
match *self {
246-
ToolFamily::Gnu | ToolFamily::Clang => {
247-
cmd.push_cc_arg("-fno-omit-frame-pointer".into());
248-
}
249-
_ => (),
250-
}
251-
}
252-
253-
/// What the flags to enable all warnings
254-
fn warnings_flags(&self) -> &'static str {
255-
match *self {
256-
ToolFamily::Msvc { .. } => "-W4",
257-
ToolFamily::Gnu | ToolFamily::Clang => "-Wall",
258-
}
259-
}
260-
261-
/// What the flags to enable extra warnings
262-
fn extra_warnings_flags(&self) -> Option<&'static str> {
263-
match *self {
264-
ToolFamily::Msvc { .. } => None,
265-
ToolFamily::Gnu | ToolFamily::Clang => Some("-Wextra"),
266-
}
267-
}
268-
269-
/// What the flag to turn warning into errors
270-
fn warnings_to_errors_flag(&self) -> &'static str {
271-
match *self {
272-
ToolFamily::Msvc { .. } => "-WX",
273-
ToolFamily::Gnu | ToolFamily::Clang => "-Werror",
274-
}
275-
}
276-
277-
fn verbose_stderr(&self) -> bool {
278-
*self == ToolFamily::Clang
279-
}
280-
}
281-
282194
/// Represents an object.
283195
///
284196
/// This is a source file -> object file pair.
@@ -3555,280 +3467,6 @@ impl Default for Build {
35553467
}
35563468
}
35573469

3558-
impl Tool {
3559-
fn new(path: PathBuf, cargo_output: &CargoOutput) -> Self {
3560-
Tool::with_features(path, None, false, cargo_output)
3561-
}
3562-
3563-
fn with_clang_driver(
3564-
path: PathBuf,
3565-
clang_driver: Option<&str>,
3566-
cargo_output: &CargoOutput,
3567-
) -> Self {
3568-
Self::with_features(path, clang_driver, false, cargo_output)
3569-
}
3570-
3571-
#[cfg(windows)]
3572-
/// Explicitly set the `ToolFamily`, skipping name-based detection.
3573-
fn with_family(path: PathBuf, family: ToolFamily) -> Self {
3574-
Self {
3575-
path,
3576-
cc_wrapper_path: None,
3577-
cc_wrapper_args: Vec::new(),
3578-
args: Vec::new(),
3579-
env: Vec::new(),
3580-
family,
3581-
cuda: false,
3582-
removed_args: Vec::new(),
3583-
has_internal_target_arg: false,
3584-
}
3585-
}
3586-
3587-
fn with_features(
3588-
path: PathBuf,
3589-
clang_driver: Option<&str>,
3590-
cuda: bool,
3591-
cargo_output: &CargoOutput,
3592-
) -> Self {
3593-
fn detect_family(path: &Path, cargo_output: &CargoOutput) -> ToolFamily {
3594-
let mut cmd = Command::new(path);
3595-
cmd.arg("--version");
3596-
3597-
let stdout = match run_output(
3598-
&mut cmd,
3599-
&path.to_string_lossy(),
3600-
// tool detection issues should always be shown as warnings
3601-
cargo_output,
3602-
)
3603-
.ok()
3604-
.and_then(|o| String::from_utf8(o).ok())
3605-
{
3606-
Some(s) => s,
3607-
None => {
3608-
// --version failed. fallback to gnu
3609-
cargo_output.print_warning(&format_args!("Failed to run: {:?}", cmd));
3610-
return ToolFamily::Gnu;
3611-
}
3612-
};
3613-
if stdout.contains("clang") {
3614-
ToolFamily::Clang
3615-
} else if stdout.contains("GCC") {
3616-
ToolFamily::Gnu
3617-
} else {
3618-
// --version doesn't include clang for GCC
3619-
cargo_output.print_warning(&format_args!(
3620-
"Compiler version doesn't include clang or GCC: {:?}",
3621-
cmd
3622-
));
3623-
ToolFamily::Gnu
3624-
}
3625-
}
3626-
3627-
// Try to detect family of the tool from its name, falling back to Gnu.
3628-
let family = if let Some(fname) = path.file_name().and_then(|p| p.to_str()) {
3629-
if fname.contains("clang-cl") {
3630-
ToolFamily::Msvc { clang_cl: true }
3631-
} else if fname.ends_with("cl") || fname == "cl.exe" {
3632-
ToolFamily::Msvc { clang_cl: false }
3633-
} else if fname.contains("clang") {
3634-
match clang_driver {
3635-
Some("cl") => ToolFamily::Msvc { clang_cl: true },
3636-
_ => ToolFamily::Clang,
3637-
}
3638-
} else {
3639-
detect_family(&path, cargo_output)
3640-
}
3641-
} else {
3642-
detect_family(&path, cargo_output)
3643-
};
3644-
3645-
Tool {
3646-
path,
3647-
cc_wrapper_path: None,
3648-
cc_wrapper_args: Vec::new(),
3649-
args: Vec::new(),
3650-
env: Vec::new(),
3651-
family,
3652-
cuda,
3653-
removed_args: Vec::new(),
3654-
has_internal_target_arg: false,
3655-
}
3656-
}
3657-
3658-
/// Add an argument to be stripped from the final command arguments.
3659-
fn remove_arg(&mut self, flag: OsString) {
3660-
self.removed_args.push(flag);
3661-
}
3662-
3663-
/// Push an "exotic" flag to the end of the compiler's arguments list.
3664-
///
3665-
/// Nvidia compiler accepts only the most common compiler flags like `-D`,
3666-
/// `-I`, `-c`, etc. Options meant specifically for the underlying
3667-
/// host C++ compiler have to be prefixed with `-Xcompiler`.
3668-
/// [Another possible future application for this function is passing
3669-
/// clang-specific flags to clang-cl, which otherwise accepts only
3670-
/// MSVC-specific options.]
3671-
fn push_cc_arg(&mut self, flag: OsString) {
3672-
if self.cuda {
3673-
self.args.push("-Xcompiler".into());
3674-
}
3675-
self.args.push(flag);
3676-
}
3677-
3678-
/// Checks if an argument or flag has already been specified or conflicts.
3679-
///
3680-
/// Currently only checks optimization flags.
3681-
fn is_duplicate_opt_arg(&self, flag: &OsString) -> bool {
3682-
let flag = flag.to_str().unwrap();
3683-
let mut chars = flag.chars();
3684-
3685-
// Only duplicate check compiler flags
3686-
if self.is_like_msvc() {
3687-
if chars.next() != Some('/') {
3688-
return false;
3689-
}
3690-
} else if self.is_like_gnu() || self.is_like_clang() {
3691-
if chars.next() != Some('-') {
3692-
return false;
3693-
}
3694-
}
3695-
3696-
// Check for existing optimization flags (-O, /O)
3697-
if chars.next() == Some('O') {
3698-
return self
3699-
.args()
3700-
.iter()
3701-
.any(|a| a.to_str().unwrap_or("").chars().nth(1) == Some('O'));
3702-
}
3703-
3704-
// TODO Check for existing -m..., -m...=..., /arch:... flags
3705-
false
3706-
}
3707-
3708-
/// Don't push optimization arg if it conflicts with existing args.
3709-
fn push_opt_unless_duplicate(&mut self, flag: OsString) {
3710-
if self.is_duplicate_opt_arg(&flag) {
3711-
println!("Info: Ignoring duplicate arg {:?}", &flag);
3712-
} else {
3713-
self.push_cc_arg(flag);
3714-
}
3715-
}
3716-
3717-
/// Converts this compiler into a `Command` that's ready to be run.
3718-
///
3719-
/// This is useful for when the compiler needs to be executed and the
3720-
/// command returned will already have the initial arguments and environment
3721-
/// variables configured.
3722-
pub fn to_command(&self) -> Command {
3723-
let mut cmd = match self.cc_wrapper_path {
3724-
Some(ref cc_wrapper_path) => {
3725-
let mut cmd = Command::new(cc_wrapper_path);
3726-
cmd.arg(&self.path);
3727-
cmd
3728-
}
3729-
None => Command::new(&self.path),
3730-
};
3731-
cmd.args(&self.cc_wrapper_args);
3732-
3733-
let value = self
3734-
.args
3735-
.iter()
3736-
.filter(|a| !self.removed_args.contains(a))
3737-
.collect::<Vec<_>>();
3738-
cmd.args(&value);
3739-
3740-
for (k, v) in self.env.iter() {
3741-
cmd.env(k, v);
3742-
}
3743-
cmd
3744-
}
3745-
3746-
/// Returns the path for this compiler.
3747-
///
3748-
/// Note that this may not be a path to a file on the filesystem, e.g. "cc",
3749-
/// but rather something which will be resolved when a process is spawned.
3750-
pub fn path(&self) -> &Path {
3751-
&self.path
3752-
}
3753-
3754-
/// Returns the default set of arguments to the compiler needed to produce
3755-
/// executables for the target this compiler generates.
3756-
pub fn args(&self) -> &[OsString] {
3757-
&self.args
3758-
}
3759-
3760-
/// Returns the set of environment variables needed for this compiler to
3761-
/// operate.
3762-
///
3763-
/// This is typically only used for MSVC compilers currently.
3764-
pub fn env(&self) -> &[(OsString, OsString)] {
3765-
&self.env
3766-
}
3767-
3768-
/// Returns the compiler command in format of CC environment variable.
3769-
/// Or empty string if CC env was not present
3770-
///
3771-
/// This is typically used by configure script
3772-
pub fn cc_env(&self) -> OsString {
3773-
match self.cc_wrapper_path {
3774-
Some(ref cc_wrapper_path) => {
3775-
let mut cc_env = cc_wrapper_path.as_os_str().to_owned();
3776-
cc_env.push(" ");
3777-
cc_env.push(self.path.to_path_buf().into_os_string());
3778-
for arg in self.cc_wrapper_args.iter() {
3779-
cc_env.push(" ");
3780-
cc_env.push(arg);
3781-
}
3782-
cc_env
3783-
}
3784-
None => OsString::from(""),
3785-
}
3786-
}
3787-
3788-
/// Returns the compiler flags in format of CFLAGS environment variable.
3789-
/// Important here - this will not be CFLAGS from env, its internal gcc's flags to use as CFLAGS
3790-
/// This is typically used by configure script
3791-
pub fn cflags_env(&self) -> OsString {
3792-
let mut flags = OsString::new();
3793-
for (i, arg) in self.args.iter().enumerate() {
3794-
if i > 0 {
3795-
flags.push(" ");
3796-
}
3797-
flags.push(arg);
3798-
}
3799-
flags
3800-
}
3801-
3802-
/// Whether the tool is GNU Compiler Collection-like.
3803-
pub fn is_like_gnu(&self) -> bool {
3804-
self.family == ToolFamily::Gnu
3805-
}
3806-
3807-
/// Whether the tool is Clang-like.
3808-
pub fn is_like_clang(&self) -> bool {
3809-
self.family == ToolFamily::Clang
3810-
}
3811-
3812-
/// Whether the tool is AppleClang under .xctoolchain
3813-
#[cfg(target_vendor = "apple")]
3814-
fn is_xctoolchain_clang(&self) -> bool {
3815-
let path = self.path.to_string_lossy();
3816-
path.contains(".xctoolchain/")
3817-
}
3818-
#[cfg(not(target_vendor = "apple"))]
3819-
fn is_xctoolchain_clang(&self) -> bool {
3820-
false
3821-
}
3822-
3823-
/// Whether the tool is MSVC-like.
3824-
pub fn is_like_msvc(&self) -> bool {
3825-
match self.family {
3826-
ToolFamily::Msvc { .. } => true,
3827-
_ => false,
3828-
}
3829-
}
3830-
}
3831-
38323470
fn fail(s: &str) -> ! {
38333471
eprintln!("\n\nerror occurred: {}\n\n", s);
38343472
std::process::exit(1);

0 commit comments

Comments
 (0)