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