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