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