Skip to content

Commit 249d207

Browse files
committed
Read from all CFLAGS-style flags
Reading from just one of these means that users setting different environment variables in different parts of the build process would not get flags from the other parts. Instead, we read all of the flags, but in a specific order such that more specific flags override less specific ones.
1 parent fcf940e commit 249d207

File tree

3 files changed

+110
-31
lines changed

3 files changed

+110
-31
lines changed

src/lib.rs

Lines changed: 53 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,12 @@ impl Build {
653653
/// ```
654654
///
655655
pub fn try_flags_from_environment(&mut self, environ_key: &str) -> Result<&mut Build, Error> {
656-
let flags = self.envflags(environ_key)?;
656+
let flags = self.envflags(environ_key)?.ok_or_else(|| {
657+
Error::new(
658+
ErrorKind::EnvVarNotFound,
659+
format!("could not find environment variable {environ_key}"),
660+
)
661+
})?;
657662
self.flags.extend(
658663
flags
659664
.into_iter()
@@ -1907,7 +1912,8 @@ impl Build {
19071912
cmd.args.push(directory.as_os_str().into());
19081913
}
19091914

1910-
if let Ok(flags) = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" }) {
1915+
let flags = self.envflags(if self.cpp { "CXXFLAGS" } else { "CFLAGS" })?;
1916+
if let Some(flags) = &flags {
19111917
for arg in flags {
19121918
cmd.push_cc_arg(arg.into());
19131919
}
@@ -1918,12 +1924,12 @@ impl Build {
19181924
// CFLAGS/CXXFLAGS, since those variables presumably already contain
19191925
// the desired set of warnings flags.
19201926

1921-
if self.warnings.unwrap_or(!self.has_flags()) {
1927+
if self.warnings.unwrap_or(flags.is_none()) {
19221928
let wflags = cmd.family.warnings_flags().into();
19231929
cmd.push_cc_arg(wflags);
19241930
}
19251931

1926-
if self.extra_warnings.unwrap_or(!self.has_flags()) {
1932+
if self.extra_warnings.unwrap_or(flags.is_none()) {
19271933
if let Some(wflags) = cmd.family.extra_warnings_flags() {
19281934
cmd.push_cc_arg(wflags.into());
19291935
}
@@ -2443,12 +2449,6 @@ impl Build {
24432449
Ok(())
24442450
}
24452451

2446-
fn has_flags(&self) -> bool {
2447-
let flags_env_var_name = if self.cpp { "CXXFLAGS" } else { "CFLAGS" };
2448-
let flags_env_var_value = self.getenv_with_target_prefixes(flags_env_var_name);
2449-
flags_env_var_value.is_ok()
2450-
}
2451-
24522452
fn msvc_macro_assembler(&self) -> Result<Command, Error> {
24532453
let target = self.get_target()?;
24542454
let tool = if target.arch == "x86_64" {
@@ -3115,8 +3115,8 @@ impl Build {
31153115
fn try_get_archiver_and_flags(&self) -> Result<(Command, PathBuf, bool), Error> {
31163116
let (mut cmd, name) = self.get_base_archiver()?;
31173117
let mut any_flags = false;
3118-
if let Ok(flags) = self.envflags("ARFLAGS") {
3119-
any_flags |= !flags.is_empty();
3118+
if let Some(flags) = self.envflags("ARFLAGS")? {
3119+
any_flags = true;
31203120
cmd.args(flags);
31213121
}
31223122
for flag in &self.ar_flags {
@@ -3162,7 +3162,7 @@ impl Build {
31623162
/// see [`Self::get_ranlib`] for the complete description.
31633163
pub fn try_get_ranlib(&self) -> Result<Command, Error> {
31643164
let mut cmd = self.get_base_ranlib()?;
3165-
if let Ok(flags) = self.envflags("RANLIBFLAGS") {
3165+
if let Some(flags) = self.envflags("RANLIBFLAGS")? {
31663166
cmd.args(flags);
31673167
}
31683168
Ok(cmd)
@@ -3643,7 +3643,8 @@ impl Build {
36433643
})
36443644
}
36453645

3646-
fn getenv_with_target_prefixes(&self, var_base: &str) -> Result<Arc<OsStr>, Error> {
3646+
/// Get a single-valued environment variable with target variants.
3647+
fn getenv_with_target_prefixes(&self, env: &str) -> Result<Arc<OsStr>, Error> {
36473648
let target = self.get_raw_target()?;
36483649
let kind = if self.get_is_cross_compile()? {
36493650
"TARGET"
@@ -3652,32 +3653,55 @@ impl Build {
36523653
};
36533654
let target_u = target.replace('-', "_");
36543655
let res = self
3655-
.getenv(&format!("{}_{}", var_base, target))
3656-
.or_else(|| self.getenv(&format!("{}_{}", var_base, target_u)))
3657-
.or_else(|| self.getenv(&format!("{}_{}", kind, var_base)))
3658-
.or_else(|| self.getenv(var_base));
3656+
.getenv(&format!("{env}_{target}"))
3657+
.or_else(|| self.getenv(&format!("{env}_{target_u}")))
3658+
.or_else(|| self.getenv(&format!("{kind}_{env}")))
3659+
.or_else(|| self.getenv(env));
36593660

36603661
match res {
36613662
Some(res) => Ok(res),
36623663
None => Err(Error::new(
36633664
ErrorKind::EnvVarNotFound,
3664-
format!("Could not find environment variable {}.", var_base),
3665+
format!("could not find environment variable {env}"),
36653666
)),
36663667
}
36673668
}
36683669

3669-
fn envflags(&self, name: &str) -> Result<Vec<String>, Error> {
3670-
let env_os = self.getenv_with_target_prefixes(name)?;
3671-
let env = env_os.to_string_lossy();
3672-
3673-
if self.get_shell_escaped_flags() {
3674-
Ok(Shlex::new(&env).collect())
3670+
/// Get values from CFLAGS-style environment variable.
3671+
fn envflags(&self, env: &str) -> Result<Option<Vec<String>>, Error> {
3672+
let target = self.get_raw_target()?;
3673+
let kind = if self.get_is_cross_compile()? {
3674+
"TARGET"
36753675
} else {
3676-
Ok(env
3677-
.split_ascii_whitespace()
3678-
.map(ToString::to_string)
3679-
.collect())
3676+
"HOST"
3677+
};
3678+
let target_u = target.replace('-', "_");
3679+
3680+
// Collect from all environment variables, in reverse order as in
3681+
// `getenv_with_target_prefixes` precedence (so that `CFLAGS_$TARGET`
3682+
// can override flags in `TARGET_CFLAGS`, which overrides those in
3683+
// `CFLAGS`).
3684+
let mut any_set = false;
3685+
let mut res = vec![];
3686+
for env in [
3687+
env,
3688+
&format!("{kind}_{env}"),
3689+
&format!("{env}_{target_u}"),
3690+
&format!("{env}_{target}"),
3691+
] {
3692+
if let Some(var) = self.getenv(env) {
3693+
any_set = true;
3694+
3695+
let var = var.to_string_lossy();
3696+
if self.get_shell_escaped_flags() {
3697+
res.extend(Shlex::new(&var));
3698+
} else {
3699+
res.extend(var.split_ascii_whitespace().map(ToString::to_string));
3700+
}
3701+
}
36803702
}
3703+
3704+
Ok(if any_set { Some(res) } else { None })
36813705
}
36823706

36833707
fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> {

tests/cc_env.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ fn main() {
1616
path_to_ccache();
1717
more_spaces();
1818
clang_cl();
19+
env_var_alternatives_override();
1920
}
2021

2122
fn ccache() {
@@ -126,3 +127,33 @@ fn clang_cl() {
126127
test_compiler(test.gcc());
127128
}
128129
}
130+
131+
fn env_var_alternatives_override() {
132+
let compiler1 = format!("clang1{}", env::consts::EXE_SUFFIX);
133+
let compiler2 = format!("clang2{}", env::consts::EXE_SUFFIX);
134+
let compiler3 = format!("clang3{}", env::consts::EXE_SUFFIX);
135+
let compiler4 = format!("clang4{}", env::consts::EXE_SUFFIX);
136+
137+
let test = Test::new();
138+
test.shim(&compiler1);
139+
test.shim(&compiler2);
140+
test.shim(&compiler3);
141+
test.shim(&compiler4);
142+
143+
env::set_var("CC", &compiler1);
144+
let compiler = test.gcc().target("x86_64-unknown-none").get_compiler();
145+
assert_eq!(compiler.path(), Path::new(&compiler1));
146+
147+
env::set_var("HOST_CC", &compiler2);
148+
env::set_var("TARGET_CC", &compiler2);
149+
let compiler = test.gcc().target("x86_64-unknown-none").get_compiler();
150+
assert_eq!(compiler.path(), Path::new(&compiler2));
151+
152+
env::set_var("CC_x86_64_unknown_none", &compiler3);
153+
let compiler = test.gcc().target("x86_64-unknown-none").get_compiler();
154+
assert_eq!(compiler.path(), Path::new(&compiler3));
155+
156+
env::set_var("CC_x86_64-unknown-none", &compiler4);
157+
let compiler = test.gcc().target("x86_64-unknown-none").get_compiler();
158+
assert_eq!(compiler.path(), Path::new(&compiler4));
159+
}

tests/cflags.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,39 @@
1+
//! This test is in its own module because it modifies the environment and would affect other tests
2+
//! when run in parallel with them.
13
mod support;
24

35
use crate::support::Test;
46
use std::env;
57

6-
/// This test is in its own module because it modifies the environment and would affect other tests
7-
/// when run in parallel with them.
88
#[test]
9+
fn cflags() {
10+
gnu_no_warnings_if_cflags();
11+
cflags_order();
12+
}
13+
914
fn gnu_no_warnings_if_cflags() {
1015
env::set_var("CFLAGS", "-arbitrary");
1116
let test = Test::gnu();
1217
test.gcc().file("foo.c").compile("foo");
1318

1419
test.cmd(0).must_not_have("-Wall").must_not_have("-Wextra");
1520
}
21+
22+
/// Test the ordering of `CFLAGS*` variables.
23+
fn cflags_order() {
24+
unsafe { env::set_var("CFLAGS", "-arbitrary1") };
25+
unsafe { env::set_var("HOST_CFLAGS", "-arbitrary2") };
26+
unsafe { env::set_var("TARGET_CFLAGS", "-arbitrary2") };
27+
unsafe { env::set_var("CFLAGS_x86_64_unknown_none", "-arbitrary3") };
28+
unsafe { env::set_var("CFLAGS_x86_64-unknown-none", "-arbitrary4") };
29+
let test = Test::gnu();
30+
test.gcc()
31+
.target("x86_64-unknown-none")
32+
.file("foo.c")
33+
.compile("foo");
34+
35+
test.cmd(0)
36+
.must_have_in_order("-arbitrary1", "-arbitrary2")
37+
.must_have_in_order("-arbitrary2", "-arbitrary3")
38+
.must_have_in_order("-arbitrary3", "-arbitrary4");
39+
}

0 commit comments

Comments
 (0)