Skip to content

Commit 1b85887

Browse files
committed
feat(sys): accept NGINX_SOURCE_DIR along with NGX_OBJS
Previously, we assumed that the source directory is a parent of `NGX_OBJS`. This isn't true, `--builddir` can point to an absolute path, and source directory can be read-only. Specifying an explicit source path and resolving includes agains it fixes the build for such configurations.
1 parent 5e7dd9e commit 1b85887

File tree

1 file changed

+93
-32
lines changed

1 file changed

+93
-32
lines changed

nginx-sys/build/main.rs

+93-32
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use std::path::{Path, PathBuf};
99
#[cfg(feature = "vendored")]
1010
mod vendored;
1111

12-
const ENV_VARS_TRIGGERING_RECOMPILE: [&str; 2] = ["OUT_DIR", "NGX_OBJS"];
12+
const ENV_VARS_TRIGGERING_RECOMPILE: &[&str] = &["OUT_DIR", "NGX_OBJS", "NGINX_SOURCE_DIR"];
1313

1414
/// The feature flags set by the nginx configuration script.
1515
///
@@ -54,34 +54,113 @@ const NGX_CONF_OS: &[&str] = &[
5454
"darwin", "freebsd", "gnu_hurd", "hpux", "linux", "solaris", "tru64", "win32",
5555
];
5656

