xref: /rust-libc-0.2.174/libc-test/build.rs (revision fa543d17)
1 #![deny(warnings)]
2 
3 extern crate cc;
4 extern crate ctest;
5 
6 use std::env;
7 
8 fn do_cc() {
9     let target = env::var("TARGET").unwrap();
10     if cfg!(unix) && !target.contains("wasi") {
11         cc::Build::new().file("src/cmsg.c").compile("cmsg");
12     }
13     if target.contains("android") || target.contains("linux") {
14         cc::Build::new().file("src/errqueue.c").compile("errqueue");
15     }
16 }
17 
18 fn do_ctest() {
19     match &env::var("TARGET").unwrap() {
20         t if t.contains("android") => return test_android(t),
21         t if t.contains("apple") => return test_apple(t),
22         t if t.contains("cloudabi") => return test_cloudabi(t),
23         t if t.contains("dragonfly") => return test_dragonflybsd(t),
24         t if t.contains("emscripten") => return test_emscripten(t),
25         t if t.contains("freebsd") => return test_freebsd(t),
26         t if t.contains("linux") => return test_linux(t),
27         t if t.contains("netbsd") => return test_netbsd(t),
28         t if t.contains("openbsd") => return test_openbsd(t),
29         t if t.contains("redox") => return test_redox(t),
30         t if t.contains("solaris") => return test_solaris(t),
31         t if t.contains("wasi") => return test_wasi(t),
32         t if t.contains("windows") => return test_windows(t),
33         t if t.contains("vxworks") => return test_vxworks(t),
34         t => panic!("unknown target {}", t),
35     }
36 }
37 
38 fn ctest_cfg() -> ctest::TestGenerator {
39     let mut cfg = ctest::TestGenerator::new();
40     let libc_cfgs = [
41         "libc_priv_mod_use",
42         "libc_union",
43         "libc_const_size_of",
44         "libc_align",
45         "libc_core_cvoid",
46         "libc_packedN",
47         "libc_thread_local",
48     ];
49     for f in &libc_cfgs {
50         cfg.cfg(f, None);
51     }
52     cfg
53 }
54 
55 fn main() {
56     do_cc();
57     do_ctest();
58 }
59 
60 macro_rules! headers {
61     ($cfg:ident: [$m:expr]: $header:literal) => {
62         if $m {
63             $cfg.header($header);
64         }
65     };
66     ($cfg:ident: $header:literal) => {
67         $cfg.header($header);
68     };
69     ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => {
70         $(headers!($cfg: $([$c]:)* $header);)*
71     };
72     ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => {
73         headers!($($cfg: $([$c]:)* $header,)*);
74     };
75     ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => {
76         headers!($($cfg: $([$c]:)* $header,)*);
77     };
78 }
79 
80 fn test_apple(target: &str) {
81     assert!(target.contains("apple"));
82     let x86_64 = target.contains("x86_64");
83     let i686 = target.contains("i686");
84 
85     let mut cfg = ctest_cfg();
86     cfg.flag("-Wno-deprecated-declarations");
87     cfg.define("__APPLE_USE_RFC_3542", None);
88 
89     headers! { cfg:
90         "aio.h",
91         "ctype.h",
92         "dirent.h",
93         "dlfcn.h",
94         "errno.h",
95         "execinfo.h",
96         "fcntl.h",
97         "glob.h",
98         "grp.h",
99         "ifaddrs.h",
100         "langinfo.h",
101         "limits.h",
102         "locale.h",
103         "mach-o/dyld.h",
104         "mach/mach_time.h",
105         "malloc/malloc.h",
106         "net/bpf.h",
107         "net/if.h",
108         "net/if_arp.h",
109         "net/if_dl.h",
110         "net/if_utun.h",
111         "net/route.h",
112         "net/route.h",
113         "netdb.h",
114         "netinet/if_ether.h",
115         "netinet/in.h",
116         "netinet/in.h",
117         "netinet/ip.h",
118         "netinet/tcp.h",
119         "netinet/udp.h",
120         "poll.h",
121         "pthread.h",
122         "pwd.h",
123         "resolv.h",
124         "sched.h",
125         "semaphore.h",
126         "signal.h",
127         "spawn.h",
128         "stddef.h",
129         "stdint.h",
130         "stdio.h",
131         "stdlib.h",
132         "string.h",
133         "sys/event.h",
134         "sys/file.h",
135         "sys/ioctl.h",
136         "sys/ipc.h",
137         "sys/kern_control.h",
138         "sys/mman.h",
139         "sys/mount.h",
140         "sys/proc_info.h",
141         "sys/ptrace.h",
142         "sys/quota.h",
143         "sys/resource.h",
144         "sys/sem.h",
145         "sys/shm.h",
146         "sys/socket.h",
147         "sys/stat.h",
148         "sys/statvfs.h",
149         "sys/sys_domain.h",
150         "sys/sysctl.h",
151         "sys/time.h",
152         "sys/times.h",
153         "sys/types.h",
154         "sys/uio.h",
155         "sys/un.h",
156         "sys/utsname.h",
157         "sys/wait.h",
158         "sys/xattr.h",
159         "syslog.h",
160         "termios.h",
161         "time.h",
162         "unistd.h",
163         "util.h",
164         "utime.h",
165         "utmpx.h",
166         "wchar.h",
167         "xlocale.h",
168         [x86_64]: "crt_externs.h",
169     }
170 
171     cfg.skip_struct(move |ty| {
172         match ty {
173             // FIXME: actually a union
174             "sigval" => true,
175 
176             _ => false,
177         }
178     });
179 
180     cfg.skip_const(move |name| {
181         match name {
182             // These OSX constants are removed in Sierra.
183             // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html
184             "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true,
185             _ => false,
186         }
187     });
188 
189     cfg.skip_fn(move |name| {
190         // skip those that are manually verified
191         match name {
192             // FIXME: https://github.com/rust-lang/libc/issues/1272
193             "execv" | "execve" | "execvp" => true,
194 
195             // close calls the close_nocancel system call
196             "close" => true,
197 
198             _ => false,
199         }
200     });
201 
202     cfg.skip_field_type(move |struct_, field| {
203         match (struct_, field) {
204             // FIXME: actually a union
205             ("sigevent", "sigev_value") => true,
206             _ => false,
207         }
208     });
209 
210     cfg.volatile_item(|i| {
211         use ctest::VolatileItemKind::*;
212         match i {
213             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
214                 true
215             }
216             _ => false,
217         }
218     });
219 
220     cfg.type_name(move |ty, is_struct, is_union| {
221         match ty {
222             // Just pass all these through, no need for a "struct" prefix
223             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
224 
225             // OSX calls this something else
226             "sighandler_t" => "sig_t".to_string(),
227 
228             t if is_union => format!("union {}", t),
229             t if t.ends_with("_t") => t.to_string(),
230             t if is_struct => format!("struct {}", t),
231             t => t.to_string(),
232         }
233     });
234 
235     cfg.field_name(move |struct_, field| {
236         match field {
237             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
238                 s.replace("e_nsec", "espec.tv_nsec")
239             }
240             // FIXME: sigaction actually contains a union with two variants:
241             // a sa_sigaction with type: (*)(int, struct __siginfo *, void *)
242             // a sa_handler with type sig_t
243             "sa_sigaction" if struct_ == "sigaction" => {
244                 "sa_handler".to_string()
245             }
246             s => s.to_string(),
247         }
248     });
249 
250     cfg.skip_roundtrip(move |s| match s {
251         // FIXME: this type has the wrong ABI
252         "max_align_t" if i686 => true,
253         _ => false,
254     });
255     cfg.generate("../src/lib.rs", "main.rs");
256 }
257 
258 fn test_openbsd(target: &str) {
259     assert!(target.contains("openbsd"));
260 
261     let mut cfg = ctest_cfg();
262     cfg.flag("-Wno-deprecated-declarations");
263 
264     headers! { cfg:
265         "errno.h",
266         "fcntl.h",
267         "limits.h",
268         "locale.h",
269         "stddef.h",
270         "stdint.h",
271         "stdio.h",
272         "stdlib.h",
273         "sys/stat.h",
274         "sys/types.h",
275         "time.h",
276         "wchar.h",
277         "ctype.h",
278         "dirent.h",
279         "sys/socket.h",
280         "net/if.h",
281         "net/route.h",
282         "net/if_arp.h",
283         "netdb.h",
284         "netinet/in.h",
285         "netinet/ip.h",
286         "netinet/tcp.h",
287         "netinet/udp.h",
288         "net/bpf.h",
289         "resolv.h",
290         "pthread.h",
291         "dlfcn.h",
292         "signal.h",
293         "string.h",
294         "sys/file.h",
295         "sys/ioctl.h",
296         "sys/mman.h",
297         "sys/resource.h",
298         "sys/socket.h",
299         "sys/time.h",
300         "sys/un.h",
301         "sys/wait.h",
302         "unistd.h",
303         "utime.h",
304         "pwd.h",
305         "grp.h",
306         "sys/utsname.h",
307         "sys/ptrace.h",
308         "sys/mount.h",
309         "sys/uio.h",
310         "sched.h",
311         "termios.h",
312         "poll.h",
313         "syslog.h",
314         "semaphore.h",
315         "sys/statvfs.h",
316         "sys/times.h",
317         "glob.h",
318         "ifaddrs.h",
319         "langinfo.h",
320         "sys/sysctl.h",
321         "utmp.h",
322         "sys/event.h",
323         "net/if_dl.h",
324         "util.h",
325         "ufs/ufs/quota.h",
326         "pthread_np.h",
327         "sys/syscall.h",
328         "sys/shm.h",
329     }
330 
331     cfg.skip_struct(move |ty| {
332         match ty {
333             // FIXME: actually a union
334             "sigval" => true,
335 
336             _ => false,
337         }
338     });
339 
340     cfg.skip_const(move |name| {
341         match name {
342             // Removed in OpenBSD 6.0
343             "KERN_USERMOUNT" | "KERN_ARND" => true,
344             _ => false,
345         }
346     });
347 
348     cfg.skip_fn(move |name| {
349         match name {
350             // FIXME: https://github.com/rust-lang/libc/issues/1272
351             "execv" | "execve" | "execvp" | "execvpe" => true,
352 
353             // Removed in OpenBSD 6.5
354             // https://marc.info/?l=openbsd-cvs&m=154723400730318
355             "mincore" => true,
356 
357             _ => false,
358         }
359     });
360 
361     cfg.type_name(move |ty, is_struct, is_union| {
362         match ty {
363             // Just pass all these through, no need for a "struct" prefix
364             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
365 
366             // OSX calls this something else
367             "sighandler_t" => "sig_t".to_string(),
368 
369             t if is_union => format!("union {}", t),
370             t if t.ends_with("_t") => t.to_string(),
371             t if is_struct => format!("struct {}", t),
372             t => t.to_string(),
373         }
374     });
375 
376     cfg.field_name(move |struct_, field| match field {
377         "st_birthtime" if struct_.starts_with("stat") => {
378             "__st_birthtime".to_string()
379         }
380         "st_birthtime_nsec" if struct_.starts_with("stat") => {
381             "__st_birthtimensec".to_string()
382         }
383         s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
384             s.replace("e_nsec", ".tv_nsec")
385         }
386         "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
387         s => s.to_string(),
388     });
389 
390     cfg.skip_field_type(move |struct_, field| {
391         // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1
392         struct_ == "siginfo_t" && field == "si_addr"
393     });
394 
395     cfg.generate("../src/lib.rs", "main.rs");
396 }
397 
398 fn test_windows(target: &str) {
399     assert!(target.contains("windows"));
400     let gnu = target.contains("gnu");
401 
402     let mut cfg = ctest_cfg();
403     cfg.define("_WIN32_WINNT", Some("0x8000"));
404 
405     headers! { cfg:
406         "direct.h",
407         "errno.h",
408         "fcntl.h",
409         "io.h",
410         "limits.h",
411         "locale.h",
412         "process.h",
413         "signal.h",
414         "stddef.h",
415         "stdint.h",
416         "stdio.h",
417         "stdlib.h",
418         "sys/stat.h",
419         "sys/types.h",
420         "sys/utime.h",
421         "time.h",
422         "wchar.h",
423         [gnu]: "ws2tcpip.h",
424         [!gnu]: "Winsock2.h",
425     }
426 
427     cfg.type_name(move |ty, is_struct, is_union| {
428         match ty {
429             // Just pass all these through, no need for a "struct" prefix
430             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
431 
432             // FIXME: these don't exist:
433             "time64_t" => "__time64_t".to_string(),
434             "ssize_t" => "SSIZE_T".to_string(),
435 
436             "sighandler_t" if !gnu => "_crt_signal_t".to_string(),
437             "sighandler_t" if gnu => "__p_sig_fn_t".to_string(),
438 
439             t if is_union => format!("union {}", t),
440             t if t.ends_with("_t") => t.to_string(),
441 
442             // Windows uppercase structs don't have `struct` in front:
443             t if is_struct => {
444                 if ty.clone().chars().next().unwrap().is_uppercase() {
445                     t.to_string()
446                 } else if t == "stat" {
447                     "struct __stat64".to_string()
448                 } else if t == "utimbuf" {
449                     "struct __utimbuf64".to_string()
450                 } else {
451                     // put `struct` in front of all structs:
452                     format!("struct {}", t)
453                 }
454             }
455             t => t.to_string(),
456         }
457     });
458 
459     cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string());
460 
461     cfg.skip_type(move |name| match name {
462         "SSIZE_T" if !gnu => true,
463         "ssize_t" if !gnu => true,
464         _ => false,
465     });
466 
467     cfg.skip_const(move |name| {
468         match name {
469             // FIXME: API error:
470             // SIG_ERR type is "void (*)(int)", not "int"
471             "SIG_ERR" => true,
472             _ => false,
473         }
474     });
475 
476     // FIXME: All functions point to the wrong addresses?
477     cfg.skip_fn_ptrcheck(|_| true);
478 
479     cfg.skip_signededness(move |c| {
480         match c {
481             // windows-isms
482             n if n.starts_with("P") => true,
483             n if n.starts_with("H") => true,
484             n if n.starts_with("LP") => true,
485             "sighandler_t" if gnu => true,
486             _ => false,
487         }
488     });
489 
490     cfg.skip_fn(move |name| {
491         match name {
492             // FIXME: https://github.com/rust-lang/libc/issues/1272
493             "execv" | "execve" | "execvp" | "execvpe" => true,
494 
495             _ => false,
496         }
497     });
498 
499     cfg.generate("../src/lib.rs", "main.rs");
500 }
501 
502 fn test_redox(target: &str) {
503     assert!(target.contains("redox"));
504 
505     let mut cfg = ctest_cfg();
506     cfg.flag("-Wno-deprecated-declarations");
507 
508     headers! {
509         cfg:
510         "ctype.h",
511         "dirent.h",
512         "dlfcn.h",
513         "errno.h",
514         "execinfo.h",
515         "fcntl.h",
516         "glob.h",
517         "grp.h",
518         "ifaddrs.h",
519         "langinfo.h",
520         "limits.h",
521         "locale.h",
522         "net/if.h",
523         "net/if_arp.h",
524         "net/route.h",
525         "netdb.h",
526         "netinet/in.h",
527         "netinet/ip.h",
528         "netinet/tcp.h",
529         "netinet/udp.h",
530         "poll.h",
531         "pthread.h",
532         "pwd.h",
533         "resolv.h",
534         "sched.h",
535         "semaphore.h",
536         "string.h",
537         "strings.h",
538         "sys/file.h",
539         "sys/ioctl.h",
540         "sys/mman.h",
541         "sys/mount.h",
542         "sys/ptrace.h",
543         "sys/quota.h",
544         "sys/resource.h",
545         "sys/socket.h",
546         "sys/stat.h",
547         "sys/statvfs.h",
548         "sys/sysctl.h",
549         "sys/time.h",
550         "sys/times.h",
551         "sys/types.h",
552         "sys/uio.h",
553         "sys/un.h",
554         "sys/utsname.h",
555         "sys/wait.h",
556         "syslog.h",
557         "termios.h",
558         "time.h",
559         "unistd.h",
560         "utime.h",
561         "utmpx.h",
562         "wchar.h",
563     }
564 
565     cfg.generate("../src/lib.rs", "main.rs");
566 }
567 
568 fn test_cloudabi(target: &str) {
569     assert!(target.contains("cloudabi"));
570 
571     let mut cfg = ctest_cfg();
572     cfg.flag("-Wno-deprecated-declarations");
573 
574     headers! {
575         cfg:
576         "execinfo.h",
577         "glob.h",
578         "ifaddrs.h",
579         "langinfo.h",
580         "sys/ptrace.h",
581         "sys/quota.h",
582         "sys/sysctl.h",
583         "utmpx.h",
584         "ctype.h",
585         "dirent.h",
586         "dlfcn.h",
587         "errno.h",
588         "fcntl.h",
589         "grp.h",
590         "limits.h",
591         "locale.h",
592         "net/if.h",
593         "net/if_arp.h",
594         "net/route.h",
595         "netdb.h",
596         "netinet/in.h",
597         "netinet/ip.h",
598         "netinet/tcp.h",
599         "netinet/udp.h",
600         "poll.h",
601         "pthread.h",
602         "pwd.h",
603         "resolv.h",
604         "sched.h",
605         "semaphore.h",
606         "signal.h",
607         "stddef.h",
608         "stdint.h",
609         "stdio.h",
610         "stdlib.h",
611         "string.h",
612         "strings.h",
613         "sys/file.h",
614         "sys/ioctl.h",
615         "sys/mman.h",
616         "sys/mount.h",
617         "sys/resource.h",
618         "sys/socket.h",
619         "sys/stat.h",
620         "sys/statvfs.h",
621         "sys/time.h",
622         "sys/times.h",
623         "sys/types.h",
624         "sys/uio.h",
625         "sys/un.h",
626         "sys/utsname.h",
627         "sys/wait.h",
628         "syslog.h",
629         "termios.h",
630         "time.h",
631         "unistd.h",
632         "utime.h",
633         "wchar.h",
634     }
635 
636     cfg.generate("../src/lib.rs", "main.rs");
637 }
638 
639 fn test_solaris(target: &str) {
640     assert!(target.contains("solaris"));
641 
642     let mut cfg = ctest_cfg();
643     cfg.flag("-Wno-deprecated-declarations");
644 
645     cfg.define("_XOPEN_SOURCE", Some("700"));
646     cfg.define("__EXTENSIONS__", None);
647     cfg.define("_LCONV_C99", None);
648 
649     headers! {
650         cfg:
651         "ctype.h",
652         "dirent.h",
653         "dlfcn.h",
654         "errno.h",
655         "execinfo.h",
656         "fcntl.h",
657         "glob.h",
658         "grp.h",
659         "ifaddrs.h",
660         "langinfo.h",
661         "limits.h",
662         "locale.h",
663         "net/if.h",
664         "net/if_arp.h",
665         "net/route.h",
666         "netdb.h",
667         "netinet/in.h",
668         "netinet/ip.h",
669         "netinet/tcp.h",
670         "netinet/udp.h",
671         "poll.h",
672         "port.h",
673         "pthread.h",
674         "pwd.h",
675         "resolv.h",
676         "sched.h",
677         "semaphore.h",
678         "signal.h",
679         "stddef.h",
680         "stdint.h",
681         "stdio.h",
682         "stdlib.h",
683         "string.h",
684         "sys/epoll.h",
685         "sys/file.h",
686         "sys/filio.h",
687         "sys/ioctl.h",
688         "sys/loadavg.h",
689         "sys/mman.h",
690         "sys/mount.h",
691         "sys/resource.h",
692         "sys/socket.h",
693         "sys/stat.h",
694         "sys/statvfs.h",
695         "sys/time.h",
696         "sys/times.h",
697         "sys/types.h",
698         "sys/uio.h",
699         "sys/un.h",
700         "sys/utsname.h",
701         "sys/wait.h",
702         "syslog.h",
703         "termios.h",
704         "time.h",
705         "ucontext.h",
706         "unistd.h",
707         "utime.h",
708         "utmpx.h",
709         "wchar.h",
710     }
711 
712     cfg.skip_const(move |name| match name {
713         "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
714         | "DT_SOCK" | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => {
715             true
716         }
717 
718         _ => false,
719     });
720 
721     cfg.skip_fn(move |name| {
722         // skip those that are manually verified
723         match name {
724             // const-ness only added recently
725             "dladdr" => true,
726 
727             // Definition of those functions as changed since unified headers
728             // from NDK r14b These changes imply some API breaking changes but
729             // are still ABI compatible. We can wait for the next major release
730             // to be compliant with the new API.
731             //
732             // FIXME: unskip these for next major release
733             "setpriority" | "personality" => true,
734 
735             // signal is defined with sighandler_t, so ignore
736             "signal" => true,
737 
738             "cfmakeraw" | "cfsetspeed" => true,
739 
740             // FIXME: mincore is defined with caddr_t on Solaris.
741             "mincore" => true,
742 
743             _ => false,
744         }
745     });
746 
747     cfg.generate("../src/lib.rs", "main.rs");
748 }
749 
750 fn test_netbsd(target: &str) {
751     assert!(target.contains("netbsd"));
752     let rumprun = target.contains("rumprun");
753     let mut cfg = ctest_cfg();
754 
755     cfg.flag("-Wno-deprecated-declarations");
756     cfg.define("_NETBSD_SOURCE", Some("1"));
757 
758     headers! {
759         cfg:
760         "errno.h",
761         "fcntl.h",
762         "limits.h",
763         "locale.h",
764         "stddef.h",
765         "stdint.h",
766         "stdio.h",
767         "stdlib.h",
768         "sys/stat.h",
769         "sys/types.h",
770         "time.h",
771         "wchar.h",
772         "aio.h",
773         "ctype.h",
774         "dirent.h",
775         "dlfcn.h",
776         "glob.h",
777         "grp.h",
778         "ifaddrs.h",
779         "langinfo.h",
780         "net/if.h",
781         "net/if_arp.h",
782         "net/if_dl.h",
783         "net/route.h",
784         "netdb.h",
785         "netinet/in.h",
786         "netinet/ip.h",
787         "netinet/tcp.h",
788         "netinet/udp.h",
789         "poll.h",
790         "pthread.h",
791         "pwd.h",
792         "resolv.h",
793         "sched.h",
794         "semaphore.h",
795         "signal.h",
796         "string.h",
797         "sys/extattr.h",
798         "sys/file.h",
799         "sys/ioctl.h",
800         "sys/ioctl_compat.h",
801         "sys/mman.h",
802         "sys/mount.h",
803         "sys/ptrace.h",
804         "sys/resource.h",
805         "sys/socket.h",
806         "sys/statvfs.h",
807         "sys/sysctl.h",
808         "sys/time.h",
809         "sys/times.h",
810         "sys/uio.h",
811         "sys/un.h",
812         "sys/utsname.h",
813         "sys/wait.h",
814         "syslog.h",
815         "termios.h",
816         "ufs/ufs/quota.h",
817         "ufs/ufs/quota1.h",
818         "unistd.h",
819         "util.h",
820         "utime.h",
821         "mqueue.h",
822         "netinet/dccp.h",
823         "sys/event.h",
824         "sys/quota.h",
825         "sys/shm.h",
826     }
827 
828     cfg.type_name(move |ty, is_struct, is_union| {
829         match ty {
830             // Just pass all these through, no need for a "struct" prefix
831             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr"
832             | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym"
833             | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr"
834             | "Elf64_Chdr" => ty.to_string(),
835 
836             // OSX calls this something else
837             "sighandler_t" => "sig_t".to_string(),
838 
839             t if is_union => format!("union {}", t),
840 
841             t if t.ends_with("_t") => t.to_string(),
842 
843             // put `struct` in front of all structs:.
844             t if is_struct => format!("struct {}", t),
845 
846             t => t.to_string(),
847         }
848     });
849 
850     cfg.field_name(move |struct_, field| {
851         match field {
852             // Our stat *_nsec fields normally don't actually exist but are part
853             // of a timeval struct
854             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
855                 s.replace("e_nsec", ".tv_nsec")
856             }
857             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
858             s => s.to_string(),
859         }
860     });
861 
862     cfg.skip_type(move |ty| {
863         match ty {
864             // FIXME: sighandler_t is crazy across platforms
865             "sighandler_t" => true,
866             _ => false,
867         }
868     });
869 
870     cfg.skip_struct(move |ty| {
871         match ty {
872             // This is actually a union, not a struct
873             "sigval" => true,
874             // These are tested as part of the linux_fcntl tests since there are
875             // header conflicts when including them with all the other structs.
876             "termios2" => true,
877             _ => false,
878         }
879     });
880 
881     cfg.skip_signededness(move |c| {
882         match c {
883             "LARGE_INTEGER" | "float" | "double" => true,
884             n if n.starts_with("pthread") => true,
885             // sem_t is a struct or pointer
886             "sem_t" => true,
887             _ => false,
888         }
889     });
890 
891     cfg.skip_const(move |name| {
892         match name {
893             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
894             "SIGUNUSED" => true,                       // removed in glibc 2.26
895 
896             // weird signed extension or something like that?
897             "MS_NOUSER" => true,
898             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
899             "BOTHER" => true,
900 
901             _ => false,
902         }
903     });
904 
905     cfg.skip_fn(move |name| {
906         match name {
907             // FIXME: https://github.com/rust-lang/libc/issues/1272
908             "execv" | "execve" | "execvp" => true,
909 
910             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
911             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
912             "prlimit" | "prlimit64" |        // non-int in 2nd arg
913 
914             // These functions presumably exist on netbsd but don't look like
915             // they're implemented on rumprun yet, just let them slide for now.
916             // Some of them look like they have headers but then don't have
917             // corresponding actual definitions either...
918             "shm_open" |
919             "shm_unlink" |
920             "syscall" |
921             "mq_open" |
922             "mq_close" |
923             "mq_getattr" |
924             "mq_notify" |
925             "mq_receive" |
926             "mq_send" |
927             "mq_setattr" |
928             "mq_timedreceive" |
929             "mq_timedsend" |
930             "mq_unlink" |
931             "ptrace" |
932             "sigaltstack" if rumprun => true,
933 
934             _ => false,
935         }
936     });
937 
938     cfg.skip_field_type(move |struct_, field| {
939         // This is a weird union, don't check the type.
940         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
941         // sighandler_t type is super weird
942         (struct_ == "sigaction" && field == "sa_sigaction") ||
943         // sigval is actually a union, but we pretend it's a struct
944         (struct_ == "sigevent" && field == "sigev_value") ||
945         // aio_buf is "volatile void*" and Rust doesn't understand volatile
946         (struct_ == "aiocb" && field == "aio_buf")
947     });
948 
949     cfg.generate("../src/lib.rs", "main.rs");
950 }
951 
952 fn test_dragonflybsd(target: &str) {
953     assert!(target.contains("dragonfly"));
954     let mut cfg = ctest_cfg();
955     cfg.flag("-Wno-deprecated-declarations");
956 
957     headers! {
958         cfg:
959         "aio.h",
960         "ctype.h",
961         "dirent.h",
962         "dlfcn.h",
963         "errno.h",
964         "execinfo.h",
965         "fcntl.h",
966         "glob.h",
967         "grp.h",
968         "ifaddrs.h",
969         "langinfo.h",
970         "limits.h",
971         "locale.h",
972         "mqueue.h",
973         "net/if.h",
974         "net/if_arp.h",
975         "net/if_dl.h",
976         "net/route.h",
977         "netdb.h",
978         "netinet/in.h",
979         "netinet/ip.h",
980         "netinet/tcp.h",
981         "netinet/udp.h",
982         "poll.h",
983         "pthread.h",
984         "pthread_np.h",
985         "pwd.h",
986         "resolv.h",
987         "sched.h",
988         "semaphore.h",
989         "signal.h",
990         "stddef.h",
991         "stdint.h",
992         "stdio.h",
993         "stdlib.h",
994         "string.h",
995         "sys/event.h",
996         "sys/file.h",
997         "sys/ioctl.h",
998         "sys/mman.h",
999         "sys/mount.h",
1000         "sys/ptrace.h",
1001         "sys/resource.h",
1002         "sys/rtprio.h",
1003         "sys/socket.h",
1004         "sys/stat.h",
1005         "sys/statvfs.h",
1006         "sys/sysctl.h",
1007         "sys/time.h",
1008         "sys/times.h",
1009         "sys/types.h",
1010         "sys/uio.h",
1011         "sys/un.h",
1012         "sys/utsname.h",
1013         "sys/wait.h",
1014         "syslog.h",
1015         "termios.h",
1016         "time.h",
1017         "ufs/ufs/quota.h",
1018         "unistd.h",
1019         "util.h",
1020         "utime.h",
1021         "utmpx.h",
1022         "wchar.h",
1023     }
1024 
1025     cfg.type_name(move |ty, is_struct, is_union| {
1026         match ty {
1027             // Just pass all these through, no need for a "struct" prefix
1028             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr"
1029             | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym"
1030             | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr"
1031             | "Elf64_Chdr" => ty.to_string(),
1032 
1033             // FIXME: OSX calls this something else
1034             "sighandler_t" => "sig_t".to_string(),
1035 
1036             t if is_union => format!("union {}", t),
1037 
1038             t if t.ends_with("_t") => t.to_string(),
1039 
1040             // put `struct` in front of all structs:.
1041             t if is_struct => format!("struct {}", t),
1042 
1043             t => t.to_string(),
1044         }
1045     });
1046 
1047     cfg.field_name(move |struct_, field| {
1048         match field {
1049             // Our stat *_nsec fields normally don't actually exist but are part
1050             // of a timeval struct
1051             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1052                 s.replace("e_nsec", ".tv_nsec")
1053             }
1054             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1055             // Field is named `type` in C but that is a Rust keyword,
1056             // so these fields are translated to `type_` in the bindings.
1057             "type_" if struct_ == "rtprio" => "type".to_string(),
1058             s => s.to_string(),
1059         }
1060     });
1061 
1062     cfg.skip_type(move |ty| {
1063         match ty {
1064             // sighandler_t is crazy across platforms
1065             "sighandler_t" => true,
1066 
1067             _ => false,
1068         }
1069     });
1070 
1071     cfg.skip_struct(move |ty| {
1072         match ty {
1073             // This is actually a union, not a struct
1074             "sigval" => true,
1075 
1076             // FIXME: These are tested as part of the linux_fcntl tests since
1077             // there are header conflicts when including them with all the other
1078             // structs.
1079             "termios2" => true,
1080 
1081             _ => false,
1082         }
1083     });
1084 
1085     cfg.skip_signededness(move |c| {
1086         match c {
1087             "LARGE_INTEGER" | "float" | "double" => true,
1088             // uuid_t is a struct, not an integer.
1089             "uuid_t" => true,
1090             n if n.starts_with("pthread") => true,
1091             // sem_t is a struct or pointer
1092             "sem_t" => true,
1093             // mqd_t is a pointer on DragonFly
1094             "mqd_t" => true,
1095 
1096             _ => false,
1097         }
1098     });
1099 
1100     cfg.skip_const(move |name| {
1101         match name {
1102             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1103 
1104             // weird signed extension or something like that?
1105             "MS_NOUSER" => true,
1106             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1107 
1108             // These are defined for Solaris 11, but the crate is tested on
1109             // illumos, where they are currently not defined
1110             "EADI"
1111             | "PORT_SOURCE_POSTWAIT"
1112             | "PORT_SOURCE_SIGNAL"
1113             | "PTHREAD_STACK_MIN" => true,
1114 
1115             _ => false,
1116         }
1117     });
1118 
1119     cfg.skip_fn(move |name| {
1120         // skip those that are manually verified
1121         match name {
1122             // FIXME: https://github.com/rust-lang/libc/issues/1272
1123             "execv" | "execve" | "execvp" => true,
1124 
1125             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1126             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1127             "prlimit" | "prlimit64"        // non-int in 2nd arg
1128              => true,
1129 
1130             _ => false,
1131         }
1132     });
1133 
1134     cfg.skip_field_type(move |struct_, field| {
1135         // This is a weird union, don't check the type.
1136         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1137         // sighandler_t type is super weird
1138         (struct_ == "sigaction" && field == "sa_sigaction") ||
1139         // sigval is actually a union, but we pretend it's a struct
1140         (struct_ == "sigevent" && field == "sigev_value") ||
1141         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1142         (struct_ == "aiocb" && field == "aio_buf")
1143     });
1144 
1145     cfg.skip_field(move |struct_, field| {
1146         // this is actually a union on linux, so we can't represent it well and
1147         // just insert some padding.
1148         (struct_ == "siginfo_t" && field == "_pad") ||
1149         // sigev_notify_thread_id is actually part of a sigev_un union
1150         (struct_ == "sigevent" && field == "sigev_notify_thread_id")
1151     });
1152 
1153     cfg.generate("../src/lib.rs", "main.rs");
1154 }
1155 
1156 fn test_wasi(target: &str) {
1157     assert!(target.contains("wasi"));
1158 
1159     let mut cfg = ctest_cfg();
1160     cfg.define("_GNU_SOURCE", None);
1161 
1162     headers! { cfg:
1163         "ctype.h",
1164         "dirent.h",
1165         "errno.h",
1166         "fcntl.h",
1167         "limits.h",
1168         "locale.h",
1169         "malloc.h",
1170         "poll.h",
1171         "sched.h",
1172         "stdbool.h",
1173         "stddef.h",
1174         "stdint.h",
1175         "stdio.h",
1176         "stdlib.h",
1177         "string.h",
1178         "sys/resource.h",
1179         "sys/select.h",
1180         "sys/socket.h",
1181         "sys/stat.h",
1182         "sys/times.h",
1183         "sys/types.h",
1184         "sys/uio.h",
1185         "sys/utsname.h",
1186         "time.h",
1187         "unistd.h",
1188         "wasi/api.h",
1189         "wasi/libc.h",
1190         "wasi/libc-find-relpath.h",
1191         "wchar.h",
1192     }
1193 
1194     cfg.type_name(move |ty, is_struct, is_union| match ty {
1195         "FILE" | "fd_set" | "DIR" => ty.to_string(),
1196         t if is_union => format!("union {}", t),
1197         t if t.starts_with("__wasi") && t.ends_with("_u") => {
1198             format!("union {}", t)
1199         }
1200         t if t.starts_with("__wasi") && is_struct => format!("struct {}", t),
1201         t if t.ends_with("_t") => t.to_string(),
1202         t if is_struct => format!("struct {}", t),
1203         t => t.to_string(),
1204     });
1205 
1206     cfg.field_name(move |_struct, field| {
1207         match field {
1208             // deal with fields as rust keywords
1209             "type_" => "type".to_string(),
1210             s => s.to_string(),
1211         }
1212     });
1213 
1214     // Looks like LLD doesn't merge duplicate imports, so if the Rust
1215     // code imports from a module and the C code also imports from a
1216     // module we end up with two imports of function pointers which
1217     // import the same thing but have different function pointers
1218     cfg.skip_fn_ptrcheck(|f| f.starts_with("__wasi"));
1219 
1220     // d_name is declared as a flexible array in WASI libc, so it
1221     // doesn't support sizeof.
1222     cfg.skip_field(|s, field| s == "dirent" && field == "d_name");
1223 
1224     // Currently Rust/clang disagree on function argument ABI, so skip these
1225     // tests. For more info see WebAssembly/tool-conventions#88
1226     cfg.skip_roundtrip(|_| true);
1227 
1228     cfg.generate("../src/lib.rs", "main.rs");
1229 }
1230 
1231 fn test_android(target: &str) {
1232     assert!(target.contains("android"));
1233     let target_pointer_width = match target {
1234         t if t.contains("aarch64") || t.contains("x86_64") => 64,
1235         t if t.contains("i686") || t.contains("arm") => 32,
1236         t => panic!("unsupported target: {}", t),
1237     };
1238     let x86 = target.contains("i686") || target.contains("x86_64");
1239 
1240     let mut cfg = ctest_cfg();
1241     cfg.define("_GNU_SOURCE", None);
1242 
1243     headers! { cfg:
1244                "arpa/inet.h",
1245                "ctype.h",
1246                "dirent.h",
1247                "dlfcn.h",
1248                "errno.h",
1249                "fcntl.h",
1250                "grp.h",
1251                "ifaddrs.h",
1252                "limits.h",
1253                "locale.h",
1254                "malloc.h",
1255                "net/ethernet.h",
1256                "net/if.h",
1257                "net/if_arp.h",
1258                "net/route.h",
1259                "netdb.h",
1260                "netinet/in.h",
1261                "netinet/ip.h",
1262                "netinet/tcp.h",
1263                "netinet/udp.h",
1264                "netpacket/packet.h",
1265                "poll.h",
1266                "pthread.h",
1267                "pty.h",
1268                "pwd.h",
1269                "resolv.h",
1270                "sched.h",
1271                "semaphore.h",
1272                "signal.h",
1273                "stddef.h",
1274                "stdint.h",
1275                "stdio.h",
1276                "stdlib.h",
1277                "string.h",
1278                "sys/epoll.h",
1279                "sys/eventfd.h",
1280                "sys/file.h",
1281                "sys/fsuid.h",
1282                "sys/inotify.h",
1283                "sys/ioctl.h",
1284                "sys/mman.h",
1285                "sys/mount.h",
1286                "sys/personality.h",
1287                "sys/prctl.h",
1288                "sys/ptrace.h",
1289                "sys/random.h",
1290                "sys/reboot.h",
1291                "sys/resource.h",
1292                "sys/sendfile.h",
1293                "sys/signalfd.h",
1294                "sys/socket.h",
1295                "sys/stat.h",
1296                "sys/statvfs.h",
1297                "sys/swap.h",
1298                "sys/syscall.h",
1299                "sys/sysinfo.h",
1300                "sys/time.h",
1301                "sys/times.h",
1302                "sys/types.h",
1303                "sys/uio.h",
1304                "sys/un.h",
1305                "sys/utsname.h",
1306                "sys/vfs.h",
1307                "sys/xattr.h",
1308                "sys/wait.h",
1309                "syslog.h",
1310                "termios.h",
1311                "time.h",
1312                "unistd.h",
1313                "utime.h",
1314                "utmp.h",
1315                "wchar.h",
1316                "xlocale.h",
1317                // time64_t is not defined for 64-bit targets If included it will
1318                // generate the error 'Your time_t is already 64-bit'
1319                [target_pointer_width == 32]: "time64.h",
1320                [x86]: "sys/reg.h",
1321     }
1322 
1323     // Include linux headers at the end:
1324     headers! { cfg:
1325                 "asm/mman.h",
1326                 "linux/dccp.h",
1327                 "linux/errqueue.h",
1328                 "linux/futex.h",
1329                 "linux/fs.h",
1330                 "linux/genetlink.h",
1331                 "linux/if_alg.h",
1332                 "linux/if_ether.h",
1333                 "linux/if_tun.h",
1334                 "linux/magic.h",
1335                 "linux/memfd.h",
1336                 "linux/module.h",
1337                 "linux/net_tstamp.h",
1338                 "linux/netfilter/nfnetlink.h",
1339                 "linux/netfilter/nfnetlink_log.h",
1340                 "linux/netfilter/nf_tables.h",
1341                 "linux/netfilter_ipv4.h",
1342                 "linux/netfilter_ipv6.h",
1343                 "linux/netlink.h",
1344                 "linux/quota.h",
1345                 "linux/reboot.h",
1346                 "linux/seccomp.h",
1347                 "linux/sockios.h",
1348 
1349     }
1350 
1351     cfg.type_name(move |ty, is_struct, is_union| {
1352         match ty {
1353             // Just pass all these through, no need for a "struct" prefix
1354             "FILE" | "fd_set" | "Dl_info" => ty.to_string(),
1355 
1356             t if is_union => format!("union {}", t),
1357 
1358             t if t.ends_with("_t") => t.to_string(),
1359 
1360             // sigval is a struct in Rust, but a union in C:
1361             "sigval" => format!("union sigval"),
1362 
1363             // put `struct` in front of all structs:.
1364             t if is_struct => format!("struct {}", t),
1365 
1366             t => t.to_string(),
1367         }
1368     });
1369 
1370     cfg.field_name(move |struct_, field| {
1371         match field {
1372             // Our stat *_nsec fields normally don't actually exist but are part
1373             // of a timeval struct
1374             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1375                 s.to_string()
1376             }
1377             // FIXME: appears that `epoll_event.data` is an union
1378             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1379             s => s.to_string(),
1380         }
1381     });
1382 
1383     cfg.skip_type(move |ty| {
1384         match ty {
1385             // FIXME: `sighandler_t` type is incorrect, see:
1386             // https://github.com/rust-lang/libc/issues/1359
1387             "sighandler_t" => true,
1388             _ => false,
1389         }
1390     });
1391 
1392     cfg.skip_struct(move |ty| {
1393         match ty {
1394             // These are tested as part of the linux_fcntl tests since there are
1395             // header conflicts when including them with all the other structs.
1396             "termios2" => true,
1397 
1398             _ => false,
1399         }
1400     });
1401 
1402     cfg.skip_const(move |name| {
1403         match name {
1404             // FIXME: deprecated: not available in any header
1405             // See: https://github.com/rust-lang/libc/issues/1356
1406             "ENOATTR" => true,
1407 
1408             // FIXME: still necessary?
1409             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1410             // FIXME: deprecated - removed in glibc 2.26
1411             "SIGUNUSED" => true,
1412 
1413             _ => false,
1414         }
1415     });
1416 
1417     cfg.skip_fn(move |name| {
1418         // skip those that are manually verified
1419         match name {
1420             // FIXME: https://github.com/rust-lang/libc/issues/1272
1421             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1422 
1423             // There are two versions of the sterror_r function, see
1424             //
1425             // https://linux.die.net/man/3/strerror_r
1426             //
1427             // An XSI-compliant version provided if:
1428             //
1429             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
1430             //
1431             // and a GNU specific version provided if _GNU_SOURCE is defined.
1432             //
1433             // libc provides bindings for the XSI-compliant version, which is
1434             // preferred for portable applications.
1435             //
1436             // We skip the test here since here _GNU_SOURCE is defined, and
1437             // test the XSI version below.
1438             "strerror_r" => true,
1439 
1440             _ => false,
1441         }
1442     });
1443 
1444     cfg.skip_field_type(move |struct_, field| {
1445         // This is a weird union, don't check the type.
1446         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1447         // sigval is actually a union, but we pretend it's a struct
1448         (struct_ == "sigevent" && field == "sigev_value")
1449     });
1450 
1451     cfg.skip_field(move |struct_, field| {
1452         // this is actually a union on linux, so we can't represent it well and
1453         // just insert some padding.
1454         (struct_ == "siginfo_t" && field == "_pad") ||
1455         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1456         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1457         (struct_ == "sigaction" && field == "sa_sigaction") ||
1458         // sigev_notify_thread_id is actually part of a sigev_un union
1459         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1460         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1461         (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
1462                                            field == "ssi_call_addr" ||
1463                                            field == "ssi_arch"))
1464     });
1465 
1466     cfg.generate("../src/lib.rs", "main.rs");
1467 
1468     test_linux_like_apis(target);
1469 }
1470 
1471 fn test_freebsd(target: &str) {
1472     assert!(target.contains("freebsd"));
1473     let mut cfg = ctest_cfg();
1474 
1475     let freebsd_ver = which_freebsd();
1476 
1477     match freebsd_ver {
1478         Some(10) => cfg.cfg("freebsd10", None),
1479         Some(11) => cfg.cfg("freebsd11", None),
1480         Some(12) => cfg.cfg("freebsd12", None),
1481         Some(13) => cfg.cfg("freebsd13", None),
1482         _ => &mut cfg,
1483     };
1484 
1485     // Required for `getline`:
1486     cfg.define("_WITH_GETLINE", None);
1487     // Required for making freebsd11_stat available in the headers
1488     match freebsd_ver {
1489         Some(10) => &mut cfg,
1490         _ => cfg.define("_WANT_FREEBSD11_STAT", None),
1491     };
1492 
1493     headers! { cfg:
1494                 "aio.h",
1495                 "arpa/inet.h",
1496                 "ctype.h",
1497                 "dirent.h",
1498                 "dlfcn.h",
1499                 "errno.h",
1500                 "fcntl.h",
1501                 "glob.h",
1502                 "grp.h",
1503                 "ifaddrs.h",
1504                 "langinfo.h",
1505                 "libutil.h",
1506                 "limits.h",
1507                 "locale.h",
1508                 "mqueue.h",
1509                 "net/bpf.h",
1510                 "net/if.h",
1511                 "net/if_arp.h",
1512                 "net/if_dl.h",
1513                 "net/route.h",
1514                 "netdb.h",
1515                 "netinet/ip.h",
1516                 "netinet/in.h",
1517                 "netinet/tcp.h",
1518                 "netinet/udp.h",
1519                 "poll.h",
1520                 "pthread.h",
1521                 "pthread_np.h",
1522                 "pwd.h",
1523                 "resolv.h",
1524                 "sched.h",
1525                 "semaphore.h",
1526                 "signal.h",
1527                 "spawn.h",
1528                 "stddef.h",
1529                 "stdint.h",
1530                 "stdio.h",
1531                 "stdlib.h",
1532                 "string.h",
1533                 "sys/event.h",
1534                 "sys/extattr.h",
1535                 "sys/file.h",
1536                 "sys/ioctl.h",
1537                 "sys/ipc.h",
1538                 "sys/jail.h",
1539                 "sys/mman.h",
1540                 "sys/mount.h",
1541                 "sys/msg.h",
1542                 "sys/procdesc.h",
1543                 "sys/ptrace.h",
1544                 "sys/resource.h",
1545                 "sys/rtprio.h",
1546                 "sys/shm.h",
1547                 "sys/socket.h",
1548                 "sys/stat.h",
1549                 "sys/statvfs.h",
1550                 "sys/sysctl.h",
1551                 "sys/time.h",
1552                 "sys/times.h",
1553                 "sys/types.h",
1554                 "sys/uio.h",
1555                 "sys/un.h",
1556                 "sys/utsname.h",
1557                 "sys/wait.h",
1558                 "syslog.h",
1559                 "termios.h",
1560                 "time.h",
1561                 "ufs/ufs/quota.h",
1562                 "unistd.h",
1563                 "utime.h",
1564                 "utmpx.h",
1565                 "wchar.h",
1566     }
1567 
1568     cfg.type_name(move |ty, is_struct, is_union| {
1569         match ty {
1570             // Just pass all these through, no need for a "struct" prefix
1571             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
1572 
1573             // FIXME: https://github.com/rust-lang/libc/issues/1273
1574             "sighandler_t" => "sig_t".to_string(),
1575 
1576             t if is_union => format!("union {}", t),
1577 
1578             t if t.ends_with("_t") => t.to_string(),
1579 
1580             // sigval is a struct in Rust, but a union in C:
1581             "sigval" => format!("union sigval"),
1582 
1583             // put `struct` in front of all structs:.
1584             t if is_struct => format!("struct {}", t),
1585 
1586             t => t.to_string(),
1587         }
1588     });
1589 
1590     cfg.field_name(move |struct_, field| {
1591         match field {
1592             // Our stat *_nsec fields normally don't actually exist but are part
1593             // of a timeval struct
1594             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1595                 s.replace("e_nsec", ".tv_nsec")
1596             }
1597             // Field is named `type` in C but that is a Rust keyword,
1598             // so these fields are translated to `type_` in the bindings.
1599             "type_" if struct_ == "rtprio" => "type".to_string(),
1600             s => s.to_string(),
1601         }
1602     });
1603 
1604     cfg.skip_const(move |name| {
1605         match name {
1606             // These constants were introduced in FreeBSD 12:
1607             "SF_USER_READAHEAD"
1608             | "EVFILT_EMPTY"
1609             | "SO_REUSEPORT_LB"
1610             | "IP_ORIGDSTADDR"
1611             | "IP_RECVORIGDSTADDR"
1612             | "IPV6_ORIGDSTADDR"
1613             | "IPV6_RECVORIGDSTADDR"
1614                 if Some(11) == freebsd_ver =>
1615             {
1616                 true
1617             }
1618 
1619             // These constants were introduced in FreeBSD 11:
1620             "SF_USER_READAHEAD"
1621             | "SF_NOCACHE"
1622             | "RLIMIT_KQUEUES"
1623             | "RLIMIT_UMTXP"
1624             | "EVFILT_PROCDESC"
1625             | "EVFILT_SENDFILE"
1626             | "EVFILT_EMPTY"
1627             | "SO_REUSEPORT_LB"
1628             | "TCP_CCALGOOPT"
1629             | "TCP_PCAP_OUT"
1630             | "TCP_PCAP_IN"
1631             | "IP_BINDMULTI"
1632             | "IP_ORIGDSTADDR"
1633             | "IP_RECVORIGDSTADDR"
1634             | "IPV6_ORIGDSTADDR"
1635             | "IPV6_RECVORIGDSTADDR"
1636             | "PD_CLOEXEC"
1637             | "PD_ALLOWED_AT_FORK"
1638             | "IP_RSS_LISTEN_BUCKET"
1639                 if Some(10) == freebsd_ver =>
1640             {
1641                 true
1642             }
1643 
1644             // FIXME: This constant has a different value in FreeBSD 10:
1645             "RLIM_NLIMITS" if Some(10) == freebsd_ver => true,
1646 
1647             // FIXME: There are deprecated - remove in a couple of releases.
1648             // These constants were removed in FreeBSD 11 (svn r273250) but will
1649             // still be accepted and ignored at runtime.
1650             "MAP_RENAME" | "MAP_NORESERVE" if Some(10) != freebsd_ver => true,
1651 
1652             // FIXME: There are deprecated - remove in a couple of releases.
1653             // These constants were removed in FreeBSD 11 (svn r262489),
1654             // and they've never had any legitimate use outside of the
1655             // base system anyway.
1656             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
1657 
1658             _ => false,
1659         }
1660     });
1661 
1662     cfg.skip_struct(move |ty| {
1663         match ty {
1664             // `mmsghdr` is not available in FreeBSD 10
1665             "mmsghdr" if Some(10) == freebsd_ver => true,
1666 
1667             // `max_align_t` is not available in FreeBSD 10
1668             "max_align_t" if Some(10) == freebsd_ver => true,
1669 
1670             _ => false,
1671         }
1672     });
1673 
1674     cfg.skip_fn(move |name| {
1675         // skip those that are manually verified
1676         match name {
1677             // FIXME: https://github.com/rust-lang/libc/issues/1272
1678             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1679 
1680             // These functions were added in FreeBSD 11:
1681             "fdatasync" | "mq_getfd_np" | "sendmmsg" | "recvmmsg"
1682                 if Some(10) == freebsd_ver =>
1683             {
1684                 true
1685             }
1686 
1687             // This function changed its return type from `int` in FreeBSD10 to
1688             // `ssize_t` in FreeBSD11:
1689             "aio_waitcomplete" if Some(10) == freebsd_ver => true,
1690 
1691             // The `uname` function in the `utsname.h` FreeBSD header is a C
1692             // inline function (has no symbol) that calls the `__xuname` symbol.
1693             // Therefore the function pointer comparison does not make sense for it.
1694             "uname" => true,
1695 
1696             // FIXME: Our API is unsound. The Rust API allows aliasing
1697             // pointers, but the C API requires pointers not to alias.
1698             // We should probably be at least using `&`/`&mut` here, see:
1699             // https://github.com/gnzlbg/ctest/issues/68
1700             "lio_listio" => true,
1701 
1702             _ => false,
1703         }
1704     });
1705 
1706     cfg.skip_signededness(move |c| {
1707         match c {
1708             // FIXME: has a different sign in FreeBSD10
1709             "blksize_t" if Some(10) == freebsd_ver => true,
1710             _ => false,
1711         }
1712     });
1713 
1714     cfg.volatile_item(|i| {
1715         use ctest::VolatileItemKind::*;
1716         match i {
1717             // aio_buf is a volatile void** but since we cannot express that in
1718             // Rust types, we have to explicitly tell the checker about it here:
1719             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
1720                 true
1721             }
1722             _ => false,
1723         }
1724     });
1725 
1726     cfg.skip_field(move |struct_, field| {
1727         match (struct_, field) {
1728             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1729             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1730             ("sigaction", "sa_sigaction") => true,
1731 
1732             // FIXME: in FreeBSD10 this field has type `char*` instead of
1733             // `void*`:
1734             ("stack_t", "ss_sp") if Some(10) == freebsd_ver => true,
1735 
1736             _ => false,
1737         }
1738     });
1739 
1740     cfg.generate("../src/lib.rs", "main.rs");
1741 }
1742 
1743 fn test_emscripten(target: &str) {
1744     assert!(target.contains("emscripten"));
1745 
1746     let mut cfg = ctest_cfg();
1747     cfg.define("_GNU_SOURCE", None); // FIXME: ??
1748 
1749     headers! { cfg:
1750                "aio.h",
1751                "ctype.h",
1752                "dirent.h",
1753                "dlfcn.h",
1754                "errno.h",
1755                "fcntl.h",
1756                "glob.h",
1757                "grp.h",
1758                "ifaddrs.h",
1759                "langinfo.h",
1760                "limits.h",
1761                "locale.h",
1762                "malloc.h",
1763                "mntent.h",
1764                "mqueue.h",
1765                "net/ethernet.h",
1766                "net/if.h",
1767                "net/if_arp.h",
1768                "net/route.h",
1769                "netdb.h",
1770                "netinet/in.h",
1771                "netinet/ip.h",
1772                "netinet/tcp.h",
1773                "netinet/udp.h",
1774                "netpacket/packet.h",
1775                "poll.h",
1776                "pthread.h",
1777                "pty.h",
1778                "pwd.h",
1779                "resolv.h",
1780                "sched.h",
1781                "sched.h",
1782                "semaphore.h",
1783                "shadow.h",
1784                "signal.h",
1785                "stddef.h",
1786                "stdint.h",
1787                "stdio.h",
1788                "stdlib.h",
1789                "string.h",
1790                "sys/epoll.h",
1791                "sys/eventfd.h",
1792                "sys/file.h",
1793                "sys/ioctl.h",
1794                "sys/ipc.h",
1795                "sys/mman.h",
1796                "sys/mount.h",
1797                "sys/msg.h",
1798                "sys/personality.h",
1799                "sys/prctl.h",
1800                "sys/ptrace.h",
1801                "sys/quota.h",
1802                "sys/reboot.h",
1803                "sys/resource.h",
1804                "sys/sem.h",
1805                "sys/sendfile.h",
1806                "sys/shm.h",
1807                "sys/signalfd.h",
1808                "sys/socket.h",
1809                "sys/stat.h",
1810                "sys/statvfs.h",
1811                "sys/swap.h",
1812                "sys/syscall.h",
1813                "sys/sysctl.h",
1814                "sys/sysinfo.h",
1815                "sys/time.h",
1816                "sys/timerfd.h",
1817                "sys/times.h",
1818                "sys/types.h",
1819                "sys/uio.h",
1820                "sys/un.h",
1821                "sys/user.h",
1822                "sys/utsname.h",
1823                "sys/vfs.h",
1824                "sys/wait.h",
1825                "sys/xattr.h",
1826                "syslog.h",
1827                "termios.h",
1828                "time.h",
1829                "ucontext.h",
1830                "unistd.h",
1831                "utime.h",
1832                "utmp.h",
1833                "utmpx.h",
1834                "wchar.h",
1835     }
1836 
1837     cfg.type_name(move |ty, is_struct, is_union| {
1838         match ty {
1839             // Just pass all these through, no need for a "struct" prefix
1840             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
1841 
1842             t if is_union => format!("union {}", t),
1843 
1844             t if t.ends_with("_t") => t.to_string(),
1845 
1846             // put `struct` in front of all structs:.
1847             t if is_struct => format!("struct {}", t),
1848 
1849             t => t.to_string(),
1850         }
1851     });
1852 
1853     cfg.field_name(move |struct_, field| {
1854         match field {
1855             // Our stat *_nsec fields normally don't actually exist but are part
1856             // of a timeval struct
1857             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1858                 s.replace("e_nsec", ".tv_nsec")
1859             }
1860             // FIXME: appears that `epoll_event.data` is an union
1861             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1862             s => s.to_string(),
1863         }
1864     });
1865 
1866     cfg.skip_type(move |ty| {
1867         match ty {
1868             // sighandler_t is crazy across platforms
1869             // FIXME: is this necessary?
1870             "sighandler_t" => true,
1871 
1872             _ => false,
1873         }
1874     });
1875 
1876     cfg.skip_struct(move |ty| {
1877         match ty {
1878             // This is actually a union, not a struct
1879             // FIXME: is this necessary?
1880             "sigval" => true,
1881 
1882             _ => false,
1883         }
1884     });
1885 
1886     cfg.skip_fn(move |name| {
1887         match name {
1888             // FIXME: https://github.com/rust-lang/libc/issues/1272
1889             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1890 
1891             _ => false,
1892         }
1893     });
1894 
1895     cfg.skip_const(move |name| {
1896         match name {
1897             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
1898             // users should use SIGSYS instead
1899             "SIGUNUSED" => true,
1900 
1901             // FIXME: emscripten uses different constants to constructs these
1902             n if n.contains("__SIZEOF_PTHREAD") => true,
1903 
1904             _ => false,
1905         }
1906     });
1907 
1908     cfg.skip_field_type(move |struct_, field| {
1909         // This is a weird union, don't check the type.
1910         // FIXME: is this necessary?
1911         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1912         // sighandler_t type is super weird
1913         // FIXME: is this necessary?
1914         (struct_ == "sigaction" && field == "sa_sigaction") ||
1915         // sigval is actually a union, but we pretend it's a struct
1916         // FIXME: is this necessary?
1917         (struct_ == "sigevent" && field == "sigev_value") ||
1918         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1919         // FIXME: is this necessary?
1920         (struct_ == "aiocb" && field == "aio_buf")
1921     });
1922 
1923     cfg.skip_field(move |struct_, field| {
1924         // this is actually a union on linux, so we can't represent it well and
1925         // just insert some padding.
1926         // FIXME: is this necessary?
1927         (struct_ == "siginfo_t" && field == "_pad") ||
1928         // musl names this __dummy1 but it's still there
1929         // FIXME: is this necessary?
1930         (struct_ == "glob_t" && field == "gl_flags") ||
1931         // musl seems to define this as an *anonymous* bitfield
1932         // FIXME: is this necessary?
1933         (struct_ == "statvfs" && field == "__f_unused") ||
1934         // sigev_notify_thread_id is actually part of a sigev_un union
1935         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1936         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
1937         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
1938                                            field == "_pad2" ||
1939                                            field == "ssi_syscall" ||
1940                                            field == "ssi_call_addr" ||
1941                                            field == "ssi_arch"))
1942     });
1943 
1944     // FIXME: test linux like
1945     cfg.generate("../src/lib.rs", "main.rs");
1946 }
1947 
1948 fn test_vxworks(target: &str) {
1949     assert!(target.contains("vxworks"));
1950 
1951     let mut cfg = ctest::TestGenerator::new();
1952     headers! { cfg:
1953                "vxWorks.h",
1954                "yvals.h",
1955                "nfs/nfsCommon.h",
1956                "rtpLibCommon.h",
1957                "randomNumGen.h",
1958                "taskLib.h",
1959                "sysLib.h",
1960                "ioLib.h",
1961                "inetLib.h",
1962                "socket.h",
1963                "errnoLib.h",
1964                "ctype.h",
1965                "dirent.h",
1966                "dlfcn.h",
1967                "elf.h",
1968                "fcntl.h",
1969                "grp.h",
1970                "sys/poll.h",
1971                "ifaddrs.h",
1972                "langinfo.h",
1973                "limits.h",
1974                "link.h",
1975                "locale.h",
1976                "sys/stat.h",
1977                "netdb.h",
1978                "pthread.h",
1979                "pwd.h",
1980                "sched.h",
1981                "semaphore.h",
1982                "signal.h",
1983                "stddef.h",
1984                "stdint.h",
1985                "stdio.h",
1986                "stdlib.h",
1987                "string.h",
1988                "sys/file.h",
1989                "sys/ioctl.h",
1990                "sys/socket.h",
1991                "sys/time.h",
1992                "sys/times.h",
1993                "sys/types.h",
1994                "sys/uio.h",
1995                "sys/un.h",
1996                "sys/utsname.h",
1997                "sys/wait.h",
1998                "netinet/tcp.h",
1999                "syslog.h",
2000                "termios.h",
2001                "time.h",
2002                "ucontext.h",
2003                "unistd.h",
2004                "utime.h",
2005                "wchar.h",
2006                "errno.h",
2007                "sys/mman.h",
2008                "pathLib.h",
2009                "mqueue.h",
2010     }
2011     /* Fix me */
2012     cfg.skip_const(move |name| match name {
2013         // sighandler_t weirdness
2014         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
2015         // This is not defined in vxWorks
2016         | "RTLD_DEFAULT"   => true,
2017         _ => false,
2018     });
2019     /* Fix me */
2020     cfg.skip_type(move |ty| match ty {
2021         "stat64" | "sighandler_t" | "off64_t" => true,
2022         _ => false,
2023     });
2024 
2025     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
2026         ("siginfo_t", "si_value")
2027         | ("stat", "st_size")
2028         | ("sigaction", "sa_u") => true,
2029         _ => false,
2030     });
2031 
2032     cfg.skip_roundtrip(move |s| match s {
2033         _ => false,
2034     });
2035 
2036     cfg.type_name(move |ty, is_struct, is_union| match ty {
2037         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
2038         t if is_union => format!("union {}", t),
2039         t if t.ends_with("_t") => t.to_string(),
2040         t if is_struct => format!("struct {}", t),
2041         t => t.to_string(),
2042     });
2043 
2044     /* Fix me */
2045     cfg.skip_fn(move |name| match name {
2046         /* sigval */
2047         "sigqueue" | "_sigqueue"
2048         /* sighandler_t*/
2049         | "signal"
2050         /* not used in static linking by default */
2051         | "dlerror" => true,
2052         _ => false,
2053     });
2054 
2055     cfg.generate("../src/lib.rs", "main.rs");
2056 }
2057 
2058 fn test_linux(target: &str) {
2059     assert!(target.contains("linux"));
2060 
2061     // target_env
2062     let gnu = target.contains("gnu");
2063     let musl = target.contains("musl");
2064     let uclibc = target.contains("uclibc");
2065 
2066     match (gnu, musl, uclibc) {
2067         (true, false, false) => (),
2068         (false, true, false) => (),
2069         (false, false, true) => (),
2070         (_, _, _) => panic!(
2071             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
2072             gnu, musl, uclibc
2073         ),
2074     }
2075 
2076     let arm = target.contains("arm");
2077     let i686 = target.contains("i686");
2078     let mips = target.contains("mips");
2079     let mips32 = mips && !target.contains("64");
2080     let mips64 = mips && target.contains("64");
2081     let ppc64 = target.contains("powerpc64");
2082     let s390x = target.contains("s390x");
2083     let sparc64 = target.contains("sparc64");
2084     let x32 = target.contains("x32");
2085     let x86_32 = target.contains("i686");
2086     let x86_64 = target.contains("x86_64");
2087     let aarch64_musl = target.contains("aarch64") && musl;
2088     let gnuabihf = target.contains("gnueabihf");
2089 
2090     let mut cfg = ctest_cfg();
2091     cfg.define("_GNU_SOURCE", None);
2092     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
2093     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
2094     // glibc versions older than 2.29.
2095     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
2096 
2097     headers! { cfg:
2098                "ctype.h",
2099                "dirent.h",
2100                "dlfcn.h",
2101                "elf.h",
2102                "fcntl.h",
2103                "glob.h",
2104                "grp.h",
2105                "ifaddrs.h",
2106                "langinfo.h",
2107                "limits.h",
2108                "link.h",
2109                "locale.h",
2110                "malloc.h",
2111                "mntent.h",
2112                "mqueue.h",
2113                "net/ethernet.h",
2114                "net/if.h",
2115                "net/if_arp.h",
2116                "net/route.h",
2117                "netdb.h",
2118                "netinet/in.h",
2119                "netinet/ip.h",
2120                "netinet/tcp.h",
2121                "netinet/udp.h",
2122                "netpacket/packet.h",
2123                "poll.h",
2124                "pthread.h",
2125                "pty.h",
2126                "pwd.h",
2127                "resolv.h",
2128                "sched.h",
2129                "semaphore.h",
2130                "shadow.h",
2131                "signal.h",
2132                "spawn.h",
2133                "stddef.h",
2134                "stdint.h",
2135                "stdio.h",
2136                "stdlib.h",
2137                "string.h",
2138                "sys/epoll.h",
2139                "sys/eventfd.h",
2140                "sys/file.h",
2141                "sys/fsuid.h",
2142                "sys/inotify.h",
2143                "sys/ioctl.h",
2144                "sys/ipc.h",
2145                "sys/mman.h",
2146                "sys/mount.h",
2147                "sys/msg.h",
2148                "sys/personality.h",
2149                "sys/prctl.h",
2150                "sys/ptrace.h",
2151                "sys/quota.h",
2152                "sys/random.h",
2153                "sys/reboot.h",
2154                "sys/resource.h",
2155                "sys/sem.h",
2156                "sys/sendfile.h",
2157                "sys/shm.h",
2158                "sys/signalfd.h",
2159                "sys/socket.h",
2160                "sys/stat.h",
2161                "sys/statvfs.h",
2162                "sys/swap.h",
2163                "sys/syscall.h",
2164                "sys/time.h",
2165                "sys/timerfd.h",
2166                "sys/times.h",
2167                "sys/types.h",
2168                "sys/uio.h",
2169                "sys/un.h",
2170                "sys/user.h",
2171                "sys/utsname.h",
2172                "sys/vfs.h",
2173                "sys/wait.h",
2174                "syslog.h",
2175                "termios.h",
2176                "time.h",
2177                "ucontext.h",
2178                "unistd.h",
2179                "utime.h",
2180                "utmp.h",
2181                "utmpx.h",
2182                "wchar.h",
2183                "errno.h",
2184                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
2185                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
2186                // Also unavailable on gnuabihf with glibc 2.30.
2187                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
2188                [(x86_64 || x86_32 || arm) && !gnuabihf]: "sys/io.h",
2189                // `sys/reg.h` is only available on x86 and x86_64
2190                [x86_64 || x86_32]: "sys/reg.h",
2191                // sysctl system call is deprecated and not available on musl
2192                // It is also unsupported in x32, deprecated since glibc 2.30:
2193                [!(x32 || musl || gnu)]: "sys/sysctl.h",
2194                // <execinfo.h> is not supported by musl:
2195                // https://www.openwall.com/lists/musl/2015/04/09/3
2196                [!musl]: "execinfo.h",
2197     }
2198 
2199     // Include linux headers at the end:
2200     headers! {
2201         cfg:
2202         "asm/mman.h",
2203         "linux/dccp.h",
2204         "linux/errqueue.h",
2205         "linux/falloc.h",
2206         "linux/fs.h",
2207         "linux/futex.h",
2208         "linux/genetlink.h",
2209         "linux/if.h",
2210         "linux/if_addr.h",
2211         "linux/if_alg.h",
2212         "linux/if_ether.h",
2213         "linux/if_tun.h",
2214         "linux/input.h",
2215         "linux/magic.h",
2216         "linux/memfd.h",
2217         "linux/module.h",
2218         "linux/net_tstamp.h",
2219         "linux/netfilter/nfnetlink.h",
2220         "linux/netfilter/nfnetlink_log.h",
2221         "linux/netfilter/nf_tables.h",
2222         "linux/netfilter_ipv4.h",
2223         "linux/netfilter_ipv6.h",
2224         "linux/netlink.h",
2225         "linux/quota.h",
2226         "linux/random.h",
2227         "linux/reboot.h",
2228         "linux/rtnetlink.h",
2229         "linux/seccomp.h",
2230         "linux/sockios.h",
2231         "linux/vm_sockets.h",
2232         "sys/auxv.h",
2233     }
2234 
2235     // note: aio.h must be included before sys/mount.h
2236     headers! {
2237         cfg:
2238         "sys/xattr.h",
2239         "sys/sysinfo.h",
2240         "aio.h",
2241     }
2242 
2243     cfg.type_name(move |ty, is_struct, is_union| {
2244         match ty {
2245             // Just pass all these through, no need for a "struct" prefix
2246             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr"
2247             | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym"
2248             | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr"
2249             | "Elf64_Chdr" => ty.to_string(),
2250 
2251             t if is_union => format!("union {}", t),
2252 
2253             t if t.ends_with("_t") => t.to_string(),
2254 
2255             // In MUSL `flock64` is a typedef to `flock`.
2256             "flock64" if musl => format!("struct {}", ty),
2257 
2258             // put `struct` in front of all structs:.
2259             t if is_struct => format!("struct {}", t),
2260 
2261             t => t.to_string(),
2262         }
2263     });
2264 
2265     cfg.field_name(move |struct_, field| {
2266         match field {
2267             // Our stat *_nsec fields normally don't actually exist but are part
2268             // of a timeval struct
2269             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2270                 s.replace("e_nsec", ".tv_nsec")
2271             }
2272             // FIXME: epoll_event.data is actuall a union in C, but in Rust
2273             // it is only a u64 because we only expose one field
2274             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
2275             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2276             // The following structs have a field called `type` in C,
2277             // but `type` is a Rust keyword, so these fields are translated
2278             // to `type_` in Rust.
2279             "type_"
2280                 if struct_ == "input_event"
2281                     || struct_ == "input_mask"
2282                     || struct_ == "ff_effect" =>
2283             {
2284                 "type".to_string()
2285             }
2286 
2287             s => s.to_string(),
2288         }
2289     });
2290 
2291     cfg.skip_type(move |ty| {
2292         match ty {
2293             // FIXME: `sighandler_t` type is incorrect, see:
2294             // https://github.com/rust-lang/libc/issues/1359
2295             "sighandler_t" => true,
2296 
2297             // These cannot be tested when "resolv.h" is included and are tested
2298             // in the `linux_elf.rs` file.
2299             "Elf64_Phdr" | "Elf32_Phdr" => true,
2300 
2301             // This type is private on Linux. It is implemented as a C `enum`
2302             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
2303             // which expect a `c_int` even though both are ABI compatible.
2304             "__rlimit_resource_t" => true,
2305 
2306             _ => false,
2307         }
2308     });
2309 
2310     cfg.skip_struct(move |ty| {
2311         match ty {
2312             // These cannot be tested when "resolv.h" is included and are tested
2313             // in the `linux_elf.rs` file.
2314             "Elf64_Phdr" | "Elf32_Phdr" => true,
2315 
2316             // On Linux, the type of `ut_tv` field of `struct utmpx`
2317             // can be an anonymous struct, so an extra struct,
2318             // which is absent in glibc, has to be defined.
2319             "__timeval" => true,
2320 
2321             // FIXME: This is actually a union, not a struct
2322             "sigval" => true,
2323 
2324             // This type is tested in the `linux_termios.rs` file since there
2325             // are header conflicts when including them with all the other
2326             // structs.
2327             "termios2" => true,
2328 
2329             // FIXME: remove once Ubuntu 20.04 LTS is released, somewhere in 2020.
2330             // ucontext_t added a new field as of glibc 2.28; our struct definition is
2331             // conservative and omits the field, but that means the size doesn't match for newer
2332             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
2333             "ucontext_t" if gnu => true,
2334 
2335             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
2336             // So let's ignore for now and re-visit later.
2337             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
2338             "statx" => true,
2339             "statx_timestamp" => true,
2340 
2341             _ => false,
2342         }
2343     });
2344 
2345     cfg.skip_const(move |name| {
2346         match name {
2347             // These constants are not available if gnu headers have been included
2348             // and can therefore not be tested here
2349             //
2350             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
2351             | "IPV6_FLOWINFO"
2352             | "IPV6_FLOWLABEL_MGR"
2353             | "IPV6_FLOWINFO_SEND"
2354             | "IPV6_FLOWINFO_FLOWLABEL"
2355             | "IPV6_FLOWINFO_PRIORITY"
2356             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
2357             | "F_CANCELLK"
2358             | "F_ADD_SEALS"
2359             | "F_GET_SEALS"
2360             | "F_SEAL_SEAL"
2361             | "F_SEAL_SHRINK"
2362             | "F_SEAL_GROW"
2363             | "F_SEAL_WRITE" => true,
2364 
2365             // The musl-sanitized kernel headers used in CI
2366             // target the Linux kernel 4.4 and do not contain the
2367             // following constants:
2368             //
2369             // Requires Linux kernel 4.9
2370             | "FALLOC_FL_UNSHARE_RANGE"
2371             //
2372             // Require Linux kernel 5.x:
2373             | "MSG_COPY"
2374                if musl  => true,
2375             // Require Linux kernel 5.1:
2376             "F_SEAL_FUTURE_WRITE" => true,
2377 
2378             // The musl version 1.1.24 used in CI does not
2379             // contain these glibc constants yet:
2380             | "RLIMIT_RTTIME" // should be in `resource.h`
2381             | "TCP_COOKIE_TRANSACTIONS"  // should be in the `netinet/tcp.h` header
2382                 if musl => true,
2383 
2384             // FIXME: deprecated: not available in any header
2385             // See: https://github.com/rust-lang/libc/issues/1356
2386             "ENOATTR" => true,
2387 
2388             // FIXME: SIGUNUSED was removed in glibc 2.26
2389             // Users should use SIGSYS instead.
2390             "SIGUNUSED" => true,
2391 
2392             // FIXME: conflicts with glibc headers and is tested in
2393             // `linux_termios.rs` below:
2394             "BOTHER" => true,
2395 
2396             // FIXME: on musl the pthread types are defined a little differently
2397             // - these constants are used by the glibc implementation.
2398             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
2399 
2400             _ => false,
2401         }
2402     });
2403 
2404     cfg.skip_fn(move |name| {
2405         // skip those that are manually verified
2406         match name {
2407             // FIXME: https://github.com/rust-lang/libc/issues/1272
2408             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2409 
2410             // There are two versions of the sterror_r function, see
2411             //
2412             // https://linux.die.net/man/3/strerror_r
2413             //
2414             // An XSI-compliant version provided if:
2415             //
2416             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
2417             //  && ! _GNU_SOURCE
2418             //
2419             // and a GNU specific version provided if _GNU_SOURCE is defined.
2420             //
2421             // libc provides bindings for the XSI-compliant version, which is
2422             // preferred for portable applications.
2423             //
2424             // We skip the test here since here _GNU_SOURCE is defined, and
2425             // test the XSI version below.
2426             "strerror_r" => true,
2427 
2428             // FIXME: Our API is unsound. The Rust API allows aliasing
2429             // pointers, but the C API requires pointers not to alias.
2430             // We should probably be at least using `&`/`&mut` here, see:
2431             // https://github.com/gnzlbg/ctest/issues/68
2432             "lio_listio" if musl => true,
2433 
2434             // FIXME: the glibc version used by the Sparc64 build jobs
2435             // which use Debian 10.0 is too old.
2436             "statx" if sparc64 => true,
2437 
2438             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
2439             "sysctl" if gnu => true,
2440 
2441             _ => false,
2442         }
2443     });
2444 
2445     cfg.skip_field_type(move |struct_, field| {
2446         // This is a weird union, don't check the type.
2447         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2448         // sighandler_t type is super weird
2449         (struct_ == "sigaction" && field == "sa_sigaction") ||
2450         // __timeval type is a patch which doesn't exist in glibc
2451         (struct_ == "utmpx" && field == "ut_tv") ||
2452         // sigval is actually a union, but we pretend it's a struct
2453         (struct_ == "sigevent" && field == "sigev_value") ||
2454         // this one is an anonymous union
2455         (struct_ == "ff_effect" && field == "u")
2456     });
2457 
2458     cfg.volatile_item(|i| {
2459         use ctest::VolatileItemKind::*;
2460         match i {
2461             // aio_buf is a volatile void** but since we cannot express that in
2462             // Rust types, we have to explicitly tell the checker about it here:
2463             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
2464                 true
2465             }
2466             _ => false,
2467         }
2468     });
2469 
2470     cfg.skip_field(move |struct_, field| {
2471         // this is actually a union on linux, so we can't represent it well and
2472         // just insert some padding.
2473         (struct_ == "siginfo_t" && field == "_pad") ||
2474         // musl names this __dummy1 but it's still there
2475         (musl && struct_ == "glob_t" && field == "gl_flags") ||
2476         // musl seems to define this as an *anonymous* bitfield
2477         (musl && struct_ == "statvfs" && field == "__f_unused") ||
2478         // sigev_notify_thread_id is actually part of a sigev_un union
2479         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2480         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
2481         // has them yet.
2482         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2483                                            field == "_pad2" ||
2484                                            field == "ssi_syscall" ||
2485                                            field == "ssi_call_addr" ||
2486                                            field == "ssi_arch")) ||
2487         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2488         // while other fields become reserved.
2489         (struct_ == "sched_param" && [
2490             "sched_ss_low_priority",
2491             "sched_ss_repl_period",
2492             "sched_ss_init_budget",
2493             "sched_ss_max_repl",
2494         ].contains(&field) && musl) ||
2495         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
2496         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl)
2497     });
2498 
2499     cfg.skip_roundtrip(move |s| match s {
2500         // FIXME:
2501         "utsname" if mips32 || mips64 => true,
2502         // FIXME:
2503         "mcontext_t" if s390x => true,
2504 
2505         "sockaddr_un" | "sembuf" | "ff_constant_effect"
2506             if mips32 && (gnu || musl) =>
2507         {
2508             true
2509         }
2510         "ipv6_mreq"
2511         | "ip_mreq_source"
2512         | "sockaddr_in6"
2513         | "sockaddr_ll"
2514         | "in_pktinfo"
2515         | "arpreq"
2516         | "arpreq_old"
2517         | "sockaddr_un"
2518         | "ff_constant_effect"
2519         | "ff_ramp_effect"
2520         | "ff_condition_effect"
2521         | "Elf32_Ehdr"
2522         | "Elf32_Chdr"
2523         | "ucred"
2524         | "in6_pktinfo"
2525         | "sockaddr_nl"
2526         | "termios"
2527         | "nlmsgerr"
2528             if (mips64 || sparc64) && gnu =>
2529         {
2530             true
2531         }
2532 
2533         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
2534         "max_align_t" if i686 || mips64 || ppc64 => true,
2535 
2536         _ => false,
2537     });
2538 
2539     cfg.generate("../src/lib.rs", "main.rs");
2540 
2541     test_linux_like_apis(target);
2542 }
2543 
2544 // This function tests APIs that are incompatible to test when other APIs
2545 // are included (e.g. because including both sets of headers clashes)
2546 fn test_linux_like_apis(target: &str) {
2547     let musl = target.contains("musl");
2548     let linux = target.contains("linux");
2549     let emscripten = target.contains("emscripten");
2550     let android = target.contains("android");
2551     assert!(linux || android || emscripten);
2552 
2553     if linux || android || emscripten {
2554         // test strerror_r from the `string.h` header
2555         let mut cfg = ctest_cfg();
2556         cfg.skip_type(|_| true).skip_static(|_| true);
2557 
2558         headers! { cfg: "string.h" }
2559         cfg.skip_fn(|f| match f {
2560             "strerror_r" => false,
2561             _ => true,
2562         })
2563         .skip_const(|_| true)
2564         .skip_struct(|_| true);
2565         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
2566     }
2567 
2568     if linux || android || emscripten {
2569         // test fcntl - see:
2570         // http://man7.org/linux/man-pages/man2/fcntl.2.html
2571         let mut cfg = ctest_cfg();
2572 
2573         if musl {
2574             cfg.header("fcntl.h");
2575         } else {
2576             cfg.header("linux/fcntl.h");
2577         }
2578 
2579         cfg.skip_type(|_| true)
2580             .skip_static(|_| true)
2581             .skip_struct(|_| true)
2582             .skip_fn(|_| true)
2583             .skip_const(move |name| match name {
2584                 // test fcntl constants:
2585                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS"
2586                 | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2587                 | "F_SEAL_WRITE" => false,
2588                 _ => true,
2589             })
2590             .type_name(move |ty, is_struct, is_union| match ty {
2591                 t if is_struct => format!("struct {}", t),
2592                 t if is_union => format!("union {}", t),
2593                 t => t.to_string(),
2594             });
2595 
2596         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
2597     }
2598 
2599     if linux || android {
2600         // test termios
2601         let mut cfg = ctest_cfg();
2602         cfg.header("asm/termbits.h");
2603         cfg.skip_type(|_| true)
2604             .skip_static(|_| true)
2605             .skip_fn(|_| true)
2606             .skip_const(|c| c != "BOTHER")
2607             .skip_struct(|s| s != "termios2")
2608             .type_name(move |ty, is_struct, is_union| match ty {
2609                 t if is_struct => format!("struct {}", t),
2610                 t if is_union => format!("union {}", t),
2611                 t => t.to_string(),
2612             });
2613         cfg.generate("../src/lib.rs", "linux_termios.rs");
2614     }
2615 
2616     if linux || android {
2617         // test IPV6_ constants:
2618         let mut cfg = ctest_cfg();
2619         headers! {
2620             cfg:
2621             "linux/in6.h"
2622         }
2623         cfg.skip_type(|_| true)
2624             .skip_static(|_| true)
2625             .skip_fn(|_| true)
2626             .skip_const(|_| true)
2627             .skip_struct(|_| true)
2628             .skip_const(move |name| match name {
2629                 "IPV6_FLOWINFO"
2630                 | "IPV6_FLOWLABEL_MGR"
2631                 | "IPV6_FLOWINFO_SEND"
2632                 | "IPV6_FLOWINFO_FLOWLABEL"
2633                 | "IPV6_FLOWINFO_PRIORITY" => false,
2634                 _ => true,
2635             })
2636             .type_name(move |ty, is_struct, is_union| match ty {
2637                 t if is_struct => format!("struct {}", t),
2638                 t if is_union => format!("union {}", t),
2639                 t => t.to_string(),
2640             });
2641         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
2642     }
2643 
2644     if linux || android {
2645         // Test Elf64_Phdr and Elf32_Phdr
2646         // These types have a field called `p_type`, but including
2647         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
2648         // making the tests for these fails when both are included.
2649         let mut cfg = ctest_cfg();
2650         cfg.header("elf.h");
2651         cfg.skip_fn(|_| true)
2652             .skip_static(|_| true)
2653             .skip_fn(|_| true)
2654             .skip_const(|_| true)
2655             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
2656             .skip_struct(move |ty| match ty {
2657                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2658                 _ => true,
2659             })
2660             .skip_type(move |ty| match ty {
2661                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2662                 _ => true,
2663             });
2664         cfg.generate("../src/lib.rs", "linux_elf.rs");
2665     }
2666 }
2667 
2668 fn which_freebsd() -> Option<i32> {
2669     let output = std::process::Command::new("freebsd-version")
2670         .output()
2671         .ok()?;
2672     if !output.status.success() {
2673         return None;
2674     }
2675 
2676     let stdout = String::from_utf8(output.stdout).ok()?;
2677 
2678     match &stdout {
2679         s if s.starts_with("10") => Some(10),
2680         s if s.starts_with("11") => Some(11),
2681         s if s.starts_with("12") => Some(12),
2682         s if s.starts_with("13") => Some(13),
2683         _ => None,
2684     }
2685 }
2686