1+ use std:: ffi:: OsStr ;
12use std:: path:: { Path , PathBuf } ;
2- use std:: process:: Command ;
33use std:: { env, fs} ;
44
55use anyhow:: * ;
66
7- use crate :: pio:: project:: SconsVariables ;
87use crate :: utils:: OsStrExt ;
8+ use crate :: { cargo, cli, cmake, cmd, cmd_output, pio} ;
99
1010pub const VAR_BINDINGS_FILE : & str = "EMBUILD_GENERATED_BINDINGS_FILE" ;
1111
12- #[ cfg( windows) ]
13- const EXE_SUFFIX : & str = ".exe" ;
14-
15- #[ cfg( not( windows) ) ]
16- const EXE_SUFFIX : & str = "" ;
17-
18- #[ cfg( windows) ]
19- const FS_CASE_INSENSITIVE : bool = true ;
20-
21- #[ cfg( not( windows) ) ]
22- const FS_CASE_INSENSITIVE : bool = false ;
23-
2412#[ derive( Clone , Default , Debug ) ]
2513pub struct Factory {
2614 pub clang_args : Vec < String > ,
2715 pub linker : Option < PathBuf > ,
2816 pub mcu : Option < String > ,
17+ pub force_cpp : bool ,
18+ pub sysroot : Option < PathBuf > ,
2919}
3020
3121impl Factory {
32- pub fn from_scons_vars ( scons_vars : & SconsVariables ) -> Result < Self > {
22+ pub fn from_scons_vars ( scons_vars : & pio:: project:: SconsVariables ) -> Result < Self > {
23+ let clang_args = cli:: NativeCommandArgs :: new ( & scons_vars. incflags )
24+ . chain ( cli:: NativeCommandArgs :: new (
25+ scons_vars
26+ . clangargs
27+ . as_deref ( )
28+ . unwrap_or_default ( ) ,
29+ ) )
30+ . collect ( ) ;
31+
3332 Ok ( Self {
34- clang_args : Self :: get_pio_clang_args (
35- & scons_vars. incflags ,
36- scons_vars. clangargs . clone ( ) ,
37- ) ,
33+ clang_args,
3834 linker : Some ( scons_vars. full_path ( scons_vars. link . clone ( ) ) ?) ,
3935 mcu : Some ( scons_vars. mcu . clone ( ) ) ,
36+ force_cpp : false ,
37+ sysroot : None ,
4038 } )
4139 }
4240
43- pub fn builder ( & self ) -> Result < bindgen:: Builder > {
41+ pub fn from_cmake ( compile_group : & cmake:: codemodel:: target:: CompileGroup ) -> Result < Self > {
42+ use crate :: cmake:: codemodel:: Language ;
43+ assert ! (
44+ compile_group. language == Language :: C || compile_group. language == Language :: Cpp ,
45+ "Generating bindings for languages other than C/C++ is not supported"
46+ ) ;
47+
48+ let clang_args = compile_group
49+ . defines
50+ . iter ( )
51+ . map ( |d| format ! ( "-D{}" , d. define) )
52+ . chain (
53+ compile_group
54+ . includes
55+ . iter ( )
56+ . map ( |i| format ! ( "-I{}" , & i. path) ) ,
57+ )
58+ . collect ( ) ;
59+
60+ Ok ( Self {
61+ clang_args,
62+ linker : None ,
63+ force_cpp : compile_group. language == Language :: Cpp ,
64+ mcu : None ,
65+ sysroot : compile_group. sysroot . as_ref ( ) . map ( |s| s. path . clone ( ) ) ,
66+ } )
67+ }
68+
69+ /// Set the linker used to determine the sysroot to be used for generating bindings.
70+ pub fn with_linker ( mut self , linker : impl Into < PathBuf > ) -> Self {
71+ self . linker = Some ( linker. into ( ) ) ;
72+ self
73+ }
74+
75+ pub fn builder ( self ) -> Result < bindgen:: Builder > {
4476 self . create_builder ( false )
4577 }
4678
47- pub fn cpp_builder ( & self ) -> Result < bindgen:: Builder > {
79+ pub fn cpp_builder ( self ) -> Result < bindgen:: Builder > {
4880 self . create_builder ( true )
4981 }
5082
51- fn create_builder ( & self , cpp : bool ) -> Result < bindgen:: Builder > {
52- let sysroot = self . get_sysroot ( ) ?;
83+ fn create_builder ( self , cpp : bool ) -> Result < bindgen:: Builder > {
84+ let cpp = self . force_cpp || cpp;
85+ let sysroot = self
86+ . sysroot
87+ . clone ( )
88+ . map_or_else ( || try_get_sysroot ( & self . linker ) , Ok ) ?;
89+
90+ let sysroot_args = [
91+ format ! ( "--sysroot={}" , sysroot. try_to_str( ) ?) ,
92+ format ! ( "-I{}" , sysroot. join( "include" ) . try_to_str( ) ?) ,
93+ ] ;
94+
95+ let cpp_args = if cpp {
96+ get_cpp_includes ( & sysroot) ?
97+ } else {
98+ vec ! [ ]
99+ } ;
53100
54101 let builder = bindgen:: Builder :: default ( )
55102 . use_core ( )
@@ -58,130 +105,25 @@ impl Factory {
58105 . derive_default ( true )
59106 //.ctypes_prefix(c_types)
60107 . clang_arg ( "-D__bindgen" )
61- . clang_arg ( format ! ( "--sysroot={}" , sysroot. display( ) ) )
62- . clang_arg ( format ! ( "-I{}" , sysroot. join( "include" ) . try_to_str( ) ?) )
108+ . clang_args ( sysroot_args)
63109 . clang_args ( & [ "-x" , if cpp { "c++" } else { "c" } ] )
64- . clang_args ( if cpp {
65- Self :: get_cpp_includes ( sysroot) ?
66- } else {
67- Vec :: new ( )
68- } )
110+ . clang_args ( cpp_args)
69111 . clang_args ( & self . clang_args ) ;
70112
71- eprintln ! (
113+ log :: debug !(
72114 "Bindgen builder factory flags: {:?}" ,
73115 builder. command_line_flags( )
74116 ) ;
75117
76118 Ok ( builder)
77119 }
78-
79- fn get_sysroot ( & self ) -> Result < PathBuf > {
80- let linker = if let Some ( linker) = self . linker . as_ref ( ) {
81- linker
82- . clone ( )
83- . into_os_string ( )
84- . into_string ( )
85- . map_err ( |_| anyhow ! ( "Cannot convert the linker variable to String" ) ) ?
86- } else if let Ok ( linker) = env:: var ( "RUSTC_LINKER" ) {
87- linker
88- } else {
89- bail ! ( "No explicit linker, and env var RUSTC_LINKER not defined either" ) ;
90- } ;
91-
92- let gcc = format ! ( "gcc{}" , EXE_SUFFIX ) ;
93- let gcc_suffix = format ! ( "-{}" , gcc) ;
94-
95- let linker_canonicalized = if FS_CASE_INSENSITIVE {
96- linker. to_lowercase ( )
97- } else {
98- linker. clone ( )
99- } ;
100-
101- let linker = if linker_canonicalized == gcc || linker_canonicalized. ends_with ( & gcc_suffix) {
102- // For whatever reason, --print-sysroot does not work with GCC
103- // Change it to LD
104- format ! ( "{}ld{}" , & linker[ 0 ..linker. len( ) - gcc. len( ) ] , EXE_SUFFIX )
105- } else {
106- linker
107- } ;
108-
109- let output = Command :: new ( linker) . arg ( "--print-sysroot" ) . output ( ) ?;
110-
111- let path_str = String :: from_utf8 ( output. stdout ) ?;
112-
113- Ok ( PathBuf :: from ( path_str. trim ( ) ) )
114- }
115-
116- fn get_cpp_includes ( sysroot : impl AsRef < Path > ) -> Result < Vec < String > > {
117- let sysroot = sysroot. as_ref ( ) ;
118- let cpp_includes_root = sysroot. join ( "include" ) . join ( "c++" ) ;
119-
120- let cpp_version = fs:: read_dir ( & cpp_includes_root) ?
121- . map ( |dir_entry_r| dir_entry_r. map ( |dir_entry| dir_entry. path ( ) ) )
122- . fold ( None , |ao : Option < PathBuf > , sr : Result < PathBuf , _ > | {
123- if let Some ( a) = ao. as_ref ( ) {
124- sr. ok ( )
125- . map_or ( ao. clone ( ) , |s| if a >= & s { ao. clone ( ) } else { Some ( s) } )
126- } else {
127- sr. ok ( )
128- }
129- } ) ;
130-
131- if let Some ( cpp_version) = cpp_version {
132- let mut cpp_include_paths = vec ! [
133- format!( "-I{}" , cpp_version. try_to_str( ) ?) ,
134- format!( "-I{}" , cpp_version. join( "backward" ) . try_to_str( ) ?) ,
135- ] ;
136-
137- if let Some ( sysroot_last_segment) = fs:: canonicalize ( sysroot) ?. file_name ( ) {
138- cpp_include_paths. push ( format ! (
139- "-I{}" ,
140- cpp_version. join( sysroot_last_segment) . try_to_str( ) ?
141- ) ) ;
142- }
143-
144- Ok ( cpp_include_paths)
145- } else {
146- Ok ( Vec :: new ( ) )
147- }
148- }
149-
150- fn get_pio_clang_args (
151- incflags : impl AsRef < str > ,
152- extra_args : Option < impl AsRef < str > > ,
153- ) -> Vec < String > {
154- let mut result = incflags
155- . as_ref ( )
156- . split ( ' ' )
157- . map ( str:: to_string)
158- . collect :: < Vec < _ > > ( ) ;
159-
160- if let Some ( extra_args) = extra_args {
161- result. append (
162- & mut extra_args
163- . as_ref ( )
164- . split ( ' ' )
165- . map ( str:: to_string)
166- . collect :: < Vec < _ > > ( ) ,
167- ) ;
168- }
169-
170- result
171- }
172120}
173121
174122pub fn run ( builder : bindgen:: Builder ) -> Result < ( ) > {
175123 let output_file = PathBuf :: from ( env:: var ( "OUT_DIR" ) ?) . join ( "bindings.rs" ) ;
176-
177124 run_for_file ( builder, & output_file) ?;
178125
179- println ! (
180- "cargo:rustc-env={}={}" ,
181- VAR_BINDINGS_FILE ,
182- output_file. display( )
183- ) ;
184-
126+ cargo:: set_rustc_env ( VAR_BINDINGS_FILE , output_file. try_to_str ( ) ?) ;
185127 Ok ( ( ) )
186128}
187129
@@ -199,12 +141,77 @@ pub fn run_for_file(builder: bindgen::Builder, output_file: impl AsRef<Path>) ->
199141
200142 // Run rustfmt on the generated bindings separately, because custom toolchains often do not have rustfmt
201143 // Hence why we need to use the rustfmt from the stable buildchain (where the assumption is, it is already installed)
202- Command :: new ( "rustup" )
203- . arg ( "run" )
204- . arg ( "stable" )
205- . arg ( "rustfmt" )
206- . arg ( output_file)
207- . status ( ) ?;
208-
144+ cmd ! ( "rustup" , "run" , "stable" , "rustfmt" , output_file) ?;
209145 Ok ( ( ) )
210146}
147+
148+ fn try_get_sysroot ( linker : & Option < impl AsRef < Path > > ) -> Result < PathBuf > {
149+ let linker = if let Some ( ref linker) = linker {
150+ linker. as_ref ( ) . to_owned ( )
151+ } else if let Some ( linker) = env:: var_os ( "RUSTC_LINKER" ) {
152+ PathBuf :: from ( linker)
153+ } else {
154+ bail ! ( "Could not determine linker: No explicit linker and `RUSTC_LINKER` not set" ) ;
155+ } ;
156+
157+ let gcc_file_stem = linker
158+ . file_stem ( )
159+ . and_then ( OsStr :: to_str)
160+ . filter ( |& s| s == "gcc" || s. ends_with ( "-gcc" ) ) ;
161+
162+ // For whatever reason, --print-sysroot does not work with GCC
163+ // Change it to LD
164+ let linker = if let Some ( stem) = gcc_file_stem {
165+ let mut ld_linker =
166+ linker. with_file_name ( format ! ( "{}{}" , stem. strip_suffix( "gcc" ) . unwrap( ) , "ld" ) ) ;
167+ if let Some ( ext) = linker. extension ( ) {
168+ ld_linker. set_extension ( ext) ;
169+ }
170+ ld_linker
171+ } else {
172+ linker
173+ } ;
174+
175+ cmd_output ! ( linker, "--print-sysroot" )
176+ . with_context ( || {
177+ anyhow ! (
178+ "Could not determine sysroot from linker '{}'" ,
179+ linker. display( )
180+ )
181+ } )
182+ . map ( PathBuf :: from)
183+ }
184+
185+ fn get_cpp_includes ( sysroot : impl AsRef < Path > ) -> Result < Vec < String > > {
186+ let sysroot = sysroot. as_ref ( ) ;
187+ let cpp_includes_root = sysroot. join ( "include" ) . join ( "c++" ) ;
188+
189+ let cpp_version = fs:: read_dir ( & cpp_includes_root) ?
190+ . map ( |dir_entry_r| dir_entry_r. map ( |dir_entry| dir_entry. path ( ) ) )
191+ . fold ( None , |ao : Option < PathBuf > , sr : Result < PathBuf , _ > | {
192+ if let Some ( a) = ao. as_ref ( ) {
193+ sr. ok ( )
194+ . map_or ( ao. clone ( ) , |s| if a >= & s { ao. clone ( ) } else { Some ( s) } )
195+ } else {
196+ sr. ok ( )
197+ }
198+ } ) ;
199+
200+ if let Some ( cpp_version) = cpp_version {
201+ let mut cpp_include_paths = vec ! [
202+ format!( "-I{}" , cpp_version. try_to_str( ) ?) ,
203+ format!( "-I{}" , cpp_version. join( "backward" ) . try_to_str( ) ?) ,
204+ ] ;
205+
206+ if let Some ( sysroot_last_segment) = fs:: canonicalize ( sysroot) ?. file_name ( ) {
207+ cpp_include_paths. push ( format ! (
208+ "-I{}" ,
209+ cpp_version. join( sysroot_last_segment) . try_to_str( ) ?
210+ ) ) ;
211+ }
212+
213+ Ok ( cpp_include_paths)
214+ } else {
215+ Ok ( Vec :: new ( ) )
216+ }
217+ }
0 commit comments