Skip to content

Commit

Permalink
Add support for parsing shell encoded *FLAGS (#1181)
Browse files Browse the repository at this point in the history
  • Loading branch information
xbjfk authored Aug 14, 2024
1 parent eb33906 commit ebe335c
Show file tree
Hide file tree
Showing 3 changed files with 57 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ rust-version = "1.63"

[dependencies]
jobserver = { version = "0.1.30", default-features = false, optional = true }
shlex = "1.3.0"

[target.'cfg(unix)'.dependencies]
# Don't turn on the feature "std" for this, see https://github.com/rust-lang/cargo/issues/4866
Expand Down
38 changes: 32 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@
//! * `CC_ENABLE_DEBUG_OUTPUT` - if set, compiler command invocations and exit codes will
//! be logged to stdout. This is useful for debugging build script issues, but can be
//! overly verbose for normal use.
//! * `CC_SHELL_ESCAPED_FLAGS` - if set, *FLAGS will be parsed as if they were shell
//! arguments, similar to `make` and `cmake`. For example, `CFLAGS='a "b c" d\ e'` will
//! be parsed as `["a", "b", "c", "d", "e"]` instead of `["a", "\"b", "c\", "d\\", "e"]`
//! * `CXX...` - see [C++ Support](#c-support).
//!
//! Furthermore, projects using this crate may specify custom environment variables
Expand Down Expand Up @@ -224,6 +227,8 @@ use std::process::Child;
use std::process::Command;
use std::sync::{Arc, RwLock};

use shlex::Shlex;

#[cfg(feature = "parallel")]
mod parallel;
mod windows;
Expand Down Expand Up @@ -301,6 +306,7 @@ pub struct Build {
apple_versions_cache: Arc<RwLock<HashMap<Box<str>, Arc<str>>>>,
emit_rerun_if_env_changed: bool,
cached_compiler_family: Arc<RwLock<HashMap<Box<Path>, ToolFamily>>>,
shell_escaped_flags: Option<bool>,
}

/// Represents the types of errors that may occur while using cc-rs.
Expand Down Expand Up @@ -425,6 +431,7 @@ impl Build {
apple_versions_cache: Arc::new(RwLock::new(HashMap::new())),
emit_rerun_if_env_changed: true,
cached_compiler_family: Arc::default(),
shell_escaped_flags: None,
}
}

Expand Down Expand Up @@ -1277,6 +1284,15 @@ impl Build {
self
}

/// Configure whether *FLAGS variables are parsed using `shlex`, similarly to `make` and
/// `cmake`.
///
/// This option defaults to `false`.
pub fn shell_escaped_flags(&mut self, shell_escaped_flags: bool) -> &mut Build {
self.shell_escaped_flags = Some(shell_escaped_flags);
self
}

#[doc(hidden)]
pub fn __set_env<A, B>(&mut self, a: A, b: B) -> &mut Build
where
Expand Down Expand Up @@ -3634,6 +3650,11 @@ impl Build {
})
}

fn get_shell_escaped_flags(&self) -> bool {
self.shell_escaped_flags
.unwrap_or_else(|| self.getenv("CC_SHELL_ESCAPED_FLAGS").is_some())
}

fn get_dwarf_version(&self) -> Option<u32> {
// Tentatively matches the DWARF version defaults as of rustc 1.62.
let target = self.get_target().ok()?;
Expand Down Expand Up @@ -3748,12 +3769,17 @@ impl Build {
}

fn envflags(&self, name: &str) -> Result<Vec<String>, Error> {
Ok(self
.getenv_with_target_prefixes(name)?
.to_string_lossy()
.split_ascii_whitespace()
.map(ToString::to_string)
.collect())
let env_os = self.getenv_with_target_prefixes(name)?;
let env = env_os.to_string_lossy();

if self.get_shell_escaped_flags() {
Ok(Shlex::new(&env).collect())
} else {
Ok(env
.split_ascii_whitespace()
.map(ToString::to_string)
.collect())
}
}

fn fix_env_for_apple_os(&self, cmd: &mut Command) -> Result<(), Error> {
Expand Down
24 changes: 24 additions & 0 deletions tests/cflags_shell_escaped.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
mod support;

use crate::support::Test;
use std::env;

/// This test is in its own module because it modifies the environment and would affect other tests
/// when run in parallel with them.
#[test]
fn gnu_test_parse_shell_escaped_flags() {
env::set_var("CFLAGS", "foo \"bar baz\"");
env::set_var("CC_SHELL_ESCAPED_FLAGS", "1");
let test = Test::gnu();
test.gcc().file("foo.c").compile("foo");

test.cmd(0).must_have("foo").must_have("bar baz");

env::remove_var("CC_SHELL_ESCAPED_FLAGS");
let test = Test::gnu();
test.gcc().file("foo.c").compile("foo");

test.cmd(0)
.must_have("foo")
.must_have_in_order("\"bar", "baz\"");
}

0 comments on commit ebe335c

Please sign in to comment.