1
+ use std:: ffi:: OsStr ;
1
2
use std:: path:: { Path , PathBuf } ;
2
- use std:: process:: Command ;
3
3
use std:: { env, fs} ;
4
4
5
5
use anyhow:: * ;
6
6
7
- use crate :: pio:: project:: SconsVariables ;
8
7
use crate :: utils:: OsStrExt ;
8
+ use crate :: { cargo, cli, cmake, cmd, cmd_output, pio} ;
9
9
10
10
pub const VAR_BINDINGS_FILE : & str = "EMBUILD_GENERATED_BINDINGS_FILE" ;
11
11
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
-
24
12
#[ derive( Clone , Default , Debug ) ]
25
13
pub struct Factory {
26
14
pub clang_args : Vec < String > ,
27
15
pub linker : Option < PathBuf > ,
28
16
pub mcu : Option < String > ,
17
+ pub force_cpp : bool ,
18
+ pub sysroot : Option < PathBuf > ,
29
19
}
30
20
31
21
impl 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
+
33
32
Ok ( Self {
34
- clang_args : Self :: get_pio_clang_args (
35
- & scons_vars. incflags ,
36
- scons_vars. clangargs . clone ( ) ,
37
- ) ,
33
+ clang_args,
38
34
linker : Some ( scons_vars. full_path ( scons_vars. link . clone ( ) ) ?) ,
39
35
mcu : Some ( scons_vars. mcu . clone ( ) ) ,
36
+ force_cpp : false ,
37
+ sysroot : None ,
40
38
} )
41
39
}
42
40
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 > {
44
76
self . create_builder ( false )
45
77
}
46
78
47
- pub fn cpp_builder ( & self ) -> Result < bindgen:: Builder > {
79
+ pub fn cpp_builder ( self ) -> Result < bindgen:: Builder > {
48
80
self . create_builder ( true )
49
81
}
50
82
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
+ } ;
53
100
54
101
let builder = bindgen:: Builder :: default ( )
55
102
. use_core ( )
@@ -58,130 +105,25 @@ impl Factory {
58
105
. derive_default ( true )
59
106
//.ctypes_prefix(c_types)
60
107
. 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)
63
109
. 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)
69
111
. clang_args ( & self . clang_args ) ;
70
112
71
- eprintln ! (
113
+ log :: debug !(
72
114
"Bindgen builder factory flags: {:?}" ,
73
115
builder. command_line_flags( )
74
116
) ;
75
117
76
118
Ok ( builder)
77
119
}
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
- }
172
120
}
173
121
174
122
pub fn run ( builder : bindgen:: Builder ) -> Result < ( ) > {
175
123
let output_file = PathBuf :: from ( env:: var ( "OUT_DIR" ) ?) . join ( "bindings.rs" ) ;
176
-
177
124
run_for_file ( builder, & output_file) ?;
178
125
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 ( ) ?) ;
185
127
Ok ( ( ) )
186
128
}
187
129
@@ -199,12 +141,77 @@ pub fn run_for_file(builder: bindgen::Builder, output_file: impl AsRef<Path>) ->
199
141
200
142
// Run rustfmt on the generated bindings separately, because custom toolchains often do not have rustfmt
201
143
// 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) ?;
209
145
Ok ( ( ) )
210
146
}
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