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