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