Skip to content

Commit d2a6839

Browse files
authored
add a function to populate flags from an arbitrary environment variable (#818)
1 parent 17c8858 commit d2a6839

File tree

2 files changed

+83
-41
lines changed

2 files changed

+83
-41
lines changed

README.md

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,17 @@ number of different environment variables.
8383
certain `TARGET`s, it also is assumed to know about other flags (most
8484
common is `-fPIC`).
8585
* `AR` - the `ar` (archiver) executable to use to build the static library.
86-
* `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in some cross compiling scenarios. Setting this variable will disable the generation of default compiler flags.
86+
* `CRATE_CC_NO_DEFAULTS` - the default compiler flags may cause conflicts in
87+
some cross compiling scenarios. Setting this variable
88+
will disable the generation of default compiler
89+
flags.
8790
* `CXX...` - see [C++ Support](#c-support).
8891

92+
Furthermore, projects using this crate may specify custom environment variables
93+
to be inspected, for example via the `Build::try_flags_from_environment`
94+
function. Consult the project’s own documentation or its use of the `cc` crate
95+
for any additional variables it may use.
96+
8997
Each of these variables can also be supplied with certain prefixes and suffixes,
9098
in the following prioritized order:
9199

@@ -94,7 +102,7 @@ in the following prioritized order:
94102
3. `<build-kind>_<var>` - for example, `HOST_CC` or `TARGET_CFLAGS`
95103
4. `<var>` - a plain `CC`, `AR` as above.
96104

97-
If none of these variables exist, cc-rs uses built-in defaults
105+
If none of these variables exist, cc-rs uses built-in defaults.
98106

99107
In addition to the above optional environment variables, `cc-rs` has some
100108
functions with hard requirements on some variables supplied by [cargo's

src/lib.rs

Lines changed: 73 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -563,6 +563,35 @@ impl Build {
563563
self
564564
}
565565

566+
/// Add flags from the specified environment variable.
567+
///
568+
/// Normally the `cc` crate will consult with the standard set of environment
569+
/// variables (such as `CFLAGS` and `CXXFLAGS`) to construct the compiler invocation. Use of
570+
/// this method provides additional levers for the end user to use when configuring the build
571+
/// process.
572+
///
573+
/// Just like the standard variables, this method will search for an environment variable with
574+
/// appropriate target prefixes, when appropriate.
575+
///
576+
/// # Examples
577+
///
578+
/// This method is particularly beneficial in introducing the ability to specify crate-specific
579+
/// flags.
580+
///
581+
/// ```no_run
582+
/// cc::Build::new()
583+
/// .file("src/foo.c")
584+
/// .try_flags_from_environment(concat!(env!("CARGO_PKG_NAME"), "_CFLAGS"))
585+
/// .expect("the environment variable must be specified and UTF-8")
586+
/// .compile("foo");
587+
/// ```
588+
///
589+
pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> {
590+
let flags = self.envflags(environ_key)?;
591+
self.flags.extend(flags.into_iter().map(Into::into));
592+
Ok(self)
593+
}
594+
566595
/// Set the `-shared` flag.
567596
///
568597
/// When enabled, the compiler will produce a shared object which can
@@ -1471,7 +1500,6 @@ impl Build {
14711500
let target = self.get_target()?;
14721501

14731502
let mut cmd = self.get_base_compiler()?;
1474-
let envflags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" });
14751503

14761504
// Disable default flag generation via `no_default_flags` or environment variable
14771505
let no_defaults = self.no_default_flags || self.getenv("CRATE_CC_NO_DEFAULTS").is_some();
@@ -1482,8 +1510,10 @@ impl Build {
14821510
println!("Info: default compiler flags are disabled");
14831511
}
14841512

1485-
for arg in envflags {
1486-
cmd.push_cc_arg(arg.into());
1513+
if let Ok(flags) = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) {
1514+
for arg in flags {
1515+
cmd.push_cc_arg(arg.into());
1516+
}
14871517
}
14881518

14891519
for directory in self.include_directories.iter() {
@@ -1990,7 +2020,7 @@ impl Build {
19902020

19912021
fn has_flags(&self) -> bool {
19922022
let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
1993-
let flags_env_var_value = self.get_var(flags_env_var_name);
2023+
let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name);
19942024
if let Ok(_) = flags_env_var_value {
19952025
true
19962026
} else {
@@ -2437,7 +2467,7 @@ impl Build {
24372467
tool.args.is_empty(),
24382468
"CUDA compilation currently assumes empty pre-existing args"
24392469
);
2440-
let nvcc = match self.get_var("NVCC") {
2470+
let nvcc = match self.getenv_with_target_prefixes("NVCC") {
24412471
Err(_) => "nvcc".into(),
24422472
Ok(nvcc) => nvcc,
24432473
};
@@ -2509,34 +2539,6 @@ impl Build {
25092539
Ok(tool)
25102540
}
25112541

2512-
fn get_var(&self, var_base: &str) -> Result<String, Error> {
2513-
let target = self.get_target()?;
2514-
let host = self.get_host()?;
2515-
let kind = if host == target { "HOST" } else { "TARGET" };
2516-
let target_u = target.replace("-", "_");
2517-
let res = self
2518-
.getenv(&format!("{}_{}", var_base, target))
2519-
.or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
2520-
.or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
2521-
.or_else(|| self.getenv(var_base));
2522-
2523-
match res {
2524-
Some(res) => Ok(res),
2525-
None => Err(Error::new(
2526-
ErrorKind::EnvVarNotFound,
2527-
&format!("Could not find environment variable {}.", var_base),
2528-
)),
2529-
}
2530-
}
2531-
2532-
fn envflags(&self, name: &str) -> Vec<String> {
2533-
self.get_var(name)
2534-
.unwrap_or(String::new())
2535-
.split_ascii_whitespace()
2536-
.map(|slice| slice.to_string())
2537-
.collect()
2538-
}
2539-
25402542
/// Returns a fallback `cc_compiler_wrapper` by introspecting `RUSTC_WRAPPER`
25412543
fn rustc_wrapper_fallback() -> Option<String> {
25422544
// No explicit CC wrapper was detected, but check if RUSTC_WRAPPER
@@ -2557,7 +2559,7 @@ impl Build {
25572559

25582560
/// Returns compiler path, optional modifier name from whitelist, and arguments vec
25592561
fn env_tool(&self, name: &str) -> Option<(String, Option<String>, Vec<String>)> {
2560-
let tool = match self.get_var(name) {
2562+
let tool = match self.getenv_with_target_prefixes(name) {
25612563
Ok(tool) => tool,
25622564
Err(_) => return None,
25632565
};
@@ -2628,7 +2630,7 @@ impl Build {
26282630
match &self.cpp_link_stdlib {
26292631
Some(s) => Ok(s.as_ref().map(|s| (*s).to_string())),
26302632
None => {
2631-
if let Ok(stdlib) = self.get_var("CXXSTDLIB") {
2633+
if let Ok(stdlib) = self.getenv_with_target_prefixes("CXXSTDLIB") {
26322634
if stdlib.is_empty() {
26332635
Ok(None)
26342636
} else {
@@ -2691,9 +2693,11 @@ impl Build {
26912693

26922694
fn try_get_archiver_and_flags(&self) -> Result<(Command, String, bool), Error> {
26932695
let (mut cmd, name) = self.get_base_archiver()?;
2694-
let flags = self.envflags("ARFLAGS");
2695-
let mut any_flags = !flags.is_empty();
2696-
cmd.args(flags);
2696+
let mut any_flags = false;
2697+
if let Ok(flags) = self.envflags("ARFLAGS") {
2698+
any_flags = any_flags | !flags.is_empty();
2699+
cmd.args(flags);
2700+
}
26972701
for flag in &self.ar_flags {
26982702
any_flags = true;
26992703
cmd.arg(&**flag);
@@ -2736,7 +2740,9 @@ impl Build {
27362740
/// see [`get_ranlib()`] for the complete description.
27372741
pub fn try_get_ranlib(&self) -> Result<Command, Error> {
27382742
let mut cmd = self.get_base_ranlib()?;
2739-
cmd.args(self.envflags("RANLIBFLAGS"));
2743+
if let Ok(flags) = self.envflags("RANLIBFLAGS") {
2744+
cmd.args(flags);
2745+
}
27402746
Ok(cmd)
27412747
}
27422748

@@ -3133,6 +3139,34 @@ impl Build {
31333139
}
31343140
}
31353141

3142+
fn getenv_with_target_prefixes(&self, var_base: &str) -> Result<String, Error> {
3143+
let target = self.get_target()?;
3144+
let host = self.get_host()?;
3145+
let kind = if host == target { "HOST" } else { "TARGET" };
3146+
let target_u = target.replace("-", "_");
3147+
let res = self
3148+
.getenv(&format!("{}_{}", var_base, target))
3149+
.or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
3150+
.or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
3151+
.or_else(|| self.getenv(var_base));
3152+
3153+
match res {
3154+
Some(res) => Ok(res),
3155+
None => Err(Error::new(
3156+
ErrorKind::EnvVarNotFound,
3157+
&format!("Could not find environment variable {}.", var_base),
3158+
)),
3159+
}
3160+
}
3161+
3162+
fn envflags(&self, name: &str) -> Result<Vec<String>, Error> {
3163+
Ok(self
3164+
.getenv_with_target_prefixes(name)?
3165+
.split_ascii_whitespace()
3166+
.map(|slice| slice.to_string())
3167+
.collect())
3168+
}
3169+
31363170
fn print(&self, s: &str) {
31373171
if self.cargo_metadata {
31383172
println!("{}", s);

0 commit comments

Comments
 (0)