@@ -2,14 +2,57 @@ extern crate bindgen;
22
33use std:: env;
44use std:: error:: Error as StdError ;
5- use std:: fs:: read_to_string;
6- use std:: path:: PathBuf ;
5+ use std:: fs:: { read_to_string, File } ;
6+ use std:: io:: Write ;
7+ use std:: path:: { Path , PathBuf } ;
78
89#[ cfg( feature = "vendored" ) ]
910mod vendored;
1011
1112const ENV_VARS_TRIGGERING_RECOMPILE : [ & str ; 2 ] = [ "OUT_DIR" , "NGX_OBJS" ] ;
1213
14+ /// The feature flags set by the nginx configuration script.
15+ ///
16+ /// This list is a subset of NGX_/NGX_HAVE_ macros known to affect the structure layout or module
17+ /// avialiability.
18+ ///
19+ /// The flags will be exposed to the buildscripts of _direct_ dependendents of this crate as
20+ /// `DEP_NGINX_FEATURES` environment variable.
21+ /// The list of recognized values will be exported as `DEP_NGINX_FEATURES_CHECK`.
22+ const NGX_CONF_FEATURES : & [ & str ] = & [
23+ "compat" ,
24+ "debug" ,
25+ "have_epollrdhup" ,
26+ "have_file_aio" ,
27+ "have_kqueue" ,
28+ "http_cache" ,
29+ "http_dav" ,
30+ "http_gzip" ,
31+ "http_realip" ,
32+ "http_ssi" ,
33+ "http_ssl" ,
34+ "http_upstream_zone" ,
35+ "http_v2" ,
36+ "http_v3" ,
37+ "http_x_forwarded_for" ,
38+ "pcre" ,
39+ "pcre2" ,
40+ "quic" ,
41+ "ssl" ,
42+ "stream_ssl" ,
43+ "stream_upstream_zone" ,
44+ "threads" ,
45+ ] ;
46+
47+ /// The operating systems supported by the nginx configuration script
48+ ///
49+ /// The detected value will be exposed to the buildsrcipts of _direct_ dependents of this crate as
50+ /// `DEP_NGINX_OS` environment variable.
51+ /// The list of recognized values will be exported as `DEP_NGINX_OS_CHECK`.
52+ const NGX_CONF_OS : & [ & str ] = & [
53+ "darwin" , "freebsd" , "gnu_hurd" , "hpux" , "linux" , "solaris" , "tru64" , "win32" ,
54+ ] ;
55+
1356/// Function invoked when `cargo build` is executed.
1457/// This function will download NGINX and all supporting dependencies, verify their integrity,
1558/// extract them, execute autoconf `configure` for NGINX, compile NGINX and finally install
@@ -37,11 +80,14 @@ fn main() -> Result<(), Box<dyn StdError>> {
3780/// Generates Rust bindings for NGINX
3881fn generate_binding ( nginx_build_dir : PathBuf ) {
3982 let autoconf_makefile_path = nginx_build_dir. join ( "Makefile" ) ;
40- let clang_args: Vec < String > = parse_includes_from_makefile ( & autoconf_makefile_path)
41- . into_iter ( )
83+ let includes = parse_includes_from_makefile ( & autoconf_makefile_path) ;
84+ let clang_args: Vec < String > = includes
85+ . iter ( )
4286 . map ( |path| format ! ( "-I{}" , path. to_string_lossy( ) ) )
4387 . collect ( ) ;
4488
89+ print_cargo_metadata ( & includes) . expect ( "cargo dependency metadata" ) ;
90+
4591 let bindings = bindgen:: Builder :: default ( )
4692 // Bindings will not compile on Linux without block listing this item
4793 // It is worth investigating why this is
@@ -132,3 +178,95 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec<P
132178 } )
133179 . collect ( )
134180}
181+
182+ /// Collect info about the nginx configuration and expose it to the dependents via
183+ /// `DEP_NGINX_...` variables.
184+ pub fn print_cargo_metadata < T : AsRef < Path > > ( includes : & [ T ] ) -> Result < ( ) , Box < dyn StdError > > {
185+ // Unquote and merge C string constants
186+ let unquote_re = regex:: Regex :: new ( r#""(.*?[^\\])"\s*"# ) . unwrap ( ) ;
187+ let unquote = |data : & str | -> String {
188+ unquote_re
189+ . captures_iter ( data)
190+ . map ( |c| c. get ( 1 ) . unwrap ( ) . as_str ( ) )
191+ . collect :: < Vec < _ > > ( )
192+ . concat ( )
193+ } ;
194+
195+ let mut ngx_features: Vec < String > = vec ! [ ] ;
196+ let mut ngx_os = String :: new ( ) ;
197+
198+ let expanded = expand_definitions ( includes) ?;
199+ for line in String :: from_utf8 ( expanded) ?. lines ( ) {
200+ let Some ( ( name, value) ) = line. trim ( ) . strip_prefix ( "RUST_CONF_" ) . and_then ( |x| x. split_once ( '=' ) ) else {
201+ continue ;
202+ } ;
203+
204+ let name = name. trim ( ) . to_ascii_lowercase ( ) ;
205+ let value = value. trim ( ) ;
206+
207+ if name == "nginx_build" {
208+ println ! ( "cargo::metadata=build={}" , unquote( value) ) ;
209+ } else if name == "nginx_version" {
210+ println ! ( "cargo::metadata=version={}" , unquote( value) ) ;
211+ } else if name == "nginx_version_number" {
212+ println ! ( "cargo::metadata=version_number={value}" ) ;
213+ } else if NGX_CONF_OS . contains ( & name. as_str ( ) ) {
214+ ngx_os = name;
215+ } else if NGX_CONF_FEATURES . contains ( & name. as_str ( ) ) && value != "0" {
216+ ngx_features. push ( name) ;
217+ }
218+ }
219+
220+ println ! (
221+ "cargo::metadata=include={}" ,
222+ // The str conversion is necessary because cargo directives must be valid UTF-8
223+ env:: join_paths( includes. iter( ) . map( |x| x. as_ref( ) ) ) ?
224+ . to_str( )
225+ . expect( "Unicode include paths" )
226+ ) ;
227+
228+ // A quoted list of all recognized features to be passed to rustc-check-cfg.
229+ println ! ( "cargo::metadata=features_check=\" {}\" " , NGX_CONF_FEATURES . join( "\" ,\" " ) ) ;
230+ // A list of features enabled in the nginx build we're using
231+ println ! ( "cargo::metadata=features={}" , ngx_features. join( "," ) ) ;
232+
233+ // A quoted list of all recognized operating systems to be passed to rustc-check-cfg.
234+ println ! ( "cargo::metadata=os_check=\" {}\" " , NGX_CONF_OS . join( "\" ,\" " ) ) ;
235+ // Current detected operating system
236+ println ! ( "cargo::metadata=os={ngx_os}" ) ;
237+
238+ Ok ( ( ) )
239+ }
240+
241+ fn expand_definitions < T : AsRef < Path > > ( includes : & [ T ] ) -> Result < Vec < u8 > , Box < dyn StdError > > {
242+ let path = PathBuf :: from ( env:: var ( "OUT_DIR" ) ?) . join ( "expand.c" ) ;
243+ let mut writer = std:: io:: BufWriter :: new ( File :: create ( & path) ?) ;
244+
245+ write ! (
246+ writer,
247+ "
248+ #include <ngx_config.h>
249+ #include <nginx.h>
250+
251+ RUST_CONF_NGINX_BUILD=NGINX_VER_BUILD
252+ RUST_CONF_NGINX_VERSION=NGINX_VER
253+ RUST_CONF_NGINX_VERSION_NUMBER=nginx_version
254+ "
255+ ) ?;
256+
257+ for flag in NGX_CONF_FEATURES . iter ( ) . chain ( NGX_CONF_OS . iter ( ) ) {
258+ let flag = flag. to_ascii_uppercase ( ) ;
259+ write ! (
260+ writer,
261+ "
262+ #if defined(NGX_{flag})
263+ RUST_CONF_{flag}=NGX_{flag}
264+ #endif"
265+ ) ?;
266+ }
267+
268+ writer. flush ( ) ?;
269+ drop ( writer) ;
270+
271+ Ok ( cc:: Build :: new ( ) . includes ( includes) . file ( path) . try_expand ( ) ?)
272+ }
0 commit comments