From e37f75acf1441aa4a3a3da83c1a58816ee21a2a6 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Fri, 28 Aug 2026 15:09:51 -0400 Subject: [PATCH 1/6] Rust wrapper: build.rs: validate WOLFSSL_PREFIX once Avoid the possibility of using WOLFSSL_PREFIX for just the lib or include halves. Fixes F-10086. --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 68 ++++++++++++++----------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index d614a4eafe..5feec53959 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -5,6 +5,7 @@ use std::env; use std::fs; use std::io::{self, Read, Result}; use std::path::{Path,PathBuf}; +use std::sync::OnceLock; /// Perform crate build. fn main() { @@ -37,33 +38,48 @@ fn wolfssl_repo_lib_dir() -> Result { Ok(format!("{}/src/.libs", wolfssl_repo_base_dir()?)) } -fn wolfssl_user_prefix() -> Option { - match env::var("WOLFSSL_PREFIX") { - Ok(prefix) => { - if !prefix.is_empty() && !prefix.contains('\n') { - Some(prefix) - } else { - println!("cargo:warning=ignoring WOLFSSL_PREFIX"); - None - } - } - Err(_) => None, +/// Returns the validated `WOLFSSL_PREFIX` installation prefix, if usable. +/// +/// A prefix is only accepted if it provides both halves of an installation: +/// a `lib` directory and an `include/wolfssl` directory. A prefix holding +/// only one of them is rejected outright, so that the headers and the library +/// we build against always come from the same place. +/// +/// The result is computed once and cached, so any warning is printed once. +fn wolfssl_prefix() -> Option<&'static str> { + static PREFIX: OnceLock> = OnceLock::new(); + PREFIX.get_or_init(compute_wolfssl_prefix).as_deref() +} + +/// Read `WOLFSSL_PREFIX` from the environment and validate its layout. +/// +/// Returns `None` (after warning) if the variable is unset, malformed, or +/// does not point at a directory containing both `lib` and `include/wolfssl`. +fn compute_wolfssl_prefix() -> Option { + let prefix = env::var("WOLFSSL_PREFIX").ok()?; + if prefix.is_empty() || prefix.contains('\n') { + println!("cargo:warning=ignoring WOLFSSL_PREFIX"); + return None; } + let prefix_path = Path::new(&prefix); + for subdir in [prefix_path.join("lib"), + prefix_path.join("include").join("wolfssl")] { + if !subdir.is_dir() { + println!("cargo:warning=ignoring WOLFSSL_PREFIX: {} is not a directory", + subdir.display()); + return None; + } + } + Some(prefix) } /// Returns the include directory for wolfssl headers. /// -/// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/include`. +/// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/include`. /// Otherwise falls back to the repo root if it exists (for in-tree host builds). fn wolfssl_include_dir() -> Result> { - if let Some(prefix) = wolfssl_user_prefix() { - let include_dir = format!("{}/include", prefix); - let wolfssl_dir = Path::new(&include_dir).join("wolfssl"); - if !wolfssl_dir.is_dir() { - println!("cargo:warning=WOLFSSL_PREFIX is set but {} is not a directory", wolfssl_dir.display()); - return Ok(None); - } - Ok(Some(include_dir)) + if let Some(prefix) = wolfssl_prefix() { + Ok(Some(format!("{}/include", prefix))) } else { let base = wolfssl_repo_base_dir()?; let base_path = Path::new(&base); @@ -80,17 +96,11 @@ fn wolfssl_include_dir() -> Result> { /// Returns the library directory for libwolfssl. /// -/// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/lib`. +/// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/lib`. /// Otherwise falls back to the in-tree build output directory if it exists. fn wolfssl_lib_dir() -> Result> { - if let Some(prefix) = wolfssl_user_prefix() { - let lib_dir = format!("{}/lib", prefix); - let lib_path = Path::new(&lib_dir); - if !lib_path.is_dir() { - println!("cargo:warning=WOLFSSL_PREFIX is set but {} is not a directory", lib_dir); - return Ok(None); - } - Ok(Some(lib_dir)) + if let Some(prefix) = wolfssl_prefix() { + Ok(Some(format!("{}/lib", prefix))) } else { let repo_lib_dir = wolfssl_repo_lib_dir()?; if Path::new(&repo_lib_dir).exists() { From 34753a8535edbfbeee41350531086b44ea53e408 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 31 Aug 2026 09:39:50 -0400 Subject: [PATCH 2/6] Rust wrapper: look for either lib or lib64 under WOLFSSL_PREFIX --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 69 ++++++++++++++++--------- 1 file changed, 46 insertions(+), 23 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 5feec53959..76a043fca0 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -38,39 +38,61 @@ fn wolfssl_repo_lib_dir() -> Result { Ok(format!("{}/src/.libs", wolfssl_repo_base_dir()?)) } -/// Returns the validated `WOLFSSL_PREFIX` installation prefix, if usable. +/// Directories located under a validated `WOLFSSL_PREFIX` installation. +struct WolfsslPrefixDirs { + include: String, + lib: String, +} + +/// Returns the directories of the `WOLFSSL_PREFIX` installation, if usable. /// /// A prefix is only accepted if it provides both halves of an installation: -/// a `lib` directory and an `include/wolfssl` directory. A prefix holding -/// only one of them is rejected outright, so that the headers and the library -/// we build against always come from the same place. +/// a library directory (`lib` or `lib64`) and an `include/wolfssl` directory. +/// A prefix holding only one of them is rejected outright, so that the headers +/// and the library we build against always come from the same place. /// /// The result is computed once and cached, so any warning is printed once. -fn wolfssl_prefix() -> Option<&'static str> { - static PREFIX: OnceLock> = OnceLock::new(); - PREFIX.get_or_init(compute_wolfssl_prefix).as_deref() +fn wolfssl_prefix_dirs() -> Option<&'static WolfsslPrefixDirs> { + static DIRS: OnceLock> = OnceLock::new(); + DIRS.get_or_init(compute_wolfssl_prefix_dirs).as_ref() } /// Read `WOLFSSL_PREFIX` from the environment and validate its layout. /// -/// Returns `None` (after warning) if the variable is unset, malformed, or -/// does not point at a directory containing both `lib` and `include/wolfssl`. -fn compute_wolfssl_prefix() -> Option { +/// Returns `None` (after warning) if the variable is unset, malformed, or does +/// not point at a directory containing both `include/wolfssl` and a library +/// directory. +fn compute_wolfssl_prefix_dirs() -> Option { let prefix = env::var("WOLFSSL_PREFIX").ok()?; if prefix.is_empty() || prefix.contains('\n') { println!("cargo:warning=ignoring WOLFSSL_PREFIX"); return None; } let prefix_path = Path::new(&prefix); - for subdir in [prefix_path.join("lib"), - prefix_path.join("include").join("wolfssl")] { - if !subdir.is_dir() { - println!("cargo:warning=ignoring WOLFSSL_PREFIX: {} is not a directory", - subdir.display()); - return None; - } + + let include_dir = prefix_path.join("include"); + if !include_dir.join("wolfssl").is_dir() { + println!("cargo:warning=ignoring WOLFSSL_PREFIX: {} is not a directory", + include_dir.join("wolfssl").display()); + return None; } - Some(prefix) + + // Installations are found under either lib/ or lib64/ depending on the + // platform and how wolfSSL was configured. + let lib_names = ["lib", "lib64"]; + let Some(lib_dir) = lib_names.iter() + .map(|name| prefix_path.join(name)) + .find(|dir| dir.is_dir()) else { + println!("cargo:warning=ignoring WOLFSSL_PREFIX: none of {} are directories", + lib_names.map(|name| prefix_path.join(name).display().to_string()) + .join(", ")); + return None; + }; + + Some(WolfsslPrefixDirs { + include: include_dir.display().to_string(), + lib: lib_dir.display().to_string(), + }) } /// Returns the include directory for wolfssl headers. @@ -78,8 +100,8 @@ fn compute_wolfssl_prefix() -> Option { /// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/include`. /// Otherwise falls back to the repo root if it exists (for in-tree host builds). fn wolfssl_include_dir() -> Result> { - if let Some(prefix) = wolfssl_prefix() { - Ok(Some(format!("{}/include", prefix))) + if let Some(dirs) = wolfssl_prefix_dirs() { + Ok(Some(dirs.include.clone())) } else { let base = wolfssl_repo_base_dir()?; let base_path = Path::new(&base); @@ -96,11 +118,12 @@ fn wolfssl_include_dir() -> Result> { /// Returns the library directory for libwolfssl. /// -/// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/lib`. +/// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/lib` or +/// `{WOLFSSL_PREFIX}/lib64`, whichever exists. /// Otherwise falls back to the in-tree build output directory if it exists. fn wolfssl_lib_dir() -> Result> { - if let Some(prefix) = wolfssl_prefix() { - Ok(Some(format!("{}/lib", prefix))) + if let Some(dirs) = wolfssl_prefix_dirs() { + Ok(Some(dirs.lib.clone())) } else { let repo_lib_dir = wolfssl_repo_lib_dir()?; if Path::new(&repo_lib_dir).exists() { From 8d84d0eb96c6f3386605e66d5fd1eb66e29dddcf Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Mon, 31 Aug 2026 09:44:37 -0400 Subject: [PATCH 3/6] Rust wrapper: rebuild if WOLFSSL_PREFIX env var changes --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 76a043fca0..2101d737ce 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -63,6 +63,7 @@ fn wolfssl_prefix_dirs() -> Option<&'static WolfsslPrefixDirs> { /// not point at a directory containing both `include/wolfssl` and a library /// directory. fn compute_wolfssl_prefix_dirs() -> Option { + println!("cargo:rerun-if-env-changed=WOLFSSL_PREFIX"); let prefix = env::var("WOLFSSL_PREFIX").ok()?; if prefix.is_empty() || prefix.contains('\n') { println!("cargo:warning=ignoring WOLFSSL_PREFIX"); From ca707535c76116d023dcea538ccbf7adcbcd3394 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Wed, 2 Sep 2026 10:31:35 -0400 Subject: [PATCH 4/6] Rust wrapper: check for wolfSSL library file under WOLFSSL_PREFIX --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 39 +++++++++++++++++-------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 2101d737ce..0fccf32e8d 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -38,6 +38,20 @@ fn wolfssl_repo_lib_dir() -> Result { Ok(format!("{}/src/.libs", wolfssl_repo_base_dir()?)) } +/// wolfSSL library file names to look for. +const WOLFSSL_LIB_FILES: [&str; 3] = + ["libwolfssl.so", "libwolfssl.dylib", "libwolfssl.a"]; + +/// Returns the name of the wolfSSL library file present in `dir`, if any. +fn wolfssl_lib_file(dir: &Path) -> Option<&'static str> { + WOLFSSL_LIB_FILES.into_iter().find(|name| dir.join(name).exists()) +} + +/// Returns true if `dir` holds a shared wolfSSL library. +fn has_shared_wolfssl_lib(dir: &Path) -> bool { + matches!(wolfssl_lib_file(dir), Some("libwolfssl.so") | Some("libwolfssl.dylib")) +} + /// Directories located under a validated `WOLFSSL_PREFIX` installation. struct WolfsslPrefixDirs { include: String, @@ -47,7 +61,8 @@ struct WolfsslPrefixDirs { /// Returns the directories of the `WOLFSSL_PREFIX` installation, if usable. /// /// A prefix is only accepted if it provides both halves of an installation: -/// a library directory (`lib` or `lib64`) and an `include/wolfssl` directory. +/// the wolfSSL library under `lib` or `lib64`, and an `include/wolfssl` +/// directory. /// A prefix holding only one of them is rejected outright, so that the headers /// and the library we build against always come from the same place. /// @@ -60,8 +75,8 @@ fn wolfssl_prefix_dirs() -> Option<&'static WolfsslPrefixDirs> { /// Read `WOLFSSL_PREFIX` from the environment and validate its layout. /// /// Returns `None` (after warning) if the variable is unset, malformed, or does -/// not point at a directory containing both `include/wolfssl` and a library -/// directory. +/// not point at a directory containing both `include/wolfssl` and the wolfSSL +/// library file. fn compute_wolfssl_prefix_dirs() -> Option { println!("cargo:rerun-if-env-changed=WOLFSSL_PREFIX"); let prefix = env::var("WOLFSSL_PREFIX").ok()?; @@ -78,15 +93,17 @@ fn compute_wolfssl_prefix_dirs() -> Option { return None; } - // Installations are found under either lib/ or lib64/ depending on the - // platform and how wolfSSL was configured. + // Installations put the library under either lib/ or lib64/ depending on + // the platform and how wolfSSL was configured. Require the library file + // itself to be present, not merely the directory. let lib_names = ["lib", "lib64"]; let Some(lib_dir) = lib_names.iter() .map(|name| prefix_path.join(name)) - .find(|dir| dir.is_dir()) else { - println!("cargo:warning=ignoring WOLFSSL_PREFIX: none of {} are directories", + .find(|dir| wolfssl_lib_file(dir).is_some()) else { + println!("cargo:warning=ignoring WOLFSSL_PREFIX: no {} found in {}", + WOLFSSL_LIB_FILES.join(" / "), lib_names.map(|name| prefix_path.join(name).display().to_string()) - .join(", ")); + .join(" or ")); return None; }; @@ -120,7 +137,7 @@ fn wolfssl_include_dir() -> Result> { /// Returns the library directory for libwolfssl. /// /// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/lib` or -/// `{WOLFSSL_PREFIX}/lib64`, whichever exists. +/// `{WOLFSSL_PREFIX}/lib64`, whichever holds the library. /// Otherwise falls back to the in-tree build output directory if it exists. fn wolfssl_lib_dir() -> Result> { if let Some(dirs) = wolfssl_prefix_dirs() { @@ -334,9 +351,7 @@ fn setup_wolfssl_link() -> Result<()> { println!("cargo:rustc-link-search={}", lib_dir); // Prefer a shared library if present, otherwise fall back to static. - let has_shared = Path::new(&lib_dir).join("libwolfssl.so").exists() - || Path::new(&lib_dir).join("libwolfssl.dylib").exists(); - if has_shared { + if has_shared_wolfssl_lib(Path::new(&lib_dir)) { println!("cargo:rustc-link-lib=wolfssl"); // Only set rpath where a dynamic linker exists (not bare-metal). let target = env::var("TARGET").unwrap(); From 447ded43722163487a37890ad05b440162dc34ed Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Wed, 2 Sep 2026 13:41:19 -0400 Subject: [PATCH 5/6] Rust wrapper: build.rs: check for MSVC/Cygwin library names --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 80 ++++++++++++++++++------- 1 file changed, 58 insertions(+), 22 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 0fccf32e8d..8d94b27b4a 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -38,18 +38,50 @@ fn wolfssl_repo_lib_dir() -> Result { Ok(format!("{}/src/.libs", wolfssl_repo_base_dir()?)) } -/// wolfSSL library file names to look for. -const WOLFSSL_LIB_FILES: [&str; 3] = - ["libwolfssl.so", "libwolfssl.dylib", "libwolfssl.a"]; - -/// Returns the name of the wolfSSL library file present in `dir`, if any. -fn wolfssl_lib_file(dir: &Path) -> Option<&'static str> { - WOLFSSL_LIB_FILES.into_iter().find(|name| dir.join(name).exists()) +/// How a wolfSSL library file has to be handed to the linker. +#[derive(Clone, Copy, PartialEq)] +enum WolfsslLibKind { + /// Shared object resolved at run time through a library search path, so + /// an rpath entry is needed to run against a non-default prefix. + SharedObject, + /// Windows import library or DLL: linked dynamically, but the loader + /// finds the DLL via PATH rather than an rpath. + ImportLib, + /// Static archive, linked with the `static=` link kind. + StaticLib, } -/// Returns true if `dir` holds a shared wolfSSL library. -fn has_shared_wolfssl_lib(dir: &Path) -> bool { - matches!(wolfssl_lib_file(dir), Some("libwolfssl.so") | Some("libwolfssl.dylib")) +/// wolfSSL library file names to look for, with the names produced by the +/// autotools, CMake and Visual Studio builds of the C library. Dynamic +/// variants come first so that a directory holding both prefers the shared +/// library, matching how the linker itself resolves `-lwolfssl`. +const WOLFSSL_LIB_FILES: [(&str, WolfsslLibKind); 7] = [ + /* ELF platforms: Linux, the BSDs, Solaris/illumos */ + ("libwolfssl.so", WolfsslLibKind::SharedObject), + /* macOS */ + ("libwolfssl.dylib", WolfsslLibKind::SharedObject), + /* MinGW / Cygwin import library for libwolfssl.dll */ + ("libwolfssl.dll.a", WolfsslLibKind::ImportLib), + /* MinGW / Cygwin DLL installed without an import library */ + ("libwolfssl.dll", WolfsslLibKind::ImportLib), + /* MSVC: static library, or the import library for wolfssl.dll */ + ("wolfssl.lib", WolfsslLibKind::ImportLib), + /* MSVC built through libtool, which keeps the "lib" prefix */ + ("libwolfssl.lib", WolfsslLibKind::ImportLib), + /* Static archive on every Unix-like platform and MinGW */ + ("libwolfssl.a", WolfsslLibKind::StaticLib), +]; + +/// Returns the kind of the wolfSSL library file present in `dir`, if any. +fn wolfssl_lib_kind(dir: &Path) -> Option { + WOLFSSL_LIB_FILES.into_iter() + .find(|(name, _)| dir.join(name).exists()) + .map(|(_, kind)| kind) +} + +/// Returns true if `dir` holds any wolfSSL library file. +fn has_wolfssl_lib(dir: &Path) -> bool { + wolfssl_lib_kind(dir).is_some() } /// Directories located under a validated `WOLFSSL_PREFIX` installation. @@ -99,9 +131,8 @@ fn compute_wolfssl_prefix_dirs() -> Option { let lib_names = ["lib", "lib64"]; let Some(lib_dir) = lib_names.iter() .map(|name| prefix_path.join(name)) - .find(|dir| wolfssl_lib_file(dir).is_some()) else { - println!("cargo:warning=ignoring WOLFSSL_PREFIX: no {} found in {}", - WOLFSSL_LIB_FILES.join(" / "), + .find(|dir| has_wolfssl_lib(dir)) else { + println!("cargo:warning=ignoring WOLFSSL_PREFIX: no wolfSSL library found in {}", lib_names.map(|name| prefix_path.join(name).display().to_string()) .join(" or ")); return None; @@ -350,16 +381,21 @@ fn setup_wolfssl_link() -> Result<()> { if let Some(lib_dir) = wolfssl_lib_dir()? { println!("cargo:rustc-link-search={}", lib_dir); - // Prefer a shared library if present, otherwise fall back to static. - if has_shared_wolfssl_lib(Path::new(&lib_dir)) { - println!("cargo:rustc-link-lib=wolfssl"); - // Only set rpath where a dynamic linker exists (not bare-metal). - let target = env::var("TARGET").unwrap(); - if !target.ends_with("-none-elf") { - println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir); + // Prefer a dynamic library if present, otherwise fall back to static. + match wolfssl_lib_kind(Path::new(&lib_dir)) { + Some(WolfsslLibKind::SharedObject) => { + println!("cargo:rustc-link-lib=wolfssl"); + // Only set rpath where a dynamic linker exists (not bare-metal). + let target = env::var("TARGET").unwrap(); + if !target.ends_with("-none-elf") { + println!("cargo:rustc-link-arg=-Wl,-rpath,{}", lib_dir); + } } - } else { - println!("cargo:rustc-link-lib=static=wolfssl"); + // The DLL is found through PATH at run time, so there is no rpath + // to set here. + Some(WolfsslLibKind::ImportLib) => println!("cargo:rustc-link-lib=wolfssl"), + Some(WolfsslLibKind::StaticLib) | None => + println!("cargo:rustc-link-lib=static=wolfssl"), } } else { // No local lib dir found; rely on whatever is installed system-wide. From 968c0b426eb253f7be14699fd0cd42cd91571bad Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Thu, 3 Sep 2026 08:49:08 -0400 Subject: [PATCH 6/6] Rust wrapper: fail crate build if WOLFSSL_PREFIX set but unusable --- wrapper/rust/wolfssl-wolfcrypt/build.rs | 55 ++++++++++++++++++------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/wrapper/rust/wolfssl-wolfcrypt/build.rs b/wrapper/rust/wolfssl-wolfcrypt/build.rs index 8d94b27b4a..0b6786abf1 100644 --- a/wrapper/rust/wolfssl-wolfcrypt/build.rs +++ b/wrapper/rust/wolfssl-wolfcrypt/build.rs @@ -90,39 +90,62 @@ struct WolfsslPrefixDirs { lib: String, } -/// Returns the directories of the `WOLFSSL_PREFIX` installation, if usable. +/// Returns the directories of the `WOLFSSL_PREFIX` installation, if set. /// /// A prefix is only accepted if it provides both halves of an installation: /// the wolfSSL library under `lib` or `lib64`, and an `include/wolfssl` /// directory. -/// A prefix holding only one of them is rejected outright, so that the headers +/// A prefix holding only one of them fails the build, so that the headers /// and the library we build against always come from the same place. /// -/// The result is computed once and cached, so any warning is printed once. +/// Returns `None` only when `WOLFSSL_PREFIX` is unset or empty, in which case +/// the build falls back to the wolfSSL repository containing this crate. +/// +/// The result is computed once and cached, so any message is printed once. fn wolfssl_prefix_dirs() -> Option<&'static WolfsslPrefixDirs> { static DIRS: OnceLock> = OnceLock::new(); DIRS.get_or_init(compute_wolfssl_prefix_dirs).as_ref() } +/// Report an unusable `WOLFSSL_PREFIX` and fail the build. +/// +/// A prefix that is set but does not hold an installation is a mistake in the +/// caller's environment. Falling back to the in-tree build would hide it and +/// silently build against a different wolfSSL than the one asked for, so fail +/// instead. +fn wolfssl_prefix_error(prefix: &str, reason: &str) -> ! { + eprintln!("error: WOLFSSL_PREFIX is set to \"{}\" but {}.", prefix, reason); + eprintln!(" Set WOLFSSL_PREFIX to the prefix of a wolfSSL installation \ + providing include/wolfssl and the wolfSSL library under lib or \ + lib64, or unset it to build against the wolfSSL repository \ + containing this crate."); + std::process::exit(1); +} + /// Read `WOLFSSL_PREFIX` from the environment and validate its layout. /// -/// Returns `None` (after warning) if the variable is unset, malformed, or does -/// not point at a directory containing both `include/wolfssl` and the wolfSSL -/// library file. +/// Returns `None` if the variable is unset or empty. A non-empty value that +/// does not point at a directory containing both `include/wolfssl` and the +/// wolfSSL library file fails the build. fn compute_wolfssl_prefix_dirs() -> Option { println!("cargo:rerun-if-env-changed=WOLFSSL_PREFIX"); let prefix = env::var("WOLFSSL_PREFIX").ok()?; - if prefix.is_empty() || prefix.contains('\n') { - println!("cargo:warning=ignoring WOLFSSL_PREFIX"); + if prefix.is_empty() { + // An empty value is treated the same as unset. return None; } + if prefix.contains('\n') { + // A newline would let the value inject further cargo directives into + // the link search path we print below. + wolfssl_prefix_error(&prefix, "its value contains a newline"); + } let prefix_path = Path::new(&prefix); let include_dir = prefix_path.join("include"); if !include_dir.join("wolfssl").is_dir() { - println!("cargo:warning=ignoring WOLFSSL_PREFIX: {} is not a directory", - include_dir.join("wolfssl").display()); - return None; + wolfssl_prefix_error(&prefix, + &format!("{} is not a directory", + include_dir.join("wolfssl").display())); } // Installations put the library under either lib/ or lib64/ depending on @@ -132,10 +155,12 @@ fn compute_wolfssl_prefix_dirs() -> Option { let Some(lib_dir) = lib_names.iter() .map(|name| prefix_path.join(name)) .find(|dir| has_wolfssl_lib(dir)) else { - println!("cargo:warning=ignoring WOLFSSL_PREFIX: no wolfSSL library found in {}", - lib_names.map(|name| prefix_path.join(name).display().to_string()) - .join(" or ")); - return None; + wolfssl_prefix_error(&prefix, + &format!("no wolfSSL library was found in {}", + lib_names.map(|name| prefix_path.join(name) + .display() + .to_string()) + .join(" or "))); }; Some(WolfsslPrefixDirs {