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