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