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