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