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