xref: /rust-libc-0.2.174/libc-test/build.rs (revision 0ac10285)
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             // FIXME: Requires recent kernel headers (5.8):
2661             "STATX_MNT_ID" => true,
2662 
2663             _ => false,
2664         }
2665     });
2666 
2667     cfg.skip_fn(move |name| {
2668         // skip those that are manually verified
2669         match name {
2670             // FIXME: https://github.com/rust-lang/libc/issues/1272
2671             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2672 
2673             // There are two versions of the sterror_r function, see
2674             //
2675             // https://linux.die.net/man/3/strerror_r
2676             //
2677             // An XSI-compliant version provided if:
2678             //
2679             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
2680             //  && ! _GNU_SOURCE
2681             //
2682             // and a GNU specific version provided if _GNU_SOURCE is defined.
2683             //
2684             // libc provides bindings for the XSI-compliant version, which is
2685             // preferred for portable applications.
2686             //
2687             // We skip the test here since here _GNU_SOURCE is defined, and
2688             // test the XSI version below.
2689             "strerror_r" => true,
2690 
2691             // FIXME: Our API is unsound. The Rust API allows aliasing
2692             // pointers, but the C API requires pointers not to alias.
2693             // We should probably be at least using `&`/`&mut` here, see:
2694             // https://github.com/gnzlbg/ctest/issues/68
2695             "lio_listio" if musl => true,
2696 
2697             // FIXME: the glibc version used by the Sparc64 build jobs
2698             // which use Debian 10.0 is too old.
2699             "statx" if sparc64 => true,
2700 
2701             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
2702             "sysctl" if gnu => true,
2703 
2704             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
2705             "gettimeofday" if gnu => true,
2706 
2707             // These are all implemented as static inline functions in uclibc, so
2708             // they cannot be linked against.
2709             // If implementations are required, they might need to be implemented
2710             // in this crate.
2711             "posix_spawnattr_init" if uclibc => true,
2712             "posix_spawnattr_destroy" if uclibc => true,
2713             "posix_spawnattr_getsigdefault" if uclibc => true,
2714             "posix_spawnattr_setsigdefault" if uclibc => true,
2715             "posix_spawnattr_getsigmask" if uclibc => true,
2716             "posix_spawnattr_setsigmask" if uclibc => true,
2717             "posix_spawnattr_getflags" if uclibc => true,
2718             "posix_spawnattr_setflags" if uclibc => true,
2719             "posix_spawnattr_getpgroup" if uclibc => true,
2720             "posix_spawnattr_setpgroup" if uclibc => true,
2721             "posix_spawnattr_getschedpolicy" if uclibc => true,
2722             "posix_spawnattr_setschedpolicy" if uclibc => true,
2723             "posix_spawnattr_getschedparam" if uclibc => true,
2724             "posix_spawnattr_setschedparam" if uclibc => true,
2725             "posix_spawn_file_actions_init" if uclibc => true,
2726             "posix_spawn_file_actions_destroy" if uclibc => true,
2727 
2728             // uclibc defines the flags type as a uint, but dependent crates
2729             // assume it's a int instead.
2730             "getnameinfo" if uclibc => true,
2731 
2732             _ => false,
2733         }
2734     });
2735 
2736     cfg.skip_field_type(move |struct_, field| {
2737         // This is a weird union, don't check the type.
2738         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2739         // sighandler_t type is super weird
2740         (struct_ == "sigaction" && field == "sa_sigaction") ||
2741         // __timeval type is a patch which doesn't exist in glibc
2742         (struct_ == "utmpx" && field == "ut_tv") ||
2743         // sigval is actually a union, but we pretend it's a struct
2744         (struct_ == "sigevent" && field == "sigev_value") ||
2745         // this one is an anonymous union
2746         (struct_ == "ff_effect" && field == "u") ||
2747         // `__exit_status` type is a patch which is absent in musl
2748         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
2749         // `can_addr` is an anonymous union
2750         (struct_ == "sockaddr_can" && field == "can_addr")
2751     });
2752 
2753     cfg.volatile_item(|i| {
2754         use ctest::VolatileItemKind::*;
2755         match i {
2756             // aio_buf is a volatile void** but since we cannot express that in
2757             // Rust types, we have to explicitly tell the checker about it here:
2758             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
2759                 true
2760             }
2761             _ => false,
2762         }
2763     });
2764 
2765     cfg.skip_field(move |struct_, field| {
2766         // this is actually a union on linux, so we can't represent it well and
2767         // just insert some padding.
2768         (struct_ == "siginfo_t" && field == "_pad") ||
2769         // musl names this __dummy1 but it's still there
2770         (musl && struct_ == "glob_t" && field == "gl_flags") ||
2771         // musl seems to define this as an *anonymous* bitfield
2772         (musl && struct_ == "statvfs" && field == "__f_unused") ||
2773         // sigev_notify_thread_id is actually part of a sigev_un union
2774         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2775         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
2776         // has them yet.
2777         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2778                                            field == "_pad2" ||
2779                                            field == "ssi_syscall" ||
2780                                            field == "ssi_call_addr" ||
2781                                            field == "ssi_arch")) ||
2782         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2783         // while other fields become reserved.
2784         (struct_ == "sched_param" && [
2785             "sched_ss_low_priority",
2786             "sched_ss_repl_period",
2787             "sched_ss_init_budget",
2788             "sched_ss_max_repl",
2789         ].contains(&field) && musl) ||
2790         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
2791         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
2792         // glibc uses unnamed fields here and Rust doesn't support that yet
2793         (struct_ == "timex" && field.starts_with("__unused")) ||
2794         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
2795         (struct_ == "ipc_perm" && field == "mode"
2796             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
2797         )
2798     });
2799 
2800     cfg.skip_roundtrip(move |s| match s {
2801         // FIXME:
2802         "utsname" if mips32 || mips64 => true,
2803         // FIXME:
2804         "mcontext_t" if s390x => true,
2805         // FIXME: This is actually a union.
2806         "fpreg_t" if s390x => true,
2807 
2808         "sockaddr_un" | "sembuf" | "ff_constant_effect"
2809             if mips32 && (gnu || musl) =>
2810         {
2811             true
2812         }
2813         "ipv6_mreq"
2814         | "ip_mreq_source"
2815         | "sockaddr_in6"
2816         | "sockaddr_ll"
2817         | "in_pktinfo"
2818         | "arpreq"
2819         | "arpreq_old"
2820         | "sockaddr_un"
2821         | "ff_constant_effect"
2822         | "ff_ramp_effect"
2823         | "ff_condition_effect"
2824         | "Elf32_Ehdr"
2825         | "Elf32_Chdr"
2826         | "ucred"
2827         | "in6_pktinfo"
2828         | "sockaddr_nl"
2829         | "termios"
2830         | "nlmsgerr"
2831             if (mips64 || sparc64) && gnu =>
2832         {
2833             true
2834         }
2835 
2836         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
2837         "max_align_t" if i686 || mips64 || ppc64 => true,
2838 
2839         _ => false,
2840     });
2841 
2842     cfg.generate("../src/lib.rs", "main.rs");
2843 
2844     test_linux_like_apis(target);
2845 }
2846 
2847 // This function tests APIs that are incompatible to test when other APIs
2848 // are included (e.g. because including both sets of headers clashes)
2849 fn test_linux_like_apis(target: &str) {
2850     let musl = target.contains("musl");
2851     let linux = target.contains("linux");
2852     let emscripten = target.contains("emscripten");
2853     let android = target.contains("android");
2854     assert!(linux || android || emscripten);
2855 
2856     if linux || android || emscripten {
2857         // test strerror_r from the `string.h` header
2858         let mut cfg = ctest_cfg();
2859         cfg.skip_type(|_| true).skip_static(|_| true);
2860 
2861         headers! { cfg: "string.h" }
2862         cfg.skip_fn(|f| match f {
2863             "strerror_r" => false,
2864             _ => true,
2865         })
2866         .skip_const(|_| true)
2867         .skip_struct(|_| true);
2868         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
2869     }
2870 
2871     if linux || android || emscripten {
2872         // test fcntl - see:
2873         // http://man7.org/linux/man-pages/man2/fcntl.2.html
2874         let mut cfg = ctest_cfg();
2875 
2876         if musl {
2877             cfg.header("fcntl.h");
2878         } else {
2879             cfg.header("linux/fcntl.h");
2880         }
2881 
2882         cfg.skip_type(|_| true)
2883             .skip_static(|_| true)
2884             .skip_struct(|_| true)
2885             .skip_fn(|_| true)
2886             .skip_const(move |name| match name {
2887                 // test fcntl constants:
2888                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS"
2889                 | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2890                 | "F_SEAL_WRITE" => false,
2891                 _ => true,
2892             })
2893             .type_name(move |ty, is_struct, is_union| match ty {
2894                 t if is_struct => format!("struct {}", t),
2895                 t if is_union => format!("union {}", t),
2896                 t => t.to_string(),
2897             });
2898 
2899         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
2900     }
2901 
2902     if linux || android {
2903         // test termios
2904         let mut cfg = ctest_cfg();
2905         cfg.header("asm/termbits.h");
2906         cfg.skip_type(|_| true)
2907             .skip_static(|_| true)
2908             .skip_fn(|_| true)
2909             .skip_const(|c| c != "BOTHER")
2910             .skip_struct(|s| s != "termios2")
2911             .type_name(move |ty, is_struct, is_union| match ty {
2912                 t if is_struct => format!("struct {}", t),
2913                 t if is_union => format!("union {}", t),
2914                 t => t.to_string(),
2915             });
2916         cfg.generate("../src/lib.rs", "linux_termios.rs");
2917     }
2918 
2919     if linux || android {
2920         // test IPV6_ constants:
2921         let mut cfg = ctest_cfg();
2922         headers! {
2923             cfg:
2924             "linux/in6.h"
2925         }
2926         cfg.skip_type(|_| true)
2927             .skip_static(|_| true)
2928             .skip_fn(|_| true)
2929             .skip_const(|_| true)
2930             .skip_struct(|_| true)
2931             .skip_const(move |name| match name {
2932                 "IPV6_FLOWINFO"
2933                 | "IPV6_FLOWLABEL_MGR"
2934                 | "IPV6_FLOWINFO_SEND"
2935                 | "IPV6_FLOWINFO_FLOWLABEL"
2936                 | "IPV6_FLOWINFO_PRIORITY" => false,
2937                 _ => true,
2938             })
2939             .type_name(move |ty, is_struct, is_union| match ty {
2940                 t if is_struct => format!("struct {}", t),
2941                 t if is_union => format!("union {}", t),
2942                 t => t.to_string(),
2943             });
2944         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
2945     }
2946 
2947     if linux || android {
2948         // Test Elf64_Phdr and Elf32_Phdr
2949         // These types have a field called `p_type`, but including
2950         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
2951         // making the tests for these fails when both are included.
2952         let mut cfg = ctest_cfg();
2953         cfg.header("elf.h");
2954         cfg.skip_fn(|_| true)
2955             .skip_static(|_| true)
2956             .skip_fn(|_| true)
2957             .skip_const(|_| true)
2958             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
2959             .skip_struct(move |ty| match ty {
2960                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2961                 _ => true,
2962             })
2963             .skip_type(move |ty| match ty {
2964                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2965                 _ => true,
2966             });
2967         cfg.generate("../src/lib.rs", "linux_elf.rs");
2968     }
2969 }
2970 
2971 fn which_freebsd() -> Option<i32> {
2972     let output = std::process::Command::new("freebsd-version")
2973         .output()
2974         .ok()?;
2975     if !output.status.success() {
2976         return None;
2977     }
2978 
2979     let stdout = String::from_utf8(output.stdout).ok()?;
2980 
2981     match &stdout {
2982         s if s.starts_with("10") => Some(10),
2983         s if s.starts_with("11") => Some(11),
2984         s if s.starts_with("12") => Some(12),
2985         s if s.starts_with("13") => Some(13),
2986         _ => None,
2987     }
2988 }
2989 
2990 fn test_haiku(target: &str) {
2991     assert!(target.contains("haiku"));
2992 
2993     let mut cfg = ctest_cfg();
2994     cfg.flag("-Wno-deprecated-declarations");
2995     cfg.define("__USE_GNU", Some("1"));
2996 
2997     // POSIX API
2998     headers! { cfg:
2999                "alloca.h",
3000                "arpa/inet.h",
3001                "arpa/nameser.h",
3002                "arpa/nameser_compat.h",
3003                "assert.h",
3004                "bsd_mem.h",
3005                "complex.h",
3006                "ctype.h",
3007                "dirent.h",
3008                "div_t.h",
3009                "dlfcn.h",
3010                "endian.h",
3011                "errno.h",
3012                "fcntl.h",
3013                "fenv.h",
3014                "fnmatch.h",
3015                "fts.h",
3016                "ftw.h",
3017                "getopt.h",
3018                "glob.h",
3019                "grp.h",
3020                "inttypes.h",
3021                "iovec.h",
3022                "langinfo.h",
3023                "libgen.h",
3024                "libio.h",
3025                "limits.h",
3026                "locale.h",
3027                "malloc.h",
3028                "malloc_debug.h",
3029                "math.h",
3030                "memory.h",
3031                "monetary.h",
3032                "net/if.h",
3033                "net/if_dl.h",
3034                "net/if_media.h",
3035                "net/if_tun.h",
3036                "net/if_types.h",
3037                "net/route.h",
3038                "netdb.h",
3039                "netinet/icmp6.h",
3040                "netinet/in.h",
3041                "netinet/ip.h",
3042                "netinet/ip6.h",
3043                "netinet/ip_icmp.h",
3044                "netinet/ip_var.h",
3045                "netinet/tcp.h",
3046                "netinet/udp.h",
3047                "netinet6/in6.h",
3048                "nl_types.h",
3049                "null.h",
3050                "poll.h",
3051                "pthread.h",
3052                "pwd.h",
3053                "regex.h",
3054                "resolv.h",
3055                "sched.h",
3056                "search.h",
3057                "semaphore.h",
3058                "setjmp.h",
3059                "shadow.h",
3060                "signal.h",
3061                "size_t.h",
3062                "spawn.h",
3063                "stdint.h",
3064                "stdio.h",
3065                "stdlib.h",
3066                "string.h",
3067                "strings.h",
3068                "sys/cdefs.h",
3069                "sys/file.h",
3070                "sys/ioctl.h",
3071                "sys/ipc.h",
3072                "sys/mman.h",
3073                "sys/msg.h",
3074                "sys/param.h",
3075                "sys/poll.h",
3076                "sys/resource.h",
3077                "sys/select.h",
3078                "sys/sem.h",
3079                "sys/socket.h",
3080                "sys/sockio.h",
3081                "sys/stat.h",
3082                "sys/statvfs.h",
3083                "sys/time.h",
3084                "sys/timeb.h",
3085                "sys/times.h",
3086                "sys/types.h",
3087                "sys/uio.h",
3088                "sys/un.h",
3089                "sys/utsname.h",
3090                "sys/wait.h",
3091                "syslog.h",
3092                "tar.h",
3093                "termios.h",
3094                "time.h",
3095                "uchar.h",
3096                "unistd.h",
3097                "utime.h",
3098                "wchar.h",
3099                "wchar_t.h",
3100                "wctype.h"
3101     }
3102 
3103     // BSD Extensions
3104     headers! { cfg:
3105                "pty.h",
3106     }
3107 
3108     // Native API
3109     headers! { cfg:
3110                "kernel/OS.h",
3111                "kernel/fs_attr.h",
3112                "kernel/fs_index.h",
3113                "kernel/fs_info.h",
3114                "kernel/fs_query.h",
3115                "kernel/fs_volume.h",
3116                "kernel/image.h",
3117                "storage/StorageDefs.h",
3118                "support/Errors.h",
3119                "support/SupportDefs.h",
3120                "support/TypeConstants.h"
3121     }
3122 
3123     cfg.skip_struct(move |ty| {
3124         match ty {
3125             // FIXME: actually a union
3126             "sigval" => true,
3127             // FIXME: locale_t does not exist on Haiku
3128             "locale_t" => true,
3129             // FIXME: rusage has a different layout on Haiku
3130             "rusage" => true,
3131             // FIXME?: complains that rust aligns on 4 byte boundary, but
3132             //         Haiku does not align it at all.
3133             "in6_addr" => true,
3134             // The d_name attribute is an array of 1 on Haiku, with the
3135             // intention that the developer allocates a larger or smaller
3136             // piece of memory depending on the expected/actual size of the name.
3137             // Other platforms have sensible defaults. In Rust, the d_name field
3138             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
3139             // newly allocated dirent objects. This breaks the automated tests.
3140             "dirent" => true,
3141 
3142             _ => false,
3143         }
3144     });
3145 
3146     cfg.skip_type(move |ty| {
3147         match ty {
3148             // FIXME: locale_t does not exist on Haiku
3149             "locale_t" => true,
3150             // These cause errors, to be reviewed in the future
3151             "sighandler_t" => true,
3152             "pthread_t" => true,
3153             "pthread_condattr_t" => true,
3154             "pthread_mutexattr_t" => true,
3155             "pthread_rwlockattr_t" => true,
3156             _ => false,
3157         }
3158     });
3159 
3160     cfg.skip_fn(move |name| {
3161         // skip those that are manually verified
3162         match name {
3163             // FIXME: https://github.com/rust-lang/libc/issues/1272
3164             "execv" | "execve" | "execvp" | "execvpe" => true,
3165             // FIXME: does not exist on haiku
3166             "open_wmemstream" => true,
3167             "mlockall" | "munlockall" => true,
3168             "tcgetsid" => true,
3169             "cfsetspeed" => true,
3170             // ignore for now, will be part of Haiku R1 beta 3
3171             "mlock" | "munlock" => true,
3172             // returns const char * on Haiku
3173             "strsignal" => true,
3174 
3175             _ => false,
3176         }
3177     });
3178 
3179     cfg.skip_const(move |name| {
3180         match name {
3181             // FIXME: these constants do not exist on Haiku
3182             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK"
3183             | "DT_REG" | "DT_LNK" | "DT_SOCK" => true,
3184             "USRQUOTA" | "GRPQUOTA" => true,
3185             "SIGIOT" => true,
3186             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM"
3187             | "ATF_PUBL" | "ATF_USETRAILERS" => true,
3188             // Haiku does not have MAP_FILE, but rustc requires it
3189             "MAP_FILE" => true,
3190             // The following does not exist on Haiku but is required by
3191             // several crates
3192             "FIOCLEX" => true,
3193             // just skip this one, it is not defined on Haiku beta 2 but
3194             // since it is meant as a mask and not a parameter it can exist
3195             // here
3196             "LOG_PRIMASK" => true,
3197             // not defined on Haiku, but [get|set]priority is, so they are
3198             // useful
3199             "PRIO_MIN" | "PRIO_MAX" => true,
3200             //
3201             _ => false,
3202         }
3203     });
3204 
3205     cfg.skip_field(move |struct_, field| {
3206         match (struct_, field) {
3207             // FIXME: the stat struct actually has timespec members, whereas
3208             //        the current representation has these unpacked.
3209             ("stat", "st_atime") => true,
3210             ("stat", "st_atime_nsec") => true,
3211             ("stat", "st_mtime") => true,
3212             ("stat", "st_mtime_nsec") => true,
3213             ("stat", "st_ctime") => true,
3214             ("stat", "st_ctime_nsec") => true,
3215             ("stat", "st_crtime") => true,
3216             ("stat", "st_crtime_nsec") => true,
3217 
3218             // these are actually unions, but we cannot represent it well
3219             ("siginfo_t", "sigval") => true,
3220             ("sem_t", "named_sem_id") => true,
3221             ("sigaction", "sa_sigaction") => true,
3222             ("sigevent", "sigev_value") => true,
3223 
3224             // skip these enum-type fields
3225             ("thread_info", "state") => true,
3226             ("image_info", "image_type") => true,
3227             _ => false,
3228         }
3229     });
3230 
3231     cfg.skip_roundtrip(move |s| match s {
3232         // FIXME: for some reason the roundtrip check fails for cpu_info
3233         "cpu_info" => true,
3234         _ => false,
3235     });
3236 
3237     cfg.type_name(move |ty, is_struct, is_union| {
3238         match ty {
3239             // Just pass all these through, no need for a "struct" prefix
3240             "area_info" | "port_info" | "port_message_info" | "team_info"
3241             | "sem_info" | "team_usage_info" | "thread_info" | "cpu_info"
3242             | "system_info" | "object_wait_info" | "image_info"
3243             | "attr_info" | "index_info" | "fs_info" | "FILE" | "DIR"
3244             | "Dl_info" => ty.to_string(),
3245 
3246             // is actually a union
3247             "sigval" => format!("union sigval"),
3248             t if is_union => format!("union {}", t),
3249             t if t.ends_with("_t") => t.to_string(),
3250             t if is_struct => format!("struct {}", t),
3251             t => t.to_string(),
3252         }
3253     });
3254 
3255     cfg.field_name(move |struct_, field| {
3256         match field {
3257             // Field is named `type` in C but that is a Rust keyword,
3258             // so these fields are translated to `type_` in the bindings.
3259             "type_" if struct_ == "object_wait_info" => "type".to_string(),
3260             "type_" if struct_ == "sem_t" => "type".to_string(),
3261             "type_" if struct_ == "attr_info" => "type".to_string(),
3262             "type_" if struct_ == "index_info" => "type".to_string(),
3263             "image_type" if struct_ == "image_info" => "type".to_string(),
3264             s => s.to_string(),
3265         }
3266     });
3267     cfg.generate("../src/lib.rs", "main.rs");
3268 }
3269