Skip to content

Commit d376aeb

Browse files
committed
Add support for parsing shell encoded *FLAGS
1 parent eb33906 commit d376aeb

File tree

3 files changed

+57
-6
lines changed

3 files changed

+57
-6
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ rust-version = "1.63"
2121

2222
[dependencies]
2323
jobserver = { version = "0.1.30", default-features = false, optional = true }
24+
shlex = "1.3.0"
2425

2526
[target.'cfg(unix)'.dependencies]
2627
# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866

src/lib.rs

+32-6
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,9 @@
8484
//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will
8585
//! be logged to stdout. This is useful for debugging build script issues, but can be
8686
//! overly verbose for normal use.
87+
//! * `CC_SHELL_ESCAPED_FLAGS` - if set, *FLAGS will be parsed as if they were shell
88+
//! arguments, similar to `make` and `cmake`. For example, `CFLAGS='a "b c" d\ e'` will
89+
//! be parsed as `["a", "b", "c", "d", "e"]` instead of `["a", "\"b", "c\", "d\\", "e"]`
8790
//! * `CXX...` - see [C++ Support](#c-support).
8891
//!
8992
//! Furthermore, projects using this crate may specify custom environment variables
@@ -224,6 +227,8 @@ use std::process::Child;
224227
use std::process::Command;
225228
use std::sync::{Arc, RwLock};
226229

230+
use shlex::Shlex;
231+
227232
#[cfg(feature = "parallel")]
228233
mod parallel;
229234
mod windows;
@@ -301,6 +306,7 @@ pub struct Build {
301306
apple_versions_cache: Arc<RwLock<HashMap<Box<str>, Arc<str>>>>,
302307
emit_rerun_if_env_changed: bool,
303308
cached_compiler_family: Arc<RwLock<HashMap<Box<Path>, ToolFamily>>>,
309+
shell_escaped_flags: Option<bool>,
304310
}
305311

306312
/// Represents the types of errors that may occur while using cc-rs.
@@ -425,6 +431,7 @@ impl Build {
425431
apple_versions_cache: Arc::new(RwLock::new(HashMap::new())),
426432
emit_rerun_if_env_changed: true,
427433
cached_compiler_family: Arc::default(),
434+
shell_escaped_flags: None,
428435
}
429436
}
430437

@@ -1277,6 +1284,15 @@ impl Build {
12771284
self
12781285
}
12791286

1287+
/// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and
1288+
/// `cmake`.
1289+
///
1290+
/// This option defaults to `false`.
1291+
pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build {
1292+
self.shell_escaped_flags = Some(shell_escaped_flags);
1293+
self
1294+
}
1295+
12801296
#[doc(hidden)]
12811297
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
12821298
where
@@ -3634,6 +3650,11 @@ impl Build {
36343650
})
36353651
}
36363652

3653+
fn get_shell_escaped_flags(&self) -> bool {
3654+
self.shell_escaped_flags
3655+
.unwrap_or_else(|| self.getenv("CC_SHELL_ESCAPED_FLAGS").is_some())
3656+
}
3657+
36373658
fn get_dwarf_version(&self) -> Option<u32> {
36383659
// Tentatively matches the DWARF version defaults as of rustc 1.62.
36393660
let target = self.get_target().ok()?;
@@ -3748,12 +3769,17 @@ impl Build {
37483769
}
37493770

37503771
fn envflags(&self, name: &str) -> Result<Vec<String>, Error> {
3751-
Ok(self
3752-
.getenv_with_target_prefixes(name)?
3753-
.to_string_lossy()
3754-
.split_ascii_whitespace()
3755-
.map(ToString::to_string)
3756-
.collect())
3772+
let env_os = self.getenv_with_target_prefixes(name)?;
3773+
let env = env_os.to_string_lossy();
3774+
3775+
if self.get_shell_escaped_flags() {
3776+
Ok(Shlex::new(&env).collect())
3777+
} else {
3778+
Ok(env
3779+
.split_ascii_whitespace()
3780+
.map(ToString::to_string)
3781+
.collect())
3782+
}
37573783
}
37583784

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

tests/cflags_shell_escaped.rs

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
mod support;
2+
3+
use crate::support::Test;
4+
use std::env;
5+
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.
8+
#[test]
9+
fn gnu_test_parse_shell_escaped_flags() {
10+
env::set_var("CFLAGS", "foo \"bar baz\"");
11+
env::set_var("CC_SHELL_ESCAPED_FLAGS", "1");
12+
let test = Test::gnu();
13+
test.gcc().file("foo.c").compile("foo");
14+
15+
test.cmd(0).must_have("foo").must_have("bar baz");
16+
17+
env::remove_var("CC_SHELL_ESCAPED_FLAGS");
18+
let test = Test::gnu();
19+
test.gcc().file("foo.c").compile("foo");
20+
21+
test.cmd(0)
22+
.must_have("foo")
23+
.must_have_in_order("\"bar", "baz\"");
24+
}

0 commit comments

Comments
 (0)