@@ -41,6 +41,38 @@ use std::path::PathBuf;
41
41
use std:: process:: Command ;
42
42
use fs_extra:: dir;
43
43
44
+ // Custom callback to handle MSVC type mapping issues
45
+ #[ derive( Debug ) ]
46
+ struct MSVCTypeCallback ;
47
+
48
+ impl bindgen:: callbacks:: ParseCallbacks for MSVCTypeCallback {
49
+ fn int_macro ( & self , name : & str , value : i64 ) -> Option < bindgen:: callbacks:: IntKind > {
50
+ // Force unsigned macros and values to use u32 on MSVC
51
+ if name. contains ( "UINT" )
52
+ || name. ends_with ( "_U" )
53
+ || ( value >= 0 && name. contains ( "UNSIGNED" ) )
54
+ {
55
+ Some ( bindgen:: callbacks:: IntKind :: U32 )
56
+ } else {
57
+ None
58
+ }
59
+ }
60
+
61
+ fn item_name ( & self , item_info : bindgen:: callbacks:: ItemInfo < ' _ > ) -> Option < String > {
62
+ // Handle specific type mappings if needed
63
+ match item_info. name {
64
+ "c_uint" => Some ( "u32" . to_string ( ) ) ,
65
+ "c_ulong" => Some ( "u32" . to_string ( ) ) ,
66
+ _ => None ,
67
+ }
68
+ }
69
+
70
+ // Chain with CargoCallbacks
71
+ fn include_file ( & self , filename : & str ) {
72
+ bindgen:: CargoCallbacks :: new ( ) . include_file ( filename) ;
73
+ }
74
+ }
75
+
44
76
#[ cfg( feature = "community" ) ]
45
77
static CBL_INCLUDE_DIR : & str = "libcblite_community/include" ;
46
78
#[ cfg( feature = "enterprise" ) ]
@@ -103,6 +135,139 @@ enum OperatingSystem {
103
135
IOs ,
104
136
}
105
137
138
+ fn find_msvc_paths ( ) -> Result < ( Option < String > , Option < String > ) , Box < dyn Error > > {
139
+ // Try to find MSVC installation paths automatically
140
+ let mut msvc_include = None ;
141
+ let mut ucrt_include = None ;
142
+
143
+ // Method 1: Use environment variables from Rust/Cargo build (preferred method)
144
+ if let Ok ( include_path) = env:: var ( "INCLUDE" ) {
145
+ // Parse the INCLUDE environment variable that MSVC sets
146
+ for path in include_path. split ( ';' ) {
147
+ let path = path. trim ( ) ;
148
+ if !path. is_empty ( ) {
149
+ if path. contains ( "VC\\ Tools\\ MSVC" ) && path. ends_with ( "include" ) {
150
+ msvc_include = Some ( path. to_string ( ) ) ;
151
+ } else if path. contains ( "Windows Kits" ) && path. ends_with ( "ucrt" ) {
152
+ ucrt_include = Some ( path. to_string ( ) ) ;
153
+ }
154
+ }
155
+ }
156
+ }
157
+
158
+ // Method 2: Try using vswhere.exe if available and no env vars found
159
+ if msvc_include. is_none ( )
160
+ && let Ok ( output) = Command :: new ( "vswhere.exe" )
161
+ . args ( [
162
+ "-latest" ,
163
+ "-products" ,
164
+ "*" ,
165
+ "-requires" ,
166
+ "Microsoft.VisualStudio.Component.VC.Tools.x86.x64" ,
167
+ "-property" ,
168
+ "installationPath" ,
169
+ ] )
170
+ . output ( )
171
+ && output. status . success ( )
172
+ {
173
+ let vs_path = String :: from_utf8_lossy ( & output. stdout ) . trim ( ) . to_string ( ) ;
174
+ if !vs_path. is_empty ( ) {
175
+ // Try to find the MSVC version
176
+ let vc_tools_path = format ! ( "{}\\ VC\\ Tools\\ MSVC" , vs_path) ;
177
+ if let Ok ( entries) = fs:: read_dir ( & vc_tools_path) {
178
+ let mut versions = Vec :: new ( ) ;
179
+ for entry in entries. flatten ( ) {
180
+ if entry. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false ) {
181
+ let version_path = entry. path ( ) ;
182
+ let include_path = version_path. join ( "include" ) ;
183
+ if include_path. exists ( ) {
184
+ let version = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
185
+ versions. push ( ( version, include_path. to_string_lossy ( ) . to_string ( ) ) ) ;
186
+ }
187
+ }
188
+ }
189
+ // Sort versions and take the latest
190
+ versions. sort_by ( |a, b| b. 0 . cmp ( & a. 0 ) ) ;
191
+ if let Some ( ( _, path) ) = versions. first ( ) {
192
+ msvc_include = Some ( path. clone ( ) ) ;
193
+ }
194
+ }
195
+ }
196
+ }
197
+
198
+ // Method 3: Check common Visual Studio locations
199
+ if msvc_include. is_none ( ) {
200
+ let common_vs_paths = [
201
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2022\\ Enterprise\\ VC\\ Tools\\ MSVC" ,
202
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2022\\ Professional\\ VC\\ Tools\\ MSVC" ,
203
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2022\\ Community\\ VC\\ Tools\\ MSVC" ,
204
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2019\\ Enterprise\\ VC\\ Tools\\ MSVC" ,
205
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2019\\ Professional\\ VC\\ Tools\\ MSVC" ,
206
+ "C:\\ Program Files\\ Microsoft Visual Studio\\ 2019\\ Community\\ VC\\ Tools\\ MSVC" ,
207
+ ] ;
208
+
209
+ for vs_path in & common_vs_paths {
210
+ if let Ok ( entries) = fs:: read_dir ( vs_path) {
211
+ let mut versions = Vec :: new ( ) ;
212
+ for entry in entries. flatten ( ) {
213
+ if entry. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false ) {
214
+ let version_path = entry. path ( ) ;
215
+ let include_path = version_path. join ( "include" ) ;
216
+ if include_path. exists ( ) {
217
+ let version = entry. file_name ( ) . to_string_lossy ( ) . to_string ( ) ;
218
+ versions. push ( ( version, include_path. to_string_lossy ( ) . to_string ( ) ) ) ;
219
+ }
220
+ }
221
+ }
222
+ // Sort versions and take the latest
223
+ versions. sort_by ( |a, b| b. 0 . cmp ( & a. 0 ) ) ;
224
+ if let Some ( ( _, path) ) = versions. first ( ) {
225
+ msvc_include = Some ( path. clone ( ) ) ;
226
+ break ;
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ // Method 4: Try to find Windows SDK UCRT if not found via env vars
233
+ if ucrt_include. is_none ( ) {
234
+ let sdk_paths = [
235
+ "C:\\ Program Files (x86)\\ Windows Kits\\ 10\\ Include" ,
236
+ "C:\\ Program Files\\ Windows Kits\\ 10\\ Include" ,
237
+ ] ;
238
+
239
+ for sdk_path in & sdk_paths {
240
+ if let Ok ( entries) = fs:: read_dir ( sdk_path) {
241
+ // Find the latest version
242
+ let mut versions = Vec :: new ( ) ;
243
+ for entry in entries. flatten ( ) {
244
+ if entry. file_type ( ) . map ( |t| t. is_dir ( ) ) . unwrap_or ( false ) {
245
+ let name = entry. file_name ( ) ;
246
+ let name_str = name. to_string_lossy ( ) ;
247
+ if name_str. starts_with ( "10." ) {
248
+ let ucrt_path = entry. path ( ) . join ( "ucrt" ) ;
249
+ if ucrt_path. exists ( ) {
250
+ versions. push ( (
251
+ name_str. to_string ( ) ,
252
+ ucrt_path. to_string_lossy ( ) . to_string ( ) ,
253
+ ) ) ;
254
+ }
255
+ }
256
+ }
257
+ }
258
+ // Sort versions and take the latest
259
+ versions. sort_by ( |a, b| b. 0 . cmp ( & a. 0 ) ) ;
260
+ if let Some ( ( _, path) ) = versions. first ( ) {
261
+ ucrt_include = Some ( path. clone ( ) ) ;
262
+ break ;
263
+ }
264
+ }
265
+ }
266
+ }
267
+
268
+ Ok ( ( msvc_include, ucrt_include) )
269
+ }
270
+
106
271
fn is_target ( target : OperatingSystem ) -> Result < bool , Box < dyn Error > > {
107
272
let target_os = env:: var ( "CARGO_CFG_TARGET_OS" ) ?;
108
273
Ok ( match target {
@@ -172,12 +337,39 @@ fn generate_bindings() -> Result<(), Box<dyn Error>> {
172
337
. clang_arg ( format ! ( "--target={}" , "x86_64-pc-windows-gnu" ) ) ;
173
338
}
174
339
175
- bindings = bindings
176
- . clang_arg ( "-IC:\\ Program Files\\ Microsoft Visual Studio\\ 2022\\ Enterprise\\ VC\\ Tools\\ MSVC\\ 14.40.33807\\ include" . to_string ( ) )
177
- . clang_arg ( "-IC:\\ Program Files (x86)\\ Windows Kits\\ 10\\ Include\\ 10.0.17763.0\\ ucrt" ) ;
340
+ // Special handling for MSVC targets to fix unsigned type generation
341
+ if is_target ( OperatingSystem :: Windows ) ? {
342
+ // Try to auto-detect MSVC paths
343
+ let ( msvc_include, ucrt_include) = find_msvc_paths ( ) ?;
344
+
345
+ if let Some ( msvc_path) = msvc_include {
346
+ bindings = bindings. clang_arg ( format ! ( "-I{}" , msvc_path) ) ;
347
+ } else {
348
+ eprintln ! ( "Warning: Could not auto-detect MSVC include path, using fallback" ) ;
349
+ bindings = bindings. clang_arg ( "-IC:\\ Program Files\\ Microsoft Visual Studio\\ 2022\\ Enterprise\\ VC\\ Tools\\ MSVC\\ 14.40.33807\\ include" ) ;
350
+ }
351
+
352
+ if let Some ( ucrt_path) = ucrt_include {
353
+ bindings = bindings. clang_arg ( format ! ( "-I{}" , ucrt_path) ) ;
354
+ } else {
355
+ eprintln ! ( "Warning: Could not auto-detect Windows SDK UCRT path, using fallback" ) ;
356
+ bindings = bindings. clang_arg (
357
+ "-IC:\\ Program Files (x86)\\ Windows Kits\\ 10\\ Include\\ 10.0.17763.0\\ ucrt" ,
358
+ ) ;
359
+ }
360
+
361
+ bindings = bindings
362
+ // Force unsigned types on MSVC
363
+ . clang_arg ( "-DUINT32=unsigned int" )
364
+ . clang_arg ( "-DULONG=unsigned long" )
365
+ // Ensure proper type detection
366
+ . clang_arg ( "-D_WIN32_WINNT=0x0601" )
367
+ . blocklist_type ( "c_uint" )
368
+ . blocklist_type ( "c_ulong" ) ;
369
+ }
178
370
179
371
let out_dir = env:: var ( "OUT_DIR" ) ?;
180
- bindings
372
+ let mut final_bindings = bindings
181
373
. allowlist_type ( "CBL.*" )
182
374
. allowlist_type ( "FL.*" )
183
375
. allowlist_var ( "k?CBL.*" )
@@ -186,7 +378,24 @@ fn generate_bindings() -> Result<(), Box<dyn Error>> {
186
378
. allowlist_function ( "_?FL.*" )
187
379
. no_copy ( "FLSliceResult" )
188
380
. size_t_is_usize ( true )
189
- . parse_callbacks ( Box :: new ( bindgen:: CargoCallbacks :: new ( ) ) )
381
+ // Fix for MSVC: Force unsigned types to be generated as u32 instead of i32
382
+ . default_enum_style ( bindgen:: EnumVariation :: Consts )
383
+ // Force constants to be generated as const u32 instead of complex enums
384
+ . translate_enum_integer_types ( true ) ;
385
+
386
+ // Add MSVC-specific type fixes
387
+ if is_target ( OperatingSystem :: Windows ) ? {
388
+ final_bindings = final_bindings
389
+ . raw_line ( "#[allow(non_camel_case_types)]" )
390
+ . raw_line ( "pub type c_uint = u32;" )
391
+ . raw_line ( "pub type c_ulong = u32;" )
392
+ . raw_line ( "pub type DWORD = u32;" )
393
+ . raw_line ( "pub type UINT = u32;" )
394
+ . raw_line ( "pub type ULONG = u32;" ) ;
395
+ }
396
+
397
+ final_bindings
398
+ . parse_callbacks ( Box :: new ( MSVCTypeCallback ) )
190
399
. generate ( )
191
400
. expect ( "Unable to generate bindings" )
192
401
. write_to_file ( PathBuf :: from ( out_dir) . join ( "bindings.rs" ) )
0 commit comments