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