@@ -134,10 +134,112 @@ fn fix_config_map(mut config_map: HashMap<String, String>) -> HashMap<String, St
134
134
config_map
135
135
}
136
136
137
- fn load_cross_compile_info ( ) -> Result < ( InterpreterConfig , HashMap < String , String > ) > {
138
- let python_include_dir = env:: var ( "PYO3_CROSS_INCLUDE_DIR" ) ?;
139
- let python_include_dir = Path :: new ( & python_include_dir) ;
137
+ fn parse_sysconfigdata ( config_path : impl AsRef < Path > ) -> Result < HashMap < String , String > > {
138
+ let config_reader = BufReader :: new ( File :: open ( config_path) ?) ;
139
+ let mut entries = HashMap :: new ( ) ;
140
+ let entry_re = regex:: Regex :: new ( r#"'([a-zA-Z_0-9]*)': ((?:"|')([\S ]*)(?:"|')|\d+)($|,$)"# ) ?;
141
+ let subsequent_re = regex:: Regex :: new ( r#"\s+(?:"|')([\S ]*)(?:"|')($|,)"# ) ?;
142
+ let mut previous_finished = None ;
143
+ for maybe_line in config_reader. lines ( ) {
144
+ let line = maybe_line?;
145
+ if previous_finished. is_none ( ) {
146
+ let captures = match entry_re. captures ( & line) {
147
+ Some ( c) => c,
148
+ None => continue ,
149
+ } ;
150
+ let key = captures[ 1 ] . to_owned ( ) ;
151
+ let val = if let Some ( val) = captures. get ( 3 ) {
152
+ val. as_str ( ) . to_owned ( )
153
+ } else {
154
+ captures[ 2 ] . to_owned ( )
155
+ } ;
156
+ if & captures[ 4 ] != "," && & captures[ 4 ] != "}" {
157
+ previous_finished = Some ( key. clone ( ) ) ;
158
+ }
159
+ entries. insert ( key, val) ;
160
+ } else if let Some ( ref key) = previous_finished {
161
+ let captures = match subsequent_re. captures ( & line) {
162
+ Some ( c) => c,
163
+ None => continue ,
164
+ } ;
165
+ let prev = entries. remove ( key) . unwrap ( ) ;
166
+ entries. insert ( key. clone ( ) , prev + & captures[ 1 ] ) ;
167
+
168
+ if & captures[ 2 ] == "," || & captures[ 2 ] == "}" {
169
+ previous_finished = None ;
170
+ }
171
+ }
172
+ }
140
173
174
+ Ok ( entries)
175
+ }
176
+
177
+ fn load_cross_compile_from_sysconfigdata (
178
+ python_include_dir : & Path ,
179
+ python_lib_dir : & str ,
180
+ ) -> Result < ( InterpreterConfig , HashMap < String , String > ) > {
181
+ // find info from sysconfig
182
+ // first find sysconfigdata file
183
+ let sysconfig_re = regex:: Regex :: new ( r"_sysconfigdata_m?_linux_([a-z_\-0-9]*)?\.py$" ) ?;
184
+ let mut walker = walkdir:: WalkDir :: new ( & python_lib_dir) . into_iter ( ) ;
185
+ let sysconfig_path = loop {
186
+ let entry = match walker. next ( ) {
187
+ Some ( Ok ( entry) ) => entry,
188
+ None => bail ! ( "Could not find sysconfigdata file" ) ,
189
+ _ => continue ,
190
+ } ;
191
+ let entry = entry. into_path ( ) ;
192
+ if sysconfig_re. is_match ( entry. to_str ( ) . unwrap ( ) ) {
193
+ break entry;
194
+ }
195
+ } ;
196
+ let config_map = parse_sysconfigdata ( sysconfig_path) ?;
197
+
198
+ let shared = match config_map
199
+ . get ( "Py_ENABLE_SHARED" )
200
+ . map ( |x| x. as_str ( ) )
201
+ . ok_or ( "Py_ENABLE_SHARED is not defined" ) ?
202
+ {
203
+ "1" | "true" | "True" => true ,
204
+ "0" | "false" | "False" => false ,
205
+ _ => panic ! ( "Py_ENABLE_SHARED must be a bool (1/true/True or 0/false/False" ) ,
206
+ } ;
207
+
208
+ let ( major, minor) = match config_map. get ( "VERSION" ) {
209
+ Some ( s) => {
210
+ let split = s. split ( "." ) . collect :: < Vec < & str > > ( ) ;
211
+ ( split[ 0 ] . parse :: < u8 > ( ) ?, split[ 1 ] . parse :: < u8 > ( ) ?)
212
+ }
213
+ None => bail ! ( "Could not find python version" ) ,
214
+ } ;
215
+
216
+ let ld_version = match config_map. get ( "LDVERSION" ) {
217
+ Some ( s) => s. clone ( ) ,
218
+ None => format ! ( "{}.{}" , major, minor) ,
219
+ } ;
220
+ let python_version = PythonVersion {
221
+ major,
222
+ minor : Some ( minor) ,
223
+ implementation : PythonInterpreterKind :: CPython ,
224
+ } ;
225
+
226
+ let interpreter_config = InterpreterConfig {
227
+ version : python_version,
228
+ libdir : Some ( python_lib_dir. to_owned ( ) ) ,
229
+ shared,
230
+ ld_version,
231
+ base_prefix : "" . to_string ( ) ,
232
+ executable : PathBuf :: new ( ) ,
233
+ calcsize_pointer : None ,
234
+ } ;
235
+
236
+ Ok ( ( interpreter_config, fix_config_map ( config_map) ) )
237
+ }
238
+
239
+ fn load_cross_compile_from_headers (
240
+ python_include_dir : & Path ,
241
+ python_lib_dir : & str ,
242
+ ) -> Result < ( InterpreterConfig , HashMap < String , String > ) > {
141
243
let patchlevel_defines = parse_header_defines ( python_include_dir. join ( "patchlevel.h" ) ) ?;
142
244
143
245
let major = match patchlevel_defines
@@ -177,9 +279,9 @@ fn load_cross_compile_info() -> Result<(InterpreterConfig, HashMap<String, Strin
177
279
178
280
let interpreter_config = InterpreterConfig {
179
281
version : python_version,
180
- libdir : Some ( env :: var ( "PYO3_CROSS_LIB_DIR" ) ? ) ,
282
+ libdir : Some ( python_lib_dir . to_owned ( ) ) ,
181
283
shared,
182
- ld_version : "" . to_string ( ) ,
284
+ ld_version : format ! ( "{}.{}" , major , minor ) ,
183
285
base_prefix : "" . to_string ( ) ,
184
286
executable : PathBuf :: new ( ) ,
185
287
calcsize_pointer : None ,
@@ -188,6 +290,20 @@ fn load_cross_compile_info() -> Result<(InterpreterConfig, HashMap<String, Strin
188
290
Ok ( ( interpreter_config, fix_config_map ( config_map) ) )
189
291
}
190
292
293
+ fn load_cross_compile_info (
294
+ python_include_dir : String ,
295
+ python_lib_dir : String ,
296
+ ) -> Result < ( InterpreterConfig , HashMap < String , String > ) > {
297
+ let python_include_dir = Path :: new ( & python_include_dir) ;
298
+ // Try to configure from the sysconfigdata file which is more accurate for the information
299
+ // provided at python's compile time
300
+ match load_cross_compile_from_sysconfigdata ( python_include_dir, & python_lib_dir) {
301
+ Ok ( ret) => Ok ( ret) ,
302
+ // If the config could not be loaded by sysconfigdata, failover to configuring from headers
303
+ Err ( _) => load_cross_compile_from_headers ( python_include_dir, & python_lib_dir) ,
304
+ }
305
+ }
306
+
191
307
/// Examine python's compile flags to pass to cfg by launching
192
308
/// the interpreter and printing variables of interest from
193
309
/// sysconfig.get_config_vars.
@@ -567,10 +683,27 @@ fn main() -> Result<()> {
567
683
// If you have troubles with your shell accepting '.' in a var name,
568
684
// try using 'env' (sorry but this isn't our fault - it just has to
569
685
// match the pkg-config package name, which is going to have a . in it).
570
- let cross_compiling =
571
- env:: var ( "PYO3_CROSS_INCLUDE_DIR" ) . is_ok ( ) && env:: var ( "PYO3_CROSS_LIB_DIR" ) . is_ok ( ) ;
686
+ //
687
+ // Detecting if cross-compiling by checking if the target triple is different from the host
688
+ // rustc's triple.
689
+ let cross_compiling = env:: var ( "TARGET" ) != env:: var ( "HOST" ) ;
572
690
let ( interpreter_config, mut config_map) = if cross_compiling {
573
- load_cross_compile_info ( ) ?
691
+ // If cross compiling we need the path to the cross-compiled include dir and lib dir, else
692
+ // fail quickly and loudly
693
+ let python_include_dir = match env:: var ( "PYO3_CROSS_INCLUDE_DIR" ) {
694
+ Ok ( v) => v,
695
+ Err ( _) => bail ! (
696
+ "Must provide PYO3_CROSS_INCLUDE_DIR environment variable when cross-compiling"
697
+ ) ,
698
+ } ;
699
+ let python_lib_dir = match env:: var ( "PYO3_CROSS_LIB_DIR" ) {
700
+ Ok ( v) => v,
701
+ Err ( _) => {
702
+ bail ! ( "Must provide PYO3_CROSS_LIB_DIR environment variable when cross-compiling" )
703
+ }
704
+ } ;
705
+
706
+ load_cross_compile_info ( python_include_dir, python_lib_dir) ?
574
707
} else {
575
708
find_interpreter_and_get_config ( ) ?
576
709
} ;
0 commit comments