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