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