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