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