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