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