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