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