xref: /rust-libc-0.2.174/libc-test/build.rs (revision 36f5f133)
1 #![deny(warnings)]
2 
3 extern crate cc;
4 extern crate ctest2 as ctest;
5 
6 use std::fs::File;
7 use std::io::{BufRead, BufReader, BufWriter, Write};
8 use std::path::{Path, PathBuf};
9 use std::{env, io};
10 
11 fn do_cc() {
12     let target = env::var("TARGET").unwrap();
13     if cfg!(unix) {
14         let exclude = ["redox", "wasi"];
15         if !exclude.iter().any(|x| target.contains(x)) {
16             let mut cmsg = cc::Build::new();
17 
18             cmsg.file("src/cmsg.c");
19 
20             if target.contains("solaris") || target.contains("illumos") {
21                 cmsg.define("_XOPEN_SOURCE", "700");
22             }
23             cmsg.compile("cmsg");
24         }
25 
26         if target.contains("linux")
27             || target.contains("android")
28             || target.contains("emscripten")
29             || target.contains("fuchsia")
30             || target.contains("bsd")
31         {
32             cc::Build::new().file("src/makedev.c").compile("makedev");
33         }
34     }
35     if target.contains("android") || target.contains("linux") {
36         cc::Build::new().file("src/errqueue.c").compile("errqueue");
37     }
38     if target.contains("linux")
39         || target.contains("l4re")
40         || target.contains("android")
41         || target.contains("emscripten")
42     {
43         cc::Build::new().file("src/sigrt.c").compile("sigrt");
44     }
45 }
46 
47 fn do_ctest() {
48     match &env::var("TARGET").unwrap() {
49         t if t.contains("android") => return test_android(t),
50         t if t.contains("apple") => return test_apple(t),
51         t if t.contains("dragonfly") => return test_dragonflybsd(t),
52         t if t.contains("emscripten") => return test_emscripten(t),
53         t if t.contains("freebsd") => return test_freebsd(t),
54         t if t.contains("haiku") => return test_haiku(t),
55         t if t.contains("linux") => return test_linux(t),
56         t if t.contains("netbsd") => return test_netbsd(t),
57         t if t.contains("openbsd") => return test_openbsd(t),
58         t if t.contains("redox") => return test_redox(t),
59         t if t.contains("solaris") => return test_solarish(t),
60         t if t.contains("illumos") => return test_solarish(t),
61         t if t.contains("wasi") => return test_wasi(t),
62         t if t.contains("windows") => return test_windows(t),
63         t if t.contains("vxworks") => return test_vxworks(t),
64         t if t.contains("nto-qnx") => return test_neutrino(t),
65         t => panic!("unknown target {}", t),
66     }
67 }
68 
69 fn ctest_cfg() -> ctest::TestGenerator {
70     let mut cfg = ctest::TestGenerator::new();
71     let libc_cfgs = [
72         "libc_priv_mod_use",
73         "libc_union",
74         "libc_const_size_of",
75         "libc_align",
76         "libc_core_cvoid",
77         "libc_packedN",
78         "libc_thread_local",
79     ];
80     for f in &libc_cfgs {
81         cfg.cfg(f, None);
82     }
83     cfg
84 }
85 
86 fn do_semver() {
87     let mut out = PathBuf::from(env::var("OUT_DIR").unwrap());
88     out.push("semver.rs");
89     let mut output = BufWriter::new(File::create(&out).unwrap());
90 
91     let family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
92     let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
93     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
94     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
95     let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
96 
97     // `libc-test/semver` dir.
98     let mut semver_root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
99     semver_root.push("semver");
100 
101     // NOTE: Windows has the same `family` as `os`, no point in including it
102     // twice.
103     // NOTE: Android doesn't include the unix file (or the Linux file) because
104     // there are some many definitions missing it's actually easier just to
105     // maintain a file for Android.
106     if family != os && os != "android" {
107         process_semver_file(&mut output, &mut semver_root, &family);
108     }
109     process_semver_file(&mut output, &mut semver_root, &vendor);
110     process_semver_file(&mut output, &mut semver_root, &os);
111     let os_arch = format!("{}-{}", os, arch);
112     process_semver_file(&mut output, &mut semver_root, &os_arch);
113     if target_env != "" {
114         let os_env = format!("{}-{}", os, target_env);
115         process_semver_file(&mut output, &mut semver_root, &os_env);
116 
117         let os_env_arch = format!("{}-{}-{}", os, target_env, arch);
118         process_semver_file(&mut output, &mut semver_root, &os_env_arch);
119     }
120 }
121 
122 fn process_semver_file<W: Write, P: AsRef<Path>>(output: &mut W, path: &mut PathBuf, file: P) {
123     // NOTE: `path` is reused between calls, so always remove the file again.
124     path.push(file);
125     path.set_extension("txt");
126 
127     println!("cargo:rerun-if-changed={}", path.display());
128     let input_file = match File::open(&*path) {
129         Ok(file) => file,
130         Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
131             path.pop();
132             return;
133         }
134         Err(err) => panic!("unexpected error opening file: {}", err),
135     };
136     let input = BufReader::new(input_file);
137 
138     write!(output, "// Source: {}.\n", path.display()).unwrap();
139     output.write(b"use libc::{\n").unwrap();
140     for line in input.lines() {
141         let line = line.unwrap().into_bytes();
142         match line.first() {
143             // Ignore comments and empty lines.
144             Some(b'#') | None => continue,
145             _ => {
146                 output.write(b"    ").unwrap();
147                 output.write(&line).unwrap();
148                 output.write(b",\n").unwrap();
149             }
150         }
151     }
152     output.write(b"};\n\n").unwrap();
153     path.pop();
154 }
155 
156 fn main() {
157     do_cc();
158     do_ctest();
159     do_semver();
160 }
161 
162 macro_rules! headers {
163     ($cfg:ident: [$m:expr]: $header:literal) => {
164         if $m {
165             $cfg.header($header);
166         }
167     };
168     ($cfg:ident: $header:literal) => {
169         $cfg.header($header);
170     };
171     ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => {
172         $(headers!($cfg: $([$c]:)* $header);)*
173     };
174     ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => {
175         headers!($($cfg: $([$c]:)* $header,)*);
176     };
177     ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => {
178         headers!($($cfg: $([$c]:)* $header,)*);
179     };
180 }
181 
182 fn test_apple(target: &str) {
183     assert!(target.contains("apple"));
184     let x86_64 = target.contains("x86_64");
185     let i686 = target.contains("i686");
186 
187     let mut cfg = ctest_cfg();
188     cfg.flag("-Wno-deprecated-declarations");
189     cfg.define("__APPLE_USE_RFC_3542", None);
190 
191     headers! { cfg:
192         "aio.h",
193         "CommonCrypto/CommonCrypto.h",
194         "CommonCrypto/CommonRandom.h",
195         "copyfile.h",
196         "crt_externs.h",
197         "ctype.h",
198         "dirent.h",
199         "dlfcn.h",
200         "errno.h",
201         "execinfo.h",
202         "fcntl.h",
203         "getopt.h",
204         "glob.h",
205         "grp.h",
206         "iconv.h",
207         "ifaddrs.h",
208         "langinfo.h",
209         "libgen.h",
210         "libproc.h",
211         "limits.h",
212         "locale.h",
213         "mach-o/dyld.h",
214         "mach/mach_init.h",
215         "mach/mach.h",
216         "mach/mach_time.h",
217         "mach/mach_types.h",
218         "mach/mach_vm.h",
219         "mach/thread_act.h",
220         "mach/thread_policy.h",
221         "malloc/malloc.h",
222         "net/bpf.h",
223         "net/dlil.h",
224         "net/if.h",
225         "net/if_arp.h",
226         "net/if_dl.h",
227         "net/if_utun.h",
228         "net/if_var.h",
229         "net/ndrv.h",
230         "net/route.h",
231         "netdb.h",
232         "netinet/if_ether.h",
233         "netinet/in.h",
234         "netinet/ip.h",
235         "netinet/tcp.h",
236         "netinet/udp.h",
237         "os/lock.h",
238         "os/signpost.h",
239         "poll.h",
240         "pthread.h",
241         "pthread_spis.h",
242         "pthread/introspection.h",
243         "pthread/spawn.h",
244         "pthread/stack_np.h",
245         "pwd.h",
246         "regex.h",
247         "resolv.h",
248         "sched.h",
249         "semaphore.h",
250         "signal.h",
251         "spawn.h",
252         "stddef.h",
253         "stdint.h",
254         "stdio.h",
255         "stdlib.h",
256         "string.h",
257         "sysdir.h",
258         "sys/appleapiopts.h",
259         "sys/attr.h",
260         "sys/clonefile.h",
261         "sys/event.h",
262         "sys/file.h",
263         "sys/ioctl.h",
264         "sys/ipc.h",
265         "sys/kern_control.h",
266         "sys/mman.h",
267         "sys/mount.h",
268         "sys/proc_info.h",
269         "sys/ptrace.h",
270         "sys/quota.h",
271         "sys/random.h",
272         "sys/resource.h",
273         "sys/sem.h",
274         "sys/shm.h",
275         "sys/socket.h",
276         "sys/stat.h",
277         "sys/statvfs.h",
278         "sys/sys_domain.h",
279         "sys/sysctl.h",
280         "sys/time.h",
281         "sys/times.h",
282         "sys/timex.h",
283         "sys/types.h",
284         "sys/uio.h",
285         "sys/un.h",
286         "sys/utsname.h",
287         "sys/vsock.h",
288         "sys/wait.h",
289         "sys/xattr.h",
290         "syslog.h",
291         "termios.h",
292         "time.h",
293         "unistd.h",
294         "util.h",
295         "utime.h",
296         "utmpx.h",
297         "wchar.h",
298         "xlocale.h",
299         [x86_64]: "crt_externs.h",
300     }
301 
302     cfg.skip_struct(move |ty| {
303         if ty.starts_with("__c_anonymous_") {
304             return true;
305         }
306         match ty {
307             // FIXME: actually a union
308             "sigval" => true,
309 
310             // FIXME: The size is changed in recent macOSes.
311             "malloc_zone_t" => true,
312 
313             _ => false,
314         }
315     });
316 
317     cfg.skip_type(move |ty| {
318         if ty.starts_with("__c_anonymous_") {
319             return true;
320         }
321         match ty {
322             _ => false,
323         }
324     });
325 
326     cfg.skip_const(move |name| {
327         // They're declared via `deprecated_mach` and we don't support it anymore.
328         if name.starts_with("VM_FLAGS_") {
329             return true;
330         }
331         match name {
332             // These OSX constants are removed in Sierra.
333             // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html
334             "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true,
335             // FIXME: the value has been changed since Catalina (0xffff0000 -> 0x3fff0000).
336             "SF_SETTABLE" => true,
337 
338             // FIXME: XCode 13.1 doesn't have it.
339             "TIOCREMOTE" => true,
340             _ => false,
341         }
342     });
343 
344     cfg.skip_fn(move |name| {
345         // skip those that are manually verified
346         match name {
347             // FIXME: https://github.com/rust-lang/libc/issues/1272
348             "execv" | "execve" | "execvp" => true,
349 
350             // close calls the close_nocancel system call
351             "close" => true,
352 
353             // FIXME: std removed libresolv support: https://github.com/rust-lang/rust/pull/102766
354             "res_init" => true,
355 
356             // FIXME: remove once the target in CI is updated
357             "pthread_jit_write_freeze_callbacks_np" => true,
358 
359             // FIXME: ABI has been changed on recent macOSes.
360             "os_unfair_lock_assert_owner" | "os_unfair_lock_assert_not_owner" => true,
361 
362             // FIXME: Once the SDK get updated to Ventura's level
363             "freadlink" | "mknodat" | "mkfifoat" => true,
364 
365             _ => false,
366         }
367     });
368 
369     cfg.skip_field(move |struct_, field| {
370         match (struct_, field) {
371             // FIXME: the array size has been changed since macOS 10.15 ([8] -> [7]).
372             ("statfs", "f_reserved") => true,
373             ("__darwin_arm_neon_state64", "__v") => true,
374             // MAXPATHLEN is too big for auto-derive traits on arrays.
375             ("vnode_info_path", "vip_path") => true,
376             ("ifreq", "ifr_ifru") => true,
377             ("ifkpi", "ifk_data") => true,
378             ("ifconf", "ifc_ifcu") => true,
379             _ => false,
380         }
381     });
382 
383     cfg.skip_field_type(move |struct_, field| {
384         match (struct_, field) {
385             // FIXME: actually a union
386             ("sigevent", "sigev_value") => true,
387             _ => false,
388         }
389     });
390 
391     cfg.volatile_item(|i| {
392         use ctest::VolatileItemKind::*;
393         match i {
394             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
395             _ => false,
396         }
397     });
398 
399     cfg.type_name(move |ty, is_struct, is_union| {
400         match ty {
401             // Just pass all these through, no need for a "struct" prefix
402             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
403 
404             // OSX calls this something else
405             "sighandler_t" => "sig_t".to_string(),
406 
407             t if is_union => format!("union {}", t),
408             t if t.ends_with("_t") => t.to_string(),
409             t if is_struct => format!("struct {}", t),
410             t => t.to_string(),
411         }
412     });
413 
414     cfg.field_name(move |struct_, field| {
415         match field {
416             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
417                 s.replace("e_nsec", "espec.tv_nsec")
418             }
419             // FIXME: sigaction actually contains a union with two variants:
420             // a sa_sigaction with type: (*)(int, struct __siginfo *, void *)
421             // a sa_handler with type sig_t
422             "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
423             s => s.to_string(),
424         }
425     });
426 
427     cfg.skip_roundtrip(move |s| match s {
428         // FIXME: this type has the wrong ABI
429         "max_align_t" if i686 => true,
430         // Can't return an array from a C function.
431         "uuid_t" | "vol_capabilities_set_t" => true,
432         _ => false,
433     });
434     cfg.generate("../src/lib.rs", "main.rs");
435 }
436 
437 fn test_openbsd(target: &str) {
438     assert!(target.contains("openbsd"));
439 
440     let mut cfg = ctest_cfg();
441     cfg.flag("-Wno-deprecated-declarations");
442 
443     let x86_64 = target.contains("x86_64");
444 
445     headers! { cfg:
446         "elf.h",
447         "errno.h",
448         "execinfo.h",
449         "fcntl.h",
450         "getopt.h",
451         "libgen.h",
452         "limits.h",
453         "link.h",
454         "locale.h",
455         "stddef.h",
456         "stdint.h",
457         "stdio.h",
458         "stdlib.h",
459         "sys/stat.h",
460         "sys/types.h",
461         "time.h",
462         "wchar.h",
463         "ctype.h",
464         "dirent.h",
465         "sys/socket.h",
466         [x86_64]:"machine/fpu.h",
467         "net/if.h",
468         "net/route.h",
469         "net/if_arp.h",
470         "netdb.h",
471         "netinet/in.h",
472         "netinet/ip.h",
473         "netinet/tcp.h",
474         "netinet/udp.h",
475         "net/bpf.h",
476         "regex.h",
477         "resolv.h",
478         "pthread.h",
479         "dlfcn.h",
480         "search.h",
481         "signal.h",
482         "string.h",
483         "sys/file.h",
484         "sys/futex.h",
485         "sys/ioctl.h",
486         "sys/ipc.h",
487         "sys/mman.h",
488         "sys/param.h",
489         "sys/resource.h",
490         "sys/shm.h",
491         "sys/socket.h",
492         "sys/time.h",
493         "sys/uio.h",
494         "sys/ktrace.h",
495         "sys/un.h",
496         "sys/wait.h",
497         "unistd.h",
498         "utime.h",
499         "pwd.h",
500         "grp.h",
501         "sys/utsname.h",
502         "sys/ptrace.h",
503         "sys/mount.h",
504         "sys/uio.h",
505         "sched.h",
506         "termios.h",
507         "poll.h",
508         "syslog.h",
509         "semaphore.h",
510         "sys/statvfs.h",
511         "sys/times.h",
512         "glob.h",
513         "ifaddrs.h",
514         "langinfo.h",
515         "sys/sysctl.h",
516         "utmp.h",
517         "sys/event.h",
518         "net/if_dl.h",
519         "util.h",
520         "ufs/ufs/quota.h",
521         "pthread_np.h",
522         "sys/reboot.h",
523         "sys/syscall.h",
524         "sys/shm.h",
525         "sys/param.h",
526     }
527 
528     cfg.skip_struct(move |ty| {
529         if ty.starts_with("__c_anonymous_") {
530             return true;
531         }
532         match ty {
533             // FIXME: actually a union
534             "sigval" => true,
535 
536             _ => false,
537         }
538     });
539 
540     cfg.skip_const(move |name| {
541         match name {
542             // Removed in OpenBSD 6.0
543             "KERN_USERMOUNT" | "KERN_ARND" => true,
544             // Removed in OpenBSD 7.2
545             "KERN_NSELCOLL" => true,
546             // Good chance it's going to be wrong depending on the host release
547             "KERN_MAXID" | "NET_RT_MAXID" => true,
548             "EV_SYSFLAGS" => true,
549             _ => false,
550         }
551     });
552 
553     cfg.skip_fn(move |name| {
554         match name {
555             // FIXME: https://github.com/rust-lang/libc/issues/1272
556             "execv" | "execve" | "execvp" | "execvpe" => true,
557 
558             // Removed in OpenBSD 6.5
559             // https://marc.info/?l=openbsd-cvs&m=154723400730318
560             "mincore" => true,
561 
562             // futex() has volatile arguments, but that doesn't exist in Rust.
563             "futex" => true,
564 
565             // Available for openBSD 7.3
566             "mimmutable" => true,
567 
568             _ => false,
569         }
570     });
571 
572     cfg.type_name(move |ty, is_struct, is_union| {
573         match ty {
574             // Just pass all these through, no need for a "struct" prefix
575             "FILE" | "DIR" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
576 
577             // OSX calls this something else
578             "sighandler_t" => "sig_t".to_string(),
579 
580             t if is_union => format!("union {}", t),
581             t if t.ends_with("_t") => t.to_string(),
582             t if is_struct => format!("struct {}", t),
583             t => t.to_string(),
584         }
585     });
586 
587     cfg.field_name(move |struct_, field| match field {
588         "st_birthtime" if struct_.starts_with("stat") => "__st_birthtime".to_string(),
589         "st_birthtime_nsec" if struct_.starts_with("stat") => "__st_birthtimensec".to_string(),
590         s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.replace("e_nsec", ".tv_nsec"),
591         "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
592         s => s.to_string(),
593     });
594 
595     cfg.skip_field_type(move |struct_, field| {
596         // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1
597         struct_ == "siginfo_t" && field == "si_addr"
598     });
599 
600     cfg.skip_field(|struct_, field| {
601         match (struct_, field) {
602             // conflicting with `p_type` macro from <resolve.h>.
603             ("Elf32_Phdr", "p_type") => true,
604             ("Elf64_Phdr", "p_type") => true,
605             // ifr_ifru is defined is an union
606             ("ifreq", "ifr_ifru") => true,
607             _ => false,
608         }
609     });
610 
611     cfg.generate("../src/lib.rs", "main.rs");
612 }
613 
614 fn test_windows(target: &str) {
615     assert!(target.contains("windows"));
616     let gnu = target.contains("gnu");
617 
618     let mut cfg = ctest_cfg();
619     if target.contains("msvc") {
620         cfg.flag("/wd4324");
621     }
622     cfg.define("_WIN32_WINNT", Some("0x8000"));
623 
624     headers! { cfg:
625         "direct.h",
626         "errno.h",
627         "fcntl.h",
628         "io.h",
629         "limits.h",
630         "locale.h",
631         "process.h",
632         "signal.h",
633         "stddef.h",
634         "stdint.h",
635         "stdio.h",
636         "stdlib.h",
637         "sys/stat.h",
638         "sys/types.h",
639         "sys/utime.h",
640         "time.h",
641         "wchar.h",
642         [gnu]: "ws2tcpip.h",
643         [!gnu]: "Winsock2.h",
644     }
645 
646     cfg.type_name(move |ty, is_struct, is_union| {
647         match ty {
648             // Just pass all these through, no need for a "struct" prefix
649             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
650 
651             // FIXME: these don't exist:
652             "time64_t" => "__time64_t".to_string(),
653             "ssize_t" => "SSIZE_T".to_string(),
654 
655             "sighandler_t" if !gnu => "_crt_signal_t".to_string(),
656             "sighandler_t" if gnu => "__p_sig_fn_t".to_string(),
657 
658             t if is_union => format!("union {}", t),
659             t if t.ends_with("_t") => t.to_string(),
660 
661             // Windows uppercase structs don't have `struct` in front:
662             t if is_struct => {
663                 if ty.chars().next().unwrap().is_uppercase() {
664                     t.to_string()
665                 } else if t == "stat" {
666                     "struct __stat64".to_string()
667                 } else if t == "utimbuf" {
668                     "struct __utimbuf64".to_string()
669                 } else {
670                     // put `struct` in front of all structs:
671                     format!("struct {}", t)
672                 }
673             }
674             t => t.to_string(),
675         }
676     });
677 
678     cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string());
679 
680     cfg.skip_type(move |name| match name {
681         "SSIZE_T" if !gnu => true,
682         "ssize_t" if !gnu => true,
683         _ => false,
684     });
685 
686     cfg.skip_struct(move |ty| {
687         if ty.starts_with("__c_anonymous_") {
688             return true;
689         }
690         return false;
691     });
692 
693     cfg.skip_const(move |name| {
694         match name {
695             // FIXME: API error:
696             // SIG_ERR type is "void (*)(int)", not "int"
697             "SIG_ERR" |
698             // Similar for SIG_DFL/IGN/GET/SGE/ACK
699             "SIG_DFL" | "SIG_IGN" | "SIG_GET" | "SIG_SGE" | "SIG_ACK" => true,
700             // FIXME: newer windows-gnu environment on CI?
701             "_O_OBTAIN_DIR" if gnu => true,
702             _ => false,
703         }
704     });
705 
706     cfg.skip_field(move |s, field| match s {
707         "CONTEXT" if field == "Fp" => true,
708         _ => false,
709     });
710     // FIXME: All functions point to the wrong addresses?
711     cfg.skip_fn_ptrcheck(|_| true);
712 
713     cfg.skip_signededness(move |c| {
714         match c {
715             // windows-isms
716             n if n.starts_with("P") => true,
717             n if n.starts_with("H") => true,
718             n if n.starts_with("LP") => true,
719             "sighandler_t" if gnu => true,
720             _ => false,
721         }
722     });
723 
724     cfg.skip_fn(move |name| {
725         match name {
726             // FIXME: https://github.com/rust-lang/libc/issues/1272
727             "execv" | "execve" | "execvp" | "execvpe" => true,
728 
729             _ => false,
730         }
731     });
732 
733     cfg.generate("../src/lib.rs", "main.rs");
734 }
735 
736 fn test_redox(target: &str) {
737     assert!(target.contains("redox"));
738 
739     let mut cfg = ctest_cfg();
740     cfg.flag("-Wno-deprecated-declarations");
741 
742     headers! {
743         cfg:
744         "ctype.h",
745         "dirent.h",
746         "dlfcn.h",
747         "errno.h",
748         "fcntl.h",
749         "grp.h",
750         "limits.h",
751         "locale.h",
752         "netdb.h",
753         "netinet/in.h",
754         "netinet/ip.h",
755         "netinet/tcp.h",
756         "poll.h",
757         "pwd.h",
758         "semaphore.h",
759         "string.h",
760         "strings.h",
761         "sys/file.h",
762         "sys/ioctl.h",
763         "sys/mman.h",
764         "sys/ptrace.h",
765         "sys/resource.h",
766         "sys/socket.h",
767         "sys/stat.h",
768         "sys/statvfs.h",
769         "sys/time.h",
770         "sys/types.h",
771         "sys/uio.h",
772         "sys/un.h",
773         "sys/utsname.h",
774         "sys/wait.h",
775         "termios.h",
776         "time.h",
777         "unistd.h",
778         "utime.h",
779         "wchar.h",
780     }
781 
782     cfg.generate("../src/lib.rs", "main.rs");
783 }
784 
785 fn test_solarish(target: &str) {
786     let is_solaris = target.contains("solaris");
787     let is_illumos = target.contains("illumos");
788     assert!(is_solaris || is_illumos);
789 
790     // ctest generates arguments supported only by clang, so make sure to run with CC=clang.
791     // While debugging, "CFLAGS=-ferror-limit=<large num>" is useful to get more error output.
792     let mut cfg = ctest_cfg();
793     cfg.flag("-Wno-deprecated-declarations");
794 
795     cfg.define("_XOPEN_SOURCE", Some("700"));
796     cfg.define("__EXTENSIONS__", None);
797     cfg.define("_LCONV_C99", None);
798 
799     headers! {
800         cfg:
801         "ctype.h",
802         "dirent.h",
803         "dlfcn.h",
804         "door.h",
805         "errno.h",
806         "execinfo.h",
807         "fcntl.h",
808         "getopt.h",
809         "glob.h",
810         "grp.h",
811         "ifaddrs.h",
812         "langinfo.h",
813         "limits.h",
814         "link.h",
815         "locale.h",
816         "mqueue.h",
817         "net/if.h",
818         "net/if_arp.h",
819         "net/route.h",
820         "netdb.h",
821         "netinet/in.h",
822         "netinet/ip.h",
823         "netinet/tcp.h",
824         "netinet/udp.h",
825         "poll.h",
826         "port.h",
827         "pthread.h",
828         "pwd.h",
829         "resolv.h",
830         "sched.h",
831         "semaphore.h",
832         "signal.h",
833         "stddef.h",
834         "stdint.h",
835         "stdio.h",
836         "stdlib.h",
837         "string.h",
838         "sys/auxv.h",
839         "sys/epoll.h",
840         "sys/eventfd.h",
841         "sys/file.h",
842         "sys/filio.h",
843         "sys/ioctl.h",
844         "sys/lgrp_user.h",
845         "sys/loadavg.h",
846         "sys/mkdev.h",
847         "sys/mman.h",
848         "sys/mount.h",
849         "sys/priv.h",
850         "sys/pset.h",
851         "sys/random.h",
852         "sys/resource.h",
853         "sys/sendfile.h",
854         "sys/socket.h",
855         "sys/stat.h",
856         "sys/statvfs.h",
857         "sys/stropts.h",
858         "sys/shm.h",
859         "sys/systeminfo.h",
860         "sys/time.h",
861         "sys/times.h",
862         "sys/timex.h",
863         "sys/types.h",
864         "sys/uio.h",
865         "sys/un.h",
866         "sys/utsname.h",
867         "sys/wait.h",
868         "syslog.h",
869         "termios.h",
870         "thread.h",
871         "time.h",
872         "priv.h",
873         "ucontext.h",
874         "unistd.h",
875         "utime.h",
876         "utmpx.h",
877         "wchar.h",
878     }
879 
880     cfg.skip_type(move |ty| match ty {
881         "sighandler_t" => true,
882         _ => false,
883     });
884 
885     cfg.type_name(move |ty, is_struct, is_union| match ty {
886         "FILE" => "__FILE".to_string(),
887         "DIR" | "Dl_info" => ty.to_string(),
888         t if t.ends_with("_t") => t.to_string(),
889         t if is_struct => format!("struct {}", t),
890         t if is_union => format!("union {}", t),
891         t => t.to_string(),
892     });
893 
894     cfg.field_name(move |struct_, field| {
895         match struct_ {
896             // rust struct uses raw u64, rather than union
897             "epoll_event" if field == "u64" => "data.u64".to_string(),
898             // rust struct was committed with typo for Solaris
899             "door_arg_t" if field == "dec_num" => "desc_num".to_string(),
900             "stat" if field.ends_with("_nsec") => {
901                 // expose stat.Xtim.tv_nsec fields
902                 field.trim_end_matches("e_nsec").to_string() + ".tv_nsec"
903             }
904             _ => field.to_string(),
905         }
906     });
907 
908     cfg.skip_const(move |name| match name {
909         "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK"
910         | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => true,
911 
912         // skip sighandler_t assignments
913         "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true,
914 
915         "DT_UNKNOWN" => true,
916 
917         "_UTX_LINESIZE" | "_UTX_USERSIZE" | "_UTX_PADSIZE" | "_UTX_IDSIZE" | "_UTX_HOSTSIZE" => {
918             true
919         }
920 
921         "EADI" | "EXTPROC" | "IPC_SEAT" => true,
922 
923         // This evaluates to a sysconf() call rather than a constant
924         "PTHREAD_STACK_MIN" => true,
925 
926         // EPOLLEXCLUSIVE is a relatively recent addition to the epoll interface and may not be
927         // defined on older systems.  It is, however, safe to use on systems which do not
928         // explicitly support it. (A no-op is an acceptable implementation of EPOLLEXCLUSIVE.)
929         "EPOLLEXCLUSIVE" => true,
930 
931         _ => false,
932     });
933 
934     cfg.skip_struct(move |ty| {
935         if ty.starts_with("__c_anonymous_") {
936             return true;
937         }
938         // the union handling is a mess
939         if ty.contains("door_desc_t_") {
940             return true;
941         }
942         match ty {
943             // union, not a struct
944             "sigval" => true,
945             // a bunch of solaris-only fields
946             "utmpx" if is_illumos => true,
947             _ => false,
948         }
949     });
950 
951     cfg.skip_field(move |s, field| {
952         match s {
953             // C99 sizing on this is tough
954             "dirent" if field == "d_name" => true,
955             // the union/macro makes this rough
956             "sigaction" if field == "sa_sigaction" => true,
957             // Missing in illumos
958             "sigevent" if field == "ss_sp" => true,
959             // Avoid sigval union issues
960             "sigevent" if field == "sigev_value" => true,
961             // const issues
962             "sigevent" if field == "sigev_notify_attributes" => true,
963 
964             // Avoid const and union issues
965             "door_arg" if field == "desc_ptr" => true,
966             "door_desc_t" if field == "d_data" => true,
967             "door_arg_t" if field.ends_with("_ptr") => true,
968             "door_arg_t" if field.ends_with("rbuf") => true,
969 
970             // anonymous union challenges
971             "fpregset_t" if field == "fp_reg_set" => true,
972 
973             // The LX brand (integrated into some illumos distros) commandeered several of the
974             // `uc_filler` fields to use for brand-specific state.
975             "ucontext_t" if is_illumos && (field == "uc_filler" || field == "uc_brand_data") => {
976                 true
977             }
978 
979             _ => false,
980         }
981     });
982 
983     cfg.skip_fn(move |name| {
984         // skip those that are manually verified
985         match name {
986             // const-ness only added recently
987             "dladdr" => true,
988 
989             // Definition of those functions as changed since unified headers
990             // from NDK r14b These changes imply some API breaking changes but
991             // are still ABI compatible. We can wait for the next major release
992             // to be compliant with the new API.
993             //
994             // FIXME: unskip these for next major release
995             "setpriority" | "personality" => true,
996 
997             // signal is defined in terms of sighandler_t, so ignore
998             "signal" => true,
999 
1000             // Currently missing
1001             "cfmakeraw" | "cfsetspeed" => true,
1002 
1003             // const-ness issues
1004             "execv" | "execve" | "execvp" | "settimeofday" | "sethostname" => true,
1005 
1006             // Solaris-different
1007             "getpwent_r" | "getgrent_r" | "updwtmpx" if is_illumos => true,
1008             "madvise" | "mprotect" if is_illumos => true,
1009             "door_call" | "door_return" | "door_create" if is_illumos => true,
1010 
1011             // The compat functions use these "native" functions linked to their
1012             // non-prefixed implementations in libc.
1013             "native_getpwent_r" | "native_getgrent_r" => true,
1014 
1015             // Not visible when build with _XOPEN_SOURCE=700
1016             "mmapobj" | "mmap64" | "meminfo" | "getpagesizes" | "getpagesizes2" => true,
1017 
1018             // These functions may return int or void depending on the exact
1019             // configuration of the compilation environment, but the return
1020             // value is not useful (always 0) so we can ignore it:
1021             "setservent" | "endservent" if is_illumos => true,
1022 
1023             // Following illumos#3729, getifaddrs was changed to a
1024             // redefine_extname symbol in order to preserve compatibility.
1025             // Until better symbol binding story is figured out, it must be
1026             // excluded from the tests.
1027             "getifaddrs" if is_illumos => true,
1028 
1029             _ => false,
1030         }
1031     });
1032 
1033     cfg.generate("../src/lib.rs", "main.rs");
1034 }
1035 
1036 fn test_netbsd(target: &str) {
1037     assert!(target.contains("netbsd"));
1038     let mut cfg = ctest_cfg();
1039 
1040     cfg.flag("-Wno-deprecated-declarations");
1041     cfg.define("_NETBSD_SOURCE", Some("1"));
1042 
1043     headers! {
1044         cfg:
1045         "elf.h",
1046         "errno.h",
1047         "fcntl.h",
1048         "getopt.h",
1049         "libgen.h",
1050         "limits.h",
1051         "link.h",
1052         "locale.h",
1053         "stddef.h",
1054         "stdint.h",
1055         "stdio.h",
1056         "stdlib.h",
1057         "sys/stat.h",
1058         "sys/types.h",
1059         "time.h",
1060         "wchar.h",
1061         "aio.h",
1062         "ctype.h",
1063         "dirent.h",
1064         "dlfcn.h",
1065         "glob.h",
1066         "grp.h",
1067         "ifaddrs.h",
1068         "langinfo.h",
1069         "net/bpf.h",
1070         "net/if.h",
1071         "net/if_arp.h",
1072         "net/if_dl.h",
1073         "net/route.h",
1074         "netdb.h",
1075         "netinet/in.h",
1076         "netinet/ip.h",
1077         "netinet/tcp.h",
1078         "netinet/udp.h",
1079         "poll.h",
1080         "pthread.h",
1081         "pwd.h",
1082         "regex.h",
1083         "resolv.h",
1084         "sched.h",
1085         "semaphore.h",
1086         "signal.h",
1087         "string.h",
1088         "sys/endian.h",
1089         "sys/exec_elf.h",
1090         "sys/xattr.h",
1091         "sys/extattr.h",
1092         "sys/file.h",
1093         "sys/ioctl.h",
1094         "sys/ioctl_compat.h",
1095         "sys/ipc.h",
1096         "sys/ktrace.h",
1097         "sys/mman.h",
1098         "sys/mount.h",
1099         "sys/ptrace.h",
1100         "sys/resource.h",
1101         "sys/shm.h",
1102         "sys/socket.h",
1103         "sys/statvfs.h",
1104         "sys/sysctl.h",
1105         "sys/time.h",
1106         "sys/times.h",
1107         "sys/timex.h",
1108         "sys/ucontext.h",
1109         "sys/ucred.h",
1110         "sys/uio.h",
1111         "sys/un.h",
1112         "sys/utsname.h",
1113         "sys/wait.h",
1114         "syslog.h",
1115         "termios.h",
1116         "ufs/ufs/quota.h",
1117         "ufs/ufs/quota1.h",
1118         "unistd.h",
1119         "util.h",
1120         "utime.h",
1121         "mqueue.h",
1122         "netinet/dccp.h",
1123         "sys/event.h",
1124         "sys/quota.h",
1125         "sys/reboot.h",
1126         "sys/shm.h",
1127         "iconv.h",
1128     }
1129 
1130     cfg.type_name(move |ty, is_struct, is_union| {
1131         match ty {
1132             // Just pass all these through, no need for a "struct" prefix
1133             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
1134             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
1135             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
1136 
1137             // OSX calls this something else
1138             "sighandler_t" => "sig_t".to_string(),
1139 
1140             t if is_union => format!("union {}", t),
1141 
1142             t if t.ends_with("_t") => t.to_string(),
1143 
1144             // put `struct` in front of all structs:.
1145             t if is_struct => format!("struct {}", t),
1146 
1147             t => t.to_string(),
1148         }
1149     });
1150 
1151     cfg.field_name(move |struct_, field| {
1152         match field {
1153             // Our stat *_nsec fields normally don't actually exist but are part
1154             // of a timeval struct
1155             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1156                 s.replace("e_nsec", ".tv_nsec")
1157             }
1158             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1159             s => s.to_string(),
1160         }
1161     });
1162 
1163     cfg.skip_type(move |ty| {
1164         if ty.starts_with("__c_anonymous_") {
1165             return true;
1166         }
1167         match ty {
1168             // FIXME: sighandler_t is crazy across platforms
1169             "sighandler_t" => true,
1170             _ => false,
1171         }
1172     });
1173 
1174     cfg.skip_struct(move |ty| {
1175         match ty {
1176             // This is actually a union, not a struct
1177             "sigval" => true,
1178             // These are tested as part of the linux_fcntl tests since there are
1179             // header conflicts when including them with all the other structs.
1180             "termios2" => true,
1181             _ => false,
1182         }
1183     });
1184 
1185     cfg.skip_signededness(move |c| {
1186         match c {
1187             "LARGE_INTEGER" | "float" | "double" => true,
1188             n if n.starts_with("pthread") => true,
1189             // sem_t is a struct or pointer
1190             "sem_t" => true,
1191             _ => false,
1192         }
1193     });
1194 
1195     cfg.skip_const(move |name| {
1196         match name {
1197             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1198             "SIGUNUSED" => true,                       // removed in glibc 2.26
1199 
1200             // weird signed extension or something like that?
1201             "MS_NOUSER" => true,
1202             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1203             "BOTHER" => true,
1204             "GRND_RANDOM" | "GRND_INSECURE" | "GRND_NONBLOCK" => true, // netbsd 10 minimum
1205 
1206             _ => false,
1207         }
1208     });
1209 
1210     cfg.skip_fn(move |name| {
1211         match name {
1212             // FIXME: https://github.com/rust-lang/libc/issues/1272
1213             "execv" | "execve" | "execvp" => true,
1214             // FIXME: netbsd 10 minimum
1215             "getentropy" | "getrandom" => true,
1216 
1217             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1218             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1219             "prlimit" | "prlimit64" |        // non-int in 2nd arg
1220 
1221             _ => false,
1222         }
1223     });
1224 
1225     cfg.skip_field_type(move |struct_, field| {
1226         // This is a weird union, don't check the type.
1227         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1228         // sighandler_t type is super weird
1229         (struct_ == "sigaction" && field == "sa_sigaction") ||
1230         // sigval is actually a union, but we pretend it's a struct
1231         (struct_ == "sigevent" && field == "sigev_value") ||
1232         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1233         (struct_ == "aiocb" && field == "aio_buf")
1234     });
1235 
1236     cfg.skip_field(|struct_, field| {
1237         match (struct_, field) {
1238             // conflicting with `p_type` macro from <resolve.h>.
1239             ("Elf32_Phdr", "p_type") => true,
1240             ("Elf64_Phdr", "p_type") => true,
1241             // pthread_spin_t is a volatile uchar
1242             ("pthread_spinlock_t", "pts_spin") => true,
1243             _ => false,
1244         }
1245     });
1246 
1247     cfg.generate("../src/lib.rs", "main.rs");
1248 }
1249 
1250 fn test_dragonflybsd(target: &str) {
1251     assert!(target.contains("dragonfly"));
1252     let mut cfg = ctest_cfg();
1253     cfg.flag("-Wno-deprecated-declarations");
1254 
1255     headers! {
1256         cfg:
1257         "aio.h",
1258         "ctype.h",
1259         "dirent.h",
1260         "dlfcn.h",
1261         "errno.h",
1262         "execinfo.h",
1263         "fcntl.h",
1264         "getopt.h",
1265         "glob.h",
1266         "grp.h",
1267         "ifaddrs.h",
1268         "kenv.h",
1269         "kvm.h",
1270         "langinfo.h",
1271         "libgen.h",
1272         "limits.h",
1273         "link.h",
1274         "locale.h",
1275         "mqueue.h",
1276         "net/bpf.h",
1277         "net/if.h",
1278         "net/if_arp.h",
1279         "net/if_dl.h",
1280         "net/route.h",
1281         "netdb.h",
1282         "netinet/in.h",
1283         "netinet/ip.h",
1284         "netinet/tcp.h",
1285         "netinet/udp.h",
1286         "poll.h",
1287         "pthread.h",
1288         "pthread_np.h",
1289         "pwd.h",
1290         "regex.h",
1291         "resolv.h",
1292         "sched.h",
1293         "semaphore.h",
1294         "signal.h",
1295         "stddef.h",
1296         "stdint.h",
1297         "stdio.h",
1298         "stdlib.h",
1299         "string.h",
1300         "sys/event.h",
1301         "sys/file.h",
1302         "sys/ioctl.h",
1303         "sys/cpuctl.h",
1304         "sys/eui64.h",
1305         "sys/ipc.h",
1306         "sys/kinfo.h",
1307         "sys/ktrace.h",
1308         "sys/malloc.h",
1309         "sys/mman.h",
1310         "sys/mount.h",
1311         "sys/procctl.h",
1312         "sys/ptrace.h",
1313         "sys/reboot.h",
1314         "sys/resource.h",
1315         "sys/rtprio.h",
1316         "sys/sched.h",
1317         "sys/shm.h",
1318         "sys/socket.h",
1319         "sys/stat.h",
1320         "sys/statvfs.h",
1321         "sys/sysctl.h",
1322         "sys/time.h",
1323         "sys/times.h",
1324         "sys/timex.h",
1325         "sys/types.h",
1326         "sys/checkpoint.h",
1327         "sys/uio.h",
1328         "sys/un.h",
1329         "sys/utsname.h",
1330         "sys/wait.h",
1331         "syslog.h",
1332         "termios.h",
1333         "time.h",
1334         "ucontext.h",
1335         "unistd.h",
1336         "util.h",
1337         "utime.h",
1338         "utmpx.h",
1339         "vfs/ufs/quota.h",
1340         "vm/vm_map.h",
1341         "wchar.h",
1342         "iconv.h",
1343     }
1344 
1345     cfg.type_name(move |ty, is_struct, is_union| {
1346         match ty {
1347             // Just pass all these through, no need for a "struct" prefix
1348             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
1349             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
1350             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
1351 
1352             // FIXME: OSX calls this something else
1353             "sighandler_t" => "sig_t".to_string(),
1354 
1355             t if is_union => format!("union {}", t),
1356 
1357             t if t.ends_with("_t") => t.to_string(),
1358 
1359             // sigval is a struct in Rust, but a union in C:
1360             "sigval" => format!("union sigval"),
1361 
1362             // put `struct` in front of all structs:.
1363             t if is_struct => format!("struct {}", t),
1364 
1365             t => t.to_string(),
1366         }
1367     });
1368 
1369     cfg.field_name(move |struct_, field| {
1370         match field {
1371             // Our stat *_nsec fields normally don't actually exist but are part
1372             // of a timeval struct
1373             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1374                 s.replace("e_nsec", ".tv_nsec")
1375             }
1376             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1377             // Field is named `type` in C but that is a Rust keyword,
1378             // so these fields are translated to `type_` in the bindings.
1379             "type_" if struct_ == "rtprio" => "type".to_string(),
1380             s => s.to_string(),
1381         }
1382     });
1383 
1384     cfg.skip_type(move |ty| {
1385         match ty {
1386             // sighandler_t is crazy across platforms
1387             "sighandler_t" => true,
1388 
1389             _ => false,
1390         }
1391     });
1392 
1393     cfg.skip_struct(move |ty| {
1394         if ty.starts_with("__c_anonymous_") {
1395             return true;
1396         }
1397         match ty {
1398             // FIXME: These are tested as part of the linux_fcntl tests since
1399             // there are header conflicts when including them with all the other
1400             // structs.
1401             "termios2" => true,
1402 
1403             _ => false,
1404         }
1405     });
1406 
1407     cfg.skip_signededness(move |c| {
1408         match c {
1409             "LARGE_INTEGER" | "float" | "double" => true,
1410             // uuid_t is a struct, not an integer.
1411             "uuid_t" => true,
1412             n if n.starts_with("pthread") => true,
1413             // sem_t is a struct or pointer
1414             "sem_t" => true,
1415             // mqd_t is a pointer on DragonFly
1416             "mqd_t" => true,
1417 
1418             _ => false,
1419         }
1420     });
1421 
1422     cfg.skip_const(move |name| {
1423         match name {
1424             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1425 
1426             // weird signed extension or something like that?
1427             "MS_NOUSER" => true,
1428             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1429 
1430             // These are defined for Solaris 11, but the crate is tested on
1431             // illumos, where they are currently not defined
1432             "EADI" | "PORT_SOURCE_POSTWAIT" | "PORT_SOURCE_SIGNAL" | "PTHREAD_STACK_MIN" => true,
1433 
1434             _ => false,
1435         }
1436     });
1437 
1438     cfg.skip_fn(move |name| {
1439         // skip those that are manually verified
1440         match name {
1441             // FIXME: https://github.com/rust-lang/libc/issues/1272
1442             "execv" | "execve" | "execvp" | "fexecve" => true,
1443 
1444             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1445             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1446             "prlimit" | "prlimit64"        // non-int in 2nd arg
1447              => true,
1448 
1449             _ => false,
1450         }
1451     });
1452 
1453     cfg.skip_field_type(move |struct_, field| {
1454         // This is a weird union, don't check the type.
1455         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1456         // sighandler_t type is super weird
1457         (struct_ == "sigaction" && field == "sa_sigaction") ||
1458         // sigval is actually a union, but we pretend it's a struct
1459         (struct_ == "sigevent" && field == "sigev_value") ||
1460         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1461         (struct_ == "aiocb" && field == "aio_buf")
1462     });
1463 
1464     cfg.skip_field(move |struct_, field| {
1465         // this is actually a union on linux, so we can't represent it well and
1466         // just insert some padding.
1467         (struct_ == "siginfo_t" && field == "_pad") ||
1468         // sigev_notify_thread_id is actually part of a sigev_un union
1469         (struct_ == "sigevent" && field == "sigev_notify_thread_id")
1470     });
1471 
1472     cfg.generate("../src/lib.rs", "main.rs");
1473 }
1474 
1475 fn test_wasi(target: &str) {
1476     assert!(target.contains("wasi"));
1477 
1478     let mut cfg = ctest_cfg();
1479     cfg.define("_GNU_SOURCE", None);
1480 
1481     headers! { cfg:
1482         "ctype.h",
1483         "dirent.h",
1484         "errno.h",
1485         "fcntl.h",
1486         "limits.h",
1487         "locale.h",
1488         "malloc.h",
1489         "poll.h",
1490         "sched.h",
1491         "stdbool.h",
1492         "stddef.h",
1493         "stdint.h",
1494         "stdio.h",
1495         "stdlib.h",
1496         "string.h",
1497         "sys/resource.h",
1498         "sys/select.h",
1499         "sys/socket.h",
1500         "sys/stat.h",
1501         "sys/times.h",
1502         "sys/types.h",
1503         "sys/uio.h",
1504         "sys/utsname.h",
1505         "sys/ioctl.h",
1506         "time.h",
1507         "unistd.h",
1508         "wasi/api.h",
1509         "wasi/libc.h",
1510         "wasi/libc-find-relpath.h",
1511         "wasi/libc-nocwd.h",
1512         "wchar.h",
1513     }
1514 
1515     cfg.type_name(move |ty, is_struct, is_union| match ty {
1516         "FILE" | "fd_set" | "DIR" => ty.to_string(),
1517         t if is_union => format!("union {}", t),
1518         t if t.starts_with("__wasi") && t.ends_with("_u") => format!("union {}", t),
1519         t if t.starts_with("__wasi") && is_struct => format!("struct {}", t),
1520         t if t.ends_with("_t") => t.to_string(),
1521         t if is_struct => format!("struct {}", t),
1522         t => t.to_string(),
1523     });
1524 
1525     cfg.field_name(move |_struct, field| {
1526         match field {
1527             // deal with fields as rust keywords
1528             "type_" => "type".to_string(),
1529             s => s.to_string(),
1530         }
1531     });
1532 
1533     // Looks like LLD doesn't merge duplicate imports, so if the Rust
1534     // code imports from a module and the C code also imports from a
1535     // module we end up with two imports of function pointers which
1536     // import the same thing but have different function pointers
1537     cfg.skip_fn_ptrcheck(|f| f.starts_with("__wasi"));
1538 
1539     // d_name is declared as a flexible array in WASI libc, so it
1540     // doesn't support sizeof.
1541     cfg.skip_field(|s, field| s == "dirent" && field == "d_name");
1542 
1543     // Currently Rust/clang disagree on function argument ABI, so skip these
1544     // tests. For more info see WebAssembly/tool-conventions#88
1545     cfg.skip_roundtrip(|_| true);
1546 
1547     cfg.generate("../src/lib.rs", "main.rs");
1548 }
1549 
1550 fn test_android(target: &str) {
1551     assert!(target.contains("android"));
1552     let target_pointer_width = match target {
1553         t if t.contains("aarch64") || t.contains("x86_64") => 64,
1554         t if t.contains("i686") || t.contains("arm") => 32,
1555         t => panic!("unsupported target: {}", t),
1556     };
1557     let x86 = target.contains("i686") || target.contains("x86_64");
1558     let aarch64 = target.contains("aarch64");
1559 
1560     let mut cfg = ctest_cfg();
1561     cfg.define("_GNU_SOURCE", None);
1562 
1563     headers! { cfg:
1564                "arpa/inet.h",
1565                "ctype.h",
1566                "dirent.h",
1567                "dlfcn.h",
1568                "elf.h",
1569                "errno.h",
1570                "fcntl.h",
1571                "getopt.h",
1572                "grp.h",
1573                "ifaddrs.h",
1574                "libgen.h",
1575                "limits.h",
1576                "link.h",
1577                "linux/sysctl.h",
1578                "locale.h",
1579                "malloc.h",
1580                "net/ethernet.h",
1581                "net/if.h",
1582                "net/if_arp.h",
1583                "net/route.h",
1584                "netdb.h",
1585                "netinet/in.h",
1586                "netinet/ip.h",
1587                "netinet/tcp.h",
1588                "netinet/udp.h",
1589                "netpacket/packet.h",
1590                "poll.h",
1591                "pthread.h",
1592                "pty.h",
1593                "pwd.h",
1594                "regex.h",
1595                "resolv.h",
1596                "sched.h",
1597                "semaphore.h",
1598                "signal.h",
1599                "stddef.h",
1600                "stdint.h",
1601                "stdio.h",
1602                "stdlib.h",
1603                "string.h",
1604                "sys/auxv.h",
1605                "sys/epoll.h",
1606                "sys/eventfd.h",
1607                "sys/file.h",
1608                "sys/fsuid.h",
1609                "sys/inotify.h",
1610                "sys/ioctl.h",
1611                "sys/mman.h",
1612                "sys/mount.h",
1613                "sys/personality.h",
1614                "sys/prctl.h",
1615                "sys/ptrace.h",
1616                "sys/random.h",
1617                "sys/reboot.h",
1618                "sys/resource.h",
1619                "sys/sendfile.h",
1620                "sys/signalfd.h",
1621                "sys/socket.h",
1622                "sys/stat.h",
1623                "sys/statvfs.h",
1624                "sys/swap.h",
1625                "sys/syscall.h",
1626                "sys/sysinfo.h",
1627                "sys/system_properties.h",
1628                "sys/time.h",
1629                "sys/timerfd.h",
1630                "sys/times.h",
1631                "sys/types.h",
1632                "sys/ucontext.h",
1633                "sys/uio.h",
1634                "sys/un.h",
1635                "sys/user.h",
1636                "sys/utsname.h",
1637                "sys/vfs.h",
1638                "sys/xattr.h",
1639                "sys/wait.h",
1640                "syslog.h",
1641                "termios.h",
1642                "time.h",
1643                "unistd.h",
1644                "utime.h",
1645                "utmp.h",
1646                "wchar.h",
1647                "xlocale.h",
1648                // time64_t is not defined for 64-bit targets If included it will
1649                // generate the error 'Your time_t is already 64-bit'
1650                [target_pointer_width == 32]: "time64.h",
1651                [x86]: "sys/reg.h",
1652     }
1653 
1654     // Include linux headers at the end:
1655     headers! { cfg:
1656                 "asm/mman.h",
1657                 "linux/auxvec.h",
1658                 "linux/dccp.h",
1659                 "linux/elf.h",
1660                 "linux/errqueue.h",
1661                 "linux/falloc.h",
1662                 "linux/filter.h",
1663                 "linux/futex.h",
1664                 "linux/fs.h",
1665                 "linux/genetlink.h",
1666                 "linux/if_alg.h",
1667                 "linux/if_addr.h",
1668                 "linux/if_ether.h",
1669                 "linux/if_link.h",
1670                 "linux/rtnetlink.h",
1671                 "linux/if_tun.h",
1672                 "linux/kexec.h",
1673                 "linux/magic.h",
1674                 "linux/membarrier.h",
1675                 "linux/memfd.h",
1676                 "linux/mempolicy.h",
1677                 "linux/module.h",
1678                 "linux/mount.h",
1679                 "linux/net_tstamp.h",
1680                 "linux/netfilter/nfnetlink.h",
1681                 "linux/netfilter/nfnetlink_log.h",
1682                 "linux/netfilter/nfnetlink_queue.h",
1683                 "linux/netfilter/nf_tables.h",
1684                 "linux/netfilter_ipv4.h",
1685                 "linux/netfilter_ipv6.h",
1686                 "linux/netfilter_ipv6/ip6_tables.h",
1687                 "linux/netlink.h",
1688                 "linux/quota.h",
1689                 "linux/reboot.h",
1690                 "linux/seccomp.h",
1691                 "linux/sched.h",
1692                 "linux/sockios.h",
1693                 "linux/uinput.h",
1694                 "linux/vm_sockets.h",
1695                 "linux/wait.h",
1696 
1697     }
1698 
1699     // Include Android-specific headers:
1700     headers! { cfg:
1701                 "android/set_abort_message.h"
1702     }
1703 
1704     cfg.type_name(move |ty, is_struct, is_union| {
1705         match ty {
1706             // Just pass all these through, no need for a "struct" prefix
1707             "FILE" | "fd_set" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
1708 
1709             t if is_union => format!("union {}", t),
1710 
1711             t if t.ends_with("_t") => t.to_string(),
1712 
1713             // sigval is a struct in Rust, but a union in C:
1714             "sigval" => format!("union sigval"),
1715 
1716             // put `struct` in front of all structs:.
1717             t if is_struct => format!("struct {}", t),
1718 
1719             t => t.to_string(),
1720         }
1721     });
1722 
1723     cfg.field_name(move |struct_, field| {
1724         match field {
1725             // Our stat *_nsec fields normally don't actually exist but are part
1726             // of a timeval struct
1727             s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.to_string(),
1728             // FIXME: appears that `epoll_event.data` is an union
1729             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1730             // The following structs have a field called `type` in C,
1731             // but `type` is a Rust keyword, so these fields are translated
1732             // to `type_` in Rust.
1733             "type_"
1734                 if struct_ == "input_event"
1735                     || struct_ == "input_mask"
1736                     || struct_ == "ff_effect" =>
1737             {
1738                 "type".to_string()
1739             }
1740 
1741             s => s.to_string(),
1742         }
1743     });
1744 
1745     cfg.skip_type(move |ty| {
1746         match ty {
1747             // FIXME: `sighandler_t` type is incorrect, see:
1748             // https://github.com/rust-lang/libc/issues/1359
1749             "sighandler_t" => true,
1750 
1751             // These are tested in the `linux_elf.rs` file.
1752             "Elf64_Phdr" | "Elf32_Phdr" => true,
1753             _ => false,
1754         }
1755     });
1756 
1757     cfg.skip_struct(move |ty| {
1758         if ty.starts_with("__c_anonymous_") {
1759             return true;
1760         }
1761         match ty {
1762             // These are tested as part of the linux_fcntl tests since there are
1763             // header conflicts when including them with all the other structs.
1764             "termios2" => true,
1765             // uc_sigmask and uc_sigmask64 of ucontext_t are an anonymous union
1766             "ucontext_t" => true,
1767             // 'private' type
1768             "prop_info" => true,
1769 
1770             // These are tested in the `linux_elf.rs` file.
1771             "Elf64_Phdr" | "Elf32_Phdr" => true,
1772 
1773             // FIXME: The type of `iv` has been changed.
1774             "af_alg_iv" => true,
1775 
1776             // FIXME: The size of struct has been changed:
1777             "inotify_event" => true,
1778             // FIXME: The field has been changed:
1779             "sockaddr_vm" => true,
1780 
1781             _ => false,
1782         }
1783     });
1784 
1785     cfg.skip_const(move |name| {
1786         match name {
1787             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
1788             | "IPV6_FLOWINFO"
1789             | "IPV6_FLOWLABEL_MGR"
1790             | "IPV6_FLOWINFO_SEND"
1791             | "IPV6_FLOWINFO_FLOWLABEL"
1792             | "IPV6_FLOWINFO_PRIORITY"
1793             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
1794             | "F_CANCELLK"
1795             | "F_ADD_SEALS"
1796             | "F_GET_SEALS"
1797             | "F_SEAL_SEAL"
1798             | "F_SEAL_SHRINK"
1799             | "F_SEAL_GROW"
1800             | "F_SEAL_WRITE" => true,
1801 
1802             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests:
1803             "ARPHRD_CAN" => true,
1804 
1805             // FIXME: deprecated: not available in any header
1806             // See: https://github.com/rust-lang/libc/issues/1356
1807             "ENOATTR" => true,
1808 
1809             // FIXME: still necessary?
1810             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1811             // FIXME: deprecated - removed in glibc 2.26
1812             "SIGUNUSED" => true,
1813 
1814             // Needs a newer Android SDK for the definition
1815             "P_PIDFD" => true,
1816 
1817             // Requires Linux kernel 5.6
1818             "VMADDR_CID_LOCAL" => true,
1819 
1820             // FIXME: conflicts with standard C headers and is tested in
1821             // `linux_termios.rs` below:
1822             "BOTHER" => true,
1823             "IBSHIFT" => true,
1824             "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => true,
1825 
1826             // is a private value for kernel usage normally
1827             "FUSE_SUPER_MAGIC" => true,
1828             // linux 5.12 min
1829             "MPOL_F_NUMA_BALANCING" => true,
1830 
1831             // GRND_INSECURE was added in platform-tools-30.0.0
1832             "GRND_INSECURE" => true,
1833 
1834             // kernel 5.10 minimum required
1835             "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ" | "MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ" => true,
1836 
1837             // kernel 5.18 minimum
1838             | "MADV_COLD"
1839             | "MADV_DONTNEED_LOCKED"
1840             | "MADV_PAGEOUT"
1841             | "MADV_POPULATE_READ"
1842             | "MADV_POPULATE_WRITE" => true,
1843 
1844             // kernel 5.6 minimum required
1845             "IPPROTO_MPTCP" | "IPPROTO_ETHERNET" => true,
1846 
1847             // kernel 6.2 minimum
1848             "TUN_F_USO4" | "TUN_F_USO6" | "IFF_NO_CARRIER" => true,
1849 
1850             // FIXME: NDK r22 minimum required
1851             | "FDB_NOTIFY_BIT"
1852             | "FDB_NOTIFY_INACTIVE_BIT"
1853             | "IFLA_ALT_IFNAME"
1854             | "IFLA_PERM_ADDRESS"
1855             | "IFLA_PROP_LIST"
1856             | "IFLA_PROTO_DOWN_REASON"
1857             | "NDA_FDB_EXT_ATTRS"
1858             | "NDA_NH_ID"
1859             | "NFEA_ACTIVITY_NOTIFY"
1860             | "NFEA_DONT_REFRESH"
1861             | "NFEA_UNSPEC" => true,
1862 
1863             // FIXME: NDK r23 minimum required
1864             | "IFLA_PARENT_DEV_BUS_NAME"
1865             | "IFLA_PARENT_DEV_NAME" => true,
1866 
1867             // FIXME: NDK r25 minimum required
1868             | "IFLA_GRO_MAX_SIZE"
1869             | "NDA_FLAGS_EXT"
1870             | "NTF_EXT_MANAGED" => true,
1871 
1872             // FIXME: NDK above r25 required
1873             | "IFLA_ALLMULTI"
1874             | "IFLA_DEVLINK_PORT"
1875             | "IFLA_GRO_IPV4_MAX_SIZE"
1876             | "IFLA_GSO_IPV4_MAX_SIZE"
1877             | "IFLA_TSO_MAX_SEGS"
1878             | "IFLA_TSO_MAX_SIZE"
1879             | "NDA_NDM_STATE_MASK"
1880             | "NDA_NDM_FLAGS_MASK"
1881             | "NDTPA_INTERVAL_PROBE_TIME_MS"
1882             | "NFQA_UNSPEC"
1883             | "NTF_EXT_LOCKED"
1884             | "ALG_SET_DRBG_ENTROPY" => true,
1885 
1886             // FIXME: Something has been changed on r26b:
1887             | "IPPROTO_MAX"
1888             | "NFNL_SUBSYS_COUNT"
1889             | "NF_NETDEV_NUMHOOKS"
1890             | "NFT_MSG_MAX"
1891             | "SW_MAX"
1892             | "SW_CNT" => true,
1893 
1894             // FIXME: aarch64 env cannot find it:
1895             | "PTRACE_GETREGS"
1896             | "PTRACE_SETREGS" if aarch64 => true,
1897             // FIXME: The value has been changed on r26b:
1898             | "SYS_syscalls" if aarch64 => true,
1899 
1900             _ => false,
1901         }
1902     });
1903 
1904     cfg.skip_fn(move |name| {
1905         // skip those that are manually verified
1906         match name {
1907             // FIXME: https://github.com/rust-lang/libc/issues/1272
1908             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1909 
1910             // There are two versions of the sterror_r function, see
1911             //
1912             // https://linux.die.net/man/3/strerror_r
1913             //
1914             // An XSI-compliant version provided if:
1915             //
1916             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
1917             //
1918             // and a GNU specific version provided if _GNU_SOURCE is defined.
1919             //
1920             // libc provides bindings for the XSI-compliant version, which is
1921             // preferred for portable applications.
1922             //
1923             // We skip the test here since here _GNU_SOURCE is defined, and
1924             // test the XSI version below.
1925             "strerror_r" => true,
1926             "reallocarray" => true,
1927             "__system_property_wait" => true,
1928 
1929             // Added in API level 30, but tests use level 28.
1930             "mlock2" => true,
1931 
1932             // Added in glibc 2.25.
1933             "getentropy" => true,
1934 
1935             // Added in API level 28, but some tests use level 24.
1936             "getrandom" => true,
1937 
1938             // Added in API level 28, but some tests use level 24.
1939             "syncfs" => true,
1940 
1941             // Added in API level 28, but some tests use level 24.
1942             "pthread_attr_getinheritsched" | "pthread_attr_setinheritsched" => true,
1943             // Added in API level 28, but some tests use level 24.
1944             "fread_unlocked" | "fwrite_unlocked" | "fgets_unlocked" | "fflush_unlocked" => true,
1945 
1946             // FIXME: bad function pointers:
1947             "isalnum" | "isalpha" | "iscntrl" | "isdigit" | "isgraph" | "islower" | "isprint"
1948             | "ispunct" | "isspace" | "isupper" | "isxdigit" | "isblank" | "tolower"
1949             | "toupper" => true,
1950 
1951             _ => false,
1952         }
1953     });
1954 
1955     cfg.skip_field_type(move |struct_, field| {
1956         // This is a weird union, don't check the type.
1957         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1958         // sigval is actually a union, but we pretend it's a struct
1959         (struct_ == "sigevent" && field == "sigev_value") ||
1960         // this one is an anonymous union
1961         (struct_ == "ff_effect" && field == "u") ||
1962         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1963         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1964         (struct_ == "sigaction" && field == "sa_sigaction") ||
1965         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1966         (struct_ == "signalfd_siginfo" && field == "ssi_call_addr") ||
1967         // FIXME: Seems the type has been changed on NDK r26b
1968         (struct_ == "flock64" && (field == "l_start" || field == "l_len"))
1969     });
1970 
1971     cfg.skip_field(move |struct_, field| {
1972         // this is actually a union on linux, so we can't represent it well and
1973         // just insert some padding.
1974         (struct_ == "siginfo_t" && field == "_pad") ||
1975         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1976         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1977         (struct_ == "sigaction" && field == "sa_sigaction") ||
1978         // sigev_notify_thread_id is actually part of a sigev_un union
1979         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1980         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1981         (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
1982                                            field == "ssi_call_addr" ||
1983                                            field == "ssi_arch"))
1984     });
1985 
1986     cfg.skip_field(|struct_, field| {
1987         match (struct_, field) {
1988             // conflicting with `p_type` macro from <resolve.h>.
1989             ("Elf32_Phdr", "p_type") => true,
1990             ("Elf64_Phdr", "p_type") => true,
1991 
1992             // this is actually a union on linux, so we can't represent it well and
1993             // just insert some padding.
1994             ("siginfo_t", "_pad") => true,
1995 
1996             _ => false,
1997         }
1998     });
1999 
2000     cfg.generate("../src/lib.rs", "main.rs");
2001 
2002     test_linux_like_apis(target);
2003 }
2004 
2005 fn test_freebsd(target: &str) {
2006     assert!(target.contains("freebsd"));
2007     let mut cfg = ctest_cfg();
2008 
2009     let freebsd_ver = which_freebsd();
2010 
2011     match freebsd_ver {
2012         Some(12) => cfg.cfg("freebsd12", None),
2013         Some(13) => cfg.cfg("freebsd13", None),
2014         Some(14) => cfg.cfg("freebsd14", None),
2015         _ => &mut cfg,
2016     };
2017 
2018     // For sched linux compat fn
2019     cfg.define("_WITH_CPU_SET_T", None);
2020     // Required for `getline`:
2021     cfg.define("_WITH_GETLINE", None);
2022     // Required for making freebsd11_stat available in the headers
2023     cfg.define("_WANT_FREEBSD11_STAT", None);
2024 
2025     let freebsd13 = match freebsd_ver {
2026         Some(n) if n >= 13 => true,
2027         _ => false,
2028     };
2029     let freebsd14 = match freebsd_ver {
2030         Some(n) if n >= 14 => true,
2031         _ => false,
2032     };
2033 
2034     headers! { cfg:
2035                 "aio.h",
2036                 "arpa/inet.h",
2037                 "bsm/audit.h",
2038                 "ctype.h",
2039                 "dirent.h",
2040                 "dlfcn.h",
2041                 "elf.h",
2042                 "errno.h",
2043                 "execinfo.h",
2044                 "fcntl.h",
2045                 "getopt.h",
2046                 "glob.h",
2047                 "grp.h",
2048                 "iconv.h",
2049                 "ifaddrs.h",
2050                 "kenv.h",
2051                 "langinfo.h",
2052                 "libgen.h",
2053                 "libutil.h",
2054                 "limits.h",
2055                 "link.h",
2056                 "locale.h",
2057                 "machine/elf.h",
2058                 "machine/reg.h",
2059                 "malloc_np.h",
2060                 "memstat.h",
2061                 "mqueue.h",
2062                 "net/bpf.h",
2063                 "net/if.h",
2064                 "net/if_arp.h",
2065                 "net/if_dl.h",
2066                 "net/if_mib.h",
2067                 "net/route.h",
2068                 "netdb.h",
2069                 "netinet/ip.h",
2070                 "netinet/in.h",
2071                 "netinet/sctp.h",
2072                 "netinet/tcp.h",
2073                 "netinet/udp.h",
2074                 "poll.h",
2075                 "pthread.h",
2076                 "pthread_np.h",
2077                 "pwd.h",
2078                 "regex.h",
2079                 "resolv.h",
2080                 "sched.h",
2081                 "semaphore.h",
2082                 "signal.h",
2083                 "spawn.h",
2084                 "stddef.h",
2085                 "stdint.h",
2086                 "stdio.h",
2087                 "stdlib.h",
2088                 "string.h",
2089                 "sys/capsicum.h",
2090                 "sys/auxv.h",
2091                 "sys/cpuset.h",
2092                 "sys/domainset.h",
2093                 "sys/eui64.h",
2094                 "sys/event.h",
2095                 [freebsd13]:"sys/eventfd.h",
2096                 "sys/extattr.h",
2097                 "sys/file.h",
2098                 "sys/ioctl.h",
2099                 "sys/ipc.h",
2100                 "sys/jail.h",
2101                 "sys/mman.h",
2102                 "sys/mount.h",
2103                 "sys/msg.h",
2104                 "sys/procctl.h",
2105                 "sys/procdesc.h",
2106                 "sys/ptrace.h",
2107                 "sys/queue.h",
2108                 "sys/random.h",
2109                 "sys/reboot.h",
2110                 "sys/resource.h",
2111                 "sys/rtprio.h",
2112                 "sys/sem.h",
2113                 "sys/shm.h",
2114                 "sys/socket.h",
2115                 "sys/stat.h",
2116                 "sys/statvfs.h",
2117                 "sys/sysctl.h",
2118                 "sys/thr.h",
2119                 "sys/time.h",
2120                 [freebsd14]:"sys/timerfd.h",
2121                 "sys/times.h",
2122                 "sys/timex.h",
2123                 "sys/types.h",
2124                 "sys/proc.h",
2125                 "kvm.h", // must be after "sys/types.h"
2126                 "sys/ucontext.h",
2127                 "sys/uio.h",
2128                 "sys/ktrace.h",
2129                 "sys/umtx.h",
2130                 "sys/un.h",
2131                 "sys/user.h",
2132                 "sys/utsname.h",
2133                 "sys/uuid.h",
2134                 "sys/vmmeter.h",
2135                 "sys/wait.h",
2136                 "libprocstat.h",
2137                 "devstat.h",
2138                 "syslog.h",
2139                 "termios.h",
2140                 "time.h",
2141                 "ufs/ufs/quota.h",
2142                 "unistd.h",
2143                 "utime.h",
2144                 "utmpx.h",
2145                 "wchar.h",
2146     }
2147 
2148     cfg.type_name(move |ty, is_struct, is_union| {
2149         match ty {
2150             // Just pass all these through, no need for a "struct" prefix
2151             "FILE"
2152             | "fd_set"
2153             | "Dl_info"
2154             | "DIR"
2155             | "Elf32_Phdr"
2156             | "Elf64_Phdr"
2157             | "Elf32_Auxinfo"
2158             | "Elf64_Auxinfo"
2159             | "devstat_select_mode"
2160             | "devstat_support_flags"
2161             | "devstat_type_flags"
2162             | "devstat_match_flags"
2163             | "devstat_priority" => ty.to_string(),
2164 
2165             // FIXME: https://github.com/rust-lang/libc/issues/1273
2166             "sighandler_t" => "sig_t".to_string(),
2167 
2168             t if is_union => format!("union {}", t),
2169 
2170             t if t.ends_with("_t") => t.to_string(),
2171 
2172             // sigval is a struct in Rust, but a union in C:
2173             "sigval" => format!("union sigval"),
2174 
2175             // put `struct` in front of all structs:.
2176             t if is_struct => format!("struct {}", t),
2177 
2178             t => t.to_string(),
2179         }
2180     });
2181 
2182     cfg.field_name(move |struct_, field| {
2183         match field {
2184             // Our stat *_nsec fields normally don't actually exist but are part
2185             // of a timeval struct
2186             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2187                 s.replace("e_nsec", ".tv_nsec")
2188             }
2189             // Field is named `type` in C but that is a Rust keyword,
2190             // so these fields are translated to `type_` in the bindings.
2191             "type_" if struct_ == "rtprio" => "type".to_string(),
2192             "type_" if struct_ == "sockstat" => "type".to_string(),
2193             "type_" if struct_ == "devstat_match_table" => "type".to_string(),
2194             s => s.to_string(),
2195         }
2196     });
2197 
2198     cfg.skip_const(move |name| {
2199         match name {
2200             // These constants were introduced in FreeBSD 13:
2201             "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2202             | "F_SEAL_WRITE"
2203                 if Some(13) > freebsd_ver =>
2204             {
2205                 true
2206             }
2207 
2208             // These constants were introduced in FreeBSD 13:
2209             "EFD_CLOEXEC" | "EFD_NONBLOCK" | "EFD_SEMAPHORE" if Some(13) > freebsd_ver => true,
2210 
2211             // These constants were introduced in FreeBSD 12:
2212             "AT_RESOLVE_BENEATH" | "O_RESOLVE_BENEATH" if Some(12) > freebsd_ver => true,
2213 
2214             // These constants were introduced in FreeBSD 13:
2215             "O_DSYNC" | "O_PATH" | "O_EMPTY_PATH" | "AT_EMPTY_PATH" if Some(13) > freebsd_ver => {
2216                 true
2217             }
2218 
2219             // These aliases were introduced in FreeBSD 13:
2220             // (note however that the constants themselves work on any version)
2221             "CLOCK_BOOTTIME" | "CLOCK_REALTIME_COARSE" | "CLOCK_MONOTONIC_COARSE"
2222                 if Some(13) > freebsd_ver =>
2223             {
2224                 true
2225             }
2226 
2227             // FIXME: These are deprecated - remove in a couple of releases.
2228             // These constants were removed in FreeBSD 11 (svn r273250) but will
2229             // still be accepted and ignored at runtime.
2230             "MAP_RENAME" | "MAP_NORESERVE" => true,
2231 
2232             // FIXME: These are deprecated - remove in a couple of releases.
2233             // These constants were removed in FreeBSD 11 (svn r262489),
2234             // and they've never had any legitimate use outside of the
2235             // base system anyway.
2236             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
2237 
2238             // FIXME: This is deprecated - remove in a couple of releases.
2239             // This was removed in FreeBSD 14 (git 1b4701fe1e8) and never
2240             // should've been used anywhere anyway.
2241             "TDF_UNUSED23" => true,
2242 
2243             // Removed in FreeBSD 14 (git a6b55ee6be1)
2244             "IFF_KNOWSEPOCH" => true,
2245 
2246             // Removed in FreeBSD 14 (git 7ff9ae90f0b)
2247             "IFF_NOGROUP" => true,
2248 
2249             // FIXME: These are deprecated - remove in a couple of releases.
2250             // These symbols are not stable across OS-versions.  They were
2251             // changed for FreeBSD 14 in git revisions b62848b0c3f and
2252             // 2cf7870864e.
2253             "PRI_MAX_ITHD" | "PRI_MIN_REALTIME" | "PRI_MAX_REALTIME" | "PRI_MIN_KERN"
2254             | "PRI_MAX_KERN" | "PSWP" | "PVM" | "PINOD" | "PRIBIO" | "PVFS" | "PZERO" | "PSOCK"
2255             | "PWAIT" | "PLOCK" | "PPAUSE" | "PRI_MIN_TIMESHARE" | "PUSER" | "PI_AV" | "PI_NET"
2256             | "PI_DISK" | "PI_TTY" | "PI_DULL" | "PI_SOFT" => true,
2257 
2258             // This symbol changed in FreeBSD 14 (git 051e7d78b03), but the new
2259             // version should be safe to use on older releases.
2260             "IFCAP_CANTCHANGE" => true,
2261 
2262             // These were removed in FreeBSD 14 (git c6d31b8306e)
2263             "TDF_ASTPENDING" | "TDF_NEEDSUSPCHK" | "TDF_NEEDRESCHED" | "TDF_NEEDSIGCHK"
2264             | "TDF_ALRMPEND" | "TDF_PROFPEND" | "TDF_MACPEND" => true,
2265 
2266             // This constant was removed in FreeBSD 13 (svn r363622), and never
2267             // had any legitimate use outside of the base system anyway.
2268             "CTL_P1003_1B_MAXID" => true,
2269 
2270             // This was renamed in FreeBSD 12.2 and 13 (r352486).
2271             "CTL_UNSPEC" | "CTL_SYSCTL" => true,
2272 
2273             // This was renamed in FreeBSD 12.2 and 13 (r350749).
2274             "IPPROTO_SEP" | "IPPROTO_DCCP" => true,
2275 
2276             // This was changed to 96(0x60) in FreeBSD 13:
2277             // https://github.com/freebsd/freebsd/
2278             // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7
2279             "MINCORE_SUPER" if Some(13) <= freebsd_ver => true,
2280 
2281             // Added in FreeBSD 13.0 (r356667)
2282             "GRND_INSECURE" if Some(13) > freebsd_ver => true,
2283 
2284             // Added in FreeBSD 13.0 (r349609)
2285             "PROC_PROTMAX_CTL"
2286             | "PROC_PROTMAX_STATUS"
2287             | "PROC_PROTMAX_FORCE_ENABLE"
2288             | "PROC_PROTMAX_FORCE_DISABLE"
2289             | "PROC_PROTMAX_NOFORCE"
2290             | "PROC_PROTMAX_ACTIVE"
2291             | "PROC_NO_NEW_PRIVS_CTL"
2292             | "PROC_NO_NEW_PRIVS_STATUS"
2293             | "PROC_NO_NEW_PRIVS_ENABLE"
2294             | "PROC_NO_NEW_PRIVS_DISABLE"
2295             | "PROC_WXMAP_CTL"
2296             | "PROC_WXMAP_STATUS"
2297             | "PROC_WX_MAPPINGS_PERMIT"
2298             | "PROC_WX_MAPPINGS_DISALLOW_EXEC"
2299             | "PROC_WXORX_ENFORCE"
2300                 if Some(13) > freebsd_ver =>
2301             {
2302                 true
2303             }
2304 
2305             // Added in in FreeBSD 13.0 (r367776 and r367287)
2306             "SCM_CREDS2" | "LOCAL_CREDS_PERSISTENT" if Some(13) > freebsd_ver => true,
2307 
2308             // Added in FreeBSD 14
2309             "SPACECTL_DEALLOC" if Some(14) > freebsd_ver => true,
2310 
2311             // Added in FreeBSD 13.
2312             "KERN_PROC_SIGFASTBLK"
2313             | "USER_LOCALBASE"
2314             | "TDP_SIGFASTBLOCK"
2315             | "TDP_UIOHELD"
2316             | "TDP_SIGFASTPENDING"
2317             | "TDP2_COMPAT32RB"
2318             | "P2_PROTMAX_ENABLE"
2319             | "P2_PROTMAX_DISABLE"
2320             | "CTLFLAG_NEEDGIANT"
2321             | "CTL_SYSCTL_NEXTNOSKIP"
2322                 if Some(13) > freebsd_ver =>
2323             {
2324                 true
2325             }
2326 
2327             // Added in freebsd 14.
2328             "IFCAP_MEXTPG" if Some(14) > freebsd_ver => true,
2329             // Added in freebsd 13.
2330             "IFCAP_TXTLS4" | "IFCAP_TXTLS6" | "IFCAP_VXLAN_HWCSUM" | "IFCAP_VXLAN_HWTSO"
2331             | "IFCAP_TXTLS_RTLMT" | "IFCAP_TXTLS"
2332                 if Some(13) > freebsd_ver =>
2333             {
2334                 true
2335             }
2336             // Added in FreeBSD 13.
2337             "PS_FST_TYPE_EVENTFD" if Some(13) > freebsd_ver => true,
2338 
2339             // Added in FreeBSD 14.
2340             "MNT_RECURSE" | "MNT_DEFERRED" if Some(14) > freebsd_ver => true,
2341 
2342             // Added in FreeBSD 13.
2343             "MNT_EXTLS" | "MNT_EXTLSCERT" | "MNT_EXTLSCERTUSER" | "MNT_NOCOVER"
2344             | "MNT_EMPTYDIR"
2345                 if Some(13) > freebsd_ver =>
2346             {
2347                 true
2348             }
2349 
2350             // Added in FreeBSD 14.
2351             "PT_COREDUMP" | "PC_ALL" | "PC_COMPRESS" | "PT_GETREGSET" | "PT_SETREGSET"
2352             | "PT_SC_REMOTE"
2353                 if Some(14) > freebsd_ver =>
2354             {
2355                 true
2356             }
2357 
2358             // Added in FreeBSD 14.
2359             "F_KINFO" => true, // FIXME: depends how frequent freebsd 14 is updated on CI, this addition went this week only.
2360             "SHM_RENAME_NOREPLACE"
2361             | "SHM_RENAME_EXCHANGE"
2362             | "SHM_LARGEPAGE_ALLOC_DEFAULT"
2363             | "SHM_LARGEPAGE_ALLOC_NOWAIT"
2364             | "SHM_LARGEPAGE_ALLOC_HARD"
2365             | "MFD_CLOEXEC"
2366             | "MFD_ALLOW_SEALING"
2367             | "MFD_HUGETLB"
2368             | "MFD_HUGE_MASK"
2369             | "MFD_HUGE_64KB"
2370             | "MFD_HUGE_512KB"
2371             | "MFD_HUGE_1MB"
2372             | "MFD_HUGE_2MB"
2373             | "MFD_HUGE_8MB"
2374             | "MFD_HUGE_16MB"
2375             | "MFD_HUGE_32MB"
2376             | "MFD_HUGE_256MB"
2377             | "MFD_HUGE_512MB"
2378             | "MFD_HUGE_1GB"
2379             | "MFD_HUGE_2GB"
2380             | "MFD_HUGE_16GB"
2381                 if Some(13) > freebsd_ver =>
2382             {
2383                 true
2384             }
2385 
2386             // Flags introduced in FreeBSD 14.
2387             "TCP_MAXUNACKTIME"
2388             | "TCP_MAXPEAKRATE"
2389             | "TCP_IDLE_REDUCE"
2390             | "TCP_REMOTE_UDP_ENCAPS_PORT"
2391             | "TCP_DELACK"
2392             | "TCP_FIN_IS_RST"
2393             | "TCP_LOG_LIMIT"
2394             | "TCP_SHARED_CWND_ALLOWED"
2395             | "TCP_PROC_ACCOUNTING"
2396             | "TCP_USE_CMP_ACKS"
2397             | "TCP_PERF_INFO"
2398             | "TCP_LRD"
2399                 if Some(14) > freebsd_ver =>
2400             {
2401                 true
2402             }
2403 
2404             // Added in FreeBSD 14
2405             "LIO_READV" | "LIO_WRITEV" | "LIO_VECTORED" if Some(14) > freebsd_ver => true,
2406 
2407             // Added in FreeBSD 13
2408             "FIOSSHMLPGCNF" if Some(13) > freebsd_ver => true,
2409 
2410             // Added in FreeBSD 14
2411             "IFCAP_NV" if Some(14) > freebsd_ver => true,
2412 
2413             // FIXME: Removed in https://reviews.freebsd.org/D38574 and https://reviews.freebsd.org/D38822
2414             // We maybe should deprecate them once a stable release ships them.
2415             "IP_BINDMULTI" | "IP_RSS_LISTEN_BUCKET" => true,
2416 
2417             // FIXME: Removed in https://reviews.freebsd.org/D39127.
2418             "KERN_VNODE" => true,
2419 
2420             // Added in FreeBSD 14
2421             "EV_KEEPUDATA" if Some(14) > freebsd_ver => true,
2422 
2423             // Added in FreeBSD 13.2
2424             "AT_USRSTACKBASE" | "AT_USRSTACKLIM" if Some(13) > freebsd_ver => true,
2425 
2426             // Added in FreeBSD 14
2427             "TFD_CLOEXEC" | "TFD_NONBLOCK" if Some(14) > freebsd_ver => true,
2428 
2429             _ => false,
2430         }
2431     });
2432 
2433     cfg.skip_type(move |ty| {
2434         match ty {
2435             // the struct "__kvm" is quite tricky to bind so since we only use a pointer to it
2436             // for now, it doesn't matter too much...
2437             "kvm_t" => true,
2438             // `eventfd(2)` and things come with it are added in FreeBSD 13
2439             "eventfd_t" if Some(13) > freebsd_ver => true,
2440 
2441             _ => false,
2442         }
2443     });
2444 
2445     cfg.skip_struct(move |ty| {
2446         if ty.starts_with("__c_anonymous_") {
2447             return true;
2448         }
2449         match ty {
2450             // `procstat` is a private struct
2451             "procstat" => true,
2452 
2453             // `spacectl_range` was introduced in FreeBSD 14
2454             "spacectl_range" if Some(14) > freebsd_ver => true,
2455 
2456             // `ptrace_coredump` introduced in FreeBSD 14.
2457             "ptrace_coredump" if Some(14) > freebsd_ver => true,
2458             // `ptrace_sc_remote` introduced in FreeBSD 14.
2459             "ptrace_sc_remote" if Some(14) > freebsd_ver => true,
2460 
2461             // `sockcred2` is not available in FreeBSD 12.
2462             "sockcred2" if Some(13) > freebsd_ver => true,
2463             // `shm_largepage_conf` was introduced in FreeBSD 13.
2464             "shm_largepage_conf" if Some(13) > freebsd_ver => true,
2465 
2466             // Those are private types
2467             "memory_type" => true,
2468             "memory_type_list" => true,
2469             "pidfh" => true,
2470             "sctp_gen_error_cause"
2471             | "sctp_error_missing_param"
2472             | "sctp_remote_error"
2473             | "sctp_assoc_change"
2474             | "sctp_send_failed_event"
2475             | "sctp_stream_reset_event" => true,
2476 
2477             _ => false,
2478         }
2479     });
2480 
2481     cfg.skip_fn(move |name| {
2482         // skip those that are manually verified
2483         match name {
2484             // FIXME: https://github.com/rust-lang/libc/issues/1272
2485             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2486 
2487             // The `uname` function in the `utsname.h` FreeBSD header is a C
2488             // inline function (has no symbol) that calls the `__xuname` symbol.
2489             // Therefore the function pointer comparison does not make sense for it.
2490             "uname" => true,
2491 
2492             // FIXME: Our API is unsound. The Rust API allows aliasing
2493             // pointers, but the C API requires pointers not to alias.
2494             // We should probably be at least using `&`/`&mut` here, see:
2495             // https://github.com/gnzlbg/ctest/issues/68
2496             "lio_listio" => true,
2497 
2498             // Those are introduced in FreeBSD 12.
2499             "clock_nanosleep" | "getrandom" | "elf_aux_info" | "setproctitle_fast"
2500             | "timingsafe_bcmp" | "timingsafe_memcmp"
2501                 if Some(12) > freebsd_ver =>
2502             {
2503                 true
2504             }
2505 
2506             // Those are introduced in FreeBSD 13.
2507             "memfd_create"
2508             | "shm_create_largepage"
2509             | "shm_rename"
2510             | "getentropy"
2511             | "eventfd"
2512             | "SOCKCRED2SIZE"
2513             | "getlocalbase"
2514             | "aio_readv"
2515             | "aio_writev"
2516             | "copy_file_range"
2517             | "eventfd_read"
2518             | "eventfd_write"
2519                 if Some(13) > freebsd_ver =>
2520             {
2521                 true
2522             }
2523 
2524             // Those are introduced in FreeBSD 14.
2525             "sched_getaffinity" | "sched_setaffinity" | "sched_getcpu" | "fspacectl"
2526                 if Some(14) > freebsd_ver =>
2527             {
2528                 true
2529             }
2530 
2531             // Those are introduced in FreeBSD 14.
2532             "timerfd_create" | "timerfd_gettime" | "timerfd_settime" if Some(14) > freebsd_ver => {
2533                 true
2534             }
2535 
2536             _ => false,
2537         }
2538     });
2539 
2540     cfg.volatile_item(|i| {
2541         use ctest::VolatileItemKind::*;
2542         match i {
2543             // aio_buf is a volatile void** but since we cannot express that in
2544             // Rust types, we have to explicitly tell the checker about it here:
2545             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2546             _ => false,
2547         }
2548     });
2549 
2550     cfg.skip_field(move |struct_, field| {
2551         match (struct_, field) {
2552             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2553             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2554             ("sigaction", "sa_sigaction") => true,
2555 
2556             // conflicting with `p_type` macro from <resolve.h>.
2557             ("Elf32_Phdr", "p_type") => true,
2558             ("Elf64_Phdr", "p_type") => true,
2559 
2560             // not available until FreeBSD 12, and is an anonymous union there.
2561             ("xucred", "cr_pid__c_anonymous_union") => true,
2562 
2563             // m_owner field is a volatile __lwpid_t
2564             ("umutex", "m_owner") => true,
2565             // c_has_waiters field is a volatile int32_t
2566             ("ucond", "c_has_waiters") => true,
2567             // is PATH_MAX long but tests can't accept multi array as equivalent.
2568             ("kinfo_vmentry", "kve_path") => true,
2569 
2570             // a_un field is a union
2571             ("Elf32_Auxinfo", "a_un") => true,
2572             ("Elf64_Auxinfo", "a_un") => true,
2573 
2574             // union fields
2575             ("if_data", "__ifi_epoch") => true,
2576             ("if_data", "__ifi_lastchange") => true,
2577             ("ifreq", "ifr_ifru") => true,
2578             ("ifconf", "ifc_ifcu") => true,
2579 
2580             // anonymous struct
2581             ("devstat", "dev_links") => true,
2582 
2583             // FIXME: structs too complicated to bind for now...
2584             ("kinfo_proc", "ki_paddr") => true,
2585             ("kinfo_proc", "ki_addr") => true,
2586             ("kinfo_proc", "ki_tracep") => true,
2587             ("kinfo_proc", "ki_textvp") => true,
2588             ("kinfo_proc", "ki_fd") => true,
2589             ("kinfo_proc", "ki_vmspace") => true,
2590             ("kinfo_proc", "ki_pcb") => true,
2591             ("kinfo_proc", "ki_tdaddr") => true,
2592             ("kinfo_proc", "ki_pd") => true,
2593 
2594             // Anonymous type.
2595             ("filestat", "next") => true,
2596 
2597             // We ignore this field because we needed to use a hack in order to make rust 1.19
2598             // happy...
2599             ("kinfo_proc", "ki_sparestrings") => true,
2600 
2601             // `__sem_base` is a private struct field
2602             ("semid_ds", "__sem_base") => true,
2603 
2604             // `snap_time` is a `long double`, but it's a nightmare to bind correctly in rust
2605             // for the moment, so it's a best effort thing...
2606             ("statinfo", "snap_time") => true,
2607             ("sctp_sndrcvinfo", "__reserve_pad") => true,
2608             ("sctp_extrcvinfo", "__reserve_pad") => true,
2609             // `tcp_snd_wscale` and `tcp_rcv_wscale` are bitfields
2610             ("tcp_info", "tcp_snd_wscale") => true,
2611             ("tcp_info", "tcp_rcv_wscale") => true,
2612 
2613             _ => false,
2614         }
2615     });
2616 
2617     cfg.generate("../src/lib.rs", "main.rs");
2618 }
2619 
2620 fn test_emscripten(target: &str) {
2621     assert!(target.contains("emscripten"));
2622 
2623     let mut cfg = ctest_cfg();
2624     cfg.define("_GNU_SOURCE", None); // FIXME: ??
2625 
2626     headers! { cfg:
2627                "aio.h",
2628                "ctype.h",
2629                "dirent.h",
2630                "dlfcn.h",
2631                "errno.h",
2632                "fcntl.h",
2633                "glob.h",
2634                "grp.h",
2635                "ifaddrs.h",
2636                "langinfo.h",
2637                "limits.h",
2638                "locale.h",
2639                "malloc.h",
2640                "mntent.h",
2641                "mqueue.h",
2642                "net/ethernet.h",
2643                "net/if.h",
2644                "net/if_arp.h",
2645                "net/route.h",
2646                "netdb.h",
2647                "netinet/in.h",
2648                "netinet/ip.h",
2649                "netinet/tcp.h",
2650                "netinet/udp.h",
2651                "netpacket/packet.h",
2652                "poll.h",
2653                "pthread.h",
2654                "pty.h",
2655                "pwd.h",
2656                "resolv.h",
2657                "sched.h",
2658                "sched.h",
2659                "semaphore.h",
2660                "shadow.h",
2661                "signal.h",
2662                "stddef.h",
2663                "stdint.h",
2664                "stdio.h",
2665                "stdlib.h",
2666                "string.h",
2667                "sys/epoll.h",
2668                "sys/eventfd.h",
2669                "sys/file.h",
2670                "sys/ioctl.h",
2671                "sys/ipc.h",
2672                "sys/mman.h",
2673                "sys/mount.h",
2674                "sys/msg.h",
2675                "sys/personality.h",
2676                "sys/prctl.h",
2677                "sys/ptrace.h",
2678                "sys/quota.h",
2679                "sys/reboot.h",
2680                "sys/resource.h",
2681                "sys/sem.h",
2682                "sys/shm.h",
2683                "sys/signalfd.h",
2684                "sys/socket.h",
2685                "sys/stat.h",
2686                "sys/statvfs.h",
2687                "sys/swap.h",
2688                "sys/syscall.h",
2689                "sys/sysctl.h",
2690                "sys/sysinfo.h",
2691                "sys/time.h",
2692                "sys/timerfd.h",
2693                "sys/times.h",
2694                "sys/types.h",
2695                "sys/uio.h",
2696                "sys/un.h",
2697                "sys/user.h",
2698                "sys/utsname.h",
2699                "sys/vfs.h",
2700                "sys/wait.h",
2701                "sys/xattr.h",
2702                "syslog.h",
2703                "termios.h",
2704                "time.h",
2705                "ucontext.h",
2706                "unistd.h",
2707                "utime.h",
2708                "utmp.h",
2709                "utmpx.h",
2710                "wchar.h",
2711     }
2712 
2713     cfg.type_name(move |ty, is_struct, is_union| {
2714         match ty {
2715             // Just pass all these through, no need for a "struct" prefix
2716             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2717 
2718             "os_unfair_lock" => "struct os_unfair_lock_s".to_string(),
2719 
2720             // LFS64 types have been removed in Emscripten 3.1.44+
2721             // https://github.com/emscripten-core/emscripten/pull/19812
2722             "off64_t" => "off_t".to_string(),
2723 
2724             // typedefs don't need any keywords
2725             t if t.ends_with("_t") => t.to_string(),
2726 
2727             // put `struct` in front of all structs:.
2728             t if is_struct => format!("struct {}", t),
2729 
2730             // put `union` in front of all unions:
2731             t if is_union => format!("union {}", t),
2732 
2733             t => t.to_string(),
2734         }
2735     });
2736 
2737     cfg.field_name(move |struct_, field| {
2738         match field {
2739             // Our stat *_nsec fields normally don't actually exist but are part
2740             // of a timeval struct
2741             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2742                 s.replace("e_nsec", ".tv_nsec")
2743             }
2744             // FIXME: appears that `epoll_event.data` is an union
2745             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2746             s => s.to_string(),
2747         }
2748     });
2749 
2750     cfg.skip_type(move |ty| {
2751         match ty {
2752             // sighandler_t is crazy across platforms
2753             // FIXME: is this necessary?
2754             "sighandler_t" => true,
2755 
2756             // FIXME: The size has been changed due to musl's time64
2757             "time_t" => true,
2758 
2759             // LFS64 types have been removed in Emscripten 3.1.44+
2760             // https://github.com/emscripten-core/emscripten/pull/19812
2761             t => t.ends_with("64") || t.ends_with("64_t"),
2762         }
2763     });
2764 
2765     cfg.skip_struct(move |ty| {
2766         match ty {
2767             // This is actually a union, not a struct
2768             // FIXME: is this necessary?
2769             "sigval" => true,
2770 
2771             // FIXME: It was removed in
2772             // emscripten-core/emscripten@953e414
2773             "pthread_mutexattr_t" => true,
2774 
2775             // FIXME: Investigate why the test fails.
2776             // Skip for now to unblock CI.
2777             "pthread_condattr_t" => true,
2778 
2779             // FIXME: The size has been changed when upgraded to musl 1.2.2
2780             "pthread_mutex_t" => true,
2781 
2782             // FIXME: Lowered from 16 to 8 bytes in
2783             // llvm/llvm-project@d1a96e9
2784             "max_align_t" => true,
2785 
2786             // FIXME: The size has been changed due to time64
2787             "utimbuf" | "timeval" | "timespec" | "rusage" | "itimerval" | "sched_param"
2788             | "stat" | "stat64" | "shmid_ds" | "msqid_ds" => true,
2789 
2790             // LFS64 types have been removed in Emscripten 3.1.44+
2791             // https://github.com/emscripten-core/emscripten/pull/19812
2792             ty => ty.ends_with("64") || ty.ends_with("64_t"),
2793         }
2794     });
2795 
2796     cfg.skip_fn(move |name| {
2797         match name {
2798             // Emscripten does not support fork/exec/wait or any kind of multi-process support
2799             // https://github.com/emscripten-core/emscripten/blob/3.1.30/tools/system_libs.py#L973
2800             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" | "wait4" => true,
2801 
2802             // FIXME: Remove after emscripten-core/emscripten#18492 is released (> 3.1.30).
2803             "clearenv" => true,
2804 
2805             _ => false,
2806         }
2807     });
2808 
2809     cfg.skip_const(move |name| {
2810         match name {
2811             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2812             // users should use SIGSYS instead
2813             "SIGUNUSED" => true,
2814 
2815             // FIXME: emscripten uses different constants to constructs these
2816             n if n.contains("__SIZEOF_PTHREAD") => true,
2817 
2818             // FIXME: `SYS_gettid` was removed in
2819             // emscripten-core/emscripten@6d6474e
2820             "SYS_gettid" => true,
2821 
2822             // FIXME: These values have been changed
2823             | "POSIX_MADV_DONTNEED" // to 4
2824             | "RLIMIT_NLIMITS" // to 16
2825             | "RLIM_NLIMITS" // to 16
2826             | "IPPROTO_MAX" // to 263
2827             | "F_GETLK" // to 5
2828             | "F_SETLK" // to 6
2829             | "F_SETLKW" // to 7
2830             | "O_TMPFILE" // to 65
2831             | "SIG_IGN" // -1
2832                 => true,
2833 
2834             // LFS64 types have been removed in Emscripten 3.1.44+
2835             // https://github.com/emscripten-core/emscripten/pull/19812
2836             n if n.starts_with("RLIM64") => true,
2837 
2838             _ => false,
2839         }
2840     });
2841 
2842     cfg.skip_field_type(move |struct_, field| {
2843         // This is a weird union, don't check the type.
2844         // FIXME: is this necessary?
2845         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2846         // sighandler_t type is super weird
2847         // FIXME: is this necessary?
2848         (struct_ == "sigaction" && field == "sa_sigaction") ||
2849         // sigval is actually a union, but we pretend it's a struct
2850         // FIXME: is this necessary?
2851         (struct_ == "sigevent" && field == "sigev_value") ||
2852         // aio_buf is "volatile void*" and Rust doesn't understand volatile
2853         // FIXME: is this necessary?
2854         (struct_ == "aiocb" && field == "aio_buf")
2855     });
2856 
2857     cfg.skip_field(move |struct_, field| {
2858         // this is actually a union on linux, so we can't represent it well and
2859         // just insert some padding.
2860         // FIXME: is this necessary?
2861         (struct_ == "siginfo_t" && field == "_pad") ||
2862         // musl names this __dummy1 but it's still there
2863         // FIXME: is this necessary?
2864         (struct_ == "glob_t" && field == "gl_flags") ||
2865         // musl seems to define this as an *anonymous* bitfield
2866         // FIXME: is this necessary?
2867         (struct_ == "statvfs" && field == "__f_unused") ||
2868         // sigev_notify_thread_id is actually part of a sigev_un union
2869         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2870         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
2871         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2872                                            field == "_pad2" ||
2873                                            field == "ssi_syscall" ||
2874                                            field == "ssi_call_addr" ||
2875                                            field == "ssi_arch")) ||
2876         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2877         // while other fields become reserved.
2878         (struct_ == "sched_param" && [
2879             "sched_ss_low_priority",
2880             "sched_ss_repl_period",
2881             "sched_ss_init_budget",
2882             "sched_ss_max_repl",
2883         ].contains(&field))
2884     });
2885 
2886     // FIXME: test linux like
2887     cfg.generate("../src/lib.rs", "main.rs");
2888 }
2889 
2890 fn test_neutrino(target: &str) {
2891     assert!(target.contains("nto-qnx"));
2892 
2893     let mut cfg = ctest_cfg();
2894 
2895     headers! { cfg:
2896         "ctype.h",
2897         "dirent.h",
2898         "dlfcn.h",
2899         "sys/elf.h",
2900         "fcntl.h",
2901         "glob.h",
2902         "grp.h",
2903         "iconv.h",
2904         "ifaddrs.h",
2905         "limits.h",
2906         "sys/link.h",
2907         "locale.h",
2908         "sys/malloc.h",
2909         "rcheck/malloc.h",
2910         "malloc.h",
2911         "mqueue.h",
2912         "net/if.h",
2913         "net/if_arp.h",
2914         "net/route.h",
2915         "netdb.h",
2916         "netinet/in.h",
2917         "netinet/ip.h",
2918         "netinet/tcp.h",
2919         "netinet/udp.h",
2920         "netinet/ip_var.h",
2921         "sys/poll.h",
2922         "pthread.h",
2923         "pwd.h",
2924         "regex.h",
2925         "resolv.h",
2926         "sys/sched.h",
2927         "sched.h",
2928         "semaphore.h",
2929         "shadow.h",
2930         "signal.h",
2931         "spawn.h",
2932         "stddef.h",
2933         "stdint.h",
2934         "stdio.h",
2935         "stdlib.h",
2936         "string.h",
2937         "sys/sysctl.h",
2938         "sys/file.h",
2939         "sys/inotify.h",
2940         "sys/ioctl.h",
2941         "sys/ipc.h",
2942         "sys/mman.h",
2943         "sys/mount.h",
2944         "sys/msg.h",
2945         "sys/resource.h",
2946         "sys/sem.h",
2947         "sys/socket.h",
2948         "sys/stat.h",
2949         "sys/statvfs.h",
2950         "sys/swap.h",
2951         "sys/termio.h",
2952         "sys/time.h",
2953         "sys/times.h",
2954         "sys/types.h",
2955         "sys/uio.h",
2956         "sys/un.h",
2957         "sys/utsname.h",
2958         "sys/wait.h",
2959         "syslog.h",
2960         "termios.h",
2961         "time.h",
2962         "sys/time.h",
2963         "ucontext.h",
2964         "unistd.h",
2965         "utime.h",
2966         "utmp.h",
2967         "wchar.h",
2968         "aio.h",
2969         "nl_types.h",
2970         "langinfo.h",
2971         "unix.h",
2972         "nbutil.h",
2973         "aio.h",
2974         "net/bpf.h",
2975         "net/if_dl.h",
2976         "sys/syspage.h",
2977 
2978         // TODO: The following header file doesn't appear as part of the default headers
2979         //       found in a standard installation of Neutrino 7.1 SDP.  The structures/
2980         //       functions dependent on it are currently commented out.
2981         //"sys/asyncmsg.h",
2982     }
2983 
2984     // Create and include a header file containing
2985     // items which are not included in any official
2986     // header file.
2987     let internal_header = "internal.h";
2988     let out_dir = env::var("OUT_DIR").unwrap();
2989     cfg.header(internal_header);
2990     cfg.include(&out_dir);
2991     std::fs::write(
2992         out_dir.to_owned() + "/" + internal_header,
2993         "#ifndef __internal_h__
2994         #define __internal_h__
2995         void __my_thread_exit(const void **);
2996         #endif",
2997     )
2998     .unwrap();
2999 
3000     cfg.type_name(move |ty, is_struct, is_union| {
3001         match ty {
3002             // Just pass all these through, no need for a "struct" prefix
3003             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3004             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3005             | "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info"
3006             | "syspage_array_info" => ty.to_string(),
3007 
3008             "Ioctl" => "int".to_string(),
3009 
3010             t if is_union => format!("union {}", t),
3011 
3012             t if t.ends_with("_t") => t.to_string(),
3013 
3014             // put `struct` in front of all structs:.
3015             t if is_struct => format!("struct {}", t),
3016 
3017             t => t.to_string(),
3018         }
3019     });
3020 
3021     cfg.field_name(move |_struct_, field| match field {
3022         "type_" => "type".to_string(),
3023 
3024         s => s.to_string(),
3025     });
3026 
3027     cfg.volatile_item(|i| {
3028         use ctest::VolatileItemKind::*;
3029         match i {
3030             // The following fields are volatie but since we cannot express that in
3031             // Rust types, we have to explicitly tell the checker about it here:
3032             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
3033             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true,
3034             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true,
3035             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true,
3036             StructField(ref n, ref f) if n == "intrspin" && f == "value" => true,
3037             _ => false,
3038         }
3039     });
3040 
3041     cfg.skip_type(move |ty| {
3042         match ty {
3043             // FIXME: `sighandler_t` type is incorrect, see:
3044             // https://github.com/rust-lang/libc/issues/1359
3045             "sighandler_t" => true,
3046 
3047             // Does not exist in Neutrino
3048             "locale_t" => true,
3049 
3050             _ => false,
3051         }
3052     });
3053 
3054     cfg.skip_struct(move |ty| {
3055         if ty.starts_with("__c_anonymous_") {
3056             return true;
3057         }
3058         match ty {
3059             "Elf64_Phdr" | "Elf32_Phdr" => true,
3060 
3061             // FIXME: This is actually a union, not a struct
3062             "sigval" => true,
3063 
3064             // union
3065             "_channel_connect_attr" => true,
3066 
3067             _ => false,
3068         }
3069     });
3070 
3071     cfg.skip_const(move |name| {
3072         match name {
3073             // These signal "functions" are actually integer values that are casted to a fn ptr
3074             // This causes the compiler to err because of "illegal cast of int to ptr".
3075             "SIG_DFL" => true,
3076             "SIG_IGN" => true,
3077             "SIG_ERR" => true,
3078 
3079             _ => false,
3080         }
3081     });
3082 
3083     cfg.skip_fn(move |name| {
3084         // skip those that are manually verified
3085         match name {
3086             // FIXME: https://github.com/rust-lang/libc/issues/1272
3087             "execv" | "execve" | "execvp" | "execvpe" => true,
3088 
3089             // wrong signature
3090             "signal" => true,
3091 
3092             // wrong signature of callback ptr
3093             "__cxa_atexit" => true,
3094 
3095             // FIXME: Our API is unsound. The Rust API allows aliasing
3096             // pointers, but the C API requires pointers not to alias.
3097             // We should probably be at least using `&`/`&mut` here, see:
3098             // https://github.com/gnzlbg/ctest/issues/68
3099             "lio_listio" => true,
3100 
3101             // 2 fields are actually unions which we're simply representing
3102             // as structures.
3103             "ChannelConnectAttr" => true,
3104 
3105             // fields contains unions
3106             "SignalKillSigval" => true,
3107             "SignalKillSigval_r" => true,
3108 
3109             // Not defined in any headers.  Defined to work around a
3110             // stack unwinding bug.
3111             "__my_thread_exit" => true,
3112 
3113             _ => false,
3114         }
3115     });
3116 
3117     cfg.skip_field_type(move |struct_, field| {
3118         // sigval is actually a union, but we pretend it's a struct
3119         struct_ == "sigevent" && field == "sigev_value" ||
3120         // Anonymous structures
3121         struct_ == "_idle_hook" && field == "time"
3122     });
3123 
3124     cfg.skip_field(move |struct_, field| {
3125         (struct_ == "__sched_param" && field == "reserved") ||
3126         (struct_ == "sched_param" && field == "reserved") ||
3127         (struct_ == "sigevent" && field == "__padding1") || // ensure alignment
3128         (struct_ == "sigevent" && field == "__padding2") || // union
3129         (struct_ == "sigevent" && field == "__sigev_un2") || // union
3130         // sighandler_t type is super weird
3131         (struct_ == "sigaction" && field == "sa_sigaction") ||
3132         // does not exist
3133         (struct_ == "syspage_entry" && field == "__reserved") ||
3134         false // keep me for smaller diffs when something is added above
3135     });
3136 
3137     cfg.skip_static(move |name| (name == "__dso_handle"));
3138 
3139     cfg.generate("../src/lib.rs", "main.rs");
3140 }
3141 
3142 fn test_vxworks(target: &str) {
3143     assert!(target.contains("vxworks"));
3144 
3145     let mut cfg = ctest::TestGenerator::new();
3146     headers! { cfg:
3147                "vxWorks.h",
3148                "yvals.h",
3149                "nfs/nfsCommon.h",
3150                "rtpLibCommon.h",
3151                "randomNumGen.h",
3152                "taskLib.h",
3153                "sysLib.h",
3154                "ioLib.h",
3155                "inetLib.h",
3156                "socket.h",
3157                "errnoLib.h",
3158                "ctype.h",
3159                "dirent.h",
3160                "dlfcn.h",
3161                "elf.h",
3162                "fcntl.h",
3163                "grp.h",
3164                "sys/poll.h",
3165                "ifaddrs.h",
3166                "langinfo.h",
3167                "limits.h",
3168                "link.h",
3169                "locale.h",
3170                "sys/stat.h",
3171                "netdb.h",
3172                "pthread.h",
3173                "pwd.h",
3174                "sched.h",
3175                "semaphore.h",
3176                "signal.h",
3177                "stddef.h",
3178                "stdint.h",
3179                "stdio.h",
3180                "stdlib.h",
3181                "string.h",
3182                "sys/file.h",
3183                "sys/ioctl.h",
3184                "sys/socket.h",
3185                "sys/time.h",
3186                "sys/times.h",
3187                "sys/types.h",
3188                "sys/uio.h",
3189                "sys/un.h",
3190                "sys/utsname.h",
3191                "sys/wait.h",
3192                "netinet/tcp.h",
3193                "syslog.h",
3194                "termios.h",
3195                "time.h",
3196                "ucontext.h",
3197                "unistd.h",
3198                "utime.h",
3199                "wchar.h",
3200                "errno.h",
3201                "sys/mman.h",
3202                "pathLib.h",
3203                "mqueue.h",
3204     }
3205     // FIXME
3206     cfg.skip_const(move |name| match name {
3207         // sighandler_t weirdness
3208         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
3209         // This is not defined in vxWorks
3210         | "RTLD_DEFAULT"   => true,
3211         _ => false,
3212     });
3213     // FIXME
3214     cfg.skip_type(move |ty| match ty {
3215         "stat64" | "sighandler_t" | "off64_t" => true,
3216         _ => false,
3217     });
3218 
3219     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
3220         ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true,
3221         _ => false,
3222     });
3223 
3224     cfg.skip_roundtrip(move |s| match s {
3225         _ => false,
3226     });
3227 
3228     cfg.type_name(move |ty, is_struct, is_union| match ty {
3229         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
3230         t if is_union => format!("union {}", t),
3231         t if t.ends_with("_t") => t.to_string(),
3232         t if is_struct => format!("struct {}", t),
3233         t => t.to_string(),
3234     });
3235 
3236     // FIXME
3237     cfg.skip_fn(move |name| match name {
3238         // sigval
3239         "sigqueue" | "_sigqueue"
3240         // sighandler_t
3241         | "signal"
3242         // not used in static linking by default
3243         | "dlerror" => true,
3244         _ => false,
3245     });
3246 
3247     cfg.generate("../src/lib.rs", "main.rs");
3248 }
3249 
3250 fn test_linux(target: &str) {
3251     assert!(target.contains("linux"));
3252 
3253     // target_env
3254     let gnu = target.contains("gnu");
3255     let musl = target.contains("musl") || target.contains("ohos");
3256     let uclibc = target.contains("uclibc");
3257 
3258     match (gnu, musl, uclibc) {
3259         (true, false, false) => (),
3260         (false, true, false) => (),
3261         (false, false, true) => (),
3262         (_, _, _) => panic!(
3263             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
3264             gnu, musl, uclibc
3265         ),
3266     }
3267 
3268     let arm = target.contains("arm");
3269     let aarch64 = target.contains("aarch64");
3270     let i686 = target.contains("i686");
3271     let ppc = target.contains("powerpc");
3272     let ppc64 = target.contains("powerpc64");
3273     let s390x = target.contains("s390x");
3274     let sparc64 = target.contains("sparc64");
3275     let x32 = target.contains("x32");
3276     let x86_32 = target.contains("i686");
3277     let x86_64 = target.contains("x86_64");
3278     let aarch64_musl = aarch64 && musl;
3279     let gnueabihf = target.contains("gnueabihf");
3280     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
3281     let riscv64 = target.contains("riscv64");
3282     let uclibc = target.contains("uclibc");
3283 
3284     let mut cfg = ctest_cfg();
3285     cfg.define("_GNU_SOURCE", None);
3286     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
3287     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
3288     // glibc versions older than 2.29.
3289     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
3290 
3291     headers! { cfg:
3292                "ctype.h",
3293                "dirent.h",
3294                "dlfcn.h",
3295                "elf.h",
3296                "fcntl.h",
3297                "getopt.h",
3298                "glob.h",
3299                [gnu]: "gnu/libc-version.h",
3300                "grp.h",
3301                "iconv.h",
3302                "ifaddrs.h",
3303                "langinfo.h",
3304                "libgen.h",
3305                "limits.h",
3306                "link.h",
3307                "linux/sysctl.h",
3308                "locale.h",
3309                "malloc.h",
3310                "mntent.h",
3311                "mqueue.h",
3312                "net/ethernet.h",
3313                "net/if.h",
3314                "net/if_arp.h",
3315                "net/route.h",
3316                "netdb.h",
3317                "netinet/in.h",
3318                "netinet/ip.h",
3319                "netinet/tcp.h",
3320                "netinet/udp.h",
3321                "netpacket/packet.h",
3322                "poll.h",
3323                "pthread.h",
3324                "pty.h",
3325                "pwd.h",
3326                "regex.h",
3327                "resolv.h",
3328                "sched.h",
3329                "semaphore.h",
3330                "shadow.h",
3331                "signal.h",
3332                "spawn.h",
3333                "stddef.h",
3334                "stdint.h",
3335                "stdio.h",
3336                "stdlib.h",
3337                "string.h",
3338                "sys/epoll.h",
3339                "sys/eventfd.h",
3340                "sys/file.h",
3341                "sys/fsuid.h",
3342                "sys/inotify.h",
3343                "sys/ioctl.h",
3344                "sys/ipc.h",
3345                "sys/mman.h",
3346                "sys/mount.h",
3347                "sys/msg.h",
3348                "sys/personality.h",
3349                "sys/prctl.h",
3350                "sys/ptrace.h",
3351                "sys/quota.h",
3352                "sys/random.h",
3353                "sys/reboot.h",
3354                "sys/resource.h",
3355                "sys/sem.h",
3356                "sys/sendfile.h",
3357                "sys/shm.h",
3358                "sys/signalfd.h",
3359                "sys/socket.h",
3360                "sys/stat.h",
3361                "sys/statvfs.h",
3362                "sys/swap.h",
3363                "sys/syscall.h",
3364                "sys/time.h",
3365                "sys/timerfd.h",
3366                "sys/times.h",
3367                "sys/timex.h",
3368                "sys/types.h",
3369                "sys/uio.h",
3370                "sys/un.h",
3371                "sys/user.h",
3372                "sys/utsname.h",
3373                "sys/vfs.h",
3374                "sys/wait.h",
3375                "syslog.h",
3376                "termios.h",
3377                "time.h",
3378                "ucontext.h",
3379                "unistd.h",
3380                "utime.h",
3381                "utmp.h",
3382                "utmpx.h",
3383                "wchar.h",
3384                "errno.h",
3385                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
3386                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
3387                // Also unavailable on gnueabihf with glibc 2.30.
3388                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
3389                [(x86_64 || x86_32 || arm) && !gnueabihf]: "sys/io.h",
3390                // `sys/reg.h` is only available on x86 and x86_64
3391                [x86_64 || x86_32]: "sys/reg.h",
3392                // sysctl system call is deprecated and not available on musl
3393                // It is also unsupported in x32, deprecated since glibc 2.30:
3394                [!(x32 || musl || gnu)]: "sys/sysctl.h",
3395                // <execinfo.h> is not supported by musl:
3396                // https://www.openwall.com/lists/musl/2015/04/09/3
3397                // <execinfo.h> is not present on uclibc.
3398                [!(musl || uclibc)]: "execinfo.h",
3399     }
3400 
3401     // Include linux headers at the end:
3402     headers! {
3403         cfg:
3404         "asm/mman.h",
3405         "linux/can.h",
3406         "linux/can/raw.h",
3407         // FIXME: requires kernel headers >= 5.4.1.
3408         [!musl]: "linux/can/j1939.h",
3409         "linux/dccp.h",
3410         "linux/errqueue.h",
3411         "linux/falloc.h",
3412         "linux/filter.h",
3413         "linux/fs.h",
3414         "linux/futex.h",
3415         "linux/genetlink.h",
3416         "linux/if.h",
3417         "linux/if_addr.h",
3418         "linux/if_alg.h",
3419         "linux/if_ether.h",
3420         "linux/if_tun.h",
3421         "linux/if_xdp.h",
3422         "linux/input.h",
3423         "linux/ipv6.h",
3424         "linux/kexec.h",
3425         "linux/keyctl.h",
3426         "linux/magic.h",
3427         "linux/memfd.h",
3428         "linux/membarrier.h",
3429         "linux/mempolicy.h",
3430         "linux/mman.h",
3431         "linux/module.h",
3432         // FIXME: requires kernel headers >= 5.1.
3433         [!musl]: "linux/mount.h",
3434         "linux/net_tstamp.h",
3435         "linux/netfilter/nfnetlink.h",
3436         "linux/netfilter/nfnetlink_log.h",
3437         "linux/netfilter/nfnetlink_queue.h",
3438         "linux/netfilter/nf_tables.h",
3439         "linux/netfilter_ipv4.h",
3440         "linux/netfilter_ipv6.h",
3441         "linux/netfilter_ipv6/ip6_tables.h",
3442         "linux/netlink.h",
3443         // FIXME: requires Linux >= 5.6:
3444         [!musl]: "linux/openat2.h",
3445         [!musl]: "linux/ptrace.h",
3446         "linux/quota.h",
3447         "linux/random.h",
3448         "linux/reboot.h",
3449         "linux/rtnetlink.h",
3450         "linux/sched.h",
3451         "linux/sctp.h",
3452         "linux/seccomp.h",
3453         "linux/sock_diag.h",
3454         "linux/sockios.h",
3455         "linux/tls.h",
3456         "linux/uinput.h",
3457         "linux/vm_sockets.h",
3458         "linux/wait.h",
3459         "linux/wireless.h",
3460         "sys/fanotify.h",
3461         // <sys/auxv.h> is not present on uclibc
3462         [!uclibc]: "sys/auxv.h",
3463         [gnu]: "linux/close_range.h",
3464     }
3465 
3466     // note: aio.h must be included before sys/mount.h
3467     headers! {
3468         cfg:
3469         "sys/xattr.h",
3470         "sys/sysinfo.h",
3471         // AIO is not supported by uclibc:
3472         [!uclibc]: "aio.h",
3473     }
3474 
3475     cfg.type_name(move |ty, is_struct, is_union| {
3476         match ty {
3477             // Just pass all these through, no need for a "struct" prefix
3478             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3479             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3480             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
3481 
3482             "Ioctl" if gnu => "unsigned long".to_string(),
3483             "Ioctl" => "int".to_string(),
3484 
3485             // LFS64 types have been removed in musl 1.2.4+
3486             "off64_t" if musl => "off_t".to_string(),
3487 
3488             // typedefs don't need any keywords
3489             t if t.ends_with("_t") => t.to_string(),
3490             // put `struct` in front of all structs:.
3491             t if is_struct => format!("struct {}", t),
3492             // put `union` in front of all unions:
3493             t if is_union => format!("union {}", t),
3494 
3495             t => t.to_string(),
3496         }
3497     });
3498 
3499     cfg.field_name(move |struct_, field| {
3500         match field {
3501             // Our stat *_nsec fields normally don't actually exist but are part
3502             // of a timeval struct
3503             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
3504                 s.replace("e_nsec", ".tv_nsec")
3505             }
3506             // FIXME: epoll_event.data is actually a union in C, but in Rust
3507             // it is only a u64 because we only expose one field
3508             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
3509             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
3510             // The following structs have a field called `type` in C,
3511             // but `type` is a Rust keyword, so these fields are translated
3512             // to `type_` in Rust.
3513             "type_"
3514                 if struct_ == "input_event"
3515                     || struct_ == "input_mask"
3516                     || struct_ == "ff_effect" =>
3517             {
3518                 "type".to_string()
3519             }
3520 
3521             s => s.to_string(),
3522         }
3523     });
3524 
3525     cfg.skip_type(move |ty| {
3526         match ty {
3527             // FIXME: `sighandler_t` type is incorrect, see:
3528             // https://github.com/rust-lang/libc/issues/1359
3529             "sighandler_t" => true,
3530 
3531             // These cannot be tested when "resolv.h" is included and are tested
3532             // in the `linux_elf.rs` file.
3533             "Elf64_Phdr" | "Elf32_Phdr" => true,
3534 
3535             // This type is private on Linux. It is implemented as a C `enum`
3536             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
3537             // which expect a `c_int` even though both are ABI compatible.
3538             "__rlimit_resource_t" => true,
3539             // on Linux, this is a volatile int
3540             "pthread_spinlock_t" => true,
3541 
3542             // For internal use only, to define architecture specific ioctl constants with a libc
3543             // specific type.
3544             "Ioctl" => true,
3545 
3546             // FIXME: requires >= 5.4.1 kernel headers
3547             "pgn_t" if musl => true,
3548             "priority_t" if musl => true,
3549             "name_t" if musl => true,
3550 
3551             t => {
3552                 if musl {
3553                     // LFS64 types have been removed in musl 1.2.4+
3554                     t.ends_with("64") || t.ends_with("64_t")
3555                 } else {
3556                     false
3557                 }
3558             }
3559         }
3560     });
3561 
3562     cfg.skip_struct(move |ty| {
3563         if ty.starts_with("__c_anonymous_") {
3564             return true;
3565         }
3566         // FIXME: musl CI has old headers
3567         if musl && ty.starts_with("uinput_") {
3568             return true;
3569         }
3570         // LFS64 types have been removed in musl 1.2.4+
3571         if musl && (ty.ends_with("64") || ty.ends_with("64_t")) {
3572             return true;
3573         }
3574         // FIXME: sparc64 CI has old headers
3575         if sparc64 && (ty == "uinput_ff_erase" || ty == "uinput_abs_setup") {
3576             return true;
3577         }
3578         // FIXME(https://github.com/rust-lang/libc/issues/1558): passing by
3579         // value corrupts the value for reasons not understood.
3580         if (gnu && sparc64) && (ty == "ip_mreqn" || ty == "hwtstamp_config") {
3581             return true;
3582         }
3583         match ty {
3584             // These cannot be tested when "resolv.h" is included and are tested
3585             // in the `linux_elf.rs` file.
3586             "Elf64_Phdr" | "Elf32_Phdr" => true,
3587 
3588             // On Linux, the type of `ut_tv` field of `struct utmpx`
3589             // can be an anonymous struct, so an extra struct,
3590             // which is absent in glibc, has to be defined.
3591             "__timeval" => true,
3592 
3593             // FIXME: This is actually a union, not a struct
3594             "sigval" => true,
3595 
3596             // This type is tested in the `linux_termios.rs` file since there
3597             // are header conflicts when including them with all the other
3598             // structs.
3599             "termios2" => true,
3600 
3601             // FIXME: remove once we set minimum supported glibc version.
3602             // ucontext_t added a new field as of glibc 2.28; our struct definition is
3603             // conservative and omits the field, but that means the size doesn't match for newer
3604             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
3605             "ucontext_t" if gnu => true,
3606 
3607             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
3608             // So let's ignore for now and re-visit later.
3609             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
3610             "statx" => true,
3611             "statx_timestamp" => true,
3612 
3613             // On Linux, the type of `ut_exit` field of struct `utmpx`
3614             // can be an anonymous struct, so an extra struct,
3615             // which is absent in musl, has to be defined.
3616             "__exit_status" if musl => true,
3617 
3618             // clone_args might differ b/w libc versions
3619             "clone_args" => true,
3620 
3621             // Might differ between kernel versions
3622             "open_how" => true,
3623 
3624             // FIXME: requires >= 5.4.1 kernel headers
3625             "j1939_filter" if musl => true,
3626 
3627             // FIXME: requires >= 5.4 kernel headers
3628             "sockaddr_can" if musl => true,
3629 
3630             "sctp_initmsg" | "sctp_sndrcvinfo" | "sctp_sndinfo" | "sctp_rcvinfo"
3631             | "sctp_nxtinfo" | "sctp_prinfo" | "sctp_authinfo" => true,
3632 
3633             // FIXME: requires >= 6.1 kernel headers
3634             "canxl_frame" => true,
3635 
3636             // FIXME: The size of `iv` has been changed since Linux v6.0
3637             // https://github.com/torvalds/linux/commit/94dfc73e7cf4a31da66b8843f0b9283ddd6b8381
3638             "af_alg_iv" => true,
3639 
3640             // FIXME: Requires >= 5.1 kernel headers.
3641             // Everything that uses install-musl.sh has 4.19 kernel headers.
3642             "tls12_crypto_info_aes_gcm_256"
3643                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
3644             {
3645                 true
3646             }
3647 
3648             // FIXME: Requires >= 5.11 kernel headers.
3649             // Everything that uses install-musl.sh has 4.19 kernel headers.
3650             "tls12_crypto_info_chacha20_poly1305"
3651                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
3652             {
3653                 true
3654             }
3655 
3656             // FIXME: Requires >= 5.3 kernel headers.
3657             // Everything that uses install-musl.sh has 4.19 kernel headers.
3658             "xdp_options" if musl => true,
3659 
3660             // FIXME: Requires >= 5.4 kernel headers.
3661             // Everything that uses install-musl.sh has 4.19 kernel headers.
3662             "xdp_umem_reg" | "xdp_ring_offset" | "xdp_mmap_offsets" if musl => true,
3663 
3664             // FIXME: Requires >= 5.9 kernel headers.
3665             // Everything that uses install-musl.sh has 4.19 kernel headers.
3666             "xdp_statistics" if musl => true,
3667 
3668             // A new field was added in kernel 5.4, this is the old version for backwards compatibility.
3669             // https://github.com/torvalds/linux/commit/77cd0d7b3f257fd0e3096b4fdcff1a7d38e99e10
3670             "xdp_ring_offset_v1" | "xdp_mmap_offsets_v1" => true,
3671 
3672             // Multiple new fields were added in kernel 5.9, this is the old version for backwards compatibility.
3673             // https://github.com/torvalds/linux/commit/77cd0d7b3f257fd0e3096b4fdcff1a7d38e99e10
3674             "xdp_statistics_v1" => true,
3675 
3676             // A new field was added in kernel 5.4, this is the old version for backwards compatibility.
3677             // https://github.com/torvalds/linux/commit/c05cd3645814724bdeb32a2b4d953b12bdea5f8c
3678             "xdp_umem_reg_v1" => true,
3679 
3680             _ => false,
3681         }
3682     });
3683 
3684     cfg.skip_const(move |name| {
3685         if !gnu {
3686             // Skip definitions from the kernel on non-glibc Linux targets.
3687             // They're libc-independent, so we only need to check them on one
3688             // libc. We don't want to break CI if musl or another libc doesn't
3689             // have the definitions yet. (We do still want to check them on
3690             // every glibc target, though, as some of them can vary by
3691             // architecture.)
3692             //
3693             // This is not an exhaustive list of kernel constants, just a list
3694             // of prefixes of all those that have appeared here or that get
3695             // updated regularly and seem likely to cause breakage.
3696             if name.starts_with("AF_")
3697                 || name.starts_with("ARPHRD_")
3698                 || name.starts_with("EPOLL")
3699                 || name.starts_with("F_")
3700                 || name.starts_with("FALLOC_FL_")
3701                 || name.starts_with("IFLA_")
3702                 || name.starts_with("KEXEC_")
3703                 || name.starts_with("MS_")
3704                 || name.starts_with("MSG_")
3705                 || name.starts_with("OPEN_TREE_")
3706                 || name.starts_with("P_")
3707                 || name.starts_with("PF_")
3708                 || name.starts_with("RLIMIT_")
3709                 || name.starts_with("RTEXT_FILTER_")
3710                 || name.starts_with("SOL_")
3711                 || name.starts_with("STATX_")
3712                 || name.starts_with("SW_")
3713                 || name.starts_with("SYS_")
3714                 || name.starts_with("TCP_")
3715                 || name.starts_with("UINPUT_")
3716                 || name.starts_with("VMADDR_")
3717             {
3718                 return true;
3719             }
3720         }
3721         if musl {
3722             // FIXME: Requires >= 5.4.1 kernel headers
3723             if name.starts_with("J1939")
3724                 || name.starts_with("RTEXT_FILTER_")
3725                 || name.starts_with("SO_J1939")
3726                 || name.starts_with("SCM_J1939")
3727             {
3728                 return true;
3729             }
3730             // FIXME: Requires >= 5.10 kernel headers
3731             if name.starts_with("MEMBARRIER_CMD_REGISTER")
3732                 || name.starts_with("MEMBARRIER_CMD_PRIVATE")
3733             {
3734                 return true;
3735             }
3736             // LFS64 types have been removed in musl 1.2.4+
3737             if name.starts_with("RLIM64") {
3738                 return true;
3739             }
3740             // CI fails because musl targets use Linux v4 kernel
3741             if name.starts_with("NI_IDN") {
3742                 return true;
3743             }
3744         }
3745         match name {
3746             // These constants are not available if gnu headers have been included
3747             // and can therefore not be tested here
3748             //
3749             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
3750             | "IPV6_FLOWINFO"
3751             | "IPV6_FLOWLABEL_MGR"
3752             | "IPV6_FLOWINFO_SEND"
3753             | "IPV6_FLOWINFO_FLOWLABEL"
3754             | "IPV6_FLOWINFO_PRIORITY"
3755             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
3756             | "F_CANCELLK"
3757             | "F_ADD_SEALS"
3758             | "F_GET_SEALS"
3759             | "F_SEAL_SEAL"
3760             | "F_SEAL_SHRINK"
3761             | "F_SEAL_GROW"
3762             | "F_SEAL_WRITE" => true,
3763             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests
3764             // because including `linux/if_arp.h` causes some conflicts:
3765             "ARPHRD_CAN" => true,
3766 
3767             // FIXME: deprecated: not available in any header
3768             // See: https://github.com/rust-lang/libc/issues/1356
3769             "ENOATTR" => true,
3770 
3771             // FIXME: SIGUNUSED was removed in glibc 2.26
3772             // Users should use SIGSYS instead.
3773             "SIGUNUSED" => true,
3774 
3775             // FIXME: conflicts with glibc headers and is tested in
3776             // `linux_termios.rs` below:
3777             | "BOTHER"
3778             | "IBSHIFT"
3779             | "TCGETS2"
3780             | "TCSETS2"
3781             | "TCSETSW2"
3782             | "TCSETSF2" => true,
3783 
3784             // FIXME: on musl the pthread types are defined a little differently
3785             // - these constants are used by the glibc implementation.
3786             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
3787 
3788             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
3789             // We should do so after a while.
3790             "SOMAXCONN" if gnu => true,
3791 
3792             // deprecated: not available from Linux kernel 5.6:
3793             "VMADDR_CID_RESERVED" => true,
3794 
3795             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
3796             | "IPPROTO_MAX"
3797             | "IPPROTO_ETHERNET"
3798             | "IPPROTO_MPTCP" => true,
3799 
3800             // FIXME: Not yet implemented on sparc64
3801             "SYS_clone3" if sparc64 => true,
3802 
3803             // FIXME: Not defined on ARM, gnueabihf, musl, PowerPC, riscv64, s390x, and sparc64.
3804             "SYS_memfd_secret" if arm | gnueabihf | musl | ppc | riscv64 | s390x | sparc64 => true,
3805 
3806             // FIXME: Added in Linux 5.16
3807             // https://github.com/torvalds/linux/commit/039c0ec9bb77446d7ada7f55f90af9299b28ca49
3808             "SYS_futex_waitv" => true,
3809 
3810             // FIXME: Added in Linux 5.17
3811             // https://github.com/torvalds/linux/commit/c6018b4b254971863bd0ad36bb5e7d0fa0f0ddb0
3812             "SYS_set_mempolicy_home_node" => true,
3813 
3814             // FIXME: Added in Linux 5.18
3815             // https://github.com/torvalds/linux/commit/8b5413647262dda8d8d0e07e14ea1de9ac7cf0b2
3816             "NFQA_PRIORITY" => true,
3817 
3818             // FIXME: requires more recent kernel headers on CI
3819             | "UINPUT_VERSION"
3820             | "SW_MAX"
3821             | "SW_CNT"
3822                 if ppc64 || riscv64 => true,
3823 
3824             // FIXME: Not currently available in headers on ARM and musl.
3825             "NETLINK_GET_STRICT_CHK" if arm || musl => true,
3826 
3827             // kernel constants not available in uclibc 1.0.34
3828             | "EXTPROC"
3829             | "IPPROTO_BEETPH"
3830             | "IPPROTO_MPLS"
3831             | "IPV6_HDRINCL"
3832             | "IPV6_MULTICAST_ALL"
3833             | "IPV6_PMTUDISC_INTERFACE"
3834             | "IPV6_PMTUDISC_OMIT"
3835             | "IPV6_ROUTER_ALERT_ISOLATE"
3836             | "PACKET_MR_UNICAST"
3837             | "RUSAGE_THREAD"
3838             | "SHM_EXEC"
3839             | "UDP_GRO"
3840             | "UDP_SEGMENT"
3841                 if uclibc => true,
3842 
3843             // headers conflicts with linux/pidfd.h
3844             "PIDFD_NONBLOCK" => true,
3845 
3846             // is a private value for kernel usage normally
3847             "FUSE_SUPER_MAGIC" => true,
3848 
3849             // linux 5.17 min
3850             "PR_SET_VMA" | "PR_SET_VMA_ANON_NAME" => true,
3851 
3852             // present in recent kernels only
3853             "PR_SCHED_CORE" | "PR_SCHED_CORE_CREATE" | "PR_SCHED_CORE_GET" | "PR_SCHED_CORE_MAX" | "PR_SCHED_CORE_SCOPE_PROCESS_GROUP" | "PR_SCHED_CORE_SCOPE_THREAD" | "PR_SCHED_CORE_SCOPE_THREAD_GROUP" | "PR_SCHED_CORE_SHARE_FROM" | "PR_SCHED_CORE_SHARE_TO" => true,
3854 
3855             // present in recent kernels only >= 5.13
3856             "PR_PAC_SET_ENABLED_KEYS" | "PR_PAC_GET_ENABLED_KEYS" => true,
3857             // present in recent kernels only >= 5.19
3858             "PR_SME_SET_VL" | "PR_SME_GET_VL" | "PR_SME_VL_LEN_MAX" | "PR_SME_SET_VL_INHERIT" | "PR_SME_SET_VL_ONE_EXEC" => true,
3859 
3860             // Added in Linux 5.14
3861             "FUTEX_LOCK_PI2" => true,
3862 
3863             // Added in  linux 6.1
3864             "STATX_DIOALIGN"
3865             | "CAN_RAW_XL_FRAMES"
3866             | "CANXL_HDR_SIZE"
3867             | "CANXL_MAX_DLC"
3868             | "CANXL_MAX_DLC_MASK"
3869             | "CANXL_MAX_DLEN"
3870             | "CANXL_MAX_MTU"
3871             | "CANXL_MIN_DLC"
3872             | "CANXL_MIN_DLEN"
3873             | "CANXL_MIN_MTU"
3874             | "CANXL_MTU"
3875             | "CANXL_PRIO_BITS"
3876             | "CANXL_PRIO_MASK"
3877             | "CANXL_SEC"
3878             | "CANXL_XLF"
3879              => true,
3880 
3881             // FIXME: Parts of netfilter/nfnetlink*.h require more recent kernel headers:
3882             | "RTNLGRP_MCTP_IFADDR" // linux v5.17+
3883             | "RTNLGRP_TUNNEL" // linux v5.18+
3884             | "RTNLGRP_STATS" // linux v5.18+
3885                 => true,
3886 
3887             // FIXME: The below is no longer const in glibc 2.34:
3888             // https://github.com/bminor/glibc/commit/5d98a7dae955bafa6740c26eaba9c86060ae0344
3889             | "PTHREAD_STACK_MIN"
3890             | "SIGSTKSZ"
3891             | "MINSIGSTKSZ"
3892                 if gnu => true,
3893 
3894             // FIXME: Linux >= 5.16 changed its value:
3895             // https://github.com/torvalds/linux/commit/42df6e1d221dddc0f2acf2be37e68d553ad65f96
3896             "NF_NETDEV_NUMHOOKS" => true,
3897 
3898             // FIXME: requires Linux >= 5.6:
3899             | "RESOLVE_BENEATH"
3900             | "RESOLVE_CACHED"
3901             | "RESOLVE_IN_ROOT"
3902             | "RESOLVE_NO_MAGICLINKS"
3903             | "RESOLVE_NO_SYMLINKS"
3904             | "RESOLVE_NO_XDEV" if musl => true,
3905 
3906             // FIXME: requires Linux >= 5.4:
3907             | "CAN_J1939"
3908             | "CAN_NPROTO" if musl => true,
3909 
3910             // FIXME: requires Linux >= 5.6
3911             "GRND_INSECURE" if musl => true,
3912 
3913             // FIXME: requires Linux >= 5.7:
3914             "MREMAP_DONTUNMAP" if musl => true,
3915 
3916             // FIXME: requires Linux >= v5.8
3917             "IF_LINK_MODE_TESTING" if musl || sparc64 => true,
3918 
3919             // FIXME: Requires more recent kernel headers (5.9 / 5.11):
3920             | "CLOSE_RANGE_UNSHARE"
3921             | "CLOSE_RANGE_CLOEXEC" if musl => true,
3922 
3923             // FIXME: requires Linux >= 5.12:
3924             "MPOL_F_NUMA_BALANCING" if musl => true,
3925 
3926             // FIXME: Requires more recent kernel headers
3927             | "NFNL_SUBSYS_COUNT" // bumped in v5.14
3928             | "NFNL_SUBSYS_HOOK" // v5.14+
3929             | "NFULA_VLAN" // v5.4+
3930             | "NFULA_L2HDR" // v5.4+
3931             | "NFULA_VLAN_PROTO" // v5.4+
3932             | "NFULA_VLAN_TCI" // v5.4+
3933             | "NFULA_VLAN_UNSPEC" // v5.4+
3934             | "RTNLGRP_NEXTHOP" // linux v5.3+
3935             | "RTNLGRP_BRVLAN" // linux v5.6+
3936             if musl => true,
3937 
3938             | "MADV_COLD"
3939             | "MADV_PAGEOUT"
3940             | "MADV_POPULATE_READ"
3941             | "MADV_POPULATE_WRITE"
3942             if musl => true,
3943             "CLONE_CLEAR_SIGHAND" | "CLONE_INTO_CGROUP" => true,
3944 
3945             // kernel 6.1 minimum
3946             "MADV_COLLAPSE" => true,
3947 
3948             // kernel 6.2 minimum
3949             "TUN_F_USO4" | "TUN_F_USO6" | "IFF_NO_CARRIER" => true,
3950 
3951             // FIXME: Requires more recent kernel headers
3952             | "IFLA_PARENT_DEV_NAME"     // linux v5.13+
3953             | "IFLA_PARENT_DEV_BUS_NAME" // linux v5.13+
3954             | "IFLA_GRO_MAX_SIZE"        // linux v5.16+
3955             | "IFLA_TSO_MAX_SIZE"        // linux v5.18+
3956             | "IFLA_TSO_MAX_SEGS"        // linux v5.18+
3957             | "IFLA_ALLMULTI"            // linux v6.0+
3958             | "MADV_DONTNEED_LOCKED"     // linux v5.18+
3959                 => true,
3960             "SCTP_FUTURE_ASSOC" | "SCTP_CURRENT_ASSOC" | "SCTP_ALL_ASSOC" | "SCTP_PEER_ADDR_THLDS_V2" => true, // linux 5.5+
3961 
3962             // FIXME: Requires more recent kernel headers
3963             "HWTSTAMP_TX_ONESTEP_P2P" if musl => true, // linux v5.6+
3964 
3965             // kernel 6.5 minimum
3966             "MOVE_MOUNT_BENEATH" => true,
3967             // FIXME: Requires linux 6.1
3968             "ALG_SET_KEY_BY_KEY_SERIAL" | "ALG_SET_DRBG_ENTROPY" => true,
3969 
3970             // FIXME: Requires more recent kernel headers
3971             | "FAN_FS_ERROR"                      // linux v5.16+
3972             | "FAN_RENAME"                        // linux v5.17+
3973             | "FAN_REPORT_TARGET_FID"             // linux v5.17+
3974             | "FAN_REPORT_DFID_NAME_TARGET"       // linux v5.17+
3975             | "FAN_MARK_EVICTABLE"                // linux v5.19+
3976             | "FAN_MARK_IGNORE"                   // linux v6.0+
3977             | "FAN_MARK_IGNORE_SURV"              // linux v6.0+
3978             | "FAN_EVENT_INFO_TYPE_ERROR"         // linux v5.16+
3979             | "FAN_EVENT_INFO_TYPE_OLD_DFID_NAME" // linux v5.17+
3980             | "FAN_EVENT_INFO_TYPE_NEW_DFID_NAME" // linux v5.17+
3981             | "FAN_RESPONSE_INFO_NONE"            // linux v5.16+
3982             | "FAN_RESPONSE_INFO_AUDIT_RULE"      // linux v5.16+
3983             | "FAN_INFO"                          // linux v5.16+
3984                 => true,
3985 
3986             // FIXME: Requires linux 5.15+
3987             "FAN_REPORT_PIDFD" if musl => true,
3988 
3989             // FIXME: Requires linux 5.9+
3990             | "FAN_REPORT_DIR_FID"
3991             | "FAN_REPORT_NAME"
3992             | "FAN_REPORT_DFID_NAME"
3993             | "FAN_EVENT_INFO_TYPE_DFID_NAME"
3994             | "FAN_EVENT_INFO_TYPE_DFID"
3995             | "FAN_EVENT_INFO_TYPE_PIDFD"
3996             | "FAN_NOPIDFD"
3997             | "FAN_EPIDFD"
3998             if musl => true,
3999 
4000             // FIXME: Requires linux 6.5
4001             "NFT_MSG_MAX" => true,
4002 
4003             // FIXME: Requires >= 5.1 kernel headers.
4004             // Everything that uses install-musl.sh has 4.19 kernel headers.
4005             "TLS_1_3_VERSION"
4006             | "TLS_1_3_VERSION_MAJOR"
4007             | "TLS_1_3_VERSION_MINOR"
4008             | "TLS_CIPHER_AES_GCM_256"
4009             | "TLS_CIPHER_AES_GCM_256_IV_SIZE"
4010             | "TLS_CIPHER_AES_GCM_256_KEY_SIZE"
4011             | "TLS_CIPHER_AES_GCM_256_SALT_SIZE"
4012             | "TLS_CIPHER_AES_GCM_256_TAG_SIZE"
4013             | "TLS_CIPHER_AES_GCM_256_REC_SEQ_SIZE"
4014                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
4015             {
4016                 true
4017             }
4018 
4019             // FIXME: Requires >= 5.11 kernel headers.
4020             // Everything that uses install-musl.sh has 4.19 kernel headers.
4021             "TLS_CIPHER_CHACHA20_POLY1305"
4022             | "TLS_CIPHER_CHACHA20_POLY1305_IV_SIZE"
4023             | "TLS_CIPHER_CHACHA20_POLY1305_KEY_SIZE"
4024             | "TLS_CIPHER_CHACHA20_POLY1305_SALT_SIZE"
4025             | "TLS_CIPHER_CHACHA20_POLY1305_TAG_SIZE"
4026             | "TLS_CIPHER_CHACHA20_POLY1305_REC_SEQ_SIZE"
4027                 if (aarch64 || arm || i686 || s390x || x86_64) && musl =>
4028             {
4029                 true
4030             }
4031 
4032             // FIXME: Requires >= 5.3 kernel headers.
4033             // Everything that uses install-musl.sh has 4.19 kernel headers.
4034             "XDP_OPTIONS_ZEROCOPY" | "XDP_OPTIONS"
4035                 if musl =>
4036             {
4037                 true
4038             }
4039 
4040             // FIXME: Requires >= 5.4 kernel headers.
4041             // Everything that uses install-musl.sh has 4.19 kernel headers.
4042             "XSK_UNALIGNED_BUF_OFFSET_SHIFT"
4043             | "XSK_UNALIGNED_BUF_ADDR_MASK"
4044             | "XDP_UMEM_UNALIGNED_CHUNK_FLAG"
4045             | "XDP_RING_NEED_WAKEUP"
4046             | "XDP_USE_NEED_WAKEUP"
4047                 if musl =>
4048             {
4049                 true
4050             }
4051 
4052             // FIXME: Requires >= 6.6 kernel headers.
4053             "XDP_USE_SG"
4054             | "XDP_PKT_CONTD"
4055                 =>
4056             {
4057                 true
4058             }
4059 
4060             _ => false,
4061         }
4062     });
4063 
4064     cfg.skip_fn(move |name| {
4065         // skip those that are manually verified
4066         match name {
4067             // FIXME: https://github.com/rust-lang/libc/issues/1272
4068             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
4069 
4070             // There are two versions of the sterror_r function, see
4071             //
4072             // https://linux.die.net/man/3/strerror_r
4073             //
4074             // An XSI-compliant version provided if:
4075             //
4076             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
4077             //  && ! _GNU_SOURCE
4078             //
4079             // and a GNU specific version provided if _GNU_SOURCE is defined.
4080             //
4081             // libc provides bindings for the XSI-compliant version, which is
4082             // preferred for portable applications.
4083             //
4084             // We skip the test here since here _GNU_SOURCE is defined, and
4085             // test the XSI version below.
4086             "strerror_r" => true,
4087 
4088             // FIXME: Our API is unsound. The Rust API allows aliasing
4089             // pointers, but the C API requires pointers not to alias.
4090             // We should probably be at least using `&`/`&mut` here, see:
4091             // https://github.com/gnzlbg/ctest/issues/68
4092             "lio_listio" if musl => true,
4093 
4094             // Needs glibc 2.34 or later.
4095             "posix_spawn_file_actions_addclosefrom_np" if gnu && sparc64 => true,
4096             // Needs glibc 2.35 or later.
4097             "posix_spawn_file_actions_addtcsetpgrp_np" if gnu && sparc64 => true,
4098 
4099             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
4100             "sysctl" if gnu => true,
4101 
4102             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
4103             "gettimeofday" if gnu => true,
4104 
4105             // These are all implemented as static inline functions in uclibc, so
4106             // they cannot be linked against.
4107             // If implementations are required, they might need to be implemented
4108             // in this crate.
4109             "posix_spawnattr_init" if uclibc => true,
4110             "posix_spawnattr_destroy" if uclibc => true,
4111             "posix_spawnattr_getsigdefault" if uclibc => true,
4112             "posix_spawnattr_setsigdefault" if uclibc => true,
4113             "posix_spawnattr_getsigmask" if uclibc => true,
4114             "posix_spawnattr_setsigmask" if uclibc => true,
4115             "posix_spawnattr_getflags" if uclibc => true,
4116             "posix_spawnattr_setflags" if uclibc => true,
4117             "posix_spawnattr_getpgroup" if uclibc => true,
4118             "posix_spawnattr_setpgroup" if uclibc => true,
4119             "posix_spawnattr_getschedpolicy" if uclibc => true,
4120             "posix_spawnattr_setschedpolicy" if uclibc => true,
4121             "posix_spawnattr_getschedparam" if uclibc => true,
4122             "posix_spawnattr_setschedparam" if uclibc => true,
4123             "posix_spawn_file_actions_init" if uclibc => true,
4124             "posix_spawn_file_actions_destroy" if uclibc => true,
4125 
4126             // uclibc defines the flags type as a uint, but dependent crates
4127             // assume it's a int instead.
4128             "getnameinfo" if uclibc => true,
4129 
4130             // FIXME: This needs musl 1.2.2 or later.
4131             "gettid" if musl => true,
4132 
4133             // Needs glibc 2.33 or later.
4134             "mallinfo2" => true,
4135 
4136             "reallocarray" if musl => true,
4137 
4138             // Not defined in uclibc as of 1.0.34
4139             "gettid" if uclibc => true,
4140 
4141             // Needs musl 1.2.3 or later.
4142             "pthread_getname_np" if musl => true,
4143 
4144             // pthread_sigqueue uses sigval, which was initially declared
4145             // as a struct but should be defined as a union. However due
4146             // to the issues described here: https://github.com/rust-lang/libc/issues/2816
4147             // it can't be changed from struct.
4148             "pthread_sigqueue" => true,
4149 
4150             // There are two versions of basename(3) on Linux with glibc, see
4151             //
4152             // https://man7.org/linux/man-pages/man3/basename.3.html
4153             //
4154             // If libgen.h is included, then the POSIX version will be available;
4155             // If _GNU_SOURCE is defined and string.h is included, then the GNU one
4156             // will be used.
4157             //
4158             // libc exposes both of them, providing a prefix to differentiate between
4159             // them.
4160             //
4161             // Because the name with prefix is not a valid symbol in C, we have to
4162             // skip the tests.
4163             "posix_basename" if gnu => true,
4164             "gnu_basename" if gnu => true,
4165 
4166             // FIXME: function pointers changed since Ubuntu 23.10
4167             "strtol" | "strtoll" | "strtoul" | "strtoull" | "fscanf" | "scanf" | "sscanf" => true,
4168 
4169             _ => false,
4170         }
4171     });
4172 
4173     cfg.skip_field_type(move |struct_, field| {
4174         // This is a weird union, don't check the type.
4175         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
4176         // sighandler_t type is super weird
4177         (struct_ == "sigaction" && field == "sa_sigaction") ||
4178         // __timeval type is a patch which doesn't exist in glibc
4179         (struct_ == "utmpx" && field == "ut_tv") ||
4180         // sigval is actually a union, but we pretend it's a struct
4181         (struct_ == "sigevent" && field == "sigev_value") ||
4182         // this one is an anonymous union
4183         (struct_ == "ff_effect" && field == "u") ||
4184         // `__exit_status` type is a patch which is absent in musl
4185         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
4186         // `can_addr` is an anonymous union
4187         (struct_ == "sockaddr_can" && field == "can_addr")
4188     });
4189 
4190     cfg.volatile_item(|i| {
4191         use ctest::VolatileItemKind::*;
4192         match i {
4193             // aio_buf is a volatile void** but since we cannot express that in
4194             // Rust types, we have to explicitly tell the checker about it here:
4195             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
4196             _ => false,
4197         }
4198     });
4199 
4200     cfg.skip_field(move |struct_, field| {
4201         // this is actually a union on linux, so we can't represent it well and
4202         // just insert some padding.
4203         (struct_ == "siginfo_t" && field == "_pad") ||
4204         // musl names this __dummy1 but it's still there
4205         (musl && struct_ == "glob_t" && field == "gl_flags") ||
4206         // musl seems to define this as an *anonymous* bitfield
4207         (musl && struct_ == "statvfs" && field == "__f_unused") ||
4208         // sigev_notify_thread_id is actually part of a sigev_un union
4209         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
4210         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
4211         // has them yet.
4212         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
4213                                            field == "_pad2" ||
4214                                            field == "ssi_syscall" ||
4215                                            field == "ssi_call_addr" ||
4216                                            field == "ssi_arch")) ||
4217         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
4218         // while other fields become reserved.
4219         (struct_ == "sched_param" && [
4220             "sched_ss_low_priority",
4221             "sched_ss_repl_period",
4222             "sched_ss_init_budget",
4223             "sched_ss_max_repl",
4224         ].contains(&field) && musl) ||
4225         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
4226         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
4227         // glibc uses unnamed fields here and Rust doesn't support that yet
4228         (struct_ == "timex" && field.starts_with("__unused")) ||
4229         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
4230         (struct_ == "ipc_perm" && field == "mode"
4231             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
4232         ) ||
4233         // the `u` field is in fact an anonymous union
4234         (gnu && struct_ == "ptrace_syscall_info" && (field == "u" || field == "pad")) ||
4235         // the vregs field is a `__uint128_t` C's type.
4236         (struct_ == "user_fpsimd_struct" && field == "vregs") ||
4237         // Linux >= 5.11 tweaked the `svm_zero` field of the `sockaddr_vm` struct.
4238         // https://github.com/torvalds/linux/commit/dc8eeef73b63ed8988224ba6b5ed19a615163a7f
4239         (struct_ == "sockaddr_vm" && field == "svm_zero") ||
4240         // the `ifr_ifru` field is an anonymous union
4241         (struct_ == "ifreq" && field == "ifr_ifru") ||
4242         // the `ifc_ifcu` field is an anonymous union
4243         (struct_ == "ifconf" && field == "ifc_ifcu") ||
4244         // glibc uses a single array `uregs` instead of individual fields.
4245         (struct_ == "user_regs" && arm)
4246     });
4247 
4248     cfg.skip_roundtrip(move |s| match s {
4249         // FIXME:
4250         "mcontext_t" if s390x => true,
4251         // FIXME: This is actually a union.
4252         "fpreg_t" if s390x => true,
4253 
4254         // The test doesn't work on some env:
4255         "ipv6_mreq"
4256         | "ip_mreq_source"
4257         | "sockaddr_in6"
4258         | "sockaddr_ll"
4259         | "in_pktinfo"
4260         | "arpreq"
4261         | "arpreq_old"
4262         | "sockaddr_un"
4263         | "ff_constant_effect"
4264         | "ff_ramp_effect"
4265         | "ff_condition_effect"
4266         | "Elf32_Ehdr"
4267         | "Elf32_Chdr"
4268         | "ucred"
4269         | "in6_pktinfo"
4270         | "sockaddr_nl"
4271         | "termios"
4272         | "nlmsgerr"
4273             if sparc64 && gnu =>
4274         {
4275             true
4276         }
4277 
4278         // The `inotify_event` and `cmsghdr` types contain Flexible Array Member fields (the
4279         // `name` and `data` fields respectively) which have unspecified calling convention.
4280         // The roundtripping tests deliberately pass the structs by value to check "by value"
4281         // layout consistency, but this would be UB for the these types.
4282         "inotify_event" => true,
4283         "cmsghdr" => true,
4284 
4285         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
4286         "max_align_t" if i686 || ppc64 => true,
4287 
4288         _ => false,
4289     });
4290 
4291     cfg.generate("../src/lib.rs", "main.rs");
4292 
4293     test_linux_like_apis(target);
4294 }
4295 
4296 // This function tests APIs that are incompatible to test when other APIs
4297 // are included (e.g. because including both sets of headers clashes)
4298 fn test_linux_like_apis(target: &str) {
4299     let gnu = target.contains("gnu");
4300     let musl = target.contains("musl") || target.contains("ohos");
4301     let linux = target.contains("linux");
4302     let emscripten = target.contains("emscripten");
4303     let android = target.contains("android");
4304     assert!(linux || android || emscripten);
4305 
4306     if linux || android || emscripten {
4307         // test strerror_r from the `string.h` header
4308         let mut cfg = ctest_cfg();
4309         cfg.skip_type(|_| true).skip_static(|_| true);
4310 
4311         headers! { cfg: "string.h" }
4312         cfg.skip_fn(|f| match f {
4313             "strerror_r" => false,
4314             _ => true,
4315         })
4316         .skip_const(|_| true)
4317         .skip_struct(|_| true);
4318         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
4319     }
4320 
4321     if linux || android || emscripten {
4322         // test fcntl - see:
4323         // http://man7.org/linux/man-pages/man2/fcntl.2.html
4324         let mut cfg = ctest_cfg();
4325 
4326         if musl {
4327             cfg.header("fcntl.h");
4328         } else {
4329             cfg.header("linux/fcntl.h");
4330         }
4331 
4332         cfg.skip_type(|_| true)
4333             .skip_static(|_| true)
4334             .skip_struct(|_| true)
4335             .skip_fn(|_| true)
4336             .skip_const(move |name| match name {
4337                 // test fcntl constants:
4338                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK"
4339                 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false,
4340                 _ => true,
4341             })
4342             .type_name(move |ty, is_struct, is_union| match ty {
4343                 t if is_struct => format!("struct {}", t),
4344                 t if is_union => format!("union {}", t),
4345                 t => t.to_string(),
4346             });
4347 
4348         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
4349     }
4350 
4351     if linux || android {
4352         // test termios
4353         let mut cfg = ctest_cfg();
4354         cfg.header("asm/termbits.h");
4355         cfg.header("linux/termios.h");
4356         cfg.skip_type(|_| true)
4357             .skip_static(|_| true)
4358             .skip_fn(|_| true)
4359             .skip_const(|c| match c {
4360                 "BOTHER" | "IBSHIFT" => false,
4361                 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => false,
4362                 _ => true,
4363             })
4364             .skip_struct(|s| s != "termios2")
4365             .type_name(move |ty, is_struct, is_union| match ty {
4366                 "Ioctl" if gnu => "unsigned long".to_string(),
4367                 "Ioctl" => "int".to_string(),
4368                 t if is_struct => format!("struct {}", t),
4369                 t if is_union => format!("union {}", t),
4370                 t => t.to_string(),
4371             });
4372         cfg.generate("../src/lib.rs", "linux_termios.rs");
4373     }
4374 
4375     if linux || android {
4376         // test IPV6_ constants:
4377         let mut cfg = ctest_cfg();
4378         headers! {
4379             cfg:
4380             "linux/in6.h"
4381         }
4382         cfg.skip_type(|_| true)
4383             .skip_static(|_| true)
4384             .skip_fn(|_| true)
4385             .skip_const(|_| true)
4386             .skip_struct(|_| true)
4387             .skip_const(move |name| match name {
4388                 "IPV6_FLOWINFO"
4389                 | "IPV6_FLOWLABEL_MGR"
4390                 | "IPV6_FLOWINFO_SEND"
4391                 | "IPV6_FLOWINFO_FLOWLABEL"
4392                 | "IPV6_FLOWINFO_PRIORITY" => false,
4393                 _ => true,
4394             })
4395             .type_name(move |ty, is_struct, is_union| match ty {
4396                 t if is_struct => format!("struct {}", t),
4397                 t if is_union => format!("union {}", t),
4398                 t => t.to_string(),
4399             });
4400         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
4401     }
4402 
4403     if linux || android {
4404         // Test Elf64_Phdr and Elf32_Phdr
4405         // These types have a field called `p_type`, but including
4406         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
4407         // making the tests for these fails when both are included.
4408         let mut cfg = ctest_cfg();
4409         cfg.header("elf.h");
4410         cfg.skip_fn(|_| true)
4411             .skip_static(|_| true)
4412             .skip_const(|_| true)
4413             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
4414             .skip_struct(move |ty| match ty {
4415                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4416                 _ => true,
4417             })
4418             .skip_type(move |ty| match ty {
4419                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4420                 _ => true,
4421             });
4422         cfg.generate("../src/lib.rs", "linux_elf.rs");
4423     }
4424 
4425     if linux || android {
4426         // Test `ARPHRD_CAN`.
4427         let mut cfg = ctest_cfg();
4428         cfg.header("linux/if_arp.h");
4429         cfg.skip_fn(|_| true)
4430             .skip_static(|_| true)
4431             .skip_const(move |name| match name {
4432                 "ARPHRD_CAN" => false,
4433                 _ => true,
4434             })
4435             .skip_struct(|_| true)
4436             .skip_type(|_| true);
4437         cfg.generate("../src/lib.rs", "linux_if_arp.rs");
4438     }
4439 }
4440 
4441 fn which_freebsd() -> Option<i32> {
4442     let output = std::process::Command::new("freebsd-version")
4443         .output()
4444         .ok()?;
4445     if !output.status.success() {
4446         return None;
4447     }
4448 
4449     let stdout = String::from_utf8(output.stdout).ok()?;
4450 
4451     match &stdout {
4452         s if s.starts_with("10") => Some(10),
4453         s if s.starts_with("11") => Some(11),
4454         s if s.starts_with("12") => Some(12),
4455         s if s.starts_with("13") => Some(13),
4456         s if s.starts_with("14") => Some(14),
4457         _ => None,
4458     }
4459 }
4460 
4461 fn test_haiku(target: &str) {
4462     assert!(target.contains("haiku"));
4463 
4464     let mut cfg = ctest_cfg();
4465     cfg.flag("-Wno-deprecated-declarations");
4466     cfg.define("__USE_GNU", Some("1"));
4467     cfg.define("_GNU_SOURCE", None);
4468     cfg.language(ctest::Lang::CXX);
4469 
4470     // POSIX API
4471     headers! { cfg:
4472                "alloca.h",
4473                "arpa/inet.h",
4474                "arpa/nameser.h",
4475                "arpa/nameser_compat.h",
4476                "assert.h",
4477                "complex.h",
4478                "ctype.h",
4479                "dirent.h",
4480                "div_t.h",
4481                "dlfcn.h",
4482                "endian.h",
4483                "errno.h",
4484                "fcntl.h",
4485                "fenv.h",
4486                "fnmatch.h",
4487                "fts.h",
4488                "ftw.h",
4489                "getopt.h",
4490                "glob.h",
4491                "grp.h",
4492                "inttypes.h",
4493                "iovec.h",
4494                "langinfo.h",
4495                "libgen.h",
4496                "libio.h",
4497                "limits.h",
4498                "locale.h",
4499                "malloc.h",
4500                "malloc_debug.h",
4501                "math.h",
4502                "memory.h",
4503                "monetary.h",
4504                "net/if.h",
4505                "net/if_dl.h",
4506                "net/if_media.h",
4507                "net/if_tun.h",
4508                "net/if_types.h",
4509                "net/route.h",
4510                "netdb.h",
4511                "netinet/in.h",
4512                "netinet/ip.h",
4513                "netinet/ip6.h",
4514                "netinet/ip_icmp.h",
4515                "netinet/ip_var.h",
4516                "netinet/tcp.h",
4517                "netinet/udp.h",
4518                "netinet6/in6.h",
4519                "nl_types.h",
4520                "null.h",
4521                "poll.h",
4522                "pthread.h",
4523                "pwd.h",
4524                "regex.h",
4525                "resolv.h",
4526                "sched.h",
4527                "search.h",
4528                "semaphore.h",
4529                "setjmp.h",
4530                "shadow.h",
4531                "signal.h",
4532                "size_t.h",
4533                "spawn.h",
4534                "stdint.h",
4535                "stdio.h",
4536                "stdlib.h",
4537                "string.h",
4538                "strings.h",
4539                "sys/cdefs.h",
4540                "sys/file.h",
4541                "sys/ioctl.h",
4542                "sys/ipc.h",
4543                "sys/mman.h",
4544                "sys/msg.h",
4545                "sys/param.h",
4546                "sys/poll.h",
4547                "sys/resource.h",
4548                "sys/select.h",
4549                "sys/sem.h",
4550                "sys/socket.h",
4551                "sys/sockio.h",
4552                "sys/stat.h",
4553                "sys/statvfs.h",
4554                "sys/time.h",
4555                "sys/timeb.h",
4556                "sys/times.h",
4557                "sys/types.h",
4558                "sys/uio.h",
4559                "sys/un.h",
4560                "sys/utsname.h",
4561                "sys/wait.h",
4562                "syslog.h",
4563                "tar.h",
4564                "termios.h",
4565                "time.h",
4566                "uchar.h",
4567                "unistd.h",
4568                "utime.h",
4569                "utmpx.h",
4570                "wchar.h",
4571                "wchar_t.h",
4572                "wctype.h"
4573     }
4574 
4575     // BSD Extensions
4576     headers! { cfg:
4577                "ifaddrs.h",
4578                "libutil.h",
4579                "link.h",
4580                "pty.h",
4581                "stringlist.h",
4582                "sys/link_elf.h",
4583     }
4584 
4585     // Native API
4586     headers! { cfg:
4587                "kernel/OS.h",
4588                "kernel/fs_attr.h",
4589                "kernel/fs_index.h",
4590                "kernel/fs_info.h",
4591                "kernel/fs_query.h",
4592                "kernel/fs_volume.h",
4593                "kernel/image.h",
4594                "kernel/scheduler.h",
4595                "storage/FindDirectory.h",
4596                "storage/StorageDefs.h",
4597                "support/Errors.h",
4598                "support/SupportDefs.h",
4599                "support/TypeConstants.h"
4600     }
4601 
4602     cfg.skip_struct(move |ty| {
4603         if ty.starts_with("__c_anonymous_") {
4604             return true;
4605         }
4606         match ty {
4607             // FIXME: actually a union
4608             "sigval" => true,
4609             // FIXME: locale_t does not exist on Haiku
4610             "locale_t" => true,
4611             // FIXME: rusage has a different layout on Haiku
4612             "rusage" => true,
4613             // FIXME?: complains that rust aligns on 4 byte boundary, but
4614             //         Haiku does not align it at all.
4615             "in6_addr" => true,
4616             // The d_name attribute is an array of 1 on Haiku, with the
4617             // intention that the developer allocates a larger or smaller
4618             // piece of memory depending on the expected/actual size of the name.
4619             // Other platforms have sensible defaults. In Rust, the d_name field
4620             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
4621             // newly allocated dirent objects. This breaks the automated tests.
4622             "dirent" => true,
4623             // The following structs contain function pointers, which cannot be initialized
4624             // with mem::zeroed(), so skip the automated test
4625             "image_info" | "thread_info" => true,
4626 
4627             "Elf64_Phdr" => true,
4628 
4629             // is an union
4630             "cpuid_info" => true,
4631 
4632             _ => false,
4633         }
4634     });
4635 
4636     cfg.skip_type(move |ty| {
4637         match ty {
4638             // FIXME: locale_t does not exist on Haiku
4639             "locale_t" => true,
4640             // These cause errors, to be reviewed in the future
4641             "sighandler_t" => true,
4642             "pthread_t" => true,
4643             "pthread_condattr_t" => true,
4644             "pthread_mutexattr_t" => true,
4645             "pthread_rwlockattr_t" => true,
4646             _ => false,
4647         }
4648     });
4649 
4650     cfg.skip_fn(move |name| {
4651         // skip those that are manually verified
4652         match name {
4653             // FIXME: https://github.com/rust-lang/libc/issues/1272
4654             "execv" | "execve" | "execvp" | "execvpe" => true,
4655             // FIXME: does not exist on haiku
4656             "open_wmemstream" => true,
4657             "mlockall" | "munlockall" => true,
4658             "tcgetsid" => true,
4659             "cfsetspeed" => true,
4660             // ignore for now, will be part of Haiku R1 beta 3
4661             "mlock" | "munlock" => true,
4662             // returns const char * on Haiku
4663             "strsignal" => true,
4664             // uses an enum as a parameter argument, which is incorrectly
4665             // translated into a struct argument
4666             "find_path" => true,
4667 
4668             "get_cpuid" => true,
4669 
4670             // uses varargs parameter
4671             "ioctl" => true,
4672 
4673             _ => false,
4674         }
4675     });
4676 
4677     cfg.skip_const(move |name| {
4678         match name {
4679             // FIXME: these constants do not exist on Haiku
4680             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
4681             | "DT_SOCK" => true,
4682             "USRQUOTA" | "GRPQUOTA" => true,
4683             "SIGIOT" => true,
4684             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL"
4685             | "ATF_USETRAILERS" => true,
4686             // Haiku does not have MAP_FILE, but rustc requires it
4687             "MAP_FILE" => true,
4688             // The following does not exist on Haiku but is required by
4689             // several crates
4690             "FIOCLEX" => true,
4691             // just skip this one, it is not defined on Haiku beta 2 but
4692             // since it is meant as a mask and not a parameter it can exist
4693             // here
4694             "LOG_PRIMASK" => true,
4695             // not defined on Haiku, but [get|set]priority is, so they are
4696             // useful
4697             "PRIO_MIN" | "PRIO_MAX" => true,
4698             //
4699             _ => false,
4700         }
4701     });
4702 
4703     cfg.skip_field(move |struct_, field| {
4704         match (struct_, field) {
4705             // FIXME: the stat struct actually has timespec members, whereas
4706             //        the current representation has these unpacked.
4707             ("stat", "st_atime") => true,
4708             ("stat", "st_atime_nsec") => true,
4709             ("stat", "st_mtime") => true,
4710             ("stat", "st_mtime_nsec") => true,
4711             ("stat", "st_ctime") => true,
4712             ("stat", "st_ctime_nsec") => true,
4713             ("stat", "st_crtime") => true,
4714             ("stat", "st_crtime_nsec") => true,
4715 
4716             // these are actually unions, but we cannot represent it well
4717             ("siginfo_t", "sigval") => true,
4718             ("sem_t", "named_sem_id") => true,
4719             ("sigaction", "sa_sigaction") => true,
4720             ("sigevent", "sigev_value") => true,
4721             ("fpu_state", "_fpreg") => true,
4722             ("cpu_topology_node_info", "data") => true,
4723             // these fields have a simplified data definition in libc
4724             ("fpu_state", "_xmm") => true,
4725             ("savefpu", "_fp_ymm") => true,
4726 
4727             // skip these enum-type fields
4728             ("thread_info", "state") => true,
4729             ("image_info", "image_type") => true,
4730             _ => false,
4731         }
4732     });
4733 
4734     cfg.skip_roundtrip(move |s| match s {
4735         // FIXME: for some reason the roundtrip check fails for cpu_info
4736         "cpu_info" => true,
4737         _ => false,
4738     });
4739 
4740     cfg.type_name(move |ty, is_struct, is_union| {
4741         match ty {
4742             // Just pass all these through, no need for a "struct" prefix
4743             "area_info"
4744             | "port_info"
4745             | "port_message_info"
4746             | "team_info"
4747             | "sem_info"
4748             | "team_usage_info"
4749             | "thread_info"
4750             | "cpu_info"
4751             | "system_info"
4752             | "object_wait_info"
4753             | "image_info"
4754             | "attr_info"
4755             | "index_info"
4756             | "fs_info"
4757             | "FILE"
4758             | "DIR"
4759             | "Dl_info"
4760             | "topology_level_type"
4761             | "cpu_topology_node_info"
4762             | "cpu_topology_root_info"
4763             | "cpu_topology_package_info"
4764             | "cpu_topology_core_info" => ty.to_string(),
4765 
4766             // enums don't need a prefix
4767             "directory_which" | "path_base_directory" | "cpu_platform" | "cpu_vendor" => {
4768                 ty.to_string()
4769             }
4770 
4771             // is actually a union
4772             "sigval" => format!("union sigval"),
4773             t if is_union => format!("union {}", t),
4774             t if t.ends_with("_t") => t.to_string(),
4775             t if is_struct => format!("struct {}", t),
4776             t => t.to_string(),
4777         }
4778     });
4779 
4780     cfg.field_name(move |struct_, field| {
4781         match field {
4782             // Field is named `type` in C but that is a Rust keyword,
4783             // so these fields are translated to `type_` in the bindings.
4784             "type_" if struct_ == "object_wait_info" => "type".to_string(),
4785             "type_" if struct_ == "sem_t" => "type".to_string(),
4786             "type_" if struct_ == "attr_info" => "type".to_string(),
4787             "type_" if struct_ == "index_info" => "type".to_string(),
4788             "type_" if struct_ == "cpu_topology_node_info" => "type".to_string(),
4789             "image_type" if struct_ == "image_info" => "type".to_string(),
4790             s => s.to_string(),
4791         }
4792     });
4793     cfg.generate("../src/lib.rs", "main.rs");
4794 }
4795