mirror of https://github.com/wolfSSL/wolfssl.git
Merge pull request #11312 from holtrop-wolfssl/f-10086
Rust wrapper: build.rs: validate WOLFSSL_PREFIX oncepull/11270/head
commit
2d85272804
|
|
@ -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,144 @@ fn wolfssl_repo_lib_dir() -> Result<String> {
|
|||
Ok(format!("{}/src/.libs", wolfssl_repo_base_dir()?))
|
||||
}
|
||||
|
||||
fn wolfssl_user_prefix() -> Option<String> {
|
||||
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,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
/// 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<WolfsslLibKind> {
|
||||
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.
|
||||
struct WolfsslPrefixDirs {
|
||||
include: String,
|
||||
lib: String,
|
||||
}
|
||||
|
||||
/// 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 fails the build, so that the headers
|
||||
/// and the library we build against always come from the same place.
|
||||
///
|
||||
/// 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<Option<WolfsslPrefixDirs>> = 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` 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<WolfsslPrefixDirs> {
|
||||
println!("cargo:rerun-if-env-changed=WOLFSSL_PREFIX");
|
||||
let prefix = env::var("WOLFSSL_PREFIX").ok()?;
|
||||
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() {
|
||||
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
|
||||
// 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| has_wolfssl_lib(dir)) else {
|
||||
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 {
|
||||
include: include_dir.display().to_string(),
|
||||
lib: lib_dir.display().to_string(),
|
||||
})
|
||||
}
|
||||
|
||||
/// 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<Option<String>> {
|
||||
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(dirs) = wolfssl_prefix_dirs() {
|
||||
Ok(Some(dirs.include.clone()))
|
||||
} else {
|
||||
let base = wolfssl_repo_base_dir()?;
|
||||
let base_path = Path::new(&base);
|
||||
|
|
@ -80,17 +192,12 @@ fn wolfssl_include_dir() -> Result<Option<String>> {
|
|||
|
||||
/// Returns the library directory for libwolfssl.
|
||||
///
|
||||
/// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/lib`.
|
||||
/// If `WOLFSSL_PREFIX` is usable, returns `{WOLFSSL_PREFIX}/lib` or
|
||||
/// `{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<Option<String>> {
|
||||
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(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() {
|
||||
|
|
@ -299,18 +406,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.
|
||||
let has_shared = Path::new(&lib_dir).join("libwolfssl.so").exists()
|
||||
|| Path::new(&lib_dir).join("libwolfssl.dylib").exists();
|
||||
if has_shared {
|
||||
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.
|
||||
|
|
|
|||
Loading…
Reference in New Issue