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