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