@@ -25,37 +25,146 @@ fn run_build() -> Result<()> {
2525 Ok ( ( ) )
2626}
2727
28- fn wrapper_dir ( ) -> Result < String > {
28+ fn crate_dir ( ) -> Result < String > {
2929 Ok ( std:: env:: current_dir ( ) ?. display ( ) . to_string ( ) )
3030}
3131
32- fn wolfssl_base_dir ( ) -> Result < String > {
33- Ok ( format ! ( "{}/../../.." , wrapper_dir ( ) ?) )
32+ fn wolfssl_repo_base_dir ( ) -> Result < String > {
33+ Ok ( format ! ( "{}/../../.." , crate_dir ( ) ?) )
3434}
3535
36- fn wolfssl_lib_dir ( ) -> Result < String > {
37- Ok ( format ! ( "{}/src/.libs" , wolfssl_base_dir( ) ?) )
36+ fn wolfssl_repo_lib_dir ( ) -> Result < String > {
37+ Ok ( format ! ( "{}/src/.libs" , wolfssl_repo_base_dir( ) ?) )
38+ }
39+
40+ /// Returns the include directory for wolfssl headers.
41+ ///
42+ /// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/include`.
43+ /// Otherwise falls back to the repo root if it exists (for in-tree host builds).
44+ fn wolfssl_include_dir ( ) -> Result < Option < String > > {
45+ if let Ok ( prefix) = env:: var ( "WOLFSSL_PREFIX" ) {
46+ Ok ( Some ( format ! ( "{}/include" , prefix) ) )
47+ } else if Path :: new ( & wolfssl_repo_base_dir ( ) ?) . exists ( ) {
48+ Ok ( Some ( wolfssl_repo_base_dir ( ) ?) )
49+ } else {
50+ Ok ( None )
51+ }
52+ }
53+
54+ /// Returns the library directory for libwolfssl.
55+ ///
56+ /// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/lib`.
57+ /// Otherwise falls back to the in-tree build output directory if it exists.
58+ fn wolfssl_lib_dir ( ) -> Result < Option < String > > {
59+ if let Ok ( prefix) = env:: var ( "WOLFSSL_PREFIX" ) {
60+ Ok ( Some ( format ! ( "{}/lib" , prefix) ) )
61+ } else if Path :: new ( & wolfssl_repo_lib_dir ( ) ?) . exists ( ) {
62+ Ok ( Some ( wolfssl_repo_lib_dir ( ) ?) )
63+ } else {
64+ Ok ( None )
65+ }
3866}
3967
4068fn bindings_path ( ) -> String {
4169 PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) . join ( "bindings.rs" ) . display ( ) . to_string ( )
4270}
4371
44- /// Generate Rust bindings for the wolfssl C library using bindgen .
72+ /// Map a Rust target triple to the equivalent clang target triple .
4573///
46- /// This function:
47- /// 1. Sets up the library and include paths
48- /// 2. Configures the build environment
49- /// 3. Generates Rust bindings using bindgen
50- /// 4. Writes the bindings to a file
74+ /// Rust triples embed ISA extensions in the arch component
75+ /// (e.g. `riscv64imac-unknown-none-elf`) while clang uses only the base arch
76+ /// (e.g. `riscv64-unknown-elf`). Bare-metal targets use `<arch>-<vendor>-elf`
77+ /// in clang convention.
78+ fn rust_target_to_clang_target ( rust_target : & str ) -> String {
79+ let parts: Vec < & str > = rust_target. splitn ( 4 , '-' ) . collect ( ) ;
80+ if parts. len ( ) < 3 {
81+ return rust_target. to_string ( ) ;
82+ }
83+
84+ // Strip ISA extensions: riscv64imac → riscv64, riscv32imac → riscv32
85+ let arch = if parts[ 0 ] . starts_with ( "riscv64" ) {
86+ "riscv64"
87+ } else if parts[ 0 ] . starts_with ( "riscv32" ) {
88+ "riscv32"
89+ } else {
90+ parts[ 0 ]
91+ } ;
92+
93+ let vendor = parts[ 1 ] ;
94+ let os = parts[ 2 ] ;
95+ let abi = parts. get ( 3 ) . copied ( ) . unwrap_or ( "" ) ;
96+
97+ // Bare-metal: (os=none, abi=elf) → <arch>-<vendor>-elf
98+ if os == "none" && abi == "elf" {
99+ format ! ( "{}-{}-elf" , arch, vendor)
100+ } else {
101+ format ! ( "{}-{}-{}-{}" , arch, vendor, os, abi)
102+ }
103+ }
104+
105+ /// Return the sysroot path for a bare-metal clang target triple, if it exists.
106+ ///
107+ /// Queries the cross-compiler for its sysroot via `--print-sysroot` rather
108+ /// than assuming a fixed install prefix. Tries the candidate compiler names
109+ /// `<arch>-<vendor>-elf-gcc` and `<arch>-elf-gcc` (vendor omitted) in order.
110+ /// Returns `None` if no suitable compiler is found or its sysroot is invalid.
111+ fn bare_metal_sysroot ( clang_target : & str ) -> Option < String > {
112+ let parts: Vec < & str > = clang_target. splitn ( 3 , '-' ) . collect ( ) ;
113+ if parts. len ( ) < 3 || !clang_target. ends_with ( "-elf" ) {
114+ return None ;
115+ }
116+ let ( arch, vendor) = ( parts[ 0 ] , parts[ 1 ] ) ;
117+ let candidates = [
118+ format ! ( "{}-{}-elf-gcc" , arch, vendor) ,
119+ format ! ( "{}-elf-gcc" , arch) ,
120+ ] ;
121+ for compiler in & candidates {
122+ if let Ok ( output) = std:: process:: Command :: new ( compiler)
123+ . arg ( "--print-sysroot" )
124+ . output ( )
125+ && output. status . success ( ) {
126+ let sysroot = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
127+ if !sysroot. is_empty ( ) && sysroot != "/" && Path :: new ( & sysroot) . exists ( ) {
128+ return Some ( sysroot) ;
129+ }
130+ }
131+ }
132+ None
133+ }
134+
135+ /// Generate Rust bindings for the wolfssl C library using bindgen.
51136///
52137/// Returns `Ok(())` if successful, or an error if binding generation fails.
53138fn generate_bindings ( ) -> Result < ( ) > {
54- let bindings = bindgen:: Builder :: default ( )
139+ let mut builder = bindgen:: Builder :: default ( )
55140 . header ( "headers.h" )
56- . clang_arg ( format ! ( "-I{}" , wolfssl_base_dir( ) ?) )
57141 . parse_callbacks ( Box :: new ( bindgen:: CargoCallbacks :: new ( ) ) )
58- . use_core ( )
142+ . use_core ( ) ;
143+
144+ if let Some ( include_dir) = wolfssl_include_dir ( ) ? {
145+ builder = builder. clang_arg ( format ! ( "-I{}" , include_dir) ) ;
146+ }
147+
148+ // When cross-compiling, tell clang the target so it generates correct
149+ // type layouts and evaluates architecture-specific preprocessor guards.
150+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
151+ let host = env:: var ( "HOST" ) . unwrap ( ) ;
152+ if target != host && target. ends_with ( "-none-elf" ) {
153+ let clang_target = rust_target_to_clang_target ( & target) ;
154+ builder = builder. clang_arg ( format ! ( "--target={}" , clang_target) ) ;
155+
156+ // For bare-metal targets, add the toolchain C runtime headers
157+ // (newlib's time.h etc.) using -idirafter so they appear after
158+ // clang's own built-in includes. This lets clang's stdatomic.h
159+ // take priority over newlib's incompatible version.
160+ if let Some ( sysroot) = bare_metal_sysroot ( & clang_target) {
161+ builder = builder
162+ . clang_arg ( "-ffreestanding" )
163+ . clang_arg ( format ! ( "-idirafter{}/include" , sysroot) ) ;
164+ }
165+ }
166+
167+ let bindings = builder
59168 . generate ( )
60169 . map_err ( |_| io:: Error :: other ( "Failed to generate bindings" ) ) ?;
61170
@@ -147,18 +256,25 @@ fn generate_fips_aliases() -> Result<()> {
147256///
148257/// Returns `Ok(())` if successful, or an error if any step fails.
149258fn setup_wolfssl_link ( ) -> Result < ( ) > {
150- println ! ( "cargo:rustc-link-lib=wolfssl" ) ;
151-
152- // TODO: do we need this if only a static library is built?
153- // println!("cargo:rustc-link-lib=static=wolfssl");
154-
155- let build_in_repo = Path :: new ( & wolfssl_lib_dir ( ) ?) . exists ( ) ;
156- if build_in_repo {
157- // When the crate is built in the wolfssl repository, link with the
158- // locally build wolfssl library to allow testing any local changes
159- // and running unit tests even if library is not installed.
160- println ! ( "cargo:rustc-link-search={}" , wolfssl_lib_dir( ) ?) ;
161- println ! ( "cargo:rustc-link-arg=-Wl,-rpath,{}" , wolfssl_lib_dir( ) ?) ;
259+ if let Some ( lib_dir) = wolfssl_lib_dir ( ) ? {
260+ println ! ( "cargo:rustc-link-search={}" , lib_dir) ;
261+
262+ // Prefer a shared library if present, otherwise fall back to static.
263+ let has_shared = Path :: new ( & lib_dir) . join ( "libwolfssl.so" ) . exists ( )
264+ || Path :: new ( & lib_dir) . join ( "libwolfssl.dylib" ) . exists ( ) ;
265+ if has_shared {
266+ println ! ( "cargo:rustc-link-lib=wolfssl" ) ;
267+ // Only set rpath where a dynamic linker exists (not bare-metal).
268+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
269+ if !target. ends_with ( "-none-elf" ) {
270+ println ! ( "cargo:rustc-link-arg=-Wl,-rpath,{}" , lib_dir) ;
271+ }
272+ } else {
273+ println ! ( "cargo:rustc-link-lib=static=wolfssl" ) ;
274+ }
275+ } else {
276+ // No local lib dir found; rely on whatever is installed system-wide.
277+ println ! ( "cargo:rustc-link-lib=wolfssl" ) ;
162278 }
163279
164280 Ok ( ( ) )
0 commit comments