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