From 6e239590963fd09f0bd6e97a7460d821b298a984 Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 28 May 2025 16:05:44 -0700 Subject: [PATCH 1/2] test: stop using paths from the nginx binary We had an inconvenient expectation that the nginx binary is installed to the configured location and the default prefix is writable. Instead of that, we should take the same approach as perl tests: * Create a temporary prefix and pass it to nginx. * Use binary from the build dir and allow overriding it with environment variable. --- Cargo.lock | 20 +++++++++--- Cargo.toml | 2 +- build.rs | 6 ++++ nginx-sys/build/main.rs | 12 +++++-- tests/build.rs | 6 ---- tests/log_test.rs | 70 ++++++++++++++++++++++++++++------------- tests/nginx.conf | 2 +- 7 files changed, 83 insertions(+), 35 deletions(-) delete mode 100644 tests/build.rs diff --git a/Cargo.lock b/Cargo.lock index ec61a41c..66a10ccd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -287,6 +287,12 @@ dependencies = [ "tokio", ] +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + [[package]] name = "filetime" version = "0.2.25" @@ -561,7 +567,7 @@ name = "ngx" version = "0.5.0" dependencies = [ "nginx-sys", - "target-triple", + "tempfile", ] [[package]] @@ -904,10 +910,16 @@ dependencies = [ ] [[package]] -name = "target-triple" -version = "0.1.4" +name = "tempfile" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac9aa371f599d22256307c24a9d748c041e548cbf599f35d890f9d365361790" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" +dependencies = [ + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] [[package]] name = "tinyvec" diff --git a/Cargo.toml b/Cargo.toml index d46cb3d9..f3cae74f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -45,4 +45,4 @@ vendored = ["nginx-sys/vendored"] maintenance = { status = "experimental" } [dev-dependencies] -target-triple = "0.1.2" +tempfile = { version = "3.20.0", default-features = false } diff --git a/build.rs b/build.rs index 1f8c5740..51fc45d5 100644 --- a/build.rs +++ b/build.rs @@ -53,6 +53,12 @@ fn main() { } } + // Pass build directory to the tests + println!("cargo::rerun-if-env-changed=DEP_NGINX_BUILD_DIR"); + if let Ok(build_dir) = std::env::var("DEP_NGINX_BUILD_DIR") { + println!("cargo::rustc-env=DEP_NGINX_BUILD_DIR={}", build_dir); + } + // Generate required compiler flags if cfg!(target_os = "macos") { // https://stackoverflow.com/questions/28124221/error-linking-with-cc-failed-exit-code-1 diff --git a/nginx-sys/build/main.rs b/nginx-sys/build/main.rs index 6028f318..91b4e2e3 100644 --- a/nginx-sys/build/main.rs +++ b/nginx-sys/build/main.rs @@ -202,7 +202,7 @@ fn generate_binding(nginx: &NginxSource) { .map(|path| format!("-I{}", path.to_string_lossy())) .collect(); - print_cargo_metadata(&includes).expect("cargo dependency metadata"); + print_cargo_metadata(nginx, &includes).expect("cargo dependency metadata"); // bindgen targets the latest known stable by default let rust_target: bindgen::RustTarget = env::var("CARGO_PKG_RUST_VERSION") @@ -287,7 +287,10 @@ fn parse_includes_from_makefile(nginx_autoconf_makefile_path: &PathBuf) -> Vec

