xref: /rust-libc-0.2.174/libc-test/build.rs (revision 54945824)
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: libstd 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             _ => false,
1762         }
1763     });
1764 
1765     cfg.skip_const(move |name| {
1766         match name {
1767             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
1768             | "IPV6_FLOWINFO"
1769             | "IPV6_FLOWLABEL_MGR"
1770             | "IPV6_FLOWINFO_SEND"
1771             | "IPV6_FLOWINFO_FLOWLABEL"
1772             | "IPV6_FLOWINFO_PRIORITY"
1773             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
1774             | "F_CANCELLK"
1775             | "F_ADD_SEALS"
1776             | "F_GET_SEALS"
1777             | "F_SEAL_SEAL"
1778             | "F_SEAL_SHRINK"
1779             | "F_SEAL_GROW"
1780             | "F_SEAL_WRITE" => true,
1781 
1782             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests:
1783             "ARPHRD_CAN" => true,
1784 
1785             // FIXME: deprecated: not available in any header
1786             // See: https://github.com/rust-lang/libc/issues/1356
1787             "ENOATTR" => true,
1788 
1789             // FIXME: still necessary?
1790             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1791             // FIXME: deprecated - removed in glibc 2.26
1792             "SIGUNUSED" => true,
1793 
1794             // Needs a newer Android SDK for the definition
1795             "P_PIDFD" => true,
1796 
1797             // Requires Linux kernel 5.6
1798             "VMADDR_CID_LOCAL" => true,
1799 
1800             // FIXME: conflicts with standard C headers and is tested in
1801             // `linux_termios.rs` below:
1802             "BOTHER" => true,
1803             "IBSHIFT" => true,
1804             "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => true,
1805 
1806             // is a private value for kernel usage normally
1807             "FUSE_SUPER_MAGIC" => true,
1808             // linux 5.12 min
1809             "MPOL_F_NUMA_BALANCING" => true,
1810 
1811             // GRND_INSECURE was added in platform-tools-30.0.0
1812             "GRND_INSECURE" => true,
1813 
1814             // kernel 5.10 minimum required
1815             "MEMBARRIER_CMD_REGISTER_PRIVATE_EXPEDITED_RSEQ" | "MEMBARRIER_CMD_PRIVATE_EXPEDITED_RSEQ" => true,
1816 
1817             // kernel 5.18 minimum
1818             | "MADV_COLD"
1819             | "MADV_DONTNEED_LOCKED"
1820             | "MADV_PAGEOUT"
1821             | "MADV_POPULATE_READ"
1822             | "MADV_POPULATE_WRITE" => true,
1823 
1824             // kernel 5.6 minimum required
1825             "IPPROTO_MPTCP" | "IPPROTO_ETHERNET" => true,
1826 
1827             // FIXME: NDK r22 minimum required
1828             | "FDB_NOTIFY_BIT"
1829             | "FDB_NOTIFY_INACTIVE_BIT"
1830             | "IFLA_ALT_IFNAME"
1831             | "IFLA_PERM_ADDRESS"
1832             | "IFLA_PROP_LIST"
1833             | "IFLA_PROTO_DOWN_REASON"
1834             | "NDA_FDB_EXT_ATTRS"
1835             | "NDA_NH_ID"
1836             | "NFEA_ACTIVITY_NOTIFY"
1837             | "NFEA_DONT_REFRESH"
1838             | "NFEA_UNSPEC" => true,
1839 
1840             // FIXME: NDK r23 minimum required
1841             | "IFLA_PARENT_DEV_BUS_NAME"
1842             | "IFLA_PARENT_DEV_NAME" => true,
1843 
1844             // FIXME: NDK r25 minimum required
1845             | "IFLA_GRO_MAX_SIZE"
1846             | "NDA_FLAGS_EXT"
1847             | "NTF_EXT_MANAGED" => true,
1848 
1849             // FIXME: NDK above r25 required
1850             | "IFLA_ALLMULTI"
1851             | "IFLA_DEVLINK_PORT"
1852             | "IFLA_GRO_IPV4_MAX_SIZE"
1853             | "IFLA_GSO_IPV4_MAX_SIZE"
1854             | "IFLA_TSO_MAX_SEGS"
1855             | "IFLA_TSO_MAX_SIZE"
1856             | "NDA_NDM_STATE_MASK"
1857             | "NDA_NDM_FLAGS_MASK"
1858             | "NDTPA_INTERVAL_PROBE_TIME_MS"
1859             | "NFQA_UNSPEC"
1860             | "NTF_EXT_LOCKED"
1861             | "ALG_SET_DRBG_ENTROPY" => true,
1862 
1863             _ => false,
1864         }
1865     });
1866 
1867     cfg.skip_fn(move |name| {
1868         // skip those that are manually verified
1869         match name {
1870             // FIXME: https://github.com/rust-lang/libc/issues/1272
1871             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1872 
1873             // There are two versions of the sterror_r function, see
1874             //
1875             // https://linux.die.net/man/3/strerror_r
1876             //
1877             // An XSI-compliant version provided if:
1878             //
1879             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
1880             //
1881             // and a GNU specific version provided if _GNU_SOURCE is defined.
1882             //
1883             // libc provides bindings for the XSI-compliant version, which is
1884             // preferred for portable applications.
1885             //
1886             // We skip the test here since here _GNU_SOURCE is defined, and
1887             // test the XSI version below.
1888             "strerror_r" => true,
1889             "reallocarray" => true,
1890             "__system_property_wait" => true,
1891 
1892             // Added in API level 30, but tests use level 28.
1893             "mlock2" => true,
1894 
1895             // Added in glibc 2.25.
1896             "getentropy" => true,
1897 
1898             // Added in API level 28, but some tests use level 24.
1899             "getrandom" => true,
1900 
1901             // Added in API level 28, but some tests use level 24.
1902             "syncfs" => true,
1903 
1904             // Added in API level 28, but some tests use level 24.
1905             "pthread_attr_getinheritsched" | "pthread_attr_setinheritsched" => true,
1906             // Added in API level 28, but some tests use level 24.
1907             "fread_unlocked" | "fwrite_unlocked" | "fgets_unlocked" | "fflush_unlocked" => true,
1908 
1909             _ => false,
1910         }
1911     });
1912 
1913     cfg.skip_field_type(move |struct_, field| {
1914         // This is a weird union, don't check the type.
1915         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1916         // sigval is actually a union, but we pretend it's a struct
1917         (struct_ == "sigevent" && field == "sigev_value") ||
1918         // this one is an anonymous union
1919         (struct_ == "ff_effect" && field == "u") ||
1920         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1921         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1922         (struct_ == "sigaction" && field == "sa_sigaction") ||
1923         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1924         (struct_ == "signalfd_siginfo" && field == "ssi_call_addr")
1925     });
1926 
1927     cfg.skip_field(move |struct_, field| {
1928         // this is actually a union on linux, so we can't represent it well and
1929         // just insert some padding.
1930         (struct_ == "siginfo_t" && field == "_pad") ||
1931         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1932         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1933         (struct_ == "sigaction" && field == "sa_sigaction") ||
1934         // sigev_notify_thread_id is actually part of a sigev_un union
1935         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1936         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1937         (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
1938                                            field == "ssi_call_addr" ||
1939                                            field == "ssi_arch"))
1940     });
1941 
1942     cfg.skip_field(|struct_, field| {
1943         match (struct_, field) {
1944             // conflicting with `p_type` macro from <resolve.h>.
1945             ("Elf32_Phdr", "p_type") => true,
1946             ("Elf64_Phdr", "p_type") => true,
1947 
1948             // this is actually a union on linux, so we can't represent it well and
1949             // just insert some padding.
1950             ("siginfo_t", "_pad") => true,
1951 
1952             _ => false,
1953         }
1954     });
1955 
1956     cfg.generate("../src/lib.rs", "main.rs");
1957 
1958     test_linux_like_apis(target);
1959 }
1960 
1961 fn test_freebsd(target: &str) {
1962     assert!(target.contains("freebsd"));
1963     let mut cfg = ctest_cfg();
1964 
1965     let freebsd_ver = which_freebsd();
1966 
1967     match freebsd_ver {
1968         Some(12) => cfg.cfg("freebsd12", None),
1969         Some(13) => cfg.cfg("freebsd13", None),
1970         Some(14) => cfg.cfg("freebsd14", None),
1971         _ => &mut cfg,
1972     };
1973 
1974     // For sched linux compat fn
1975     cfg.define("_WITH_CPU_SET_T", None);
1976     // Required for `getline`:
1977     cfg.define("_WITH_GETLINE", None);
1978     // Required for making freebsd11_stat available in the headers
1979     cfg.define("_WANT_FREEBSD11_STAT", None);
1980 
1981     let freebsd13 = match freebsd_ver {
1982         Some(n) if n >= 13 => true,
1983         _ => false,
1984     };
1985     let freebsd14 = match freebsd_ver {
1986         Some(n) if n >= 14 => true,
1987         _ => false,
1988     };
1989 
1990     headers! { cfg:
1991                 "aio.h",
1992                 "arpa/inet.h",
1993                 "bsm/audit.h",
1994                 "ctype.h",
1995                 "dirent.h",
1996                 "dlfcn.h",
1997                 "elf.h",
1998                 "errno.h",
1999                 "execinfo.h",
2000                 "fcntl.h",
2001                 "getopt.h",
2002                 "glob.h",
2003                 "grp.h",
2004                 "iconv.h",
2005                 "ifaddrs.h",
2006                 "kenv.h",
2007                 "langinfo.h",
2008                 "libgen.h",
2009                 "libutil.h",
2010                 "limits.h",
2011                 "link.h",
2012                 "locale.h",
2013                 "machine/elf.h",
2014                 "machine/reg.h",
2015                 "malloc_np.h",
2016                 "memstat.h",
2017                 "mqueue.h",
2018                 "net/bpf.h",
2019                 "net/if.h",
2020                 "net/if_arp.h",
2021                 "net/if_dl.h",
2022                 "net/if_mib.h",
2023                 "net/route.h",
2024                 "netdb.h",
2025                 "netinet/ip.h",
2026                 "netinet/in.h",
2027                 "netinet/sctp.h",
2028                 "netinet/tcp.h",
2029                 "netinet/udp.h",
2030                 "poll.h",
2031                 "pthread.h",
2032                 "pthread_np.h",
2033                 "pwd.h",
2034                 "regex.h",
2035                 "resolv.h",
2036                 "sched.h",
2037                 "semaphore.h",
2038                 "signal.h",
2039                 "spawn.h",
2040                 "stddef.h",
2041                 "stdint.h",
2042                 "stdio.h",
2043                 "stdlib.h",
2044                 "string.h",
2045                 "sys/capsicum.h",
2046                 "sys/auxv.h",
2047                 "sys/cpuset.h",
2048                 "sys/domainset.h",
2049                 "sys/eui64.h",
2050                 "sys/event.h",
2051                 [freebsd13]:"sys/eventfd.h",
2052                 "sys/extattr.h",
2053                 "sys/file.h",
2054                 "sys/ioctl.h",
2055                 "sys/ipc.h",
2056                 "sys/jail.h",
2057                 "sys/mman.h",
2058                 "sys/mount.h",
2059                 "sys/msg.h",
2060                 "sys/procctl.h",
2061                 "sys/procdesc.h",
2062                 "sys/ptrace.h",
2063                 "sys/queue.h",
2064                 "sys/random.h",
2065                 "sys/reboot.h",
2066                 "sys/resource.h",
2067                 "sys/rtprio.h",
2068                 "sys/sem.h",
2069                 "sys/shm.h",
2070                 "sys/socket.h",
2071                 "sys/stat.h",
2072                 "sys/statvfs.h",
2073                 "sys/sysctl.h",
2074                 "sys/thr.h",
2075                 "sys/time.h",
2076                 [freebsd14]:"sys/timerfd.h",
2077                 "sys/times.h",
2078                 "sys/timex.h",
2079                 "sys/types.h",
2080                 "sys/proc.h",
2081                 "kvm.h", // must be after "sys/types.h"
2082                 "sys/ucontext.h",
2083                 "sys/uio.h",
2084                 "sys/ktrace.h",
2085                 "sys/umtx.h",
2086                 "sys/un.h",
2087                 "sys/user.h",
2088                 "sys/utsname.h",
2089                 "sys/uuid.h",
2090                 "sys/vmmeter.h",
2091                 "sys/wait.h",
2092                 "libprocstat.h",
2093                 "devstat.h",
2094                 "syslog.h",
2095                 "termios.h",
2096                 "time.h",
2097                 "ufs/ufs/quota.h",
2098                 "unistd.h",
2099                 "utime.h",
2100                 "utmpx.h",
2101                 "wchar.h",
2102     }
2103 
2104     cfg.type_name(move |ty, is_struct, is_union| {
2105         match ty {
2106             // Just pass all these through, no need for a "struct" prefix
2107             "FILE"
2108             | "fd_set"
2109             | "Dl_info"
2110             | "DIR"
2111             | "Elf32_Phdr"
2112             | "Elf64_Phdr"
2113             | "Elf32_Auxinfo"
2114             | "Elf64_Auxinfo"
2115             | "devstat_select_mode"
2116             | "devstat_support_flags"
2117             | "devstat_type_flags"
2118             | "devstat_match_flags"
2119             | "devstat_priority" => ty.to_string(),
2120 
2121             // FIXME: https://github.com/rust-lang/libc/issues/1273
2122             "sighandler_t" => "sig_t".to_string(),
2123 
2124             t if is_union => format!("union {}", t),
2125 
2126             t if t.ends_with("_t") => t.to_string(),
2127 
2128             // sigval is a struct in Rust, but a union in C:
2129             "sigval" => format!("union sigval"),
2130 
2131             // put `struct` in front of all structs:.
2132             t if is_struct => format!("struct {}", t),
2133 
2134             t => t.to_string(),
2135         }
2136     });
2137 
2138     cfg.field_name(move |struct_, field| {
2139         match field {
2140             // Our stat *_nsec fields normally don't actually exist but are part
2141             // of a timeval struct
2142             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2143                 s.replace("e_nsec", ".tv_nsec")
2144             }
2145             // Field is named `type` in C but that is a Rust keyword,
2146             // so these fields are translated to `type_` in the bindings.
2147             "type_" if struct_ == "rtprio" => "type".to_string(),
2148             "type_" if struct_ == "sockstat" => "type".to_string(),
2149             "type_" if struct_ == "devstat_match_table" => "type".to_string(),
2150             s => s.to_string(),
2151         }
2152     });
2153 
2154     cfg.skip_const(move |name| {
2155         match name {
2156             // These constants were introduced in FreeBSD 13:
2157             "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2158             | "F_SEAL_WRITE"
2159                 if Some(13) > freebsd_ver =>
2160             {
2161                 true
2162             }
2163 
2164             // These constants were introduced in FreeBSD 13:
2165             "EFD_CLOEXEC" | "EFD_NONBLOCK" | "EFD_SEMAPHORE" if Some(13) > freebsd_ver => true,
2166 
2167             // These constants were introduced in FreeBSD 12:
2168             "AT_RESOLVE_BENEATH" | "O_RESOLVE_BENEATH" if Some(12) > freebsd_ver => true,
2169 
2170             // These constants were introduced in FreeBSD 13:
2171             "O_DSYNC" | "O_PATH" | "O_EMPTY_PATH" | "AT_EMPTY_PATH" if Some(13) > freebsd_ver => {
2172                 true
2173             }
2174 
2175             // These aliases were introduced in FreeBSD 13:
2176             // (note however that the constants themselves work on any version)
2177             "CLOCK_BOOTTIME" | "CLOCK_REALTIME_COARSE" | "CLOCK_MONOTONIC_COARSE"
2178                 if Some(13) > freebsd_ver =>
2179             {
2180                 true
2181             }
2182 
2183             // FIXME: These are deprecated - remove in a couple of releases.
2184             // These constants were removed in FreeBSD 11 (svn r273250) but will
2185             // still be accepted and ignored at runtime.
2186             "MAP_RENAME" | "MAP_NORESERVE" => true,
2187 
2188             // FIXME: These are deprecated - remove in a couple of releases.
2189             // These constants were removed in FreeBSD 11 (svn r262489),
2190             // and they've never had any legitimate use outside of the
2191             // base system anyway.
2192             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
2193 
2194             // FIXME: This is deprecated - remove in a couple of releases.
2195             // This was removed in FreeBSD 14 (git 1b4701fe1e8) and never
2196             // should've been used anywhere anyway.
2197             "TDF_UNUSED23" => true,
2198 
2199             // Removed in FreeBSD 14 (git a6b55ee6be1)
2200             "IFF_KNOWSEPOCH" => true,
2201 
2202             // Removed in FreeBSD 14 (git 7ff9ae90f0b)
2203             "IFF_NOGROUP" => true,
2204 
2205             // FIXME: These are deprecated - remove in a couple of releases.
2206             // These symbols are not stable across OS-versions.  They were
2207             // changed for FreeBSD 14 in git revisions b62848b0c3f and
2208             // 2cf7870864e.
2209             "PRI_MAX_ITHD" | "PRI_MIN_REALTIME" | "PRI_MAX_REALTIME" | "PRI_MIN_KERN"
2210             | "PRI_MAX_KERN" | "PSWP" | "PVM" | "PINOD" | "PRIBIO" | "PVFS" | "PZERO" | "PSOCK"
2211             | "PWAIT" | "PLOCK" | "PPAUSE" | "PRI_MIN_TIMESHARE" | "PUSER" | "PI_AV" | "PI_NET"
2212             | "PI_DISK" | "PI_TTY" | "PI_DULL" | "PI_SOFT" => true,
2213 
2214             // This symbol changed in FreeBSD 14 (git 051e7d78b03), but the new
2215             // version should be safe to use on older releases.
2216             "IFCAP_CANTCHANGE" => true,
2217 
2218             // These were removed in FreeBSD 14 (git c6d31b8306e)
2219             "TDF_ASTPENDING" | "TDF_NEEDSUSPCHK" | "TDF_NEEDRESCHED" | "TDF_NEEDSIGCHK"
2220             | "TDF_ALRMPEND" | "TDF_PROFPEND" | "TDF_MACPEND" => true,
2221 
2222             // This constant was removed in FreeBSD 13 (svn r363622), and never
2223             // had any legitimate use outside of the base system anyway.
2224             "CTL_P1003_1B_MAXID" => true,
2225 
2226             // This was renamed in FreeBSD 12.2 and 13 (r352486).
2227             "CTL_UNSPEC" | "CTL_SYSCTL" => true,
2228 
2229             // This was renamed in FreeBSD 12.2 and 13 (r350749).
2230             "IPPROTO_SEP" | "IPPROTO_DCCP" => true,
2231 
2232             // This was changed to 96(0x60) in FreeBSD 13:
2233             // https://github.com/freebsd/freebsd/
2234             // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7
2235             "MINCORE_SUPER" if Some(13) <= freebsd_ver => true,
2236 
2237             // Added in FreeBSD 13.0 (r356667)
2238             "GRND_INSECURE" if Some(13) > freebsd_ver => true,
2239 
2240             // Added in FreeBSD 13.0 (r349609)
2241             "PROC_PROTMAX_CTL"
2242             | "PROC_PROTMAX_STATUS"
2243             | "PROC_PROTMAX_FORCE_ENABLE"
2244             | "PROC_PROTMAX_FORCE_DISABLE"
2245             | "PROC_PROTMAX_NOFORCE"
2246             | "PROC_PROTMAX_ACTIVE"
2247             | "PROC_NO_NEW_PRIVS_CTL"
2248             | "PROC_NO_NEW_PRIVS_STATUS"
2249             | "PROC_NO_NEW_PRIVS_ENABLE"
2250             | "PROC_NO_NEW_PRIVS_DISABLE"
2251             | "PROC_WXMAP_CTL"
2252             | "PROC_WXMAP_STATUS"
2253             | "PROC_WX_MAPPINGS_PERMIT"
2254             | "PROC_WX_MAPPINGS_DISALLOW_EXEC"
2255             | "PROC_WXORX_ENFORCE"
2256                 if Some(13) > freebsd_ver =>
2257             {
2258                 true
2259             }
2260 
2261             // Added in in FreeBSD 13.0 (r367776 and r367287)
2262             "SCM_CREDS2" | "LOCAL_CREDS_PERSISTENT" if Some(13) > freebsd_ver => true,
2263 
2264             // Added in FreeBSD 14
2265             "SPACECTL_DEALLOC" if Some(14) > freebsd_ver => true,
2266 
2267             // Added in FreeBSD 13.
2268             "KERN_PROC_SIGFASTBLK"
2269             | "USER_LOCALBASE"
2270             | "TDP_SIGFASTBLOCK"
2271             | "TDP_UIOHELD"
2272             | "TDP_SIGFASTPENDING"
2273             | "TDP2_COMPAT32RB"
2274             | "P2_PROTMAX_ENABLE"
2275             | "P2_PROTMAX_DISABLE"
2276             | "CTLFLAG_NEEDGIANT"
2277             | "CTL_SYSCTL_NEXTNOSKIP"
2278                 if Some(13) > freebsd_ver =>
2279             {
2280                 true
2281             }
2282 
2283             // Added in freebsd 14.
2284             "IFCAP_MEXTPG" if Some(14) > freebsd_ver => true,
2285             // Added in freebsd 13.
2286             "IFCAP_TXTLS4" | "IFCAP_TXTLS6" | "IFCAP_VXLAN_HWCSUM" | "IFCAP_VXLAN_HWTSO"
2287             | "IFCAP_TXTLS_RTLMT" | "IFCAP_TXTLS"
2288                 if Some(13) > freebsd_ver =>
2289             {
2290                 true
2291             }
2292             // Added in FreeBSD 13.
2293             "PS_FST_TYPE_EVENTFD" if Some(13) > freebsd_ver => true,
2294 
2295             // Added in FreeBSD 14.
2296             "MNT_RECURSE" | "MNT_DEFERRED" if Some(14) > freebsd_ver => true,
2297 
2298             // Added in FreeBSD 13.
2299             "MNT_EXTLS" | "MNT_EXTLSCERT" | "MNT_EXTLSCERTUSER" | "MNT_NOCOVER"
2300             | "MNT_EMPTYDIR"
2301                 if Some(13) > freebsd_ver =>
2302             {
2303                 true
2304             }
2305 
2306             // Added in FreeBSD 14.
2307             "PT_COREDUMP" | "PC_ALL" | "PC_COMPRESS" | "PT_GETREGSET" | "PT_SETREGSET"
2308             | "PT_SC_REMOTE"
2309                 if Some(14) > freebsd_ver =>
2310             {
2311                 true
2312             }
2313 
2314             // Added in FreeBSD 14.
2315             "F_KINFO" => true, // FIXME: depends how frequent freebsd 14 is updated on CI, this addition went this week only.
2316             "SHM_RENAME_NOREPLACE"
2317             | "SHM_RENAME_EXCHANGE"
2318             | "SHM_LARGEPAGE_ALLOC_DEFAULT"
2319             | "SHM_LARGEPAGE_ALLOC_NOWAIT"
2320             | "SHM_LARGEPAGE_ALLOC_HARD"
2321             | "MFD_CLOEXEC"
2322             | "MFD_ALLOW_SEALING"
2323             | "MFD_HUGETLB"
2324             | "MFD_HUGE_MASK"
2325             | "MFD_HUGE_64KB"
2326             | "MFD_HUGE_512KB"
2327             | "MFD_HUGE_1MB"
2328             | "MFD_HUGE_2MB"
2329             | "MFD_HUGE_8MB"
2330             | "MFD_HUGE_16MB"
2331             | "MFD_HUGE_32MB"
2332             | "MFD_HUGE_256MB"
2333             | "MFD_HUGE_512MB"
2334             | "MFD_HUGE_1GB"
2335             | "MFD_HUGE_2GB"
2336             | "MFD_HUGE_16GB"
2337                 if Some(13) > freebsd_ver =>
2338             {
2339                 true
2340             }
2341 
2342             // Flags introduced in FreeBSD 14.
2343             "TCP_MAXUNACKTIME"
2344             | "TCP_MAXPEAKRATE"
2345             | "TCP_IDLE_REDUCE"
2346             | "TCP_REMOTE_UDP_ENCAPS_PORT"
2347             | "TCP_DELACK"
2348             | "TCP_FIN_IS_RST"
2349             | "TCP_LOG_LIMIT"
2350             | "TCP_SHARED_CWND_ALLOWED"
2351             | "TCP_PROC_ACCOUNTING"
2352             | "TCP_USE_CMP_ACKS"
2353             | "TCP_PERF_INFO"
2354             | "TCP_LRD"
2355                 if Some(14) > freebsd_ver =>
2356             {
2357                 true
2358             }
2359 
2360             // Added in FreeBSD 14
2361             "LIO_READV" | "LIO_WRITEV" | "LIO_VECTORED" if Some(14) > freebsd_ver => true,
2362 
2363             // Added in FreeBSD 13
2364             "FIOSSHMLPGCNF" if Some(13) > freebsd_ver => true,
2365 
2366             // Added in FreeBSD 14
2367             "IFCAP_NV" if Some(14) > freebsd_ver => true,
2368 
2369             // FIXME: Removed in https://reviews.freebsd.org/D38574 and https://reviews.freebsd.org/D38822
2370             // We maybe should deprecate them once a stable release ships them.
2371             "IP_BINDMULTI" | "IP_RSS_LISTEN_BUCKET" => true,
2372 
2373             // FIXME: Removed in https://reviews.freebsd.org/D39127.
2374             "KERN_VNODE" => true,
2375 
2376             // Added in FreeBSD 14
2377             "EV_KEEPUDATA" if Some(14) > freebsd_ver => true,
2378 
2379             // Added in FreeBSD 13.2
2380             "AT_USRSTACKBASE" | "AT_USRSTACKLIM" if Some(13) > freebsd_ver => true,
2381 
2382             // Added in FreeBSD 14
2383             "TFD_CLOEXEC" | "TFD_NONBLOCK" if Some(14) > freebsd_ver => true,
2384 
2385             _ => false,
2386         }
2387     });
2388 
2389     cfg.skip_type(move |ty| {
2390         match ty {
2391             // the struct "__kvm" is quite tricky to bind so since we only use a pointer to it
2392             // for now, it doesn't matter too much...
2393             "kvm_t" => true,
2394 
2395             _ => false,
2396         }
2397     });
2398 
2399     cfg.skip_struct(move |ty| {
2400         if ty.starts_with("__c_anonymous_") {
2401             return true;
2402         }
2403         match ty {
2404             // `procstat` is a private struct
2405             "procstat" => true,
2406 
2407             // `spacectl_range` was introduced in FreeBSD 14
2408             "spacectl_range" if Some(14) > freebsd_ver => true,
2409 
2410             // `ptrace_coredump` introduced in FreeBSD 14.
2411             "ptrace_coredump" if Some(14) > freebsd_ver => true,
2412             // `ptrace_sc_remote` introduced in FreeBSD 14.
2413             "ptrace_sc_remote" if Some(14) > freebsd_ver => true,
2414 
2415             // `sockcred2` is not available in FreeBSD 12.
2416             "sockcred2" if Some(13) > freebsd_ver => true,
2417             // `shm_largepage_conf` was introduced in FreeBSD 13.
2418             "shm_largepage_conf" if Some(13) > freebsd_ver => true,
2419 
2420             // Those are private types
2421             "memory_type" => true,
2422             "memory_type_list" => true,
2423             "pidfh" => true,
2424             "sctp_gen_error_cause"
2425             | "sctp_error_missing_param"
2426             | "sctp_remote_error"
2427             | "sctp_assoc_change"
2428             | "sctp_send_failed_event"
2429             | "sctp_stream_reset_event" => true,
2430 
2431             _ => false,
2432         }
2433     });
2434 
2435     cfg.skip_fn(move |name| {
2436         // skip those that are manually verified
2437         match name {
2438             // FIXME: https://github.com/rust-lang/libc/issues/1272
2439             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2440 
2441             // The `uname` function in the `utsname.h` FreeBSD header is a C
2442             // inline function (has no symbol) that calls the `__xuname` symbol.
2443             // Therefore the function pointer comparison does not make sense for it.
2444             "uname" => true,
2445 
2446             // FIXME: Our API is unsound. The Rust API allows aliasing
2447             // pointers, but the C API requires pointers not to alias.
2448             // We should probably be at least using `&`/`&mut` here, see:
2449             // https://github.com/gnzlbg/ctest/issues/68
2450             "lio_listio" => true,
2451 
2452             // Those are introduced in FreeBSD 12.
2453             "clock_nanosleep" | "getrandom" | "elf_aux_info" | "setproctitle_fast"
2454             | "timingsafe_bcmp" | "timingsafe_memcmp"
2455                 if Some(12) > freebsd_ver =>
2456             {
2457                 true
2458             }
2459 
2460             // Those are introduced in FreeBSD 13.
2461             "memfd_create"
2462             | "shm_create_largepage"
2463             | "shm_rename"
2464             | "getentropy"
2465             | "eventfd"
2466             | "SOCKCRED2SIZE"
2467             | "getlocalbase"
2468             | "aio_readv"
2469             | "aio_writev"
2470             | "copy_file_range"
2471                 if Some(13) > freebsd_ver =>
2472             {
2473                 true
2474             }
2475 
2476             // Those are introduced in FreeBSD 14.
2477             "sched_getaffinity" | "sched_setaffinity" | "sched_getcpu" | "fspacectl"
2478                 if Some(14) > freebsd_ver =>
2479             {
2480                 true
2481             }
2482 
2483             // Those are introduced in FreeBSD 14.
2484             "timerfd_create" | "timerfd_gettime" | "timerfd_settime" if Some(14) > freebsd_ver => {
2485                 true
2486             }
2487 
2488             _ => false,
2489         }
2490     });
2491 
2492     cfg.volatile_item(|i| {
2493         use ctest::VolatileItemKind::*;
2494         match i {
2495             // aio_buf is a volatile void** but since we cannot express that in
2496             // Rust types, we have to explicitly tell the checker about it here:
2497             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2498             _ => false,
2499         }
2500     });
2501 
2502     cfg.skip_field(move |struct_, field| {
2503         match (struct_, field) {
2504             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2505             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2506             ("sigaction", "sa_sigaction") => true,
2507 
2508             // conflicting with `p_type` macro from <resolve.h>.
2509             ("Elf32_Phdr", "p_type") => true,
2510             ("Elf64_Phdr", "p_type") => true,
2511 
2512             // not available until FreeBSD 12, and is an anonymous union there.
2513             ("xucred", "cr_pid__c_anonymous_union") => true,
2514 
2515             // m_owner field is a volatile __lwpid_t
2516             ("umutex", "m_owner") => true,
2517             // c_has_waiters field is a volatile int32_t
2518             ("ucond", "c_has_waiters") => true,
2519             // is PATH_MAX long but tests can't accept multi array as equivalent.
2520             ("kinfo_vmentry", "kve_path") => true,
2521 
2522             // a_un field is a union
2523             ("Elf32_Auxinfo", "a_un") => true,
2524             ("Elf64_Auxinfo", "a_un") => true,
2525 
2526             // union fields
2527             ("if_data", "__ifi_epoch") => true,
2528             ("if_data", "__ifi_lastchange") => true,
2529             ("ifreq", "ifr_ifru") => true,
2530             ("ifconf", "ifc_ifcu") => true,
2531 
2532             // anonymous struct
2533             ("devstat", "dev_links") => true,
2534 
2535             // FIXME: structs too complicated to bind for now...
2536             ("kinfo_proc", "ki_paddr") => true,
2537             ("kinfo_proc", "ki_addr") => true,
2538             ("kinfo_proc", "ki_tracep") => true,
2539             ("kinfo_proc", "ki_textvp") => true,
2540             ("kinfo_proc", "ki_fd") => true,
2541             ("kinfo_proc", "ki_vmspace") => true,
2542             ("kinfo_proc", "ki_pcb") => true,
2543             ("kinfo_proc", "ki_tdaddr") => true,
2544             ("kinfo_proc", "ki_pd") => true,
2545 
2546             // Anonymous type.
2547             ("filestat", "next") => true,
2548 
2549             // We ignore this field because we needed to use a hack in order to make rust 1.19
2550             // happy...
2551             ("kinfo_proc", "ki_sparestrings") => true,
2552 
2553             // `__sem_base` is a private struct field
2554             ("semid_ds", "__sem_base") => true,
2555 
2556             // `snap_time` is a `long double`, but it's a nightmare to bind correctly in rust
2557             // for the moment, so it's a best effort thing...
2558             ("statinfo", "snap_time") => true,
2559             ("sctp_sndrcvinfo", "__reserve_pad") => true,
2560             ("sctp_extrcvinfo", "__reserve_pad") => true,
2561             // `tcp_snd_wscale` and `tcp_rcv_wscale` are bitfields
2562             ("tcp_info", "tcp_snd_wscale") => true,
2563             ("tcp_info", "tcp_rcv_wscale") => true,
2564 
2565             _ => false,
2566         }
2567     });
2568 
2569     cfg.generate("../src/lib.rs", "main.rs");
2570 }
2571 
2572 fn test_emscripten(target: &str) {
2573     assert!(target.contains("emscripten"));
2574 
2575     let mut cfg = ctest_cfg();
2576     cfg.define("_GNU_SOURCE", None); // FIXME: ??
2577 
2578     headers! { cfg:
2579                "aio.h",
2580                "ctype.h",
2581                "dirent.h",
2582                "dlfcn.h",
2583                "errno.h",
2584                "fcntl.h",
2585                "glob.h",
2586                "grp.h",
2587                "ifaddrs.h",
2588                "langinfo.h",
2589                "limits.h",
2590                "locale.h",
2591                "malloc.h",
2592                "mntent.h",
2593                "mqueue.h",
2594                "net/ethernet.h",
2595                "net/if.h",
2596                "net/if_arp.h",
2597                "net/route.h",
2598                "netdb.h",
2599                "netinet/in.h",
2600                "netinet/ip.h",
2601                "netinet/tcp.h",
2602                "netinet/udp.h",
2603                "netpacket/packet.h",
2604                "poll.h",
2605                "pthread.h",
2606                "pty.h",
2607                "pwd.h",
2608                "resolv.h",
2609                "sched.h",
2610                "sched.h",
2611                "semaphore.h",
2612                "shadow.h",
2613                "signal.h",
2614                "stddef.h",
2615                "stdint.h",
2616                "stdio.h",
2617                "stdlib.h",
2618                "string.h",
2619                "sys/epoll.h",
2620                "sys/eventfd.h",
2621                "sys/file.h",
2622                "sys/ioctl.h",
2623                "sys/ipc.h",
2624                "sys/mman.h",
2625                "sys/mount.h",
2626                "sys/msg.h",
2627                "sys/personality.h",
2628                "sys/prctl.h",
2629                "sys/ptrace.h",
2630                "sys/quota.h",
2631                "sys/reboot.h",
2632                "sys/resource.h",
2633                "sys/sem.h",
2634                "sys/shm.h",
2635                "sys/signalfd.h",
2636                "sys/socket.h",
2637                "sys/stat.h",
2638                "sys/statvfs.h",
2639                "sys/swap.h",
2640                "sys/syscall.h",
2641                "sys/sysctl.h",
2642                "sys/sysinfo.h",
2643                "sys/time.h",
2644                "sys/timerfd.h",
2645                "sys/times.h",
2646                "sys/types.h",
2647                "sys/uio.h",
2648                "sys/un.h",
2649                "sys/user.h",
2650                "sys/utsname.h",
2651                "sys/vfs.h",
2652                "sys/wait.h",
2653                "sys/xattr.h",
2654                "syslog.h",
2655                "termios.h",
2656                "time.h",
2657                "ucontext.h",
2658                "unistd.h",
2659                "utime.h",
2660                "utmp.h",
2661                "utmpx.h",
2662                "wchar.h",
2663     }
2664 
2665     cfg.type_name(move |ty, is_struct, is_union| {
2666         match ty {
2667             // Just pass all these through, no need for a "struct" prefix
2668             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2669 
2670             "os_unfair_lock" => "struct os_unfair_lock_s".to_string(),
2671 
2672             // LFS64 types have been removed in Emscripten 3.1.44+
2673             // https://github.com/emscripten-core/emscripten/pull/19812
2674             "off64_t" => "off_t".to_string(),
2675 
2676             // typedefs don't need any keywords
2677             t if t.ends_with("_t") => t.to_string(),
2678 
2679             // put `struct` in front of all structs:.
2680             t if is_struct => format!("struct {}", t),
2681 
2682             // put `union` in front of all unions:
2683             t if is_union => format!("union {}", t),
2684 
2685             t => t.to_string(),
2686         }
2687     });
2688 
2689     cfg.field_name(move |struct_, field| {
2690         match field {
2691             // Our stat *_nsec fields normally don't actually exist but are part
2692             // of a timeval struct
2693             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2694                 s.replace("e_nsec", ".tv_nsec")
2695             }
2696             // FIXME: appears that `epoll_event.data` is an union
2697             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2698             s => s.to_string(),
2699         }
2700     });
2701 
2702     cfg.skip_type(move |ty| {
2703         match ty {
2704             // sighandler_t is crazy across platforms
2705             // FIXME: is this necessary?
2706             "sighandler_t" => true,
2707 
2708             // FIXME: The size has been changed due to musl's time64
2709             "time_t" => true,
2710 
2711             // LFS64 types have been removed in Emscripten 3.1.44+
2712             // https://github.com/emscripten-core/emscripten/pull/19812
2713             t => t.ends_with("64") || t.ends_with("64_t"),
2714         }
2715     });
2716 
2717     cfg.skip_struct(move |ty| {
2718         match ty {
2719             // This is actually a union, not a struct
2720             // FIXME: is this necessary?
2721             "sigval" => true,
2722 
2723             // FIXME: It was removed in
2724             // emscripten-core/emscripten@953e414
2725             "pthread_mutexattr_t" => true,
2726 
2727             // FIXME: Investigate why the test fails.
2728             // Skip for now to unblock CI.
2729             "pthread_condattr_t" => true,
2730 
2731             // FIXME: The size has been changed when upgraded to musl 1.2.2
2732             "pthread_mutex_t" => true,
2733 
2734             // FIXME: Lowered from 16 to 8 bytes in
2735             // llvm/llvm-project@d1a96e9
2736             "max_align_t" => true,
2737 
2738             // FIXME: The size has been changed due to time64
2739             "utimbuf" | "timeval" | "timespec" | "rusage" | "itimerval" | "sched_param"
2740             | "stat" | "stat64" | "shmid_ds" | "msqid_ds" => true,
2741 
2742             // LFS64 types have been removed in Emscripten 3.1.44+
2743             // https://github.com/emscripten-core/emscripten/pull/19812
2744             ty => ty.ends_with("64") || ty.ends_with("64_t"),
2745         }
2746     });
2747 
2748     cfg.skip_fn(move |name| {
2749         match name {
2750             // Emscripten does not support fork/exec/wait or any kind of multi-process support
2751             // https://github.com/emscripten-core/emscripten/blob/3.1.30/tools/system_libs.py#L973
2752             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" | "wait4" => true,
2753 
2754             // FIXME: Remove after emscripten-core/emscripten#18492 is released (> 3.1.30).
2755             "clearenv" => true,
2756 
2757             _ => false,
2758         }
2759     });
2760 
2761     cfg.skip_const(move |name| {
2762         match name {
2763             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2764             // users should use SIGSYS instead
2765             "SIGUNUSED" => true,
2766 
2767             // FIXME: emscripten uses different constants to constructs these
2768             n if n.contains("__SIZEOF_PTHREAD") => true,
2769 
2770             // FIXME: `SYS_gettid` was removed in
2771             // emscripten-core/emscripten@6d6474e
2772             "SYS_gettid" => true,
2773 
2774             // FIXME: These values have been changed
2775             | "POSIX_MADV_DONTNEED" // to 4
2776             | "RLIMIT_NLIMITS" // to 16
2777             | "RLIM_NLIMITS" // to 16
2778             | "IPPROTO_MAX" // to 263
2779             | "F_GETLK" // to 5
2780             | "F_SETLK" // to 6
2781             | "F_SETLKW" // to 7
2782             | "O_TMPFILE" // to 65
2783             | "SIG_IGN" // -1
2784                 => true,
2785 
2786             // LFS64 types have been removed in Emscripten 3.1.44+
2787             // https://github.com/emscripten-core/emscripten/pull/19812
2788             n if n.starts_with("RLIM64") => true,
2789 
2790             _ => false,
2791         }
2792     });
2793 
2794     cfg.skip_field_type(move |struct_, field| {
2795         // This is a weird union, don't check the type.
2796         // FIXME: is this necessary?
2797         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2798         // sighandler_t type is super weird
2799         // FIXME: is this necessary?
2800         (struct_ == "sigaction" && field == "sa_sigaction") ||
2801         // sigval is actually a union, but we pretend it's a struct
2802         // FIXME: is this necessary?
2803         (struct_ == "sigevent" && field == "sigev_value") ||
2804         // aio_buf is "volatile void*" and Rust doesn't understand volatile
2805         // FIXME: is this necessary?
2806         (struct_ == "aiocb" && field == "aio_buf")
2807     });
2808 
2809     cfg.skip_field(move |struct_, field| {
2810         // this is actually a union on linux, so we can't represent it well and
2811         // just insert some padding.
2812         // FIXME: is this necessary?
2813         (struct_ == "siginfo_t" && field == "_pad") ||
2814         // musl names this __dummy1 but it's still there
2815         // FIXME: is this necessary?
2816         (struct_ == "glob_t" && field == "gl_flags") ||
2817         // musl seems to define this as an *anonymous* bitfield
2818         // FIXME: is this necessary?
2819         (struct_ == "statvfs" && field == "__f_unused") ||
2820         // sigev_notify_thread_id is actually part of a sigev_un union
2821         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2822         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
2823         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2824                                            field == "_pad2" ||
2825                                            field == "ssi_syscall" ||
2826                                            field == "ssi_call_addr" ||
2827                                            field == "ssi_arch")) ||
2828         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2829         // while other fields become reserved.
2830         (struct_ == "sched_param" && [
2831             "sched_ss_low_priority",
2832             "sched_ss_repl_period",
2833             "sched_ss_init_budget",
2834             "sched_ss_max_repl",
2835         ].contains(&field))
2836     });
2837 
2838     // FIXME: test linux like
2839     cfg.generate("../src/lib.rs", "main.rs");
2840 }
2841 
2842 fn test_neutrino(target: &str) {
2843     assert!(target.contains("nto-qnx"));
2844 
2845     let mut cfg = ctest_cfg();
2846 
2847     headers! { cfg:
2848         "ctype.h",
2849         "dirent.h",
2850         "dlfcn.h",
2851         "sys/elf.h",
2852         "fcntl.h",
2853         "glob.h",
2854         "grp.h",
2855         "iconv.h",
2856         "ifaddrs.h",
2857         "limits.h",
2858         "sys/link.h",
2859         "locale.h",
2860         "sys/malloc.h",
2861         "rcheck/malloc.h",
2862         "malloc.h",
2863         "mqueue.h",
2864         "net/if.h",
2865         "net/if_arp.h",
2866         "net/route.h",
2867         "netdb.h",
2868         "netinet/in.h",
2869         "netinet/ip.h",
2870         "netinet/tcp.h",
2871         "netinet/udp.h",
2872         "netinet/ip_var.h",
2873         "sys/poll.h",
2874         "pthread.h",
2875         "pwd.h",
2876         "regex.h",
2877         "resolv.h",
2878         "sys/sched.h",
2879         "sched.h",
2880         "semaphore.h",
2881         "shadow.h",
2882         "signal.h",
2883         "spawn.h",
2884         "stddef.h",
2885         "stdint.h",
2886         "stdio.h",
2887         "stdlib.h",
2888         "string.h",
2889         "sys/sysctl.h",
2890         "sys/file.h",
2891         "sys/inotify.h",
2892         "sys/ioctl.h",
2893         "sys/ipc.h",
2894         "sys/mman.h",
2895         "sys/mount.h",
2896         "sys/msg.h",
2897         "sys/resource.h",
2898         "sys/sem.h",
2899         "sys/socket.h",
2900         "sys/stat.h",
2901         "sys/statvfs.h",
2902         "sys/swap.h",
2903         "sys/termio.h",
2904         "sys/time.h",
2905         "sys/times.h",
2906         "sys/types.h",
2907         "sys/uio.h",
2908         "sys/un.h",
2909         "sys/utsname.h",
2910         "sys/wait.h",
2911         "syslog.h",
2912         "termios.h",
2913         "time.h",
2914         "sys/time.h",
2915         "ucontext.h",
2916         "unistd.h",
2917         "utime.h",
2918         "utmp.h",
2919         "wchar.h",
2920         "aio.h",
2921         "nl_types.h",
2922         "langinfo.h",
2923         "unix.h",
2924         "nbutil.h",
2925         "aio.h",
2926         "net/bpf.h",
2927         "net/if_dl.h",
2928         "sys/syspage.h",
2929 
2930         // TODO: The following header file doesn't appear as part of the default headers
2931         //       found in a standard installation of Neutrino 7.1 SDP.  The structures/
2932         //       functions dependent on it are currently commented out.
2933         //"sys/asyncmsg.h",
2934     }
2935 
2936     // Create and include a header file containing
2937     // items which are not included in any official
2938     // header file.
2939     let internal_header = "internal.h";
2940     let out_dir = env::var("OUT_DIR").unwrap();
2941     cfg.header(internal_header);
2942     cfg.include(&out_dir);
2943     std::fs::write(
2944         out_dir.to_owned() + "/" + internal_header,
2945         "#ifndef __internal_h__
2946         #define __internal_h__
2947         void __my_thread_exit(const void **);
2948         #endif",
2949     )
2950     .unwrap();
2951 
2952     cfg.type_name(move |ty, is_struct, is_union| {
2953         match ty {
2954             // Just pass all these through, no need for a "struct" prefix
2955             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
2956             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
2957             | "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info"
2958             | "syspage_array_info" => ty.to_string(),
2959 
2960             "Ioctl" => "int".to_string(),
2961 
2962             t if is_union => format!("union {}", t),
2963 
2964             t if t.ends_with("_t") => t.to_string(),
2965 
2966             // put `struct` in front of all structs:.
2967             t if is_struct => format!("struct {}", t),
2968 
2969             t => t.to_string(),
2970         }
2971     });
2972 
2973     cfg.field_name(move |_struct_, field| match field {
2974         "type_" => "type".to_string(),
2975 
2976         s => s.to_string(),
2977     });
2978 
2979     cfg.volatile_item(|i| {
2980         use ctest::VolatileItemKind::*;
2981         match i {
2982             // The following fields are volatie but since we cannot express that in
2983             // Rust types, we have to explicitly tell the checker about it here:
2984             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2985             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true,
2986             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true,
2987             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true,
2988             StructField(ref n, ref f) if n == "intrspin" && f == "value" => true,
2989             _ => false,
2990         }
2991     });
2992 
2993     cfg.skip_type(move |ty| {
2994         match ty {
2995             // FIXME: `sighandler_t` type is incorrect, see:
2996             // https://github.com/rust-lang/libc/issues/1359
2997             "sighandler_t" => true,
2998 
2999             // Does not exist in Neutrino
3000             "locale_t" => true,
3001 
3002             _ => false,
3003         }
3004     });
3005 
3006     cfg.skip_struct(move |ty| {
3007         if ty.starts_with("__c_anonymous_") {
3008             return true;
3009         }
3010         match ty {
3011             "Elf64_Phdr" | "Elf32_Phdr" => true,
3012 
3013             // FIXME: This is actually a union, not a struct
3014             "sigval" => true,
3015 
3016             // union
3017             "_channel_connect_attr" => true,
3018 
3019             _ => false,
3020         }
3021     });
3022 
3023     cfg.skip_const(move |name| {
3024         match name {
3025             // These signal "functions" are actually integer values that are casted to a fn ptr
3026             // This causes the compiler to err because of "illegal cast of int to ptr".
3027             "SIG_DFL" => true,
3028             "SIG_IGN" => true,
3029             "SIG_ERR" => true,
3030 
3031             _ => false,
3032         }
3033     });
3034 
3035     cfg.skip_fn(move |name| {
3036         // skip those that are manually verified
3037         match name {
3038             // FIXME: https://github.com/rust-lang/libc/issues/1272
3039             "execv" | "execve" | "execvp" | "execvpe" => true,
3040 
3041             // wrong signature
3042             "signal" => true,
3043 
3044             // wrong signature of callback ptr
3045             "__cxa_atexit" => true,
3046 
3047             // FIXME: Our API is unsound. The Rust API allows aliasing
3048             // pointers, but the C API requires pointers not to alias.
3049             // We should probably be at least using `&`/`&mut` here, see:
3050             // https://github.com/gnzlbg/ctest/issues/68
3051             "lio_listio" => true,
3052 
3053             // 2 fields are actually unions which we're simply representing
3054             // as structures.
3055             "ChannelConnectAttr" => true,
3056 
3057             // fields contains unions
3058             "SignalKillSigval" => true,
3059             "SignalKillSigval_r" => true,
3060 
3061             // Not defined in any headers.  Defined to work around a
3062             // stack unwinding bug.
3063             "__my_thread_exit" => true,
3064 
3065             _ => false,
3066         }
3067     });
3068 
3069     cfg.skip_field_type(move |struct_, field| {
3070         // sigval is actually a union, but we pretend it's a struct
3071         struct_ == "sigevent" && field == "sigev_value" ||
3072         // Anonymous structures
3073         struct_ == "_idle_hook" && field == "time"
3074     });
3075 
3076     cfg.skip_field(move |struct_, field| {
3077         (struct_ == "__sched_param" && field == "reserved") ||
3078         (struct_ == "sched_param" && field == "reserved") ||
3079         (struct_ == "sigevent" && field == "__padding1") || // ensure alignment
3080         (struct_ == "sigevent" && field == "__padding2") || // union
3081         (struct_ == "sigevent" && field == "__sigev_un2") || // union
3082         // sighandler_t type is super weird
3083         (struct_ == "sigaction" && field == "sa_sigaction") ||
3084         // does not exist
3085         (struct_ == "syspage_entry" && field == "__reserved") ||
3086         false // keep me for smaller diffs when something is added above
3087     });
3088 
3089     cfg.skip_static(move |name| (name == "__dso_handle"));
3090 
3091     cfg.generate("../src/lib.rs", "main.rs");
3092 }
3093 
3094 fn test_vxworks(target: &str) {
3095     assert!(target.contains("vxworks"));
3096 
3097     let mut cfg = ctest::TestGenerator::new();
3098     headers! { cfg:
3099                "vxWorks.h",
3100                "yvals.h",
3101                "nfs/nfsCommon.h",
3102                "rtpLibCommon.h",
3103                "randomNumGen.h",
3104                "taskLib.h",
3105                "sysLib.h",
3106                "ioLib.h",
3107                "inetLib.h",
3108                "socket.h",
3109                "errnoLib.h",
3110                "ctype.h",
3111                "dirent.h",
3112                "dlfcn.h",
3113                "elf.h",
3114                "fcntl.h",
3115                "grp.h",
3116                "sys/poll.h",
3117                "ifaddrs.h",
3118                "langinfo.h",
3119                "limits.h",
3120                "link.h",
3121                "locale.h",
3122                "sys/stat.h",
3123                "netdb.h",
3124                "pthread.h",
3125                "pwd.h",
3126                "sched.h",
3127                "semaphore.h",
3128                "signal.h",
3129                "stddef.h",
3130                "stdint.h",
3131                "stdio.h",
3132                "stdlib.h",
3133                "string.h",
3134                "sys/file.h",
3135                "sys/ioctl.h",
3136                "sys/socket.h",
3137                "sys/time.h",
3138                "sys/times.h",
3139                "sys/types.h",
3140                "sys/uio.h",
3141                "sys/un.h",
3142                "sys/utsname.h",
3143                "sys/wait.h",
3144                "netinet/tcp.h",
3145                "syslog.h",
3146                "termios.h",
3147                "time.h",
3148                "ucontext.h",
3149                "unistd.h",
3150                "utime.h",
3151                "wchar.h",
3152                "errno.h",
3153                "sys/mman.h",
3154                "pathLib.h",
3155                "mqueue.h",
3156     }
3157     // FIXME
3158     cfg.skip_const(move |name| match name {
3159         // sighandler_t weirdness
3160         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
3161         // This is not defined in vxWorks
3162         | "RTLD_DEFAULT"   => true,
3163         _ => false,
3164     });
3165     // FIXME
3166     cfg.skip_type(move |ty| match ty {
3167         "stat64" | "sighandler_t" | "off64_t" => true,
3168         _ => false,
3169     });
3170 
3171     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
3172         ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true,
3173         _ => false,
3174     });
3175 
3176     cfg.skip_roundtrip(move |s| match s {
3177         _ => false,
3178     });
3179 
3180     cfg.type_name(move |ty, is_struct, is_union| match ty {
3181         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
3182         t if is_union => format!("union {}", t),
3183         t if t.ends_with("_t") => t.to_string(),
3184         t if is_struct => format!("struct {}", t),
3185         t => t.to_string(),
3186     });
3187 
3188     // FIXME
3189     cfg.skip_fn(move |name| match name {
3190         // sigval
3191         "sigqueue" | "_sigqueue"
3192         // sighandler_t
3193         | "signal"
3194         // not used in static linking by default
3195         | "dlerror" => true,
3196         _ => false,
3197     });
3198 
3199     cfg.generate("../src/lib.rs", "main.rs");
3200 }
3201 
3202 fn test_linux(target: &str) {
3203     assert!(target.contains("linux"));
3204 
3205     // target_env
3206     let gnu = target.contains("gnu");
3207     let musl = target.contains("musl") || target.contains("ohos");
3208     let uclibc = target.contains("uclibc");
3209 
3210     match (gnu, musl, uclibc) {
3211         (true, false, false) => (),
3212         (false, true, false) => (),
3213         (false, false, true) => (),
3214         (_, _, _) => panic!(
3215             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
3216             gnu, musl, uclibc
3217         ),
3218     }
3219 
3220     let arm = target.contains("arm");
3221     let i686 = target.contains("i686");
3222     let mips = target.contains("mips");
3223     let mips32 = mips && !target.contains("64");
3224     let mips64 = mips && target.contains("64");
3225     let ppc = target.contains("powerpc");
3226     let ppc64 = target.contains("powerpc64");
3227     let s390x = target.contains("s390x");
3228     let sparc64 = target.contains("sparc64");
3229     let x32 = target.contains("x32");
3230     let x86_32 = target.contains("i686");
3231     let x86_64 = target.contains("x86_64");
3232     let aarch64_musl = target.contains("aarch64") && musl;
3233     let gnueabihf = target.contains("gnueabihf");
3234     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
3235     let riscv64 = target.contains("riscv64");
3236     let uclibc = target.contains("uclibc");
3237 
3238     let mut cfg = ctest_cfg();
3239     cfg.define("_GNU_SOURCE", None);
3240     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
3241     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
3242     // glibc versions older than 2.29.
3243     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
3244 
3245     headers! { cfg:
3246                "ctype.h",
3247                "dirent.h",
3248                "dlfcn.h",
3249                "elf.h",
3250                "fcntl.h",
3251                "getopt.h",
3252                "glob.h",
3253                [gnu]: "gnu/libc-version.h",
3254                "grp.h",
3255                "iconv.h",
3256                "ifaddrs.h",
3257                "langinfo.h",
3258                "libgen.h",
3259                "limits.h",
3260                "link.h",
3261                "linux/sysctl.h",
3262                "locale.h",
3263                "malloc.h",
3264                "mntent.h",
3265                "mqueue.h",
3266                "net/ethernet.h",
3267                "net/if.h",
3268                "net/if_arp.h",
3269                "net/route.h",
3270                "netdb.h",
3271                "netinet/in.h",
3272                "netinet/ip.h",
3273                "netinet/tcp.h",
3274                "netinet/udp.h",
3275                "netpacket/packet.h",
3276                "poll.h",
3277                "pthread.h",
3278                "pty.h",
3279                "pwd.h",
3280                "regex.h",
3281                "resolv.h",
3282                "sched.h",
3283                "semaphore.h",
3284                "shadow.h",
3285                "signal.h",
3286                "spawn.h",
3287                "stddef.h",
3288                "stdint.h",
3289                "stdio.h",
3290                "stdlib.h",
3291                "string.h",
3292                "sys/epoll.h",
3293                "sys/eventfd.h",
3294                "sys/file.h",
3295                "sys/fsuid.h",
3296                "sys/inotify.h",
3297                "sys/ioctl.h",
3298                "sys/ipc.h",
3299                "sys/mman.h",
3300                "sys/mount.h",
3301                "sys/msg.h",
3302                "sys/personality.h",
3303                "sys/prctl.h",
3304                "sys/ptrace.h",
3305                "sys/quota.h",
3306                "sys/random.h",
3307                "sys/reboot.h",
3308                "sys/resource.h",
3309                "sys/sem.h",
3310                "sys/sendfile.h",
3311                "sys/shm.h",
3312                "sys/signalfd.h",
3313                "sys/socket.h",
3314                "sys/stat.h",
3315                "sys/statvfs.h",
3316                "sys/swap.h",
3317                "sys/syscall.h",
3318                "sys/time.h",
3319                "sys/timerfd.h",
3320                "sys/times.h",
3321                "sys/timex.h",
3322                "sys/types.h",
3323                "sys/uio.h",
3324                "sys/un.h",
3325                "sys/user.h",
3326                "sys/utsname.h",
3327                "sys/vfs.h",
3328                "sys/wait.h",
3329                "syslog.h",
3330                "termios.h",
3331                "time.h",
3332                "ucontext.h",
3333                "unistd.h",
3334                "utime.h",
3335                "utmp.h",
3336                "utmpx.h",
3337                "wchar.h",
3338                "errno.h",
3339                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
3340                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
3341                // Also unavailable on gnueabihf with glibc 2.30.
3342                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
3343                [(x86_64 || x86_32 || arm) && !gnueabihf]: "sys/io.h",
3344                // `sys/reg.h` is only available on x86 and x86_64
3345                [x86_64 || x86_32]: "sys/reg.h",
3346                // sysctl system call is deprecated and not available on musl
3347                // It is also unsupported in x32, deprecated since glibc 2.30:
3348                [!(x32 || musl || gnu)]: "sys/sysctl.h",
3349                // <execinfo.h> is not supported by musl:
3350                // https://www.openwall.com/lists/musl/2015/04/09/3
3351                // <execinfo.h> is not present on uclibc.
3352                [!(musl || uclibc)]: "execinfo.h",
3353     }
3354 
3355     // Include linux headers at the end:
3356     headers! {
3357         cfg:
3358         "asm/mman.h",
3359         "linux/can.h",
3360         "linux/can/raw.h",
3361         // FIXME: requires kernel headers >= 5.4.1.
3362         [!musl]: "linux/can/j1939.h",
3363         "linux/dccp.h",
3364         "linux/errqueue.h",
3365         "linux/falloc.h",
3366         "linux/filter.h",
3367         "linux/fs.h",
3368         "linux/futex.h",
3369         "linux/genetlink.h",
3370         "linux/if.h",
3371         "linux/if_addr.h",
3372         "linux/if_alg.h",
3373         "linux/if_ether.h",
3374         "linux/if_tun.h",
3375         "linux/input.h",
3376         "linux/ipv6.h",
3377         "linux/kexec.h",
3378         "linux/keyctl.h",
3379         "linux/magic.h",
3380         "linux/memfd.h",
3381         "linux/membarrier.h",
3382         "linux/mempolicy.h",
3383         "linux/mman.h",
3384         "linux/module.h",
3385         // FIXME: requires kernel headers >= 5.1.
3386         [!musl]: "linux/mount.h",
3387         "linux/net_tstamp.h",
3388         "linux/netfilter/nfnetlink.h",
3389         "linux/netfilter/nfnetlink_log.h",
3390         "linux/netfilter/nfnetlink_queue.h",
3391         "linux/netfilter/nf_tables.h",
3392         "linux/netfilter_ipv4.h",
3393         "linux/netfilter_ipv6.h",
3394         "linux/netfilter_ipv6/ip6_tables.h",
3395         "linux/netlink.h",
3396         // FIXME: requires Linux >= 5.6:
3397         [!musl]: "linux/openat2.h",
3398         [!musl]: "linux/ptrace.h",
3399         "linux/quota.h",
3400         "linux/random.h",
3401         "linux/reboot.h",
3402         "linux/rtnetlink.h",
3403         "linux/sched.h",
3404         "linux/sctp.h",
3405         "linux/seccomp.h",
3406         "linux/sock_diag.h",
3407         "linux/sockios.h",
3408         "linux/tls.h",
3409         "linux/uinput.h",
3410         "linux/vm_sockets.h",
3411         "linux/wait.h",
3412         "sys/fanotify.h",
3413         // <sys/auxv.h> is not present on uclibc
3414         [!uclibc]: "sys/auxv.h",
3415         [gnu]: "linux/close_range.h",
3416     }
3417 
3418     // note: aio.h must be included before sys/mount.h
3419     headers! {
3420         cfg:
3421         "sys/xattr.h",
3422         "sys/sysinfo.h",
3423         // AIO is not supported by uclibc:
3424         [!uclibc]: "aio.h",
3425     }
3426 
3427     cfg.type_name(move |ty, is_struct, is_union| {
3428         match ty {
3429             // Just pass all these through, no need for a "struct" prefix
3430             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3431             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3432             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
3433 
3434             "Ioctl" if gnu => "unsigned long".to_string(),
3435             "Ioctl" => "int".to_string(),
3436 
3437             // LFS64 types have been removed in musl 1.2.4+
3438             "off64_t" if musl => "off_t".to_string(),
3439 
3440             // typedefs don't need any keywords
3441             t if t.ends_with("_t") => t.to_string(),
3442             // put `struct` in front of all structs:.
3443             t if is_struct => format!("struct {}", t),
3444             // put `union` in front of all unions:
3445             t if is_union => format!("union {}", t),
3446 
3447             t => t.to_string(),
3448         }
3449     });
3450 
3451     cfg.field_name(move |struct_, field| {
3452         match field {
3453             // Our stat *_nsec fields normally don't actually exist but are part
3454             // of a timeval struct
3455             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
3456                 s.replace("e_nsec", ".tv_nsec")
3457             }
3458             // FIXME: epoll_event.data is actually a union in C, but in Rust
3459             // it is only a u64 because we only expose one field
3460             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
3461             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
3462             // The following structs have a field called `type` in C,
3463             // but `type` is a Rust keyword, so these fields are translated
3464             // to `type_` in Rust.
3465             "type_"
3466                 if struct_ == "input_event"
3467                     || struct_ == "input_mask"
3468                     || struct_ == "ff_effect" =>
3469             {
3470                 "type".to_string()
3471             }
3472 
3473             s => s.to_string(),
3474         }
3475     });
3476 
3477     cfg.skip_type(move |ty| {
3478         match ty {
3479             // FIXME: `sighandler_t` type is incorrect, see:
3480             // https://github.com/rust-lang/libc/issues/1359
3481             "sighandler_t" => true,
3482 
3483             // These cannot be tested when "resolv.h" is included and are tested
3484             // in the `linux_elf.rs` file.
3485             "Elf64_Phdr" | "Elf32_Phdr" => true,
3486 
3487             // This type is private on Linux. It is implemented as a C `enum`
3488             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
3489             // which expect a `c_int` even though both are ABI compatible.
3490             "__rlimit_resource_t" => true,
3491             // on Linux, this is a volatile int
3492             "pthread_spinlock_t" => true,
3493 
3494             // For internal use only, to define architecture specific ioctl constants with a libc
3495             // specific type.
3496             "Ioctl" => true,
3497 
3498             // FIXME: requires >= 5.4.1 kernel headers
3499             "pgn_t" if musl => true,
3500             "priority_t" if musl => true,
3501             "name_t" if musl => true,
3502 
3503             t => {
3504                 if musl {
3505                     // LFS64 types have been removed in musl 1.2.4+
3506                     t.ends_with("64") || t.ends_with("64_t")
3507                 } else {
3508                     false
3509                 }
3510             }
3511         }
3512     });
3513 
3514     cfg.skip_struct(move |ty| {
3515         if ty.starts_with("__c_anonymous_") {
3516             return true;
3517         }
3518         // FIXME: musl CI has old headers
3519         if musl && ty.starts_with("uinput_") {
3520             return true;
3521         }
3522         // LFS64 types have been removed in musl 1.2.4+
3523         if musl && (ty.ends_with("64") || ty.ends_with("64_t")) {
3524             return true;
3525         }
3526         // FIXME: sparc64 CI has old headers
3527         if sparc64 && (ty == "uinput_ff_erase" || ty == "uinput_abs_setup") {
3528             return true;
3529         }
3530         // FIXME(https://github.com/rust-lang/libc/issues/1558): passing by
3531         // value corrupts the value for reasons not understood.
3532         if (gnu && sparc64) && (ty == "ip_mreqn" || ty == "hwtstamp_config") {
3533             return true;
3534         }
3535         match ty {
3536             // These cannot be tested when "resolv.h" is included and are tested
3537             // in the `linux_elf.rs` file.
3538             "Elf64_Phdr" | "Elf32_Phdr" => true,
3539 
3540             // On Linux, the type of `ut_tv` field of `struct utmpx`
3541             // can be an anonymous struct, so an extra struct,
3542             // which is absent in glibc, has to be defined.
3543             "__timeval" => true,
3544 
3545             // FIXME: This is actually a union, not a struct
3546             "sigval" => true,
3547 
3548             // This type is tested in the `linux_termios.rs` file since there
3549             // are header conflicts when including them with all the other
3550             // structs.
3551             "termios2" => true,
3552 
3553             // FIXME: remove once we set minimum supported glibc version.
3554             // ucontext_t added a new field as of glibc 2.28; our struct definition is
3555             // conservative and omits the field, but that means the size doesn't match for newer
3556             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
3557             "ucontext_t" if gnu => true,
3558 
3559             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
3560             // So let's ignore for now and re-visit later.
3561             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
3562             "statx" => true,
3563             "statx_timestamp" => true,
3564 
3565             // On Linux, the type of `ut_exit` field of struct `utmpx`
3566             // can be an anonymous struct, so an extra struct,
3567             // which is absent in musl, has to be defined.
3568             "__exit_status" if musl => true,
3569 
3570             // clone_args might differ b/w libc versions
3571             "clone_args" => true,
3572 
3573             // Might differ between kernel versions
3574             "open_how" => true,
3575 
3576             // FIXME: requires >= 5.4.1 kernel headers
3577             "j1939_filter" if musl => true,
3578 
3579             // FIXME: requires >= 5.4 kernel headers
3580             "sockaddr_can" if musl => true,
3581 
3582             "sctp_initmsg" | "sctp_sndrcvinfo" | "sctp_sndinfo" | "sctp_rcvinfo"
3583             | "sctp_nxtinfo" | "sctp_prinfo" | "sctp_authinfo" => true,
3584 
3585             // FIXME: requires >= 6.1 kernel headers
3586             "canxl_frame" => true,
3587 
3588             // FIXME: The size of `iv` has been changed since Linux v6.0
3589             // https://github.com/torvalds/linux/commit/94dfc73e7cf4a31da66b8843f0b9283ddd6b8381
3590             "af_alg_iv" => true,
3591             _ => false,
3592         }
3593     });
3594 
3595     cfg.skip_const(move |name| {
3596         if !gnu {
3597             // Skip definitions from the kernel on non-glibc Linux targets.
3598             // They're libc-independent, so we only need to check them on one
3599             // libc. We don't want to break CI if musl or another libc doesn't
3600             // have the definitions yet. (We do still want to check them on
3601             // every glibc target, though, as some of them can vary by
3602             // architecture.)
3603             //
3604             // This is not an exhaustive list of kernel constants, just a list
3605             // of prefixes of all those that have appeared here or that get
3606             // updated regularly and seem likely to cause breakage.
3607             if name.starts_with("AF_")
3608                 || name.starts_with("ARPHRD_")
3609                 || name.starts_with("EPOLL")
3610                 || name.starts_with("F_")
3611                 || name.starts_with("FALLOC_FL_")
3612                 || name.starts_with("IFLA_")
3613                 || name.starts_with("KEXEC_")
3614                 || name.starts_with("MS_")
3615                 || name.starts_with("MSG_")
3616                 || name.starts_with("OPEN_TREE_")
3617                 || name.starts_with("P_")
3618                 || name.starts_with("PF_")
3619                 || name.starts_with("RLIMIT_")
3620                 || name.starts_with("RTEXT_FILTER_")
3621                 || name.starts_with("SOL_")
3622                 || name.starts_with("STATX_")
3623                 || name.starts_with("SW_")
3624                 || name.starts_with("SYS_")
3625                 || name.starts_with("TCP_")
3626                 || name.starts_with("UINPUT_")
3627                 || name.starts_with("VMADDR_")
3628             {
3629                 return true;
3630             }
3631         }
3632         if musl {
3633             // FIXME: Requires >= 5.4.1 kernel headers
3634             if name.starts_with("J1939")
3635                 || name.starts_with("RTEXT_FILTER_")
3636                 || name.starts_with("SO_J1939")
3637                 || name.starts_with("SCM_J1939")
3638             {
3639                 return true;
3640             }
3641             // FIXME: Requires >= 5.10 kernel headers
3642             if name.starts_with("MEMBARRIER_CMD_REGISTER")
3643                 || name.starts_with("MEMBARRIER_CMD_PRIVATE")
3644             {
3645                 return true;
3646             }
3647             // LFS64 types have been removed in musl 1.2.4+
3648             if name.starts_with("RLIM64") {
3649                 return true;
3650             }
3651         }
3652         match name {
3653             // These constants are not available if gnu headers have been included
3654             // and can therefore not be tested here
3655             //
3656             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
3657             | "IPV6_FLOWINFO"
3658             | "IPV6_FLOWLABEL_MGR"
3659             | "IPV6_FLOWINFO_SEND"
3660             | "IPV6_FLOWINFO_FLOWLABEL"
3661             | "IPV6_FLOWINFO_PRIORITY"
3662             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
3663             | "F_CANCELLK"
3664             | "F_ADD_SEALS"
3665             | "F_GET_SEALS"
3666             | "F_SEAL_SEAL"
3667             | "F_SEAL_SHRINK"
3668             | "F_SEAL_GROW"
3669             | "F_SEAL_WRITE" => true,
3670             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests
3671             // because including `linux/if_arp.h` causes some conflicts:
3672             "ARPHRD_CAN" => true,
3673 
3674             // FIXME: deprecated: not available in any header
3675             // See: https://github.com/rust-lang/libc/issues/1356
3676             "ENOATTR" => true,
3677 
3678             // FIXME: SIGUNUSED was removed in glibc 2.26
3679             // Users should use SIGSYS instead.
3680             "SIGUNUSED" => true,
3681 
3682             // FIXME: conflicts with glibc headers and is tested in
3683             // `linux_termios.rs` below:
3684             | "BOTHER"
3685             | "IBSHIFT"
3686             | "TCGETS2"
3687             | "TCSETS2"
3688             | "TCSETSW2"
3689             | "TCSETSF2" => true,
3690 
3691             // FIXME: on musl the pthread types are defined a little differently
3692             // - these constants are used by the glibc implementation.
3693             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
3694 
3695             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
3696             // We should do so after a while.
3697             "SOMAXCONN" if gnu => true,
3698 
3699             // deprecated: not available from Linux kernel 5.6:
3700             "VMADDR_CID_RESERVED" => true,
3701 
3702             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
3703             | "IPPROTO_MAX"
3704             | "IPPROTO_ETHERNET"
3705             | "IPPROTO_MPTCP" => true,
3706 
3707             // FIXME: Not currently available in headers
3708             "P_PIDFD" if mips => true,
3709             "SYS_pidfd_open" if mips => true,
3710 
3711             // FIXME: Not currently available in headers on MIPS
3712             // Not yet implemented on sparc64
3713             "SYS_clone3" if mips | sparc64 => true,
3714 
3715             // FIXME: Not defined on ARM, gnueabihf, MIPS, musl, PowerPC, riscv64, s390x, and sparc64.
3716             "SYS_memfd_secret" if arm | gnueabihf | mips | musl | ppc | riscv64 | s390x | sparc64 => true,
3717 
3718             // FIXME: Added in Linux 5.16
3719             // https://github.com/torvalds/linux/commit/039c0ec9bb77446d7ada7f55f90af9299b28ca49
3720             "SYS_futex_waitv" => true,
3721 
3722             // FIXME: Added in Linux 5.17
3723             // https://github.com/torvalds/linux/commit/c6018b4b254971863bd0ad36bb5e7d0fa0f0ddb0
3724             "SYS_set_mempolicy_home_node" => true,
3725 
3726             // FIXME: Added in Linux 5.18
3727             // https://github.com/torvalds/linux/commit/8b5413647262dda8d8d0e07e14ea1de9ac7cf0b2
3728             "NFQA_PRIORITY" => true,
3729 
3730             // FIXME: requires more recent kernel headers on CI
3731             | "UINPUT_VERSION"
3732             | "SW_MAX"
3733             | "SW_CNT"
3734                 if mips || ppc64 || riscv64 => true,
3735 
3736             // FIXME: Not currently available in headers on ARM, MIPS and musl.
3737             "NETLINK_GET_STRICT_CHK" if arm || mips || musl => true,
3738 
3739             // kernel constants not available in uclibc 1.0.34
3740             | "EXTPROC"
3741             | "IPPROTO_BEETPH"
3742             | "IPPROTO_MPLS"
3743             | "IPV6_HDRINCL"
3744             | "IPV6_MULTICAST_ALL"
3745             | "IPV6_PMTUDISC_INTERFACE"
3746             | "IPV6_PMTUDISC_OMIT"
3747             | "IPV6_ROUTER_ALERT_ISOLATE"
3748             | "PACKET_MR_UNICAST"
3749             | "RUSAGE_THREAD"
3750             | "SHM_EXEC"
3751             | "UDP_GRO"
3752             | "UDP_SEGMENT"
3753                 if uclibc => true,
3754 
3755             // headers conflicts with linux/pidfd.h
3756             "PIDFD_NONBLOCK" => true,
3757 
3758             // is a private value for kernel usage normally
3759             "FUSE_SUPER_MAGIC" => true,
3760 
3761             // linux 5.17 min
3762             "PR_SET_VMA" | "PR_SET_VMA_ANON_NAME" => true,
3763 
3764             // present in recent kernels only
3765             "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,
3766 
3767             // present in recent kernels only >= 5.13
3768             "PR_PAC_SET_ENABLED_KEYS" | "PR_PAC_GET_ENABLED_KEYS" => true,
3769             // present in recent kernels only >= 5.19
3770             "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,
3771 
3772             // Added in Linux 5.14
3773             "FUTEX_LOCK_PI2" => true,
3774 
3775             // Added in  linux 6.1
3776             "STATX_DIOALIGN"
3777             | "CAN_RAW_XL_FRAMES"
3778             | "CANXL_HDR_SIZE"
3779             | "CANXL_MAX_DLC"
3780             | "CANXL_MAX_DLC_MASK"
3781             | "CANXL_MAX_DLEN"
3782             | "CANXL_MAX_MTU"
3783             | "CANXL_MIN_DLC"
3784             | "CANXL_MIN_DLEN"
3785             | "CANXL_MIN_MTU"
3786             | "CANXL_MTU"
3787             | "CANXL_PRIO_BITS"
3788             | "CANXL_PRIO_MASK"
3789             | "CANXL_SEC"
3790             | "CANXL_XLF"
3791              => true,
3792 
3793             // FIXME: Parts of netfilter/nfnetlink*.h require more recent kernel headers:
3794             | "RTNLGRP_MCTP_IFADDR" // linux v5.17+
3795             | "RTNLGRP_TUNNEL" // linux v5.18+
3796             | "RTNLGRP_STATS" // linux v5.18+
3797                 => true,
3798 
3799             // FIXME: The below is no longer const in glibc 2.34:
3800             // https://github.com/bminor/glibc/commit/5d98a7dae955bafa6740c26eaba9c86060ae0344
3801             | "PTHREAD_STACK_MIN"
3802             | "SIGSTKSZ"
3803             | "MINSIGSTKSZ"
3804                 if gnu => true,
3805 
3806             // FIXME: Linux >= 5.16 changed its value:
3807             // https://github.com/torvalds/linux/commit/42df6e1d221dddc0f2acf2be37e68d553ad65f96
3808             "NF_NETDEV_NUMHOOKS" => true,
3809 
3810             // FIXME: requires Linux >= 5.6:
3811             | "RESOLVE_BENEATH"
3812             | "RESOLVE_CACHED"
3813             | "RESOLVE_IN_ROOT"
3814             | "RESOLVE_NO_MAGICLINKS"
3815             | "RESOLVE_NO_SYMLINKS"
3816             | "RESOLVE_NO_XDEV" if musl => true,
3817 
3818             // FIXME: requires Linux >= 5.4:
3819             | "CAN_J1939"
3820             | "CAN_NPROTO" if musl => true,
3821 
3822             // FIXME: requires Linux >= 5.6
3823             "GRND_INSECURE" if musl => true,
3824 
3825             // FIXME: requires Linux >= 5.7:
3826             "MREMAP_DONTUNMAP" if musl => true,
3827 
3828             // FIXME: requires Linux >= v5.8
3829             "IF_LINK_MODE_TESTING" if musl || sparc64 => true,
3830 
3831             // FIXME: Requires more recent kernel headers (5.9 / 5.11):
3832             | "CLOSE_RANGE_UNSHARE"
3833             | "CLOSE_RANGE_CLOEXEC" if musl => true,
3834 
3835             // FIXME: requires Linux >= 5.12:
3836             "MPOL_F_NUMA_BALANCING" if musl => true,
3837 
3838             // FIXME: Requires more recent kernel headers
3839             | "NFNL_SUBSYS_COUNT" // bumped in v5.14
3840             | "NFNL_SUBSYS_HOOK" // v5.14+
3841             | "NFULA_VLAN" // v5.4+
3842             | "NFULA_L2HDR" // v5.4+
3843             | "NFULA_VLAN_PROTO" // v5.4+
3844             | "NFULA_VLAN_TCI" // v5.4+
3845             | "NFULA_VLAN_UNSPEC" // v5.4+
3846             | "RTNLGRP_NEXTHOP" // linux v5.3+
3847             | "RTNLGRP_BRVLAN" // linux v5.6+
3848             if musl => true,
3849 
3850             | "MADV_COLD"
3851             | "MADV_PAGEOUT"
3852             | "MADV_POPULATE_READ"
3853             | "MADV_POPULATE_WRITE"
3854             if musl => true,
3855             "CLONE_CLEAR_SIGHAND" | "CLONE_INTO_CGROUP" => true,
3856 
3857             // kernel 6.1 minimum
3858             "MADV_COLLAPSE" => true,
3859 
3860             // FIXME: Requires more recent kernel headers
3861             | "IFLA_PARENT_DEV_NAME"     // linux v5.13+
3862             | "IFLA_PARENT_DEV_BUS_NAME" // linux v5.13+
3863             | "IFLA_GRO_MAX_SIZE"        // linux v5.16+
3864             | "IFLA_TSO_MAX_SIZE"        // linux v5.18+
3865             | "IFLA_TSO_MAX_SEGS"        // linux v5.18+
3866             | "IFLA_ALLMULTI"            // linux v6.0+
3867             | "MADV_DONTNEED_LOCKED"     // linux v5.18+
3868                 => true,
3869             "SCTP_FUTURE_ASSOC" | "SCTP_CURRENT_ASSOC" | "SCTP_ALL_ASSOC" | "SCTP_PEER_ADDR_THLDS_V2" => true, // linux 5.5+
3870 
3871             // FIXME: Requires more recent kernel headers
3872             "HWTSTAMP_TX_ONESTEP_P2P" if musl => true, // linux v5.6+
3873 
3874             // kernel 6.5 minimum
3875             "MOVE_MOUNT_BENEATH" => true,
3876             // FIXME: Requires linux 6.1
3877             "ALG_SET_KEY_BY_KEY_SERIAL" | "ALG_SET_DRBG_ENTROPY" => true,
3878 
3879             // FIXME: Requires more recent kernel headers
3880             | "FAN_FS_ERROR"                      // linux v5.16+
3881             | "FAN_RENAME"                        // linux v5.17+
3882             | "FAN_REPORT_TARGET_FID"             // linux v5.17+
3883             | "FAN_REPORT_DFID_NAME_TARGET"       // linux v5.17+
3884             | "FAN_MARK_EVICTABLE"                // linux v5.19+
3885             | "FAN_MARK_IGNORE"                   // linux v6.0+
3886             | "FAN_MARK_IGNORE_SURV"              // linux v6.0+
3887             | "FAN_EVENT_INFO_TYPE_ERROR"         // linux v5.16+
3888             | "FAN_EVENT_INFO_TYPE_OLD_DFID_NAME" // linux v5.17+
3889             | "FAN_EVENT_INFO_TYPE_NEW_DFID_NAME" // linux v5.17+
3890             | "FAN_RESPONSE_INFO_NONE"            // linux v5.16+
3891             | "FAN_RESPONSE_INFO_AUDIT_RULE"      // linux v5.16+
3892             | "FAN_INFO"                          // linux v5.16+
3893                 => true,
3894 
3895             // FIXME: Requires linux 5.15+
3896             "FAN_REPORT_PIDFD" if musl => true,
3897 
3898             // FIXME: Requires linux 5.9+
3899             | "FAN_REPORT_DIR_FID"
3900             | "FAN_REPORT_NAME"
3901             | "FAN_REPORT_DFID_NAME"
3902             | "FAN_EVENT_INFO_TYPE_DFID_NAME"
3903             | "FAN_EVENT_INFO_TYPE_DFID"
3904             | "FAN_EVENT_INFO_TYPE_PIDFD"
3905             | "FAN_NOPIDFD"
3906             | "FAN_EPIDFD"
3907             if musl => true,
3908 
3909             // FIXME: Requires linux 6.5
3910             "NFT_MSG_MAX" => true,
3911 
3912             _ => false,
3913         }
3914     });
3915 
3916     cfg.skip_fn(move |name| {
3917         // skip those that are manually verified
3918         match name {
3919             // FIXME: https://github.com/rust-lang/libc/issues/1272
3920             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
3921 
3922             // There are two versions of the sterror_r function, see
3923             //
3924             // https://linux.die.net/man/3/strerror_r
3925             //
3926             // An XSI-compliant version provided if:
3927             //
3928             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
3929             //  && ! _GNU_SOURCE
3930             //
3931             // and a GNU specific version provided if _GNU_SOURCE is defined.
3932             //
3933             // libc provides bindings for the XSI-compliant version, which is
3934             // preferred for portable applications.
3935             //
3936             // We skip the test here since here _GNU_SOURCE is defined, and
3937             // test the XSI version below.
3938             "strerror_r" => true,
3939 
3940             // FIXME: Our API is unsound. The Rust API allows aliasing
3941             // pointers, but the C API requires pointers not to alias.
3942             // We should probably be at least using `&`/`&mut` here, see:
3943             // https://github.com/gnzlbg/ctest/issues/68
3944             "lio_listio" if musl => true,
3945 
3946             // Needs glibc 2.34 or later.
3947             "posix_spawn_file_actions_addclosefrom_np" if gnu && sparc64 => true,
3948             // Needs glibc 2.35 or later.
3949             "posix_spawn_file_actions_addtcsetpgrp_np" if gnu && sparc64 => true,
3950 
3951             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
3952             "sysctl" if gnu => true,
3953 
3954             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
3955             "gettimeofday" if gnu => true,
3956 
3957             // These are all implemented as static inline functions in uclibc, so
3958             // they cannot be linked against.
3959             // If implementations are required, they might need to be implemented
3960             // in this crate.
3961             "posix_spawnattr_init" if uclibc => true,
3962             "posix_spawnattr_destroy" if uclibc => true,
3963             "posix_spawnattr_getsigdefault" if uclibc => true,
3964             "posix_spawnattr_setsigdefault" if uclibc => true,
3965             "posix_spawnattr_getsigmask" if uclibc => true,
3966             "posix_spawnattr_setsigmask" if uclibc => true,
3967             "posix_spawnattr_getflags" if uclibc => true,
3968             "posix_spawnattr_setflags" if uclibc => true,
3969             "posix_spawnattr_getpgroup" if uclibc => true,
3970             "posix_spawnattr_setpgroup" if uclibc => true,
3971             "posix_spawnattr_getschedpolicy" if uclibc => true,
3972             "posix_spawnattr_setschedpolicy" if uclibc => true,
3973             "posix_spawnattr_getschedparam" if uclibc => true,
3974             "posix_spawnattr_setschedparam" if uclibc => true,
3975             "posix_spawn_file_actions_init" if uclibc => true,
3976             "posix_spawn_file_actions_destroy" if uclibc => true,
3977 
3978             // uclibc defines the flags type as a uint, but dependent crates
3979             // assume it's a int instead.
3980             "getnameinfo" if uclibc => true,
3981 
3982             // FIXME: This needs musl 1.2.2 or later.
3983             "gettid" if musl => true,
3984 
3985             // Needs glibc 2.33 or later.
3986             "mallinfo2" => true,
3987 
3988             "reallocarray" if musl => true,
3989 
3990             // Not defined in uclibc as of 1.0.34
3991             "gettid" if uclibc => true,
3992 
3993             // Needs musl 1.2.3 or later.
3994             "pthread_getname_np" if musl => true,
3995 
3996             // pthread_sigqueue uses sigval, which was initially declared
3997             // as a struct but should be defined as a union. However due
3998             // to the issues described here: https://github.com/rust-lang/libc/issues/2816
3999             // it can't be changed from struct.
4000             "pthread_sigqueue" => true,
4001 
4002             // There are two versions of basename(3) on Linux with glibc, see
4003             //
4004             // https://man7.org/linux/man-pages/man3/basename.3.html
4005             //
4006             // If libgen.h is included, then the POSIX version will be available;
4007             // If _GNU_SOURCE is defined and string.h is included, then the GNU one
4008             // will be used.
4009             //
4010             // libc exposes both of them, providing a prefix to differentiate between
4011             // them.
4012             //
4013             // Because the name with prefix is not a valid symbol in C, we have to
4014             // skip the tests.
4015             "posix_basename" if gnu => true,
4016             "gnu_basename" if gnu => true,
4017 
4018             // FIXME: function pointers changed since Ubuntu 23.10
4019             "strtol" | "strtoll" | "strtoul" | "strtoull" | "fscanf" | "scanf" | "sscanf" => true,
4020 
4021             _ => false,
4022         }
4023     });
4024 
4025     cfg.skip_field_type(move |struct_, field| {
4026         // This is a weird union, don't check the type.
4027         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
4028         // sighandler_t type is super weird
4029         (struct_ == "sigaction" && field == "sa_sigaction") ||
4030         // __timeval type is a patch which doesn't exist in glibc
4031         (struct_ == "utmpx" && field == "ut_tv") ||
4032         // sigval is actually a union, but we pretend it's a struct
4033         (struct_ == "sigevent" && field == "sigev_value") ||
4034         // this one is an anonymous union
4035         (struct_ == "ff_effect" && field == "u") ||
4036         // `__exit_status` type is a patch which is absent in musl
4037         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
4038         // `can_addr` is an anonymous union
4039         (struct_ == "sockaddr_can" && field == "can_addr")
4040     });
4041 
4042     cfg.volatile_item(|i| {
4043         use ctest::VolatileItemKind::*;
4044         match i {
4045             // aio_buf is a volatile void** but since we cannot express that in
4046             // Rust types, we have to explicitly tell the checker about it here:
4047             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
4048             _ => false,
4049         }
4050     });
4051 
4052     cfg.skip_field(move |struct_, field| {
4053         // this is actually a union on linux, so we can't represent it well and
4054         // just insert some padding.
4055         (struct_ == "siginfo_t" && field == "_pad") ||
4056         // musl names this __dummy1 but it's still there
4057         (musl && struct_ == "glob_t" && field == "gl_flags") ||
4058         // musl seems to define this as an *anonymous* bitfield
4059         (musl && struct_ == "statvfs" && field == "__f_unused") ||
4060         // sigev_notify_thread_id is actually part of a sigev_un union
4061         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
4062         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
4063         // has them yet.
4064         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
4065                                            field == "_pad2" ||
4066                                            field == "ssi_syscall" ||
4067                                            field == "ssi_call_addr" ||
4068                                            field == "ssi_arch")) ||
4069         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
4070         // while other fields become reserved.
4071         (struct_ == "sched_param" && [
4072             "sched_ss_low_priority",
4073             "sched_ss_repl_period",
4074             "sched_ss_init_budget",
4075             "sched_ss_max_repl",
4076         ].contains(&field) && musl) ||
4077         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
4078         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
4079         // glibc uses unnamed fields here and Rust doesn't support that yet
4080         (struct_ == "timex" && field.starts_with("__unused")) ||
4081         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
4082         (struct_ == "ipc_perm" && field == "mode"
4083             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
4084         ) ||
4085         // the `u` field is in fact an anonymous union
4086         (gnu && struct_ == "ptrace_syscall_info" && (field == "u" || field == "pad")) ||
4087         // the vregs field is a `__uint128_t` C's type.
4088         (struct_ == "user_fpsimd_struct" && field == "vregs") ||
4089         // Linux >= 5.11 tweaked the `svm_zero` field of the `sockaddr_vm` struct.
4090         // https://github.com/torvalds/linux/commit/dc8eeef73b63ed8988224ba6b5ed19a615163a7f
4091         (struct_ == "sockaddr_vm" && field == "svm_zero") ||
4092         // the `ifr_ifru` field is an anonymous union
4093         (struct_ == "ifreq" && field == "ifr_ifru") ||
4094         // the `ifc_ifcu` field is an anonymous union
4095         (struct_ == "ifconf" && field == "ifc_ifcu") ||
4096         // glibc uses a single array `uregs` instead of individual fields.
4097         (struct_ == "user_regs" && arm)
4098     });
4099 
4100     cfg.skip_roundtrip(move |s| match s {
4101         // FIXME:
4102         "utsname" if mips32 || mips64 => true,
4103         // FIXME:
4104         "mcontext_t" if s390x => true,
4105         // FIXME: This is actually a union.
4106         "fpreg_t" if s390x => true,
4107 
4108         "sockaddr_un" | "sembuf" | "ff_constant_effect" if mips32 && (gnu || musl) => true,
4109 
4110         // The test doesn't work on some env:
4111         "ipv6_mreq"
4112         | "ip_mreq_source"
4113         | "sockaddr_in6"
4114         | "sockaddr_ll"
4115         | "in_pktinfo"
4116         | "arpreq"
4117         | "arpreq_old"
4118         | "sockaddr_un"
4119         | "ff_constant_effect"
4120         | "ff_ramp_effect"
4121         | "ff_condition_effect"
4122         | "Elf32_Ehdr"
4123         | "Elf32_Chdr"
4124         | "ucred"
4125         | "in6_pktinfo"
4126         | "sockaddr_nl"
4127         | "termios"
4128         | "nlmsgerr"
4129             if (mips64 || sparc64) && gnu =>
4130         {
4131             true
4132         }
4133 
4134         // The `inotify_event` and `cmsghdr` types contain Flexible Array Member fields (the
4135         // `name` and `data` fields respectively) which have unspecified calling convention.
4136         // The roundtripping tests deliberately pass the structs by value to check "by value"
4137         // layout consistency, but this would be UB for the these types.
4138         "inotify_event" => true,
4139         "cmsghdr" => true,
4140 
4141         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
4142         "max_align_t" if i686 || mips64 || ppc64 => true,
4143 
4144         _ => false,
4145     });
4146 
4147     cfg.generate("../src/lib.rs", "main.rs");
4148 
4149     test_linux_like_apis(target);
4150 }
4151 
4152 // This function tests APIs that are incompatible to test when other APIs
4153 // are included (e.g. because including both sets of headers clashes)
4154 fn test_linux_like_apis(target: &str) {
4155     let gnu = target.contains("gnu");
4156     let musl = target.contains("musl") || target.contains("ohos");
4157     let linux = target.contains("linux");
4158     let emscripten = target.contains("emscripten");
4159     let android = target.contains("android");
4160     assert!(linux || android || emscripten);
4161 
4162     if linux || android || emscripten {
4163         // test strerror_r from the `string.h` header
4164         let mut cfg = ctest_cfg();
4165         cfg.skip_type(|_| true).skip_static(|_| true);
4166 
4167         headers! { cfg: "string.h" }
4168         cfg.skip_fn(|f| match f {
4169             "strerror_r" => false,
4170             _ => true,
4171         })
4172         .skip_const(|_| true)
4173         .skip_struct(|_| true);
4174         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
4175     }
4176 
4177     if linux || android || emscripten {
4178         // test fcntl - see:
4179         // http://man7.org/linux/man-pages/man2/fcntl.2.html
4180         let mut cfg = ctest_cfg();
4181 
4182         if musl {
4183             cfg.header("fcntl.h");
4184         } else {
4185             cfg.header("linux/fcntl.h");
4186         }
4187 
4188         cfg.skip_type(|_| true)
4189             .skip_static(|_| true)
4190             .skip_struct(|_| true)
4191             .skip_fn(|_| true)
4192             .skip_const(move |name| match name {
4193                 // test fcntl constants:
4194                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK"
4195                 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false,
4196                 _ => true,
4197             })
4198             .type_name(move |ty, is_struct, is_union| match ty {
4199                 t if is_struct => format!("struct {}", t),
4200                 t if is_union => format!("union {}", t),
4201                 t => t.to_string(),
4202             });
4203 
4204         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
4205     }
4206 
4207     if linux || android {
4208         // test termios
4209         let mut cfg = ctest_cfg();
4210         cfg.header("asm/termbits.h");
4211         cfg.header("linux/termios.h");
4212         cfg.skip_type(|_| true)
4213             .skip_static(|_| true)
4214             .skip_fn(|_| true)
4215             .skip_const(|c| match c {
4216                 "BOTHER" | "IBSHIFT" => false,
4217                 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => false,
4218                 _ => true,
4219             })
4220             .skip_struct(|s| s != "termios2")
4221             .type_name(move |ty, is_struct, is_union| match ty {
4222                 "Ioctl" if gnu => "unsigned long".to_string(),
4223                 "Ioctl" => "int".to_string(),
4224                 t if is_struct => format!("struct {}", t),
4225                 t if is_union => format!("union {}", t),
4226                 t => t.to_string(),
4227             });
4228         cfg.generate("../src/lib.rs", "linux_termios.rs");
4229     }
4230 
4231     if linux || android {
4232         // test IPV6_ constants:
4233         let mut cfg = ctest_cfg();
4234         headers! {
4235             cfg:
4236             "linux/in6.h"
4237         }
4238         cfg.skip_type(|_| true)
4239             .skip_static(|_| true)
4240             .skip_fn(|_| true)
4241             .skip_const(|_| true)
4242             .skip_struct(|_| true)
4243             .skip_const(move |name| match name {
4244                 "IPV6_FLOWINFO"
4245                 | "IPV6_FLOWLABEL_MGR"
4246                 | "IPV6_FLOWINFO_SEND"
4247                 | "IPV6_FLOWINFO_FLOWLABEL"
4248                 | "IPV6_FLOWINFO_PRIORITY" => false,
4249                 _ => true,
4250             })
4251             .type_name(move |ty, is_struct, is_union| match ty {
4252                 t if is_struct => format!("struct {}", t),
4253                 t if is_union => format!("union {}", t),
4254                 t => t.to_string(),
4255             });
4256         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
4257     }
4258 
4259     if linux || android {
4260         // Test Elf64_Phdr and Elf32_Phdr
4261         // These types have a field called `p_type`, but including
4262         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
4263         // making the tests for these fails when both are included.
4264         let mut cfg = ctest_cfg();
4265         cfg.header("elf.h");
4266         cfg.skip_fn(|_| true)
4267             .skip_static(|_| true)
4268             .skip_const(|_| true)
4269             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
4270             .skip_struct(move |ty| match ty {
4271                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4272                 _ => true,
4273             })
4274             .skip_type(move |ty| match ty {
4275                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4276                 _ => true,
4277             });
4278         cfg.generate("../src/lib.rs", "linux_elf.rs");
4279     }
4280 
4281     if linux || android {
4282         // Test `ARPHRD_CAN`.
4283         let mut cfg = ctest_cfg();
4284         cfg.header("linux/if_arp.h");
4285         cfg.skip_fn(|_| true)
4286             .skip_static(|_| true)
4287             .skip_const(move |name| match name {
4288                 "ARPHRD_CAN" => false,
4289                 _ => true,
4290             })
4291             .skip_struct(|_| true)
4292             .skip_type(|_| true);
4293         cfg.generate("../src/lib.rs", "linux_if_arp.rs");
4294     }
4295 }
4296 
4297 fn which_freebsd() -> Option<i32> {
4298     let output = std::process::Command::new("freebsd-version")
4299         .output()
4300         .ok()?;
4301     if !output.status.success() {
4302         return None;
4303     }
4304 
4305     let stdout = String::from_utf8(output.stdout).ok()?;
4306 
4307     match &stdout {
4308         s if s.starts_with("10") => Some(10),
4309         s if s.starts_with("11") => Some(11),
4310         s if s.starts_with("12") => Some(12),
4311         s if s.starts_with("13") => Some(13),
4312         s if s.starts_with("14") => Some(14),
4313         _ => None,
4314     }
4315 }
4316 
4317 fn test_haiku(target: &str) {
4318     assert!(target.contains("haiku"));
4319 
4320     let mut cfg = ctest_cfg();
4321     cfg.flag("-Wno-deprecated-declarations");
4322     cfg.define("__USE_GNU", Some("1"));
4323     cfg.define("_GNU_SOURCE", None);
4324     cfg.language(ctest::Lang::CXX);
4325 
4326     // POSIX API
4327     headers! { cfg:
4328                "alloca.h",
4329                "arpa/inet.h",
4330                "arpa/nameser.h",
4331                "arpa/nameser_compat.h",
4332                "assert.h",
4333                "complex.h",
4334                "ctype.h",
4335                "dirent.h",
4336                "div_t.h",
4337                "dlfcn.h",
4338                "endian.h",
4339                "errno.h",
4340                "fcntl.h",
4341                "fenv.h",
4342                "fnmatch.h",
4343                "fts.h",
4344                "ftw.h",
4345                "getopt.h",
4346                "glob.h",
4347                "grp.h",
4348                "inttypes.h",
4349                "iovec.h",
4350                "langinfo.h",
4351                "libgen.h",
4352                "libio.h",
4353                "limits.h",
4354                "locale.h",
4355                "malloc.h",
4356                "malloc_debug.h",
4357                "math.h",
4358                "memory.h",
4359                "monetary.h",
4360                "net/if.h",
4361                "net/if_dl.h",
4362                "net/if_media.h",
4363                "net/if_tun.h",
4364                "net/if_types.h",
4365                "net/route.h",
4366                "netdb.h",
4367                "netinet/in.h",
4368                "netinet/ip.h",
4369                "netinet/ip6.h",
4370                "netinet/ip_icmp.h",
4371                "netinet/ip_var.h",
4372                "netinet/tcp.h",
4373                "netinet/udp.h",
4374                "netinet6/in6.h",
4375                "nl_types.h",
4376                "null.h",
4377                "poll.h",
4378                "pthread.h",
4379                "pwd.h",
4380                "regex.h",
4381                "resolv.h",
4382                "sched.h",
4383                "search.h",
4384                "semaphore.h",
4385                "setjmp.h",
4386                "shadow.h",
4387                "signal.h",
4388                "size_t.h",
4389                "spawn.h",
4390                "stdint.h",
4391                "stdio.h",
4392                "stdlib.h",
4393                "string.h",
4394                "strings.h",
4395                "sys/cdefs.h",
4396                "sys/file.h",
4397                "sys/ioctl.h",
4398                "sys/ipc.h",
4399                "sys/mman.h",
4400                "sys/msg.h",
4401                "sys/param.h",
4402                "sys/poll.h",
4403                "sys/resource.h",
4404                "sys/select.h",
4405                "sys/sem.h",
4406                "sys/socket.h",
4407                "sys/sockio.h",
4408                "sys/stat.h",
4409                "sys/statvfs.h",
4410                "sys/time.h",
4411                "sys/timeb.h",
4412                "sys/times.h",
4413                "sys/types.h",
4414                "sys/uio.h",
4415                "sys/un.h",
4416                "sys/utsname.h",
4417                "sys/wait.h",
4418                "syslog.h",
4419                "tar.h",
4420                "termios.h",
4421                "time.h",
4422                "uchar.h",
4423                "unistd.h",
4424                "utime.h",
4425                "utmpx.h",
4426                "wchar.h",
4427                "wchar_t.h",
4428                "wctype.h"
4429     }
4430 
4431     // BSD Extensions
4432     headers! { cfg:
4433                "ifaddrs.h",
4434                "libutil.h",
4435                "link.h",
4436                "pty.h",
4437                "stringlist.h",
4438                "sys/link_elf.h",
4439     }
4440 
4441     // Native API
4442     headers! { cfg:
4443                "kernel/OS.h",
4444                "kernel/fs_attr.h",
4445                "kernel/fs_index.h",
4446                "kernel/fs_info.h",
4447                "kernel/fs_query.h",
4448                "kernel/fs_volume.h",
4449                "kernel/image.h",
4450                "kernel/scheduler.h",
4451                "storage/FindDirectory.h",
4452                "storage/StorageDefs.h",
4453                "support/Errors.h",
4454                "support/SupportDefs.h",
4455                "support/TypeConstants.h"
4456     }
4457 
4458     cfg.skip_struct(move |ty| {
4459         if ty.starts_with("__c_anonymous_") {
4460             return true;
4461         }
4462         match ty {
4463             // FIXME: actually a union
4464             "sigval" => true,
4465             // FIXME: locale_t does not exist on Haiku
4466             "locale_t" => true,
4467             // FIXME: rusage has a different layout on Haiku
4468             "rusage" => true,
4469             // FIXME?: complains that rust aligns on 4 byte boundary, but
4470             //         Haiku does not align it at all.
4471             "in6_addr" => true,
4472             // The d_name attribute is an array of 1 on Haiku, with the
4473             // intention that the developer allocates a larger or smaller
4474             // piece of memory depending on the expected/actual size of the name.
4475             // Other platforms have sensible defaults. In Rust, the d_name field
4476             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
4477             // newly allocated dirent objects. This breaks the automated tests.
4478             "dirent" => true,
4479             // The following structs contain function pointers, which cannot be initialized
4480             // with mem::zeroed(), so skip the automated test
4481             "image_info" | "thread_info" => true,
4482 
4483             "Elf64_Phdr" => true,
4484 
4485             // is an union
4486             "cpuid_info" => true,
4487 
4488             _ => false,
4489         }
4490     });
4491 
4492     cfg.skip_type(move |ty| {
4493         match ty {
4494             // FIXME: locale_t does not exist on Haiku
4495             "locale_t" => true,
4496             // These cause errors, to be reviewed in the future
4497             "sighandler_t" => true,
4498             "pthread_t" => true,
4499             "pthread_condattr_t" => true,
4500             "pthread_mutexattr_t" => true,
4501             "pthread_rwlockattr_t" => true,
4502             _ => false,
4503         }
4504     });
4505 
4506     cfg.skip_fn(move |name| {
4507         // skip those that are manually verified
4508         match name {
4509             // FIXME: https://github.com/rust-lang/libc/issues/1272
4510             "execv" | "execve" | "execvp" | "execvpe" => true,
4511             // FIXME: does not exist on haiku
4512             "open_wmemstream" => true,
4513             "mlockall" | "munlockall" => true,
4514             "tcgetsid" => true,
4515             "cfsetspeed" => true,
4516             // ignore for now, will be part of Haiku R1 beta 3
4517             "mlock" | "munlock" => true,
4518             // returns const char * on Haiku
4519             "strsignal" => true,
4520             // uses an enum as a parameter argument, which is incorrectly
4521             // translated into a struct argument
4522             "find_path" => true,
4523 
4524             "get_cpuid" => true,
4525 
4526             // uses varargs parameter
4527             "ioctl" => true,
4528 
4529             _ => false,
4530         }
4531     });
4532 
4533     cfg.skip_const(move |name| {
4534         match name {
4535             // FIXME: these constants do not exist on Haiku
4536             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
4537             | "DT_SOCK" => true,
4538             "USRQUOTA" | "GRPQUOTA" => true,
4539             "SIGIOT" => true,
4540             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL"
4541             | "ATF_USETRAILERS" => true,
4542             // Haiku does not have MAP_FILE, but rustc requires it
4543             "MAP_FILE" => true,
4544             // The following does not exist on Haiku but is required by
4545             // several crates
4546             "FIOCLEX" => true,
4547             // just skip this one, it is not defined on Haiku beta 2 but
4548             // since it is meant as a mask and not a parameter it can exist
4549             // here
4550             "LOG_PRIMASK" => true,
4551             // not defined on Haiku, but [get|set]priority is, so they are
4552             // useful
4553             "PRIO_MIN" | "PRIO_MAX" => true,
4554             //
4555             _ => false,
4556         }
4557     });
4558 
4559     cfg.skip_field(move |struct_, field| {
4560         match (struct_, field) {
4561             // FIXME: the stat struct actually has timespec members, whereas
4562             //        the current representation has these unpacked.
4563             ("stat", "st_atime") => true,
4564             ("stat", "st_atime_nsec") => true,
4565             ("stat", "st_mtime") => true,
4566             ("stat", "st_mtime_nsec") => true,
4567             ("stat", "st_ctime") => true,
4568             ("stat", "st_ctime_nsec") => true,
4569             ("stat", "st_crtime") => true,
4570             ("stat", "st_crtime_nsec") => true,
4571 
4572             // these are actually unions, but we cannot represent it well
4573             ("siginfo_t", "sigval") => true,
4574             ("sem_t", "named_sem_id") => true,
4575             ("sigaction", "sa_sigaction") => true,
4576             ("sigevent", "sigev_value") => true,
4577             ("fpu_state", "_fpreg") => true,
4578             // these fields have a simplified data definition in libc
4579             ("fpu_state", "_xmm") => true,
4580             ("savefpu", "_fp_ymm") => true,
4581 
4582             // skip these enum-type fields
4583             ("thread_info", "state") => true,
4584             ("image_info", "image_type") => true,
4585             _ => false,
4586         }
4587     });
4588 
4589     cfg.skip_roundtrip(move |s| match s {
4590         // FIXME: for some reason the roundtrip check fails for cpu_info
4591         "cpu_info" => true,
4592         _ => false,
4593     });
4594 
4595     cfg.type_name(move |ty, is_struct, is_union| {
4596         match ty {
4597             // Just pass all these through, no need for a "struct" prefix
4598             "area_info" | "port_info" | "port_message_info" | "team_info" | "sem_info"
4599             | "team_usage_info" | "thread_info" | "cpu_info" | "system_info"
4600             | "object_wait_info" | "image_info" | "attr_info" | "index_info" | "fs_info"
4601             | "FILE" | "DIR" | "Dl_info" => ty.to_string(),
4602 
4603             // enums don't need a prefix
4604             "directory_which" | "path_base_directory" => ty.to_string(),
4605 
4606             // is actually a union
4607             "sigval" => format!("union sigval"),
4608             t if is_union => format!("union {}", t),
4609             t if t.ends_with("_t") => t.to_string(),
4610             t if is_struct => format!("struct {}", t),
4611             t => t.to_string(),
4612         }
4613     });
4614 
4615     cfg.field_name(move |struct_, field| {
4616         match field {
4617             // Field is named `type` in C but that is a Rust keyword,
4618             // so these fields are translated to `type_` in the bindings.
4619             "type_" if struct_ == "object_wait_info" => "type".to_string(),
4620             "type_" if struct_ == "sem_t" => "type".to_string(),
4621             "type_" if struct_ == "attr_info" => "type".to_string(),
4622             "type_" if struct_ == "index_info" => "type".to_string(),
4623             "image_type" if struct_ == "image_info" => "type".to_string(),
4624             s => s.to_string(),
4625         }
4626     });
4627     cfg.generate("../src/lib.rs", "main.rs");
4628 }
4629