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