22compile_error ! ( "One of the features `pio` or `native` must be selected." ) ;
33use std:: iter:: once;
44
5+ use crate :: native:: cargo_driver:: chip:: Chip ;
56use anyhow:: * ;
67use bindgen:: callbacks:: { IntKind , ParseCallbacks } ;
78use common:: * ;
89use embuild:: bindgen:: BindgenExt ;
910use embuild:: utils:: OsStrExt ;
1011use embuild:: { bindgen as bindgen_utils, build, cargo, kconfig, path_buf} ;
12+ use std:: fs:: File ;
13+ use std:: io:: BufReader ;
14+ use std:: io:: { BufRead , Write } ;
15+ use std:: str:: FromStr ;
1116
1217mod common;
1318mod config;
@@ -48,6 +53,132 @@ impl ParseCallbacks for BindgenCallbacks {
4853 }
4954}
5055
56+ const STATIC_INLINE : & str = "static_inlines" ;
57+
58+ fn static_inlines_c ( ) -> String {
59+ format ! ( "{}.c" , STATIC_INLINE )
60+ }
61+
62+ fn static_inlines_o ( ) -> String {
63+ format ! ( "{}.o" , STATIC_INLINE )
64+ }
65+
66+ fn static_inlines_tmp ( ) -> String {
67+ format ! ( "{}_tmp.c" , STATIC_INLINE )
68+ }
69+
70+ fn static_inlines_a ( ) -> String {
71+ format ! ( "lib{}.a" , STATIC_INLINE )
72+ }
73+
74+ // TODO: The symbols from the components/esp_rom/<mcu>/ld are hard coded
75+ // addresses resolved during link time, rust linker cant find those symbols
76+ // and hence the inlines that depend on those dont work. Ignore them for now
77+ const IGNORE_STATIC_INLINES : [ & str ; 3 ] = [
78+ "_xtos_interrupt_enable__extern" ,
79+ "_xtos_interrupt_disable__extern" ,
80+ "esp_cpu_intr_get_handler_arg__extern" ,
81+ ] ;
82+
83+ fn strip_quotes ( args : & str ) -> Vec < String > {
84+ let mut out = vec ! [ ] ;
85+ for arg in args. split_whitespace ( ) {
86+ let mut chars = arg. chars ( ) ;
87+ let first = chars. next ( ) ;
88+ chars. next_back ( ) ;
89+ let trim = if first == Some ( '\"' ) {
90+ chars. as_str ( )
91+ } else {
92+ arg
93+ } ;
94+ out. push ( trim. to_string ( ) ) ;
95+ }
96+ out
97+ }
98+
99+ fn ignore_api ( api : & str ) -> bool {
100+ for ignore in IGNORE_STATIC_INLINES . iter ( ) {
101+ if api. contains ( ignore) {
102+ return true ;
103+ }
104+ }
105+ false
106+ }
107+
108+ fn process_static_inlines (
109+ build_output_args : & str ,
110+ clang_args : Vec < String > ,
111+ mcu : & str ,
112+ headers : Vec < std:: path:: PathBuf > ,
113+ ) -> anyhow:: Result < ( ) > {
114+ let chip = Chip :: from_str ( mcu) ?;
115+ let gcc = format ! ( "{}-gcc" , chip. gcc_toolchain( ) ) ;
116+ let ar = format ! ( "{}-gcc-ar" , chip. gcc_toolchain( ) ) ;
117+
118+ let out_dir_path = cargo:: out_dir ( ) ;
119+ let file = File :: open ( out_dir_path. join ( static_inlines_c ( ) ) ) . unwrap ( ) ;
120+ let mut tmp = File :: create ( out_dir_path. join ( static_inlines_tmp ( ) ) ) . unwrap ( ) ;
121+ let lines = BufReader :: new ( file) . lines ( ) ;
122+ for line in lines {
123+ let line = line. unwrap ( ) ;
124+ if !ignore_api ( & line) {
125+ tmp. write_all ( line. as_bytes ( ) ) ?;
126+ writeln ! ( tmp) ?;
127+ }
128+ }
129+ tmp. flush ( ) ?;
130+
131+ let mut gcc_cmd = std:: process:: Command :: new ( gcc) ;
132+ let mut gcc_args = gcc_cmd
133+ . arg ( "-mlongcalls" )
134+ . arg ( "-O" )
135+ . arg ( "-c" )
136+ . arg ( "-o" )
137+ . arg ( out_dir_path. join ( & static_inlines_o ( ) ) )
138+ . arg ( out_dir_path. join ( & static_inlines_tmp ( ) ) ) ;
139+ for hdr in headers. iter ( ) {
140+ gcc_args = gcc_args. arg ( "-include" ) . arg ( hdr) ;
141+ }
142+ gcc_args = gcc_args. args ( strip_quotes ( build_output_args) ) ;
143+ gcc_args = gcc_args. args ( clang_args) ;
144+
145+ let gcc_output = gcc_args. output ( ) . unwrap ( ) ;
146+ if !gcc_output. status . success ( ) {
147+ panic ! (
148+ "Could not compile object file:\n {}" ,
149+ String :: from_utf8_lossy( & gcc_output. stderr)
150+ ) ;
151+ }
152+
153+ #[ cfg( not( target_os = "windows" ) ) ]
154+ let lib_output = std:: process:: Command :: new ( ar)
155+ . arg ( "rcs" )
156+ . arg ( out_dir_path. join ( static_inlines_a ( ) ) )
157+ . arg ( out_dir_path. join ( static_inlines_o ( ) ) )
158+ . output ( )
159+ . unwrap ( ) ;
160+ #[ cfg( target_os = "windows" ) ]
161+ let lib_output = std:: process:: Command :: new ( "lib" )
162+ . arg ( & out_dir_path. join ( static_inlines_o ( ) ) )
163+ . output ( )
164+ . unwrap ( ) ;
165+
166+ if !lib_output. status . success ( ) {
167+ panic ! (
168+ "Could not emit library file:\n {}" ,
169+ String :: from_utf8_lossy( & lib_output. stderr)
170+ ) ;
171+ }
172+
173+ println ! (
174+ "cargo:rustc-link-search=native={}" ,
175+ out_dir_path. to_string_lossy( )
176+ ) ;
177+ println ! ( "cargo:rustc-link-lib=static={}" , STATIC_INLINE ) ;
178+
179+ Ok ( ( ) )
180+ }
181+
51182fn main ( ) -> anyhow:: Result < ( ) > {
52183 let build_output = build_driver:: build ( ) ?;
53184
@@ -97,9 +228,13 @@ fn main() -> anyhow::Result<()> {
97228 // Because we have multiple bindgen invocations and we can't clone a bindgen::Builder,
98229 // we have to set the options every time.
99230 let configure_bindgen = |bindgen : bindgen:: Builder | {
231+ let mut outdir = cargo:: out_dir ( ) ;
232+ outdir. push ( STATIC_INLINE ) ;
100233 Ok ( bindgen
101234 . parse_callbacks ( Box :: new ( BindgenCallbacks ) )
102235 . use_core ( )
236+ . wrap_static_fns ( true )
237+ . wrap_static_fns_path ( outdir)
103238 . enable_function_attribute_detection ( )
104239 . clang_arg ( "-DESP_PLATFORM" )
105240 . blocklist_function ( "strtold" )
@@ -145,7 +280,7 @@ fn main() -> anyhow::Result<()> {
145280 ) ;
146281
147282 configure_bindgen ( build_output. bindgen . clone ( ) . builder ( ) ?) ?
148- . headers ( headers) ?
283+ . headers ( headers. clone ( ) ) ?
149284 . generate ( )
150285 . with_context ( bindgen_err) ?
151286 . write_to_file ( & bindings_file)
@@ -185,7 +320,7 @@ fn main() -> anyhow::Result<()> {
185320 . into_iter ( )
186321 . chain ( EspIdfVersion :: parse ( bindings_file) ?. cfg_args ( ) )
187322 . chain ( build_output. components . cfg_args ( ) )
188- . chain ( once ( mcu) )
323+ . chain ( once ( mcu. clone ( ) ) )
189324 . collect ( ) ,
190325 } ;
191326 cfg_args. propagate ( ) ;
@@ -211,5 +346,8 @@ fn main() -> anyhow::Result<()> {
211346 link_args. propagate ( ) ;
212347 }
213348
349+ let clang_args: Vec < String > = build_output. components . clang_args ( ) . collect ( ) ;
350+ process_static_inlines ( & build_output. cincl_args . args , clang_args, & mcu, headers) ?;
351+
214352 Ok ( ( ) )
215353}
0 commit comments