Skip to content

Commit f312bcb

Browse files
magic-akarikddnewton
authored andcommitted
Allow custom C flags in ruby-prism-sys
Add RUBY_PRISM_CFLAGS for crate-scoped compiler arguments. Parse the value with shell quoting and apply the resulting arguments to both the vendored C build and bindgen. Pass existing vendored and WASI arguments to bindgen explicitly through the same argument list instead of mutating BINDGEN_EXTRA_CLANG_ARGS. This allows freestanding targets to provide sysroots, include paths, and Prism configuration macros without affecting unrelated Cargo dependencies. Closes #4165.
1 parent 5e4f457 commit f312bcb

5 files changed

Lines changed: 37 additions & 18 deletions

File tree

rust/Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/ruby-prism-sys/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ include = ["src/", "build/", "Cargo.toml", "Cargo.lock", "README.md", "vendor"]
2424
[build-dependencies]
2525
bindgen = "0.72"
2626
cc = { version = "1.0", optional = true }
27+
shlex = "1.3"
2728

2829
[features]
2930
default = ["vendored"]

rust/ruby-prism-sys/README.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,17 @@
22

33
Rust bindings to [ruby/prism](https://github.com/ruby/prism)'s C API.
44

5+
## Custom C flags
6+
7+
Set `RUBY_PRISM_CFLAGS` to pass crate-specific arguments to both the C compiler
8+
and bindgen. Arguments use shell quoting, so paths containing spaces can be
9+
quoted. For example, a freestanding build can provide its sysroot and Prism
10+
configuration without changing the flags of unrelated dependencies:
11+
12+
```sh
13+
RUBY_PRISM_CFLAGS='--sysroot=/path/to/sysroot -DPRISM_HAS_NO_FILESYSTEM=1' cargo build
14+
```
15+
516
## Examples
617

718
Currently the best examples are found in the integration tests (in `tests/`).

rust/ruby-prism-sys/build/main.rs

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@ mod vendored;
44
use std::path::{Path, PathBuf};
55

66
fn main() {
7+
let clang_args = ruby_prism_cflags();
8+
79
#[cfg(feature = "vendored")]
8-
vendored::build().expect("failed to build Prism from source");
10+
let clang_args = vendored::build(clang_args).expect("failed to build Prism from source");
911

1012
let ruby_build_path = prism_lib_path();
1113
let ruby_include_path = prism_include_path();
@@ -18,13 +20,14 @@ fn main() {
1820
println!("cargo:rustc-link-search=native={}", ruby_build_path.to_str().unwrap());
1921

2022
// This is where the magic happens.
21-
let bindings = generate_bindings(&ruby_include_path);
23+
let bindings = generate_bindings(&ruby_include_path, &clang_args);
2224

2325
// Write the bindings to file.
2426
write_bindings(&bindings);
2527
}
2628

2729
fn emit_rerun_hints(ruby_include_path: &Path) {
30+
println!("cargo:rerun-if-env-changed=RUBY_PRISM_CFLAGS");
2831
println!("cargo:rerun-if-env-changed=PRISM_INCLUDE_DIR");
2932
println!("cargo:rerun-if-env-changed=PRISM_LIB_DIR");
3033
println!("cargo:rerun-if-changed={}", ruby_include_path.display());
@@ -37,6 +40,14 @@ fn emit_rerun_hints(ruby_include_path: &Path) {
3740
}
3841
}
3942

43+
fn ruby_prism_cflags() -> Vec<String> {
44+
let Ok(cflags) = std::env::var("RUBY_PRISM_CFLAGS") else {
45+
return Vec::new();
46+
};
47+
48+
shlex::split(&cflags).expect("RUBY_PRISM_CFLAGS contains invalid shell quoting")
49+
}
50+
4051
/// Gets the path to project files (`libprism*`) at `[root]/build/`.
4152
///
4253
fn prism_lib_path() -> PathBuf {
@@ -110,14 +121,15 @@ impl bindgen::callbacks::ParseCallbacks for Callbacks {
110121
///
111122
/// This method only generates code in memory here--it doesn't write it to file.
112123
///
113-
fn generate_bindings(ruby_include_path: &Path) -> bindgen::Bindings {
124+
fn generate_bindings(ruby_include_path: &Path, clang_args: &[String]) -> bindgen::Bindings {
114125
bindgen::Builder::default()
115126
.derive_default(true)
116127
.generate_block(true)
117128
.generate_comments(true)
118129
.header(ruby_include_path.join("prism.h").to_str().unwrap())
119130
.clang_arg(format!("-I{}", ruby_include_path.to_str().unwrap()))
120131
.clang_arg("-fparse-all-comments")
132+
.clang_args(clang_args)
121133
.impl_debug(true)
122134
.layout_tests(true)
123135
.merge_extern_blocks(true)

rust/ruby-prism-sys/build/vendored.rs

Lines changed: 9 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::{
44
};
55

66
/// Builds libprism.a from source, and configures the build script to use it.
7-
pub fn build() -> Result<(), Box<dyn std::error::Error>> {
7+
pub fn build(custom_cflags: Vec<String>) -> Result<Vec<String>, Box<dyn std::error::Error>> {
88
assert!(
99
vendor_dir().exists(),
1010
"Prism source directory does not exist, expected: {}",
@@ -45,19 +45,23 @@ pub fn build() -> Result<(), Box<dyn std::error::Error>> {
4545
}
4646
}
4747

48+
flags.extend(custom_cflags);
49+
50+
let mut bindgen_args = Vec::new();
51+
4852
for (key, value) in defines {
4953
build.define(key, value);
50-
push_bindgen_extra_clang_args(format!("-D{key}={value}"));
54+
bindgen_args.push(format!("-D{key}={value}"));
5155
}
5256

5357
for include in includes {
5458
build.include(&include);
55-
push_bindgen_extra_clang_args(format!("-I{}", include.display()));
59+
bindgen_args.push(format!("-I{}", include.display()));
5660
}
5761

5862
for flag in flags {
5963
build.flag(&flag);
60-
push_bindgen_extra_clang_args(flag);
64+
bindgen_args.push(flag);
6165
}
6266

6367
build.files(source_files(src_dir()));
@@ -67,7 +71,7 @@ pub fn build() -> Result<(), Box<dyn std::error::Error>> {
6771
std::env::set_var("PRISM_INCLUDE_DIR", include_dir());
6872
std::env::set_var("PRISM_LIB_DIR", out_dir);
6973

70-
Ok(())
74+
Ok(bindgen_args)
7175
}
7276

7377
fn version() -> &'static str {
@@ -92,16 +96,6 @@ fn include_dir() -> PathBuf {
9296
vendor_dir().join("include")
9397
}
9498

95-
fn push_bindgen_extra_clang_args<T: AsRef<str>>(arg: T) {
96-
let env_var_name = format!("BINDGEN_EXTRA_CLANG_ARGS_{}", std::env::var("TARGET").unwrap());
97-
98-
if let Ok(preexisting_arg) = std::env::var(&env_var_name) {
99-
std::env::set_var(env_var_name, format!("{} {}", preexisting_arg, arg.as_ref()));
100-
} else {
101-
std::env::set_var(env_var_name, arg.as_ref());
102-
}
103-
}
104-
10599
fn source_files<P: AsRef<Path>>(root_dir: P) -> Vec<String> {
106100
let mut files = Vec::new();
107101

0 commit comments

Comments
 (0)