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