>(includes: &[T]) -> Result<(), Box> { +pub fn print_cargo_metadata>( + nginx: &NginxSource, + includes: &[T], +) -> Result<(), Box> { // Unquote and merge C string constants let unquote_re = regex::Regex::new(r#""(.*?[^\\])"\s*"#).unwrap(); let unquote = |data: &str| -> String { @@ -327,6 +330,11 @@ pub fn print_cargo_metadata>(includes: &[T]) -> Result<(), Box Option { @@ -22,42 +24,68 @@ pub fn cstr_to_path(val: &std::ffi::CStr) -> Option { Some(PathBuf::from(str)) } +/// Find nginx binary in the build directory +pub fn find_nginx_binary() -> io::Result { + let path = [ + // TEST_NGINX_BINARY is specified for tests + env::var("TEST_NGINX_BINARY").ok().map(PathBuf::from), + // The module is built against an external NGINX source tree + env::var("NGINX_BUILD_DIR") + .map(PathBuf::from) + .map(|x| x.join(NGINX_BINARY_NAME)) + .ok(), + env::var("NGINX_SOURCE_DIR") + .map(PathBuf::from) + .map(|x| x.join("objs").join(NGINX_BINARY_NAME)) + .ok(), + // Fallback to the build directory exposed by nginx-sys + option_env!("DEP_NGINX_BUILD_DIR") + .map(PathBuf::from) + .map(|x| x.join(NGINX_BINARY_NAME)), + ] + .into_iter() + .flatten() + .find(|x| x.is_file()) + .ok_or(io::ErrorKind::NotFound)?; + + Ok(path) +} + /// harness to test nginx pub struct Nginx { - pub install_path: PathBuf, + pub prefix: tempfile::TempDir, + pub bin_path: PathBuf, pub config_path: PathBuf, } impl Default for Nginx { /// create nginx with default fn default() -> Nginx { - let install_path = cstr_to_path(NGX_PREFIX).expect("installation prefix"); - Nginx::new(install_path) + let binary = find_nginx_binary().expect("nginx binary"); + Nginx::new(binary).expect("test harness") } } impl Nginx { - pub fn new>(path: P) -> Nginx { - let install_path = path.as_ref(); - let config_path = cstr_to_path(NGX_CONF_PATH).expect("configuration path"); - let config_path = install_path.join(config_path); - - Nginx { - install_path: install_path.into(), - config_path, - } - } + pub fn new(binary: impl AsRef) -> io::Result { + let prefix = tempfile::tempdir()?; + let config = prefix.path().join("nginx.conf"); + + fs::create_dir(prefix.path().join("logs"))?; - /// get bin path to nginx instance - pub fn bin_path(&mut self) -> PathBuf { - let bin_path = cstr_to_path(NGX_SBIN_PATH).expect("binary path"); - self.install_path.join(bin_path) + Ok(Nginx { + prefix, + bin_path: binary.as_ref().to_owned(), + config_path: config, + }) } /// start nginx process with arguments - pub fn cmd(&mut self, args: &[&str]) -> Result { - let bin_path = self.bin_path(); - let result = Command::new(bin_path).args(args).output(); + pub fn cmd(&self, args: &[&str]) -> Result { + let prefix = self.prefix.path().to_string_lossy(); + let config_path = self.config_path.to_string_lossy(); + let args = [&["-p", &prefix, "-c", &config_path], args].concat(); + let result = Command::new(&self.bin_path).args(args).output(); match result { Err(e) => Err(e), diff --git a/tests/nginx.conf b/tests/nginx.conf index 495fc070..1d2e2079 100644 --- a/tests/nginx.conf +++ b/tests/nginx.conf @@ -16,7 +16,7 @@ events { http { - include mime.types; + # include mime.types; default_type application/octet-stream; From 0e6e19ef27f72d0c9da53df57127dcb603a4950b Mon Sep 17 00:00:00 2001 From: Aleksei Bavshin Date: Wed, 28 May 2025 12:14:04 -0700 Subject: [PATCH 2/2] wip: add nginx-src crate with vendored nginx sources Fixes: #85 --- .github/workflows/ci.yaml | 6 ++ .gitmodules | 4 + Cargo.lock | 222 ++------------------------------------ Cargo.toml | 1 + deny.toml | 6 +- nginx-src/Cargo.toml | 13 +++ nginx-src/LICENSE | 24 +++++ nginx-src/README.md | 40 +++++++ nginx-src/nginx | 1 + nginx-src/src/lib.rs | 127 ++++++++++++++++++++++ nginx-sys/Cargo.toml | 8 +- nginx-sys/build/main.rs | 8 +- 12 files changed, 230 insertions(+), 230 deletions(-) create mode 100644 .gitmodules create mode 100644 nginx-src/Cargo.toml create mode 100644 nginx-src/LICENSE create mode 100644 nginx-src/README.md create mode 160000 nginx-src/nginx create mode 100644 nginx-src/src/lib.rs diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 05686417..a6964786 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -34,6 +34,8 @@ jobs: steps: - name: checkout source uses: actions/checkout@v4 + with: + submodules: true - name: set up cargo cache uses: actions/cache@v4 continue-on-error: false @@ -89,6 +91,8 @@ jobs: steps: - name: checkout source uses: actions/checkout@v4 + with: + submodules: true - name: set up cargo cache uses: actions/cache@v4 continue-on-error: false @@ -123,6 +127,8 @@ jobs: - name: install command line dependencies run: brew install make gnupg - uses: actions/checkout@v4 + with: + submodules: true - uses: dtolnay/rust-toolchain@stable - name: set up cargo cache uses: actions/cache@v4 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..d4fe7294 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "nginx-src/nginx"] + path = nginx-src/nginx + url = https://github.com/nginx/nginx.git + branch = stable-1.28 diff --git a/Cargo.lock b/Cargo.lock index 66a10ccd..2b9988a3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -87,12 +87,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "base64" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" - [[package]] name = "bindgen" version = "0.71.1" @@ -204,15 +198,6 @@ dependencies = [ "libc", ] -[[package]] -name = "crc32fast" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" -dependencies = [ - "cfg-if", -] - [[package]] name = "crypto-common" version = "0.1.6" @@ -257,12 +242,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "errno" version = "0.3.12" @@ -293,28 +272,6 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" -[[package]] -name = "filetime" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" -dependencies = [ - "cfg-if", - "libc", - "libredox", - "windows-sys 0.59.0", -] - -[[package]] -name = "flate2" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - [[package]] name = "fnv" version = "1.0.7" @@ -380,12 +337,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - [[package]] name = "iana-time-zone" version = "0.1.63" @@ -482,17 +433,6 @@ dependencies = [ "windows-targets 0.53.0", ] -[[package]] -name = "libredox" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" -dependencies = [ - "bitflags", - "libc", - "redox_syscall", -] - [[package]] name = "linux-raw-sys" version = "0.9.4" @@ -547,19 +487,22 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "nginx-src" +version = "1028.0.0" +dependencies = [ + "duct", +] + [[package]] name = "nginx-sys" version = "0.5.0" dependencies = [ "bindgen", "cc", - "duct", "dunce", - "flate2", + "nginx-src", "regex", - "tar", - "ureq", - "which", ] [[package]] @@ -754,50 +697,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rustls" -version = "0.23.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" -dependencies = [ - "log", - "once_cell", - "ring", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-pemfile" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "rustls-pki-types" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" -dependencies = [ - "zeroize", -] - -[[package]] -name = "rustls-webpki" -version = "0.103.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" -dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", -] - [[package]] name = "rustversion" version = "1.0.21" @@ -881,12 +780,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - [[package]] name = "syn" version = "2.0.101" @@ -898,17 +791,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "tar" -version = "0.4.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "tempfile" version = "3.20.0" @@ -1004,36 +886,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "3.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7a3e9af6113ecd57b8c63d3cd76a385b2e3881365f1f489e54f49801d0c83ea" -dependencies = [ - "base64", - "flate2", - "log", - "percent-encoding", - "rustls", - "rustls-pemfile", - "rustls-pki-types", - "ureq-proto", - "utf-8", - "webpki-roots 0.26.11", -] - -[[package]] -name = "ureq-proto" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fadf18427d33828c311234884b7ba2afb57143e6e7e69fda7ee883b624661e36" -dependencies = [ - "base64", - "http", - "httparse", - "log", -] - [[package]] name = "url" version = "2.5.4" @@ -1045,12 +897,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -1127,36 +973,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "webpki-roots" -version = "0.26.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" -dependencies = [ - "webpki-roots 1.0.0", -] - -[[package]] -name = "webpki-roots" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" -dependencies = [ - "rustls-pki-types", -] - -[[package]] -name = "which" -version = "7.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d643ce3fd3e5b54854602a080f34fb10ab75e0b813ee32d00ca2b44fa74762" -dependencies = [ - "either", - "env_home", - "rustix", - "winsafe", -] - [[package]] name = "windows-core" version = "0.61.2" @@ -1361,25 +1177,3 @@ name = "windows_x86_64_msvc" version = "0.53.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" - -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - -[[package]] -name = "xattr" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d65cbf2f12c15564212d48f4e3dfb87923d25d611f2aed18f4cb23f0413d89e" -dependencies = [ - "libc", - "rustix", -] - -[[package]] -name = "zeroize" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index f3cae74f..e9718a5f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "nginx-src", "nginx-sys", "examples", ] diff --git a/deny.toml b/deny.toml index 39f40a6a..71ff852d 100644 --- a/deny.toml +++ b/deny.toml @@ -6,8 +6,8 @@ all-features = true [licenses] allow = [ - "Apache-2.0 WITH LLVM-exception", "Apache-2.0", + "BSD-2-Clause", "BSD-3-Clause", "ISC", "MIT", @@ -15,10 +15,6 @@ allow = [ ] confidence-threshold = 0.8 -[[licenses.exceptions]] -crate = "webpki-roots" -allow = ["CDLA-Permissive-2.0", "MPL-2.0"] - [[licenses.clarify]] crate = "ring" expression = "MIT AND ISC AND OpenSSL" diff --git a/nginx-src/Cargo.toml b/nginx-src/Cargo.toml new file mode 100644 index 00000000..dd2204ab --- /dev/null +++ b/nginx-src/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "nginx-src" +version = "1028.0.0" +license = "BSD-2-Clause" +description = "Source of NGINX" +keywords = ["nginx", "module", "sys"] +edition.workspace = true +homepage.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +duct = "1" diff --git a/nginx-src/LICENSE b/nginx-src/LICENSE new file mode 100644 index 00000000..4efd90b5 --- /dev/null +++ b/nginx-src/LICENSE @@ -0,0 +1,24 @@ +Copyright (C) 2002-2021 Igor Sysoev +Copyright (C) 2011-2025 Nginx, Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +SUCH DAMAGE. diff --git a/nginx-src/README.md b/nginx-src/README.md new file mode 100644 index 00000000..13f55c50 --- /dev/null +++ b/nginx-src/README.md @@ -0,0 +1,40 @@ +# nginx-src + +This crate contains a vendored copy of the NGINX source and the logic to +build it. It is intended to be consumed by the `nginx-sys` crate for CI +builds, tests or rustdoc generation. + +It is notably not intended for producing binaries suitable for production +use. For such scenaros we recommend building the ngx-rust based module +against prebuilt packages from https://nginx.org/ or your preferred +distribution. See the `nginx-sys` documentation for building ngx-rust +modules against an existing pre-configured NGINX source tree. + +## Versioning + +This crate follows the latest stable branch of NGINX. + + * The major version is derived from the major and minor version of the + NGINX stable branch being used: (`nginx.major` * 1000 + `nginx.minor`). + * The minor version is taken from the NGINX patch version. + * The patch version is incremented on changes to the build logic or crate + metadata. + +## Build Requirements + +The crate can be built on common Unix-like operating systems and requires +all the usual NGINX build dependencies (including development headers +for the libraries) installed in system paths: + +* C compiler and toolchain +* SSL library, OpenSSL or LibreSSL +* PCRE or PCRE2 +* Zlib or zlib-ng witn zlib compatibile API enabled + +We don't intend to support Windows at the moment, as NGINX does not +support dynamic modules for this target. + +## License + +This crate contains the source code of NGINX and thus inherits the +[BSD-2-Clause](nginx/LICENSE) license. diff --git a/nginx-src/nginx b/nginx-src/nginx new file mode 160000 index 00000000..481d28cb --- /dev/null +++ b/nginx-src/nginx @@ -0,0 +1 @@ +Subproject commit 481d28cb4e04c8096b9b6134856891dc52ecc68f diff --git a/nginx-src/src/lib.rs b/nginx-src/src/lib.rs new file mode 100644 index 00000000..f152f58a --- /dev/null +++ b/nginx-src/src/lib.rs @@ -0,0 +1,127 @@ +use std::ffi::OsString; +use std::path::{Path, PathBuf}; +use std::{env, io, thread}; + +pub static NGINX_DEFAULT_SOURCE_DIR: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/nginx"); + +const NGINX_BUILD_INFO: &str = "last-build-info"; +const NGINX_BINARY: &str = "nginx"; + +static NGINX_CONFIGURE_FLAGS: &[&str] = &[ + "--with-compat", + "--with-http_realip_module", + "--with-http_ssl_module", + "--with-http_v2_module", + "--with-stream_realip_module", + "--with-stream_ssl_module", + "--with-stream", + "--with-threads", +]; + +pub fn build(build_dir: impl AsRef) -> io::Result<(PathBuf, PathBuf)> { + let source_dir = PathBuf::from(NGINX_DEFAULT_SOURCE_DIR); + let build_dir = build_dir.as_ref().to_owned(); + + configure(&source_dir, &build_dir, NGINX_CONFIGURE_FLAGS)?; + + make(&source_dir, &build_dir)?; + + Ok((source_dir, build_dir)) +} + +/// Run external process invoking autoconf `configure` for NGINX. +pub fn configure(source_dir: &Path, build_dir: &Path, configure_flags: &[&str]) -> io::Result<()> { + let build_info = build_info(configure_flags); + + if build_dir.join("Makefile").is_file() + && build_dir.join(NGINX_BINARY).is_file() + && matches!( + std::fs::read_to_string(build_dir.join(NGINX_BUILD_INFO)).map(|x| x == build_info), + Ok(true) + ) + { + println!("Build info unchanged, skipping configure"); + return Ok(()); + } + + println!("Using NGINX source at {:?}", source_dir); + + let configure = ["configure", "auto/configure"] + .into_iter() + .map(|x| source_dir.join(x)) + .find(|x| x.is_file()) + .ok_or(io::ErrorKind::NotFound)?; + + println!( + "Running NGINX configure script with flags: {:?}", + configure_flags.join(" ") + ); + + let mut build_dir_arg: OsString = "--builddir=".into(); + build_dir_arg.push(build_dir); + + let mut flags: Vec = configure_flags.iter().map(|x| x.into()).collect(); + flags.push(build_dir_arg); + + let output = duct::cmd(configure, flags) + .dir(source_dir) + .stderr_to_stdout() + .run()?; + + if !output.status.success() { + println!("configure failed with {:?}", output.status); + return Err(io::ErrorKind::Other.into()); + } + + let _ = std::fs::write(build_dir.join(NGINX_BUILD_INFO), build_info); + + Ok(()) +} + +/// Run `make` within the NGINX source directory as an external process. +fn make(source_dir: &Path, build_dir: &Path) -> io::Result<()> { + // Level of concurrency to use when building nginx - cargo nicely provides this information + let num_jobs = match env::var("NUM_JOBS") { + Ok(s) => s.parse::().ok(), + Err(_) => thread::available_parallelism().ok().map(|n| n.get()), + } + .unwrap_or(1); + + let run_make = |x| -> io::Result<_> { + /* Use the duct dependency here to merge the output of STDOUT and STDERR into a single stream, + and to provide the combined output as a reader which can be iterated over line-by-line. We + use duct to do this because it is a lot of work to implement this from scratch. */ + let out = duct::cmd!( + x, + "-j", + num_jobs.to_string(), + "-f", + build_dir.join("Makefile") + ) + .dir(source_dir) + .stderr_to_stdout() + .run()?; + + if !out.status.success() { + println!("{} failed with {:?}", x, out.status); + return Err(io::ErrorKind::Other.into()); + } + + Ok(()) + }; + + // Give preference to the binary with the name of gmake if it exists because this is typically + // the GNU 4+ on MacOS (if it is installed via homebrew). + match run_make("gmake") { + Ok(out) => Ok(out), + Err(err) if err.kind() == io::ErrorKind::NotFound => run_make("make"), + Err(err) => Err(err), + } +} + +/// Returns the options in which NGINX was built with +fn build_info(nginx_configure_flags: &[&str]) -> String { + // Flags should contain strings pointing to OS/platform as well as dependency versions, + // so if any of that changes, it can trigger a rebuild + nginx_configure_flags.join(" ") +} diff --git a/nginx-sys/Cargo.toml b/nginx-sys/Cargo.toml index 9188bab9..fc40260b 100644 --- a/nginx-sys/Cargo.toml +++ b/nginx-sys/Cargo.toml @@ -19,13 +19,9 @@ rust-version.workspace = true [build-dependencies] bindgen = "0.71" cc = "1.2.0" -duct = { version = "1", optional = true } dunce = "1.0.5" -flate2 = { version = "1.0.28", optional = true } regex = "1.11.1" -tar = { version = "0.4.40", optional = true } -ureq = { version = "3.0.10", optional = true } -which = { version = "7.0.0", optional = true } +nginx-src = { version = "1028.0.0", optional = true, path = "../nginx-src" } [features] -vendored = ["dep:which", "dep:duct", "dep:ureq", "dep:flate2", "dep:tar"] +vendored = ["dep:nginx-src"] diff --git a/nginx-sys/build/main.rs b/nginx-sys/build/main.rs index 91b4e2e3..d7e526b4 100644 --- a/nginx-sys/build/main.rs +++ b/nginx-sys/build/main.rs @@ -6,9 +6,6 @@ use std::fs::{read_to_string, File}; use std::io::Write; use std::path::{Path, PathBuf}; -#[cfg(feature = "vendored")] -mod vendored; - const ENV_VARS_TRIGGERING_RECOMPILE: &[&str] = &["OUT_DIR", "NGINX_BUILD_DIR", "NGINX_SOURCE_DIR"]; /// The feature flags set by the nginx configuration script. @@ -130,8 +127,9 @@ impl NginxSource { #[cfg(feature = "vendored")] pub fn from_vendored() -> Self { - let build_dir = vendored::build().expect("vendored build"); - let source_dir = build_dir.parent().expect("source directory").to_path_buf(); + let out_dir = env::var("OUT_DIR").unwrap(); + let build_dir = PathBuf::from(out_dir).join("objs"); + let (source_dir, build_dir) = nginx_src::build(build_dir).expect("nginx-src build"); Self { source_dir,