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