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