Skip to content

fix: use CFLAGS from Makefile #174

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 15, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions nginx-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ cc = "1.2.0"
dunce = "1.0.5"
regex = "1.11.1"
nginx-src = { version = "~1.28.0", optional = true, path = "../nginx-src" }
shlex = "1.3"

[features]
vendored = ["dep:nginx-src"]
137 changes: 98 additions & 39 deletions nginx-sys/build/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,8 @@ impl NginxSource {
/// Generates Rust bindings for NGINX
fn generate_binding(nginx: &NginxSource) {
let autoconf_makefile_path = nginx.build_dir.join("Makefile");
let includes: Vec<_> = parse_includes_from_makefile(&autoconf_makefile_path)
let (includes, defines) = parse_makefile(&autoconf_makefile_path);
let includes: Vec<_> = includes
.into_iter()
.map(|path| {
if path.is_absolute() {
Expand All @@ -202,12 +203,20 @@ fn generate_binding(nginx: &NginxSource) {
}
})
.collect();
let clang_args: Vec<String> = includes
let mut clang_args: Vec<String> = includes
.iter()
.map(|path| format!("-I{}", path.to_string_lossy()))
.collect();

print_cargo_metadata(nginx, &includes).expect("cargo dependency metadata");
clang_args.extend(defines.iter().map(|(n, ov)| {
if let Some(v) = ov {
format!("-D{n}={v}")
} else {
format!("-D{n}")
}
}));

print_cargo_metadata(nginx, &includes, &defines).expect("cargo dependency metadata");

// bindgen targets the latest known stable by default
let rust_target: bindgen::RustTarget = env::var("CARGO_PKG_RUST_VERSION")
Expand Down Expand Up @@ -241,22 +250,47 @@ fn generate_binding(nginx: &NginxSource) {
}

/// Reads through the makefile generated by autoconf and finds all of the includes
/// used to compile nginx. This is used to generate the correct bindings for the
/// nginx source code.
fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<PathBuf> {
fn extract_include_part(line: &str) -> &str {
line.strip_suffix('\\').map_or(line, |s| s.trim())
}
/// Extracts the include path from a line of the autoconf generated makefile.
fn extract_after_i_flag(line: &str) -> Option<&str> {
let mut parts = line.split("-I ");
match parts.next() {
Some(_) => parts.next().map(extract_include_part),
None => None,
/// and definitions used to compile nginx. This is used to generate the correct bindings
/// for the nginx source code.
pub fn parse_makefile(
nginx_autoconf_makefile_path: &PathBuf,
) -> (Vec<PathBuf>, Vec<(String, Option<String>)>) {
fn parse_line(
includes: &mut Vec<String>,
defines: &mut Vec<(String, Option<String>)>,
line: &str,
) {
let mut words = shlex::Shlex::new(line);

while let Some(word) = words.next() {
if let Some(inc) = word.strip_prefix("-I") {
let value = if inc.is_empty() {
words.next().expect("-I argument")
} else {
inc.to_string()
};
includes.push(value);
} else if let Some(def) = word.strip_prefix("-D") {
let def = if def.is_empty() {
words.next().expect("-D argument")
} else {
def.to_string()
};

if let Some((name, value)) = def.split_once("=") {
defines.push((name.to_string(), Some(value.to_string())));
} else {
defines.push((def.to_string(), None));
}
}
}
}

let mut includes = vec![];
let mut all_incs = vec![];
let mut cflags_includes = vec![];

let mut defines = vec![];

let makefile_contents = match read_to_string(nginx_autoconf_makefile_path) {
Ok(path) => path,
Err(e) => {
Expand All @@ -268,35 +302,39 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<P
}
};

let mut includes_lines = false;
for line in makefile_contents.lines() {
if !includes_lines {
if let Some(stripped) = line.strip_prefix("ALL_INCS") {
includes_lines = true;
if let Some(part) = extract_after_i_flag(stripped) {
includes.push(part);
}
continue;
}
let lines = makefile_contents.lines();
let mut line: String = "".to_string();
for l in lines {
if let Some(part) = l.strip_suffix("\\") {
line += part;
continue;
}

if includes_lines {
if let Some(part) = extract_after_i_flag(line) {
includes.push(part);
} else {
break;
}
line += l;

if let Some(tail) = line.strip_prefix("ALL_INCS") {
parse_line(&mut all_incs, &mut defines, tail);
} else if let Some(tail) = line.strip_prefix("CFLAGS") {
parse_line(&mut cflags_includes, &mut defines, tail);
}

line.clear();
}

includes.into_iter().map(PathBuf::from).collect()
cflags_includes.extend(all_incs);

(
cflags_includes.into_iter().map(PathBuf::from).collect(),
defines,
)
}

/// Collect info about the nginx configuration and expose it to the dependents via
/// `DEP_NGINX_...` variables.
pub fn print_cargo_metadata<T: AsRef<Path>>(
nginx: &NginxSource,
includes: &[T],
defines: &[(String, Option<String>)],
) -> Result<(), Box<dyn StdError>> {
// Unquote and merge C string constants
let unquote_re = regex::Regex::new(r#""(.*?[^\\])"\s*"#).unwrap();
Expand All @@ -311,7 +349,7 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
let mut ngx_features: Vec<String> = vec![];
let mut ngx_os = String::new();

let expanded = expand_definitions(includes)?;
let expanded = expand_definitions(includes, defines)?;
for line in String::from_utf8(expanded)?.lines() {
let Some((name, value)) = line
.trim()
Expand Down Expand Up @@ -350,6 +388,19 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
.expect("Unicode include paths")
);

println!(
"cargo:metadata=cflags={}",
defines
.iter()
.map(|(n, ov)| if let Some(v) = ov {
format!("-D{n}={v}")
} else {
format!("-D{n}")
})
.collect::<Vec<_>>()
.join(" ")
);

// A quoted list of all recognized features to be passed to rustc-check-cfg.
let values = NGX_CONF_FEATURES.join("\",\"");
println!("cargo::metadata=features_check=\"{values}\"");
Expand All @@ -372,7 +423,10 @@ pub fn print_cargo_metadata<T: AsRef<Path>>(
Ok(())
}

fn expand_definitions<T: AsRef<Path>>(includes: &[T]) -> Result<Vec<u8>, Box<dyn StdError>> {
fn expand_definitions<T: AsRef<Path>>(
includes: &[T],
defines: &[(String, Option<String>)],
) -> Result<Vec<u8>, Box<dyn StdError>> {
let path = PathBuf::from(env::var("OUT_DIR")?).join("expand.c");
let mut writer = std::io::BufWriter::new(File::create(&path)?);

Expand Down Expand Up @@ -418,8 +472,13 @@ RUST_CONF_{flag}=NGX_{flag}
writer.flush()?;
drop(writer);

Ok(cc::Build::new()
.includes(includes)
.file(path)
.try_expand()?)
let mut builder = cc::Build::new();

builder.includes(includes).file(path);

for def in defines {
builder.define(&def.0, def.1.as_deref());
}

Ok(builder.try_expand()?)
}
Loading