xref: /rust-libc-0.2.174/libc-test/build.rs (revision 10347e86)
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/auxv.h",
1426                "sys/epoll.h",
1427                "sys/eventfd.h",
1428                "sys/file.h",
1429                "sys/fsuid.h",
1430                "sys/inotify.h",
1431                "sys/ioctl.h",
1432                "sys/mman.h",
1433                "sys/mount.h",
1434                "sys/personality.h",
1435                "sys/prctl.h",
1436                "sys/ptrace.h",
1437                "sys/random.h",
1438                "sys/reboot.h",
1439                "sys/resource.h",
1440                "sys/sendfile.h",
1441                "sys/signalfd.h",
1442                "sys/socket.h",
1443                "sys/stat.h",
1444                "sys/statvfs.h",
1445                "sys/swap.h",
1446                "sys/syscall.h",
1447                "sys/sysinfo.h",
1448                "sys/time.h",
1449                "sys/times.h",
1450                "sys/timerfd.h",
1451                "sys/types.h",
1452                "sys/ucontext.h",
1453                "sys/uio.h",
1454                "sys/un.h",
1455                "sys/utsname.h",
1456                "sys/vfs.h",
1457                "sys/xattr.h",
1458                "sys/wait.h",
1459                "syslog.h",
1460                "termios.h",
1461                "time.h",
1462                "unistd.h",
1463                "utime.h",
1464                "utmp.h",
1465                "wchar.h",
1466                "xlocale.h",
1467                // time64_t is not defined for 64-bit targets If included it will
1468                // generate the error 'Your time_t is already 64-bit'
1469                [target_pointer_width == 32]: "time64.h",
1470                [x86]: "sys/reg.h",
1471     }
1472 
1473     // Include linux headers at the end:
1474     headers! { cfg:
1475                 "asm/mman.h",
1476                 "linux/auxvec.h",
1477                 "linux/dccp.h",
1478                 "linux/errqueue.h",
1479                 "linux/falloc.h",
1480                 "linux/futex.h",
1481                 "linux/fs.h",
1482                 "linux/genetlink.h",
1483                 "linux/if_alg.h",
1484                 "linux/if_ether.h",
1485                 "linux/if_tun.h",
1486                 "linux/magic.h",
1487                 "linux/memfd.h",
1488                 "linux/module.h",
1489                 "linux/net_tstamp.h",
1490                 "linux/netfilter/nfnetlink.h",
1491                 "linux/netfilter/nfnetlink_log.h",
1492                 "linux/netfilter/nfnetlink_queue.h",
1493                 "linux/netfilter/nf_tables.h",
1494                 "linux/netfilter_ipv4.h",
1495                 "linux/netfilter_ipv6.h",
1496                 "linux/netfilter_ipv6/ip6_tables.h",
1497                 "linux/netlink.h",
1498                 "linux/quota.h",
1499                 "linux/reboot.h",
1500                 "linux/seccomp.h",
1501                 "linux/sched.h",
1502                 "linux/sockios.h",
1503                 "linux/vm_sockets.h",
1504                 "linux/wait.h",
1505 
1506     }
1507 
1508     cfg.type_name(move |ty, is_struct, is_union| {
1509         match ty {
1510             // Just pass all these through, no need for a "struct" prefix
1511             "FILE" | "fd_set" | "Dl_info" => ty.to_string(),
1512 
1513             t if is_union => format!("union {}", t),
1514 
1515             t if t.ends_with("_t") => t.to_string(),
1516 
1517             // sigval is a struct in Rust, but a union in C:
1518             "sigval" => format!("union sigval"),
1519 
1520             // put `struct` in front of all structs:.
1521             t if is_struct => format!("struct {}", t),
1522 
1523             t => t.to_string(),
1524         }
1525     });
1526 
1527     cfg.field_name(move |struct_, field| {
1528         match field {
1529             // Our stat *_nsec fields normally don't actually exist but are part
1530             // of a timeval struct
1531             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1532                 s.to_string()
1533             }
1534             // FIXME: appears that `epoll_event.data` is an union
1535             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1536             s => s.to_string(),
1537         }
1538     });
1539 
1540     cfg.skip_type(move |ty| {
1541         match ty {
1542             // FIXME: `sighandler_t` type is incorrect, see:
1543             // https://github.com/rust-lang/libc/issues/1359
1544             "sighandler_t" => true,
1545             _ => false,
1546         }
1547     });
1548 
1549     cfg.skip_struct(move |ty| {
1550         if ty.starts_with("__c_anonymous_") {
1551             return true;
1552         }
1553         match ty {
1554             // These are tested as part of the linux_fcntl tests since there are
1555             // header conflicts when including them with all the other structs.
1556             "termios2" => true,
1557             // uc_sigmask and uc_sigmask64 of ucontext_t are an anonymous union
1558             "ucontext_t" => true,
1559 
1560             _ => false,
1561         }
1562     });
1563 
1564     cfg.skip_const(move |name| {
1565         match name {
1566             // FIXME: deprecated: not available in any header
1567             // See: https://github.com/rust-lang/libc/issues/1356
1568             "ENOATTR" => true,
1569 
1570             // FIXME: still necessary?
1571             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1572             // FIXME: deprecated - removed in glibc 2.26
1573             "SIGUNUSED" => true,
1574 
1575             // Needs a newer Android SDK for the definition
1576             "P_PIDFD" => true,
1577 
1578             // Requires Linux kernel 5.6
1579             "VMADDR_CID_LOCAL" => true,
1580 
1581             _ => false,
1582         }
1583     });
1584 
1585     cfg.skip_fn(move |name| {
1586         // skip those that are manually verified
1587         match name {
1588             // FIXME: https://github.com/rust-lang/libc/issues/1272
1589             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1590 
1591             // There are two versions of the sterror_r function, see
1592             //
1593             // https://linux.die.net/man/3/strerror_r
1594             //
1595             // An XSI-compliant version provided if:
1596             //
1597             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
1598             //
1599             // and a GNU specific version provided if _GNU_SOURCE is defined.
1600             //
1601             // libc provides bindings for the XSI-compliant version, which is
1602             // preferred for portable applications.
1603             //
1604             // We skip the test here since here _GNU_SOURCE is defined, and
1605             // test the XSI version below.
1606             "strerror_r" => true,
1607 
1608             _ => false,
1609         }
1610     });
1611 
1612     cfg.skip_field_type(move |struct_, field| {
1613         // This is a weird union, don't check the type.
1614         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1615         // sigval is actually a union, but we pretend it's a struct
1616         (struct_ == "sigevent" && field == "sigev_value")
1617     });
1618 
1619     cfg.skip_field(move |struct_, field| {
1620         // this is actually a union on linux, so we can't represent it well and
1621         // just insert some padding.
1622         (struct_ == "siginfo_t" && field == "_pad") ||
1623         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1624         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1625         (struct_ == "sigaction" && field == "sa_sigaction") ||
1626         // sigev_notify_thread_id is actually part of a sigev_un union
1627         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1628         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1629         (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
1630                                            field == "ssi_call_addr" ||
1631                                            field == "ssi_arch"))
1632     });
1633 
1634     cfg.generate("../src/lib.rs", "main.rs");
1635 
1636     test_linux_like_apis(target);
1637 }
1638 
1639 fn test_freebsd(target: &str) {
1640     assert!(target.contains("freebsd"));
1641     let mut cfg = ctest_cfg();
1642 
1643     let freebsd_ver = which_freebsd();
1644 
1645     match freebsd_ver {
1646         Some(10) => cfg.cfg("freebsd10", None),
1647         Some(11) => cfg.cfg("freebsd11", None),
1648         Some(12) => cfg.cfg("freebsd12", None),
1649         Some(13) => cfg.cfg("freebsd13", None),
1650         _ => &mut cfg,
1651     };
1652 
1653     // Required for `getline`:
1654     cfg.define("_WITH_GETLINE", None);
1655     // Required for making freebsd11_stat available in the headers
1656     match freebsd_ver {
1657         Some(10) => &mut cfg,
1658         _ => cfg.define("_WANT_FREEBSD11_STAT", None),
1659     };
1660 
1661     headers! { cfg:
1662                 "aio.h",
1663                 "arpa/inet.h",
1664                 "ctype.h",
1665                 "dirent.h",
1666                 "dlfcn.h",
1667                 "elf.h",
1668                 "errno.h",
1669                 "fcntl.h",
1670                 "glob.h",
1671                 "grp.h",
1672                 "ifaddrs.h",
1673                 "langinfo.h",
1674                 "libutil.h",
1675                 "limits.h",
1676                 "link.h",
1677                 "locale.h",
1678                 "machine/reg.h",
1679                 "mqueue.h",
1680                 "net/bpf.h",
1681                 "net/if.h",
1682                 "net/if_arp.h",
1683                 "net/if_dl.h",
1684                 "net/route.h",
1685                 "netdb.h",
1686                 "netinet/ip.h",
1687                 "netinet/in.h",
1688                 "netinet/tcp.h",
1689                 "netinet/udp.h",
1690                 "poll.h",
1691                 "pthread.h",
1692                 "pthread_np.h",
1693                 "pwd.h",
1694                 "regex.h",
1695                 "resolv.h",
1696                 "sched.h",
1697                 "semaphore.h",
1698                 "signal.h",
1699                 "spawn.h",
1700                 "stddef.h",
1701                 "stdint.h",
1702                 "stdio.h",
1703                 "stdlib.h",
1704                 "string.h",
1705                 "sys/event.h",
1706                 "sys/extattr.h",
1707                 "sys/file.h",
1708                 "sys/ioctl.h",
1709                 "sys/ipc.h",
1710                 "sys/jail.h",
1711                 "sys/mman.h",
1712                 "sys/mount.h",
1713                 "sys/msg.h",
1714                 "sys/procdesc.h",
1715                 "sys/ptrace.h",
1716                 "sys/random.h",
1717                 "sys/resource.h",
1718                 "sys/rtprio.h",
1719                 "sys/shm.h",
1720                 "sys/socket.h",
1721                 "sys/stat.h",
1722                 "sys/statvfs.h",
1723                 "sys/sysctl.h",
1724                 "sys/time.h",
1725                 "sys/times.h",
1726                 "sys/timex.h",
1727                 "sys/types.h",
1728                 "sys/ucontext.h",
1729                 "sys/uio.h",
1730                 "sys/un.h",
1731                 "sys/utsname.h",
1732                 "sys/wait.h",
1733                 "syslog.h",
1734                 "termios.h",
1735                 "time.h",
1736                 "ufs/ufs/quota.h",
1737                 "unistd.h",
1738                 "utime.h",
1739                 "utmpx.h",
1740                 "wchar.h",
1741     }
1742 
1743     cfg.type_name(move |ty, is_struct, is_union| {
1744         match ty {
1745             // Just pass all these through, no need for a "struct" prefix
1746             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr"
1747             | "Elf64_Phdr" => ty.to_string(),
1748 
1749             // FIXME: https://github.com/rust-lang/libc/issues/1273
1750             "sighandler_t" => "sig_t".to_string(),
1751 
1752             t if is_union => format!("union {}", t),
1753 
1754             t if t.ends_with("_t") => t.to_string(),
1755 
1756             // sigval is a struct in Rust, but a union in C:
1757             "sigval" => format!("union sigval"),
1758 
1759             // put `struct` in front of all structs:.
1760             t if is_struct => format!("struct {}", t),
1761 
1762             t => t.to_string(),
1763         }
1764     });
1765 
1766     cfg.field_name(move |struct_, field| {
1767         match field {
1768             // Our stat *_nsec fields normally don't actually exist but are part
1769             // of a timeval struct
1770             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1771                 s.replace("e_nsec", ".tv_nsec")
1772             }
1773             // Field is named `type` in C but that is a Rust keyword,
1774             // so these fields are translated to `type_` in the bindings.
1775             "type_" if struct_ == "rtprio" => "type".to_string(),
1776             s => s.to_string(),
1777         }
1778     });
1779 
1780     cfg.skip_const(move |name| {
1781         match name {
1782             // These constants are to be introduced in yet-unreleased FreeBSD 12.2.
1783             "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL"
1784             | "F_SEAL_SHRINK" | "F_SEAL_GROW" | "F_SEAL_WRITE"
1785                 if Some(12) <= freebsd_ver =>
1786             {
1787                 true
1788             }
1789 
1790             // These constants were introduced in FreeBSD 12:
1791             "SF_USER_READAHEAD"
1792             | "EVFILT_EMPTY"
1793             | "SO_REUSEPORT_LB"
1794             | "IP_ORIGDSTADDR"
1795             | "IP_RECVORIGDSTADDR"
1796             | "IPV6_ORIGDSTADDR"
1797             | "IPV6_RECVORIGDSTADDR"
1798             | "NI_NUMERICSCOPE"
1799                 if Some(11) == freebsd_ver =>
1800             {
1801                 true
1802             }
1803 
1804             // These constants were introduced in FreeBSD 11:
1805             "SF_USER_READAHEAD"
1806             | "SF_NOCACHE"
1807             | "RLIMIT_KQUEUES"
1808             | "RLIMIT_UMTXP"
1809             | "EVFILT_PROCDESC"
1810             | "EVFILT_SENDFILE"
1811             | "EVFILT_EMPTY"
1812             | "SO_REUSEPORT_LB"
1813             | "TCP_CCALGOOPT"
1814             | "TCP_PCAP_OUT"
1815             | "TCP_PCAP_IN"
1816             | "IP_BINDMULTI"
1817             | "IP_ORIGDSTADDR"
1818             | "IP_RECVORIGDSTADDR"
1819             | "IPV6_ORIGDSTADDR"
1820             | "IPV6_RECVORIGDSTADDR"
1821             | "PD_CLOEXEC"
1822             | "PD_ALLOWED_AT_FORK"
1823             | "IP_RSS_LISTEN_BUCKET"
1824             | "NI_NUMERICSCOPE"
1825                 if Some(10) == freebsd_ver =>
1826             {
1827                 true
1828             }
1829 
1830             // FIXME: This constant has a different value in FreeBSD 10:
1831             "RLIM_NLIMITS" if Some(10) == freebsd_ver => true,
1832 
1833             // FIXME: There are deprecated - remove in a couple of releases.
1834             // These constants were removed in FreeBSD 11 (svn r273250) but will
1835             // still be accepted and ignored at runtime.
1836             "MAP_RENAME" | "MAP_NORESERVE" if Some(10) != freebsd_ver => true,
1837 
1838             // FIXME: There are deprecated - remove in a couple of releases.
1839             // These constants were removed in FreeBSD 11 (svn r262489),
1840             // and they've never had any legitimate use outside of the
1841             // base system anyway.
1842             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
1843 
1844             // This constant was removed in FreeBSD 13 (svn r363622), and never
1845             // had any legitimate use outside of the base system anyway.
1846             "CTL_P1003_1B_MAXID" => true,
1847 
1848             // This was renamed in FreeBSD 12.2 and 13 (r352486).
1849             "CTL_UNSPEC" | "CTL_SYSCTL" => true,
1850 
1851             // These were added in FreeBSD 12.2 and 13 (r352486),
1852             // but they are just names for magic numbers that existed for ages.
1853             "CTL_SYSCTL_DEBUG"
1854             | "CTL_SYSCTL_NAME"
1855             | "CTL_SYSCTL_NEXT"
1856             | "CTL_SYSCTL_NAME2OID"
1857             | "CTL_SYSCTL_OIDFMT"
1858             | "CTL_SYSCTL_OIDDESCR"
1859             | "CTL_SYSCTL_OIDLABEL" => true,
1860 
1861             // This was renamed in FreeBSD 12.2 and 13 (r350749).
1862             "IPPROTO_SEP" | "IPPROTO_DCCP" => true,
1863 
1864             // This was changed to 96(0x60) in FreeBSD 13:
1865             // https://github.com/freebsd/freebsd/
1866             // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7
1867             "MINCORE_SUPER" if Some(13) == freebsd_ver => true,
1868 
1869             // This was increased to 97 in FreeBSD 12.2 and 13.
1870             // https://github.com/freebsd/freebsd/
1871             // commit/72a21ba0f62da5e86a1c0b462aeb3f5ff849a1b7
1872             "ELAST" if Some(12) == freebsd_ver => true,
1873 
1874             _ => false,
1875         }
1876     });
1877 
1878     cfg.skip_struct(move |ty| {
1879         match ty {
1880             // `mmsghdr` is not available in FreeBSD 10
1881             "mmsghdr" if Some(10) == freebsd_ver => true,
1882 
1883             // `max_align_t` is not available in FreeBSD 10
1884             "max_align_t" if Some(10) == freebsd_ver => true,
1885 
1886             _ => false,
1887         }
1888     });
1889 
1890     cfg.skip_fn(move |name| {
1891         // skip those that are manually verified
1892         match name {
1893             // FIXME: https://github.com/rust-lang/libc/issues/1272
1894             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1895 
1896             // These functions were added in FreeBSD 11:
1897             "fdatasync" | "mq_getfd_np" | "sendmmsg" | "recvmmsg"
1898                 if Some(10) == freebsd_ver =>
1899             {
1900                 true
1901             }
1902 
1903             // This function changed its return type from `int` in FreeBSD10 to
1904             // `ssize_t` in FreeBSD11:
1905             "aio_waitcomplete" if Some(10) == freebsd_ver => true,
1906 
1907             // The `uname` function in the `utsname.h` FreeBSD header is a C
1908             // inline function (has no symbol) that calls the `__xuname` symbol.
1909             // Therefore the function pointer comparison does not make sense for it.
1910             "uname" => true,
1911 
1912             // FIXME: Our API is unsound. The Rust API allows aliasing
1913             // pointers, but the C API requires pointers not to alias.
1914             // We should probably be at least using `&`/`&mut` here, see:
1915             // https://github.com/gnzlbg/ctest/issues/68
1916             "lio_listio" => true,
1917 
1918             _ => false,
1919         }
1920     });
1921 
1922     cfg.skip_signededness(move |c| {
1923         match c {
1924             // FIXME: has a different sign in FreeBSD10
1925             "blksize_t" if Some(10) == freebsd_ver => true,
1926             _ => false,
1927         }
1928     });
1929 
1930     cfg.volatile_item(|i| {
1931         use ctest::VolatileItemKind::*;
1932         match i {
1933             // aio_buf is a volatile void** but since we cannot express that in
1934             // Rust types, we have to explicitly tell the checker about it here:
1935             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
1936                 true
1937             }
1938             _ => false,
1939         }
1940     });
1941 
1942     cfg.skip_field(move |struct_, field| {
1943         match (struct_, field) {
1944             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1945             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1946             ("sigaction", "sa_sigaction") => true,
1947 
1948             // FIXME: in FreeBSD10 this field has type `char*` instead of
1949             // `void*`:
1950             ("stack_t", "ss_sp") if Some(10) == freebsd_ver => true,
1951 
1952             // conflicting with `p_type` macro from <resolve.h>.
1953             ("Elf32_Phdr", "p_type") => true,
1954             ("Elf64_Phdr", "p_type") => true,
1955 
1956             _ => false,
1957         }
1958     });
1959 
1960     cfg.generate("../src/lib.rs", "main.rs");
1961 }
1962 
1963 fn test_emscripten(target: &str) {
1964     assert!(target.contains("emscripten"));
1965 
1966     let mut cfg = ctest_cfg();
1967     cfg.define("_GNU_SOURCE", None); // FIXME: ??
1968 
1969     headers! { cfg:
1970                "aio.h",
1971                "ctype.h",
1972                "dirent.h",
1973                "dlfcn.h",
1974                "errno.h",
1975                "fcntl.h",
1976                "glob.h",
1977                "grp.h",
1978                "ifaddrs.h",
1979                "langinfo.h",
1980                "limits.h",
1981                "locale.h",
1982                "malloc.h",
1983                "mntent.h",
1984                "mqueue.h",
1985                "net/ethernet.h",
1986                "net/if.h",
1987                "net/if_arp.h",
1988                "net/route.h",
1989                "netdb.h",
1990                "netinet/in.h",
1991                "netinet/ip.h",
1992                "netinet/tcp.h",
1993                "netinet/udp.h",
1994                "netpacket/packet.h",
1995                "poll.h",
1996                "pthread.h",
1997                "pty.h",
1998                "pwd.h",
1999                "resolv.h",
2000                "sched.h",
2001                "sched.h",
2002                "semaphore.h",
2003                "shadow.h",
2004                "signal.h",
2005                "stddef.h",
2006                "stdint.h",
2007                "stdio.h",
2008                "stdlib.h",
2009                "string.h",
2010                "sys/epoll.h",
2011                "sys/eventfd.h",
2012                "sys/file.h",
2013                "sys/ioctl.h",
2014                "sys/ipc.h",
2015                "sys/mman.h",
2016                "sys/mount.h",
2017                "sys/msg.h",
2018                "sys/personality.h",
2019                "sys/prctl.h",
2020                "sys/ptrace.h",
2021                "sys/quota.h",
2022                "sys/reboot.h",
2023                "sys/resource.h",
2024                "sys/sem.h",
2025                "sys/sendfile.h",
2026                "sys/shm.h",
2027                "sys/signalfd.h",
2028                "sys/socket.h",
2029                "sys/stat.h",
2030                "sys/statvfs.h",
2031                "sys/swap.h",
2032                "sys/syscall.h",
2033                "sys/sysctl.h",
2034                "sys/sysinfo.h",
2035                "sys/time.h",
2036                "sys/timerfd.h",
2037                "sys/times.h",
2038                "sys/types.h",
2039                "sys/uio.h",
2040                "sys/un.h",
2041                "sys/user.h",
2042                "sys/utsname.h",
2043                "sys/vfs.h",
2044                "sys/wait.h",
2045                "sys/xattr.h",
2046                "syslog.h",
2047                "termios.h",
2048                "time.h",
2049                "ucontext.h",
2050                "unistd.h",
2051                "utime.h",
2052                "utmp.h",
2053                "utmpx.h",
2054                "wchar.h",
2055     }
2056 
2057     cfg.type_name(move |ty, is_struct, is_union| {
2058         match ty {
2059             // Just pass all these through, no need for a "struct" prefix
2060             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2061 
2062             t if is_union => format!("union {}", t),
2063 
2064             t if t.ends_with("_t") => t.to_string(),
2065 
2066             // put `struct` in front of all structs:.
2067             t if is_struct => format!("struct {}", t),
2068 
2069             t => t.to_string(),
2070         }
2071     });
2072 
2073     cfg.field_name(move |struct_, field| {
2074         match field {
2075             // Our stat *_nsec fields normally don't actually exist but are part
2076             // of a timeval struct
2077             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2078                 s.replace("e_nsec", ".tv_nsec")
2079             }
2080             // FIXME: appears that `epoll_event.data` is an union
2081             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2082             s => s.to_string(),
2083         }
2084     });
2085 
2086     cfg.skip_type(move |ty| {
2087         match ty {
2088             // sighandler_t is crazy across platforms
2089             // FIXME: is this necessary?
2090             "sighandler_t" => true,
2091 
2092             _ => false,
2093         }
2094     });
2095 
2096     cfg.skip_struct(move |ty| {
2097         match ty {
2098             // This is actually a union, not a struct
2099             // FIXME: is this necessary?
2100             "sigval" => true,
2101 
2102             // FIXME: It was removed in
2103             // emscripten-core/emscripten@953e414
2104             "pthread_mutexattr_t" => true,
2105 
2106             // FIXME: Investigate why the test fails.
2107             // Skip for now to unblock CI.
2108             "pthread_condattr_t" => true,
2109 
2110             _ => false,
2111         }
2112     });
2113 
2114     cfg.skip_fn(move |name| {
2115         match name {
2116             // FIXME: https://github.com/rust-lang/libc/issues/1272
2117             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2118 
2119             // FIXME: Investigate why CI is missing it.
2120             "clearenv" => true,
2121 
2122             _ => false,
2123         }
2124     });
2125 
2126     cfg.skip_const(move |name| {
2127         match name {
2128             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2129             // users should use SIGSYS instead
2130             "SIGUNUSED" => true,
2131 
2132             // FIXME: emscripten uses different constants to constructs these
2133             n if n.contains("__SIZEOF_PTHREAD") => true,
2134 
2135             // FIXME: `SYS_gettid` was removed in
2136             // emscripten-core/emscripten@6d6474e
2137             "SYS_gettid" => true,
2138 
2139             _ => false,
2140         }
2141     });
2142 
2143     cfg.skip_field_type(move |struct_, field| {
2144         // This is a weird union, don't check the type.
2145         // FIXME: is this necessary?
2146         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2147         // sighandler_t type is super weird
2148         // FIXME: is this necessary?
2149         (struct_ == "sigaction" && field == "sa_sigaction") ||
2150         // sigval is actually a union, but we pretend it's a struct
2151         // FIXME: is this necessary?
2152         (struct_ == "sigevent" && field == "sigev_value") ||
2153         // aio_buf is "volatile void*" and Rust doesn't understand volatile
2154         // FIXME: is this necessary?
2155         (struct_ == "aiocb" && field == "aio_buf")
2156     });
2157 
2158     cfg.skip_field(move |struct_, field| {
2159         // this is actually a union on linux, so we can't represent it well and
2160         // just insert some padding.
2161         // FIXME: is this necessary?
2162         (struct_ == "siginfo_t" && field == "_pad") ||
2163         // musl names this __dummy1 but it's still there
2164         // FIXME: is this necessary?
2165         (struct_ == "glob_t" && field == "gl_flags") ||
2166         // musl seems to define this as an *anonymous* bitfield
2167         // FIXME: is this necessary?
2168         (struct_ == "statvfs" && field == "__f_unused") ||
2169         // sigev_notify_thread_id is actually part of a sigev_un union
2170         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2171         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
2172         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2173                                            field == "_pad2" ||
2174                                            field == "ssi_syscall" ||
2175                                            field == "ssi_call_addr" ||
2176                                            field == "ssi_arch"))
2177     });
2178 
2179     // FIXME: test linux like
2180     cfg.generate("../src/lib.rs", "main.rs");
2181 }
2182 
2183 fn test_vxworks(target: &str) {
2184     assert!(target.contains("vxworks"));
2185 
2186     let mut cfg = ctest::TestGenerator::new();
2187     headers! { cfg:
2188                "vxWorks.h",
2189                "yvals.h",
2190                "nfs/nfsCommon.h",
2191                "rtpLibCommon.h",
2192                "randomNumGen.h",
2193                "taskLib.h",
2194                "sysLib.h",
2195                "ioLib.h",
2196                "inetLib.h",
2197                "socket.h",
2198                "errnoLib.h",
2199                "ctype.h",
2200                "dirent.h",
2201                "dlfcn.h",
2202                "elf.h",
2203                "fcntl.h",
2204                "grp.h",
2205                "sys/poll.h",
2206                "ifaddrs.h",
2207                "langinfo.h",
2208                "limits.h",
2209                "link.h",
2210                "locale.h",
2211                "sys/stat.h",
2212                "netdb.h",
2213                "pthread.h",
2214                "pwd.h",
2215                "sched.h",
2216                "semaphore.h",
2217                "signal.h",
2218                "stddef.h",
2219                "stdint.h",
2220                "stdio.h",
2221                "stdlib.h",
2222                "string.h",
2223                "sys/file.h",
2224                "sys/ioctl.h",
2225                "sys/socket.h",
2226                "sys/time.h",
2227                "sys/times.h",
2228                "sys/types.h",
2229                "sys/uio.h",
2230                "sys/un.h",
2231                "sys/utsname.h",
2232                "sys/wait.h",
2233                "netinet/tcp.h",
2234                "syslog.h",
2235                "termios.h",
2236                "time.h",
2237                "ucontext.h",
2238                "unistd.h",
2239                "utime.h",
2240                "wchar.h",
2241                "errno.h",
2242                "sys/mman.h",
2243                "pathLib.h",
2244                "mqueue.h",
2245     }
2246     // FIXME
2247     cfg.skip_const(move |name| match name {
2248         // sighandler_t weirdness
2249         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
2250         // This is not defined in vxWorks
2251         | "RTLD_DEFAULT"   => true,
2252         _ => false,
2253     });
2254     // FIXME
2255     cfg.skip_type(move |ty| match ty {
2256         "stat64" | "sighandler_t" | "off64_t" => true,
2257         _ => false,
2258     });
2259 
2260     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
2261         ("siginfo_t", "si_value")
2262         | ("stat", "st_size")
2263         | ("sigaction", "sa_u") => true,
2264         _ => false,
2265     });
2266 
2267     cfg.skip_roundtrip(move |s| match s {
2268         _ => false,
2269     });
2270 
2271     cfg.type_name(move |ty, is_struct, is_union| match ty {
2272         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
2273         t if is_union => format!("union {}", t),
2274         t if t.ends_with("_t") => t.to_string(),
2275         t if is_struct => format!("struct {}", t),
2276         t => t.to_string(),
2277     });
2278 
2279     // FIXME
2280     cfg.skip_fn(move |name| match name {
2281         // sigval
2282         "sigqueue" | "_sigqueue"
2283         // sighandler_t
2284         | "signal"
2285         // not used in static linking by default
2286         | "dlerror" => true,
2287         _ => false,
2288     });
2289 
2290     cfg.generate("../src/lib.rs", "main.rs");
2291 }
2292 
2293 fn test_linux(target: &str) {
2294     assert!(target.contains("linux"));
2295 
2296     // target_env
2297     let gnu = target.contains("gnu");
2298     let musl = target.contains("musl");
2299     let uclibc = target.contains("uclibc");
2300 
2301     match (gnu, musl, uclibc) {
2302         (true, false, false) => (),
2303         (false, true, false) => (),
2304         (false, false, true) => (),
2305         (_, _, _) => panic!(
2306             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
2307             gnu, musl, uclibc
2308         ),
2309     }
2310 
2311     let arm = target.contains("arm");
2312     let i686 = target.contains("i686");
2313     let mips = target.contains("mips");
2314     let mips32 = mips && !target.contains("64");
2315     let mips64 = mips && target.contains("64");
2316     let ppc64 = target.contains("powerpc64");
2317     let s390x = target.contains("s390x");
2318     let sparc64 = target.contains("sparc64");
2319     let x32 = target.contains("x32");
2320     let x86_32 = target.contains("i686");
2321     let x86_64 = target.contains("x86_64");
2322     let aarch64_musl = target.contains("aarch64") && musl;
2323     let gnuabihf = target.contains("gnueabihf");
2324     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
2325     let riscv64 = target.contains("riscv64");
2326 
2327     let mut cfg = ctest_cfg();
2328     cfg.define("_GNU_SOURCE", None);
2329     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
2330     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
2331     // glibc versions older than 2.29.
2332     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
2333 
2334     headers! { cfg:
2335                "ctype.h",
2336                "dirent.h",
2337                "dlfcn.h",
2338                "elf.h",
2339                "fcntl.h",
2340                "glob.h",
2341                "grp.h",
2342                "ifaddrs.h",
2343                "langinfo.h",
2344                "limits.h",
2345                "link.h",
2346                "locale.h",
2347                "malloc.h",
2348                "mntent.h",
2349                "mqueue.h",
2350                "net/ethernet.h",
2351                "net/if.h",
2352                "net/if_arp.h",
2353                "net/route.h",
2354                "netdb.h",
2355                "netinet/in.h",
2356                "netinet/ip.h",
2357                "netinet/tcp.h",
2358                "netinet/udp.h",
2359                "netpacket/packet.h",
2360                "poll.h",
2361                "pthread.h",
2362                "pty.h",
2363                "pwd.h",
2364                "regex.h",
2365                "resolv.h",
2366                "sched.h",
2367                "semaphore.h",
2368                "shadow.h",
2369                "signal.h",
2370                "spawn.h",
2371                "stddef.h",
2372                "stdint.h",
2373                "stdio.h",
2374                "stdlib.h",
2375                "string.h",
2376                "sys/epoll.h",
2377                "sys/eventfd.h",
2378                "sys/file.h",
2379                "sys/fsuid.h",
2380                "sys/inotify.h",
2381                "sys/ioctl.h",
2382                "sys/ipc.h",
2383                "sys/mman.h",
2384                "sys/mount.h",
2385                "sys/msg.h",
2386                "sys/personality.h",
2387                "sys/prctl.h",
2388                "sys/ptrace.h",
2389                "sys/quota.h",
2390                "sys/random.h",
2391                "sys/reboot.h",
2392                "sys/resource.h",
2393                "sys/sem.h",
2394                "sys/sendfile.h",
2395                "sys/shm.h",
2396                "sys/signalfd.h",
2397                "sys/socket.h",
2398                "sys/stat.h",
2399                "sys/statvfs.h",
2400                "sys/swap.h",
2401                "sys/syscall.h",
2402                "sys/time.h",
2403                "sys/timerfd.h",
2404                "sys/times.h",
2405                "sys/timex.h",
2406                "sys/types.h",
2407                "sys/uio.h",
2408                "sys/un.h",
2409                "sys/user.h",
2410                "sys/utsname.h",
2411                "sys/vfs.h",
2412                "sys/wait.h",
2413                "syslog.h",
2414                "termios.h",
2415                "time.h",
2416                "ucontext.h",
2417                "unistd.h",
2418                "utime.h",
2419                "utmp.h",
2420                "utmpx.h",
2421                "wchar.h",
2422                "errno.h",
2423                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
2424                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
2425                // Also unavailable on gnuabihf with glibc 2.30.
2426                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
2427                [(x86_64 || x86_32 || arm) && !gnuabihf]: "sys/io.h",
2428                // `sys/reg.h` is only available on x86 and x86_64
2429                [x86_64 || x86_32]: "sys/reg.h",
2430                // sysctl system call is deprecated and not available on musl
2431                // It is also unsupported in x32, deprecated since glibc 2.30:
2432                [!(x32 || musl || gnu)]: "sys/sysctl.h",
2433                // <execinfo.h> is not supported by musl:
2434                // https://www.openwall.com/lists/musl/2015/04/09/3
2435                [!musl]: "execinfo.h",
2436     }
2437 
2438     // Include linux headers at the end:
2439     headers! {
2440         cfg:
2441         "asm/mman.h",
2442         "linux/dccp.h",
2443         "linux/errqueue.h",
2444         "linux/falloc.h",
2445         "linux/fs.h",
2446         "linux/futex.h",
2447         "linux/genetlink.h",
2448         "linux/if.h",
2449         "linux/if_addr.h",
2450         "linux/if_alg.h",
2451         "linux/if_ether.h",
2452         "linux/if_tun.h",
2453         "linux/input.h",
2454         "linux/keyctl.h",
2455         "linux/magic.h",
2456         "linux/memfd.h",
2457         "linux/mman.h",
2458         "linux/module.h",
2459         "linux/net_tstamp.h",
2460         "linux/netfilter/nfnetlink.h",
2461         "linux/netfilter/nfnetlink_log.h",
2462         "linux/netfilter/nfnetlink_queue.h",
2463         "linux/netfilter/nf_tables.h",
2464         "linux/netfilter_ipv4.h",
2465         "linux/netfilter_ipv6.h",
2466         "linux/netfilter_ipv6/ip6_tables.h",
2467         "linux/netlink.h",
2468         "linux/quota.h",
2469         "linux/random.h",
2470         "linux/reboot.h",
2471         "linux/rtnetlink.h",
2472         "linux/seccomp.h",
2473         "linux/sockios.h",
2474         "linux/vm_sockets.h",
2475         "linux/wait.h",
2476         "sys/auxv.h",
2477         "sys/fanotify.h",
2478     }
2479 
2480     // note: aio.h must be included before sys/mount.h
2481     headers! {
2482         cfg:
2483         "sys/xattr.h",
2484         "sys/sysinfo.h",
2485         "aio.h",
2486     }
2487 
2488     cfg.type_name(move |ty, is_struct, is_union| {
2489         match ty {
2490             // Just pass all these through, no need for a "struct" prefix
2491             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr"
2492             | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym"
2493             | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr"
2494             | "Elf64_Chdr" => ty.to_string(),
2495 
2496             t if is_union => format!("union {}", t),
2497 
2498             t if t.ends_with("_t") => t.to_string(),
2499 
2500             // In MUSL `flock64` is a typedef to `flock`.
2501             "flock64" if musl => format!("struct {}", ty),
2502 
2503             // put `struct` in front of all structs:.
2504             t if is_struct => format!("struct {}", t),
2505 
2506             t => t.to_string(),
2507         }
2508     });
2509 
2510     cfg.field_name(move |struct_, field| {
2511         match field {
2512             // Our stat *_nsec fields normally don't actually exist but are part
2513             // of a timeval struct
2514             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2515                 s.replace("e_nsec", ".tv_nsec")
2516             }
2517             // FIXME: epoll_event.data is actuall a union in C, but in Rust
2518             // it is only a u64 because we only expose one field
2519             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
2520             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2521             // The following structs have a field called `type` in C,
2522             // but `type` is a Rust keyword, so these fields are translated
2523             // to `type_` in Rust.
2524             "type_"
2525                 if struct_ == "input_event"
2526                     || struct_ == "input_mask"
2527                     || struct_ == "ff_effect" =>
2528             {
2529                 "type".to_string()
2530             }
2531 
2532             s => s.to_string(),
2533         }
2534     });
2535 
2536     cfg.skip_type(move |ty| {
2537         match ty {
2538             // FIXME: `sighandler_t` type is incorrect, see:
2539             // https://github.com/rust-lang/libc/issues/1359
2540             "sighandler_t" => true,
2541 
2542             // These cannot be tested when "resolv.h" is included and are tested
2543             // in the `linux_elf.rs` file.
2544             "Elf64_Phdr" | "Elf32_Phdr" => true,
2545 
2546             // This type is private on Linux. It is implemented as a C `enum`
2547             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
2548             // which expect a `c_int` even though both are ABI compatible.
2549             "__rlimit_resource_t" => true,
2550 
2551             _ => false,
2552         }
2553     });
2554 
2555     cfg.skip_struct(move |ty| {
2556         match ty {
2557             // These cannot be tested when "resolv.h" is included and are tested
2558             // in the `linux_elf.rs` file.
2559             "Elf64_Phdr" | "Elf32_Phdr" => true,
2560 
2561             // On Linux, the type of `ut_tv` field of `struct utmpx`
2562             // can be an anonymous struct, so an extra struct,
2563             // which is absent in glibc, has to be defined.
2564             "__timeval" => true,
2565 
2566             // FIXME: This is actually a union, not a struct
2567             "sigval" => true,
2568 
2569             // This type is tested in the `linux_termios.rs` file since there
2570             // are header conflicts when including them with all the other
2571             // structs.
2572             "termios2" => true,
2573 
2574             // FIXME: remove once we set minimum supported glibc version.
2575             // ucontext_t added a new field as of glibc 2.28; our struct definition is
2576             // conservative and omits the field, but that means the size doesn't match for newer
2577             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
2578             "ucontext_t" if gnu => true,
2579 
2580             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
2581             // So let's ignore for now and re-visit later.
2582             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
2583             "statx" => true,
2584             "statx_timestamp" => true,
2585 
2586             // On Linux, the type of `ut_exit` field of struct `utmpx`
2587             // can be an anonymous struct, so an extra struct,
2588             // which is absent in musl, has to be defined.
2589             "__exit_status" if musl => true,
2590 
2591             _ => false,
2592         }
2593     });
2594 
2595     cfg.skip_const(move |name| {
2596         match name {
2597             // These constants are not available if gnu headers have been included
2598             // and can therefore not be tested here
2599             //
2600             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
2601             | "IPV6_FLOWINFO"
2602             | "IPV6_FLOWLABEL_MGR"
2603             | "IPV6_FLOWINFO_SEND"
2604             | "IPV6_FLOWINFO_FLOWLABEL"
2605             | "IPV6_FLOWINFO_PRIORITY"
2606             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
2607             | "F_CANCELLK"
2608             | "F_ADD_SEALS"
2609             | "F_GET_SEALS"
2610             | "F_SEAL_SEAL"
2611             | "F_SEAL_SHRINK"
2612             | "F_SEAL_GROW"
2613             | "F_SEAL_WRITE" => true,
2614 
2615             // The musl-sanitized kernel headers used in CI
2616             // target the Linux kernel 4.4 and do not contain the
2617             // following constants:
2618             //
2619             // Requires Linux kernel 4.9
2620             | "FALLOC_FL_UNSHARE_RANGE"
2621             //
2622             // Require Linux kernel 5.x:
2623             | "MSG_COPY"
2624                if musl  => true,
2625             // Require Linux kernel 5.1:
2626             "F_SEAL_FUTURE_WRITE" => true,
2627 
2628             // The musl version 1.1.24 used in CI does not
2629             // contain these glibc constants yet:
2630             | "RLIMIT_RTTIME" // should be in `resource.h`
2631             | "TCP_COOKIE_TRANSACTIONS"  // should be in the `netinet/tcp.h` header
2632                 if musl => true,
2633 
2634             // FIXME: deprecated: not available in any header
2635             // See: https://github.com/rust-lang/libc/issues/1356
2636             "ENOATTR" => true,
2637 
2638             // FIXME: SIGUNUSED was removed in glibc 2.26
2639             // Users should use SIGSYS instead.
2640             "SIGUNUSED" => true,
2641 
2642             // FIXME: conflicts with glibc headers and is tested in
2643             // `linux_termios.rs` below:
2644             "BOTHER" => true,
2645 
2646             // FIXME: on musl the pthread types are defined a little differently
2647             // - these constants are used by the glibc implementation.
2648             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
2649 
2650             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
2651             // We should do so after a while.
2652             "SOMAXCONN" if gnu => true,
2653 
2654             // deprecated: not available from Linux kernel 5.6:
2655             "VMADDR_CID_RESERVED" => true,
2656 
2657             // Require Linux kernel 5.6:
2658             "VMADDR_CID_LOCAL" => true,
2659 
2660             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
2661             | "IPPROTO_MAX"
2662             | "IPPROTO_MPTCP" => true,
2663 
2664             // Defined in kernel headers but musl removes it; need musl 1.2 for definition in musl
2665             // headers.
2666             "P_PIDFD" => true,
2667 
2668             // FIXME: Not currently available in headers
2669             "SYS_pidfd_open" if mips => true,
2670 
2671             // FIXME: Not currently available in headers on MIPS
2672             // Not yet implemented on sparc64
2673             "SYS_clone3" if mips | sparc64 => true,
2674 
2675             _ => false,
2676         }
2677     });
2678 
2679     cfg.skip_fn(move |name| {
2680         // skip those that are manually verified
2681         match name {
2682             // FIXME: https://github.com/rust-lang/libc/issues/1272
2683             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2684 
2685             // There are two versions of the sterror_r function, see
2686             //
2687             // https://linux.die.net/man/3/strerror_r
2688             //
2689             // An XSI-compliant version provided if:
2690             //
2691             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
2692             //  && ! _GNU_SOURCE
2693             //
2694             // and a GNU specific version provided if _GNU_SOURCE is defined.
2695             //
2696             // libc provides bindings for the XSI-compliant version, which is
2697             // preferred for portable applications.
2698             //
2699             // We skip the test here since here _GNU_SOURCE is defined, and
2700             // test the XSI version below.
2701             "strerror_r" => true,
2702 
2703             // FIXME: Our API is unsound. The Rust API allows aliasing
2704             // pointers, but the C API requires pointers not to alias.
2705             // We should probably be at least using `&`/`&mut` here, see:
2706             // https://github.com/gnzlbg/ctest/issues/68
2707             "lio_listio" if musl => true,
2708 
2709             // FIXME: the glibc version used by the Sparc64 build jobs
2710             // which use Debian 10.0 is too old.
2711             "statx" if sparc64 => true,
2712 
2713             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
2714             "sysctl" if gnu => true,
2715 
2716             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
2717             "gettimeofday" if gnu => true,
2718 
2719             _ => false,
2720         }
2721     });
2722 
2723     cfg.skip_field_type(move |struct_, field| {
2724         // This is a weird union, don't check the type.
2725         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2726         // sighandler_t type is super weird
2727         (struct_ == "sigaction" && field == "sa_sigaction") ||
2728         // __timeval type is a patch which doesn't exist in glibc
2729         (struct_ == "utmpx" && field == "ut_tv") ||
2730         // sigval is actually a union, but we pretend it's a struct
2731         (struct_ == "sigevent" && field == "sigev_value") ||
2732         // this one is an anonymous union
2733         (struct_ == "ff_effect" && field == "u") ||
2734         // `__exit_status` type is a patch which is absent in musl
2735         (struct_ == "utmpx" && field == "ut_exit" && musl)
2736     });
2737 
2738     cfg.volatile_item(|i| {
2739         use ctest::VolatileItemKind::*;
2740         match i {
2741             // aio_buf is a volatile void** but since we cannot express that in
2742             // Rust types, we have to explicitly tell the checker about it here:
2743             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => {
2744                 true
2745             }
2746             _ => false,
2747         }
2748     });
2749 
2750     cfg.skip_field(move |struct_, field| {
2751         // this is actually a union on linux, so we can't represent it well and
2752         // just insert some padding.
2753         (struct_ == "siginfo_t" && field == "_pad") ||
2754         // musl names this __dummy1 but it's still there
2755         (musl && struct_ == "glob_t" && field == "gl_flags") ||
2756         // musl seems to define this as an *anonymous* bitfield
2757         (musl && struct_ == "statvfs" && field == "__f_unused") ||
2758         // sigev_notify_thread_id is actually part of a sigev_un union
2759         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2760         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
2761         // has them yet.
2762         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2763                                            field == "_pad2" ||
2764                                            field == "ssi_syscall" ||
2765                                            field == "ssi_call_addr" ||
2766                                            field == "ssi_arch")) ||
2767         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2768         // while other fields become reserved.
2769         (struct_ == "sched_param" && [
2770             "sched_ss_low_priority",
2771             "sched_ss_repl_period",
2772             "sched_ss_init_budget",
2773             "sched_ss_max_repl",
2774         ].contains(&field) && musl) ||
2775         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
2776         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
2777         // glibc uses unnamed fields here and Rust doesn't support that yet
2778         (struct_ == "timex" && field.starts_with("__unused")) ||
2779         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
2780         (struct_ == "ipc_perm" && field == "mode"
2781             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
2782         )
2783     });
2784 
2785     cfg.skip_roundtrip(move |s| match s {
2786         // FIXME:
2787         "utsname" if mips32 || mips64 => true,
2788         // FIXME:
2789         "mcontext_t" if s390x => true,
2790         // FIXME: This is actually a union.
2791         "fpreg_t" if s390x => true,
2792 
2793         "sockaddr_un" | "sembuf" | "ff_constant_effect"
2794             if mips32 && (gnu || musl) =>
2795         {
2796             true
2797         }
2798         "ipv6_mreq"
2799         | "ip_mreq_source"
2800         | "sockaddr_in6"
2801         | "sockaddr_ll"
2802         | "in_pktinfo"
2803         | "arpreq"
2804         | "arpreq_old"
2805         | "sockaddr_un"
2806         | "ff_constant_effect"
2807         | "ff_ramp_effect"
2808         | "ff_condition_effect"
2809         | "Elf32_Ehdr"
2810         | "Elf32_Chdr"
2811         | "ucred"
2812         | "in6_pktinfo"
2813         | "sockaddr_nl"
2814         | "termios"
2815         | "nlmsgerr"
2816             if (mips64 || sparc64) && gnu =>
2817         {
2818             true
2819         }
2820 
2821         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
2822         "max_align_t" if i686 || mips64 || ppc64 => true,
2823 
2824         _ => false,
2825     });
2826 
2827     cfg.generate("../src/lib.rs", "main.rs");
2828 
2829     test_linux_like_apis(target);
2830 }
2831 
2832 // This function tests APIs that are incompatible to test when other APIs
2833 // are included (e.g. because including both sets of headers clashes)
2834 fn test_linux_like_apis(target: &str) {
2835     let musl = target.contains("musl");
2836     let linux = target.contains("linux");
2837     let emscripten = target.contains("emscripten");
2838     let android = target.contains("android");
2839     assert!(linux || android || emscripten);
2840 
2841     if linux || android || emscripten {
2842         // test strerror_r from the `string.h` header
2843         let mut cfg = ctest_cfg();
2844         cfg.skip_type(|_| true).skip_static(|_| true);
2845 
2846         headers! { cfg: "string.h" }
2847         cfg.skip_fn(|f| match f {
2848             "strerror_r" => false,
2849             _ => true,
2850         })
2851         .skip_const(|_| true)
2852         .skip_struct(|_| true);
2853         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
2854     }
2855 
2856     if linux || android || emscripten {
2857         // test fcntl - see:
2858         // http://man7.org/linux/man-pages/man2/fcntl.2.html
2859         let mut cfg = ctest_cfg();
2860 
2861         if musl {
2862             cfg.header("fcntl.h");
2863         } else {
2864             cfg.header("linux/fcntl.h");
2865         }
2866 
2867         cfg.skip_type(|_| true)
2868             .skip_static(|_| true)
2869             .skip_struct(|_| true)
2870             .skip_fn(|_| true)
2871             .skip_const(move |name| match name {
2872                 // test fcntl constants:
2873                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS"
2874                 | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
2875                 | "F_SEAL_WRITE" => false,
2876                 _ => true,
2877             })
2878             .type_name(move |ty, is_struct, is_union| match ty {
2879                 t if is_struct => format!("struct {}", t),
2880                 t if is_union => format!("union {}", t),
2881                 t => t.to_string(),
2882             });
2883 
2884         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
2885     }
2886 
2887     if linux || android {
2888         // test termios
2889         let mut cfg = ctest_cfg();
2890         cfg.header("asm/termbits.h");
2891         cfg.skip_type(|_| true)
2892             .skip_static(|_| true)
2893             .skip_fn(|_| true)
2894             .skip_const(|c| c != "BOTHER")
2895             .skip_struct(|s| s != "termios2")
2896             .type_name(move |ty, is_struct, is_union| match ty {
2897                 t if is_struct => format!("struct {}", t),
2898                 t if is_union => format!("union {}", t),
2899                 t => t.to_string(),
2900             });
2901         cfg.generate("../src/lib.rs", "linux_termios.rs");
2902     }
2903 
2904     if linux || android {
2905         // test IPV6_ constants:
2906         let mut cfg = ctest_cfg();
2907         headers! {
2908             cfg:
2909             "linux/in6.h"
2910         }
2911         cfg.skip_type(|_| true)
2912             .skip_static(|_| true)
2913             .skip_fn(|_| true)
2914             .skip_const(|_| true)
2915             .skip_struct(|_| true)
2916             .skip_const(move |name| match name {
2917                 "IPV6_FLOWINFO"
2918                 | "IPV6_FLOWLABEL_MGR"
2919                 | "IPV6_FLOWINFO_SEND"
2920                 | "IPV6_FLOWINFO_FLOWLABEL"
2921                 | "IPV6_FLOWINFO_PRIORITY" => false,
2922                 _ => true,
2923             })
2924             .type_name(move |ty, is_struct, is_union| match ty {
2925                 t if is_struct => format!("struct {}", t),
2926                 t if is_union => format!("union {}", t),
2927                 t => t.to_string(),
2928             });
2929         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
2930     }
2931 
2932     if linux || android {
2933         // Test Elf64_Phdr and Elf32_Phdr
2934         // These types have a field called `p_type`, but including
2935         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
2936         // making the tests for these fails when both are included.
2937         let mut cfg = ctest_cfg();
2938         cfg.header("elf.h");
2939         cfg.skip_fn(|_| true)
2940             .skip_static(|_| true)
2941             .skip_fn(|_| true)
2942             .skip_const(|_| true)
2943             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
2944             .skip_struct(move |ty| match ty {
2945                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2946                 _ => true,
2947             })
2948             .skip_type(move |ty| match ty {
2949                 "Elf64_Phdr" | "Elf32_Phdr" => false,
2950                 _ => true,
2951             });
2952         cfg.generate("../src/lib.rs", "linux_elf.rs");
2953     }
2954 }
2955 
2956 fn which_freebsd() -> Option<i32> {
2957     let output = std::process::Command::new("freebsd-version")
2958         .output()
2959         .ok()?;
2960     if !output.status.success() {
2961         return None;
2962     }
2963 
2964     let stdout = String::from_utf8(output.stdout).ok()?;
2965 
2966     match &stdout {
2967         s if s.starts_with("10") => Some(10),
2968         s if s.starts_with("11") => Some(11),
2969         s if s.starts_with("12") => Some(12),
2970         s if s.starts_with("13") => Some(13),
2971         _ => None,
2972     }
2973 }
2974