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