57+
type BoxError = Box<dyn StdError>;
58+
5759
/// Function invoked when `cargo build` is executed.
5860
/// This function will download NGINX and all supporting dependencies, verify their integrity,
5961
/// extract them, execute autoconf `configure` for NGINX, compile NGINX and finally install
6062
/// NGINX in a subdirectory with the project.
61-
fn main() -> Result<(), Box<dyn StdError>> {
62-
let nginx_build_dir = match std::env::var("NGX_OBJS") {
63-
Ok(v) => dunce::canonicalize(v)?,
64-
#[cfg(feature = "vendored")]
65-
Err(_) => vendored::build()?,
66-
#[cfg(not(feature = "vendored"))]
67-
Err(_) => panic!("\"nginx-sys/vendored\" feature is disabled and NGX_OBJS is not specified"),
68-
};
63+
fn main() -> Result<(), BoxError> {
6964
// Hint cargo to rebuild if any of the these environment variables values change
7065
// because they will trigger a recompilation of NGINX with different parameters
7166
for var in ENV_VARS_TRIGGERING_RECOMPILE {
7267
println!("cargo:rerun-if-env-changed={var}");
7368
}
7469
println!("cargo:rerun-if-changed=build/main.rs");
7570
println!("cargo:rerun-if-changed=build/wrapper.h");
71+
72+
let nginx = NginxSource::from_env();
7673
// Read autoconf generated makefile for NGINX and generate Rust bindings based on its includes
77-
generate_binding(nginx_build_dir);
74+
generate_binding(&nginx);
7875
Ok(())
7976
}
8077

78+
pub struct NginxSource {
79+
source_dir: PathBuf,
80+
build_dir: PathBuf,
81+
}
82+
83+
impl NginxSource {
84+
pub fn new(source_dir: impl AsRef<Path>, build_dir: impl AsRef<Path>) -> Self {
85+
let source_dir = NginxSource::check_source_dir(source_dir).expect("source directory");
86+
let build_dir = NginxSource::check_build_dir(build_dir).expect("build directory");
87+
88+
Self { source_dir, build_dir }
89+
}
90+
91+
pub fn from_env() -> Self {
92+
match (env::var_os("NGINX_SOURCE_DIR"), env::var_os("NGX_OBJS")) {
93+
(Some(source_dir), Some(build_dir)) => NginxSource::new(source_dir, build_dir),
94+
(Some(source_dir), None) => Self::from_source_dir(source_dir),
95+
(None, Some(build_dir)) => Self::from_build_dir(build_dir),
96+
_ => Self::from_vendored(),
97+
}
98+
}
99+
100+
pub fn from_source_dir(source_dir: impl AsRef<Path>) -> Self {
101+
let build_dir = source_dir.as_ref().join("objs");
102+
103+
// todo!("Build from source");
104+
105+
Self::new(source_dir, build_dir)
106+
}
107+
108+
pub fn from_build_dir(build_dir: impl AsRef<Path>) -> Self {
109+
let source_dir = build_dir.as_ref().parent().expect("source directory").to_owned();
110+
Self::new(source_dir, build_dir)
111+
}
112+
113+
#[cfg(feature = "vendored")]
114+
pub fn from_vendored() -> Self {
115+
let build_dir = vendored::build().expect("vendored build");
116+
let source_dir = build_dir.parent().expect("source directory").to_path_buf();
117+
118+
Self { source_dir, build_dir }
119+
}
120+
121+
#[cfg(not(feature = "vendored"))]
122+
pub fn from_vendored() -> Self {
123+
panic!("\"nginx-sys/vendored\" feature is disabled and neither NGINX_SOURCE_DIR nor NGX_OBJS is set");
124+
}
125+
126+
fn check_source_dir(source_dir: impl AsRef<Path>) -> Result<PathBuf, BoxError> {
127+
match dunce::canonicalize(&source_dir) {
128+
Ok(path) if path.join("src/core/nginx.h").is_file() => Ok(path),
129+
Err(err) => Err(format!("Invalid nginx source directory: {:?}. {}", source_dir.as_ref(), err).into()),
130+
_ => Err(format!(
131+
"Invalid nginx source directory: {:?}. NGINX_SOURCE_DIR is not specified or contains invalid value.",
132+
source_dir.as_ref()
133+
)
134+
.into()),
135+
}
136+
}
137+
138+
fn check_build_dir(build_dir: impl AsRef<Path>) -> Result<PathBuf, BoxError> {
139+
match dunce::canonicalize(&build_dir) {
140+
Ok(path) if path.join("ngx_auto_config.h").is_file() => Ok(path),
141+
Err(err) => Err(format!("Invalid nginx build directory: {:?}. {}", build_dir.as_ref(), err).into()),
142+
_ => Err(format!(
143+
"Invalid NGINX build directory: {:?}. NGX_OBJS is not specified or contains invalid value.",
144+
build_dir.as_ref()
145+
)
146+
.into()),
147+
}
148+
}
149+
}
150+
81151
/// Generates Rust bindings for NGINX
82-
fn generate_binding(nginx_build_dir: PathBuf) {
83-
let autoconf_makefile_path = nginx_build_dir.join("Makefile");
84-
let includes = parse_includes_from_makefile(&autoconf_makefile_path);
152+
fn generate_binding(nginx: &NginxSource) {
153+
let autoconf_makefile_path = nginx.build_dir.join("Makefile");
154+
let includes: Vec<_> = parse_includes_from_makefile(&autoconf_makefile_path)
155+
.into_iter()
156+
.map(|path| {
157+
if path.is_absolute() {
158+
path
159+
} else {
160+
nginx.source_dir.join(path)
161+
}
162+
})
163+
.collect();
85164
let clang_args: Vec<String> = includes
86165
.iter()
87166
.map(|path| format!("-I{}", path.to_string_lossy()))
@@ -166,25 +245,7 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<P
166245
}
167246
}
168247

169-
let makefile_dir = nginx_autoconf_makefile_path
170-
.parent()
171-
.expect("makefile path has no parent")
172-
.parent()
173-
.expect("objs dir has no parent");
174-
175-
includes
176-
.into_iter()
177-
.map(PathBuf::from)
178-
.map(|path| {
179-
if path.is_absolute() {
180-
path
181-
} else {
182-
makefile_dir.join(path)
183-
}
184-
})
185-
.map(dunce::canonicalize)
186-
.collect::<Result<Vec<_>, _>>()
187-
.expect("canonicalize include paths")
248+
includes.into_iter().map(PathBuf::from).collect()
188249
}
189250

190251
/// Collect info about the nginx configuration and expose it to the dependents via

0 commit comments

Comments
 (0)