@@ -25,37 +25,166 @@ 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+ let include_dir = format ! ( "{}/include" , prefix) ;
47+ let wolfssl_dir = Path :: new ( & include_dir) . join ( "wolfssl" ) ;
48+ if !wolfssl_dir. is_dir ( ) {
49+ println ! ( "cargo:warning=WOLFSSL_PREFIX is set but {} does not exist" , wolfssl_dir. display( ) ) ;
50+ return Ok ( None ) ;
51+ }
52+ Ok ( Some ( include_dir) )
53+ } else {
54+ let base = wolfssl_repo_base_dir ( ) ?;
55+ let base_path = Path :: new ( & base) ;
56+ // Treat this as an in-tree wolfSSL repo only if the expected layout exists.
57+ let wolfssl_dir = base_path. join ( "wolfssl" ) ;
58+ let wolfssl_options = wolfssl_dir. join ( "options.h" ) ;
59+ if wolfssl_options. is_file ( ) {
60+ Ok ( Some ( base) )
61+ } else {
62+ Ok ( None )
63+ }
64+ }
65+ }
66+
67+ /// Returns the library directory for libwolfssl.
68+ ///
69+ /// If `WOLFSSL_PREFIX` is set, returns `{WOLFSSL_PREFIX}/lib`.
70+ /// Otherwise falls back to the in-tree build output directory if it exists.
71+ fn wolfssl_lib_dir ( ) -> Result < Option < String > > {
72+ if let Ok ( prefix) = env:: var ( "WOLFSSL_PREFIX" ) {
73+ Ok ( Some ( format ! ( "{}/lib" , prefix) ) )
74+ } else {
75+ let repo_lib_dir = wolfssl_repo_lib_dir ( ) ?;
76+ if Path :: new ( & repo_lib_dir) . exists ( ) {
77+ Ok ( Some ( repo_lib_dir) )
78+ } else {
79+ Ok ( None )
80+ }
81+ }
3882}
3983
4084fn bindings_path ( ) -> String {
4185 PathBuf :: from ( env:: var ( "OUT_DIR" ) . unwrap ( ) ) . join ( "bindings.rs" ) . display ( ) . to_string ( )
4286}
4387
44- /// Generate Rust bindings for the wolfssl C library using bindgen .
88+ /// Map a Rust target triple to the equivalent clang target triple .
4589///
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
90+ /// Rust triples embed ISA extensions in the arch component
91+ /// (e.g. `riscv64imac-unknown-none-elf`) while clang uses only the base arch
92+ /// (e.g. `riscv64-unknown-elf`). Bare-metal targets use `<arch>-<vendor>-elf`
93+ /// in clang convention.
94+ fn rust_target_to_clang_target ( rust_target : & str ) -> String {
95+ let parts: Vec < & str > = rust_target. splitn ( 4 , '-' ) . collect ( ) ;
96+ if parts. len ( ) < 3 {
97+ return rust_target. to_string ( ) ;
98+ }
99+
100+ // Strip ISA extensions: riscv64imac → riscv64, riscv32imac → riscv32
101+ let arch = if parts[ 0 ] . starts_with ( "riscv64" ) {
102+ "riscv64"
103+ } else if parts[ 0 ] . starts_with ( "riscv32" ) {
104+ "riscv32"
105+ } else {
106+ parts[ 0 ]
107+ } ;
108+
109+ let vendor = parts[ 1 ] ;
110+ let os = parts[ 2 ] ;
111+ let abi = parts. get ( 3 ) . copied ( ) . unwrap_or ( "" ) ;
112+
113+ // Bare-metal: (os=none, abi=elf) → <arch>-<vendor>-elf
114+ if os == "none" && abi == "elf" {
115+ format ! ( "{}-{}-elf" , arch, vendor)
116+ } else if abi. is_empty ( ) {
117+ format ! ( "{}-{}-{}" , arch, vendor, os)
118+ } else {
119+ format ! ( "{}-{}-{}-{}" , arch, vendor, os, abi)
120+ }
121+ }
122+
123+ /// Return the sysroot path for a bare-metal clang target triple, if it exists.
124+ ///
125+ /// Queries the cross-compiler for its sysroot via `--print-sysroot` rather
126+ /// than assuming a fixed install prefix. Tries the candidate compiler names
127+ /// `<arch>-<vendor>-elf-gcc` and `<arch>-elf-gcc` (vendor omitted) in order.
128+ /// Returns `None` if no suitable compiler is found or its sysroot is invalid.
129+ fn bare_metal_sysroot ( clang_target : & str ) -> Option < String > {
130+ let parts: Vec < & str > = clang_target. splitn ( 3 , '-' ) . collect ( ) ;
131+ if parts. len ( ) < 3 || !clang_target. ends_with ( "-elf" ) {
132+ return None ;
133+ }
134+ let ( arch, vendor) = ( parts[ 0 ] , parts[ 1 ] ) ;
135+ let candidates = [
136+ format ! ( "{}-{}-elf-gcc" , arch, vendor) ,
137+ format ! ( "{}-elf-gcc" , arch) ,
138+ ] ;
139+ for compiler in & candidates {
140+ if let Ok ( output) = std:: process:: Command :: new ( compiler)
141+ . arg ( "--print-sysroot" )
142+ . output ( )
143+ && output. status . success ( ) {
144+ let sysroot = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
145+ if !sysroot. is_empty ( ) && sysroot != "/" && Path :: new ( & sysroot) . exists ( ) {
146+ return Some ( sysroot) ;
147+ }
148+ }
149+ }
150+ None
151+ }
152+
153+ /// Generate Rust bindings for the wolfssl C library using bindgen.
51154///
52155/// Returns `Ok(())` if successful, or an error if binding generation fails.
53156fn generate_bindings ( ) -> Result < ( ) > {
54- let bindings = bindgen:: Builder :: default ( )
157+ let mut builder = bindgen:: Builder :: default ( )
55158 . header ( "headers.h" )
56- . clang_arg ( format ! ( "-I{}" , wolfssl_base_dir( ) ?) )
57159 . parse_callbacks ( Box :: new ( bindgen:: CargoCallbacks :: new ( ) ) )
58- . use_core ( )
160+ . use_core ( ) ;
161+
162+ if let Some ( include_dir) = wolfssl_include_dir ( ) ? {
163+ builder = builder. clang_arg ( format ! ( "-I{}" , include_dir) ) ;
164+ }
165+
166+ // When cross-compiling, tell clang the target so it generates correct
167+ // type layouts and evaluates architecture-specific preprocessor guards.
168+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
169+ let host = env:: var ( "HOST" ) . unwrap ( ) ;
170+ if target != host {
171+ let clang_target = rust_target_to_clang_target ( & target) ;
172+ builder = builder. clang_arg ( format ! ( "--target={}" , clang_target) ) ;
173+
174+ if target. ends_with ( "-none-elf" ) {
175+ // For bare-metal targets, add the toolchain C runtime headers
176+ // (newlib's time.h etc.) using -idirafter so they appear after
177+ // clang's own built-in includes. This lets clang's stdatomic.h
178+ // take priority over newlib's incompatible version.
179+ if let Some ( sysroot) = bare_metal_sysroot ( & clang_target) {
180+ builder = builder
181+ . clang_arg ( "-ffreestanding" )
182+ . clang_arg ( format ! ( "-idirafter{}/include" , sysroot) ) ;
183+ }
184+ }
185+ }
186+
187+ let bindings = builder
59188 . generate ( )
60189 . map_err ( |_| io:: Error :: other ( "Failed to generate bindings" ) ) ?;
61190
@@ -147,18 +276,25 @@ fn generate_fips_aliases() -> Result<()> {
147276///
148277/// Returns `Ok(())` if successful, or an error if any step fails.
149278fn 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( ) ?) ;
279+ if let Some ( lib_dir) = wolfssl_lib_dir ( ) ? {
280+ println ! ( "cargo:rustc-link-search={}" , lib_dir) ;
281+
282+ // Prefer a shared library if present, otherwise fall back to static.
283+ let has_shared = Path :: new ( & lib_dir) . join ( "libwolfssl.so" ) . exists ( )
284+ || Path :: new ( & lib_dir) . join ( "libwolfssl.dylib" ) . exists ( ) ;
285+ if has_shared {
286+ println ! ( "cargo:rustc-link-lib=wolfssl" ) ;
287+ // Only set rpath where a dynamic linker exists (not bare-metal).
288+ let target = env:: var ( "TARGET" ) . unwrap ( ) ;
289+ if !target. ends_with ( "-none-elf" ) {
290+ println ! ( "cargo:rustc-link-arg=-Wl,-rpath,{}" , lib_dir) ;
291+ }
292+ } else {
293+ println ! ( "cargo:rustc-link-lib=static=wolfssl" ) ;
294+ }
295+ } else {
296+ // No local lib dir found; rely on whatever is installed system-wide.
297+ println ! ( "cargo:rustc-link-lib=wolfssl" ) ;
162298 }
163299
164300 Ok ( ( ) )
0 commit comments