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