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