xref: /rust-libc-0.2.174/libc-test/build.rs (revision b9c8323b)
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             _ => false,
2350         }
2351     });
2352 
2353     cfg.skip_type(move |ty| {
2354         match ty {
2355             // the struct "__kvm" is quite tricky to bind so since we only use a pointer to it
2356             // for now, it doesn't matter too much...
2357             "kvm_t" => true,
2358 
2359             _ => false,
2360         }
2361     });
2362 
2363     cfg.skip_struct(move |ty| {
2364         if ty.starts_with("__c_anonymous_") {
2365             return true;
2366         }
2367         match ty {
2368             // `procstat` is a private struct
2369             "procstat" => true,
2370 
2371             // `spacectl_range` was introduced in FreeBSD 14
2372             "spacectl_range" if Some(14) > freebsd_ver => true,
2373 
2374             // `ptrace_coredump` introduced in FreeBSD 14.
2375             "ptrace_coredump" if Some(14) > freebsd_ver => true,
2376             // `ptrace_sc_remote` introduced in FreeBSD 14.
2377             "ptrace_sc_remote" if Some(14) > freebsd_ver => true,
2378 
2379             // `sockcred2` is not available in FreeBSD 12.
2380             "sockcred2" if Some(13) > freebsd_ver => true,
2381             // `shm_largepage_conf` was introduced in FreeBSD 13.
2382             "shm_largepage_conf" if Some(13) > freebsd_ver => true,
2383 
2384             // Those are private types
2385             "memory_type" => true,
2386             "memory_type_list" => true,
2387             "pidfh" => true,
2388             "sctp_gen_error_cause"
2389             | "sctp_error_missing_param"
2390             | "sctp_remote_error"
2391             | "sctp_assoc_change"
2392             | "sctp_send_failed_event"
2393             | "sctp_stream_reset_event" => true,
2394 
2395             _ => false,
2396         }
2397     });
2398 
2399     cfg.skip_fn(move |name| {
2400         // skip those that are manually verified
2401         match name {
2402             // FIXME: https://github.com/rust-lang/libc/issues/1272
2403             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2404 
2405             // The `uname` function in the `utsname.h` FreeBSD header is a C
2406             // inline function (has no symbol) that calls the `__xuname` symbol.
2407             // Therefore the function pointer comparison does not make sense for it.
2408             "uname" => true,
2409 
2410             // FIXME: Our API is unsound. The Rust API allows aliasing
2411             // pointers, but the C API requires pointers not to alias.
2412             // We should probably be at least using `&`/`&mut` here, see:
2413             // https://github.com/gnzlbg/ctest/issues/68
2414             "lio_listio" => true,
2415 
2416             // Those are introduced in FreeBSD 12.
2417             "clock_nanosleep" | "getrandom" | "elf_aux_info" | "setproctitle_fast"
2418             | "timingsafe_bcmp" | "timingsafe_memcmp"
2419                 if Some(12) > freebsd_ver =>
2420             {
2421                 true
2422             }
2423 
2424             // Those are introduced in FreeBSD 13.
2425             "memfd_create"
2426             | "shm_create_largepage"
2427             | "shm_rename"
2428             | "getentropy"
2429             | "eventfd"
2430             | "SOCKCRED2SIZE"
2431             | "getlocalbase"
2432             | "aio_readv"
2433             | "aio_writev"
2434             | "copy_file_range"
2435                 if Some(13) > freebsd_ver =>
2436             {
2437                 true
2438             }
2439 
2440             // Those are introduced in FreeBSD 14.
2441             "sched_getaffinity" | "sched_setaffinity" | "sched_getcpu" | "fspacectl"
2442                 if Some(14) > freebsd_ver =>
2443             {
2444                 true
2445             }
2446 
2447             _ => false,
2448         }
2449     });
2450 
2451     cfg.volatile_item(|i| {
2452         use ctest::VolatileItemKind::*;
2453         match i {
2454             // aio_buf is a volatile void** but since we cannot express that in
2455             // Rust types, we have to explicitly tell the checker about it here:
2456             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2457             _ => false,
2458         }
2459     });
2460 
2461     cfg.skip_field(move |struct_, field| {
2462         match (struct_, field) {
2463             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2464             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2465             ("sigaction", "sa_sigaction") => true,
2466 
2467             // conflicting with `p_type` macro from <resolve.h>.
2468             ("Elf32_Phdr", "p_type") => true,
2469             ("Elf64_Phdr", "p_type") => true,
2470 
2471             // not available until FreeBSD 12, and is an anonymous union there.
2472             ("xucred", "cr_pid__c_anonymous_union") => true,
2473 
2474             // m_owner field is a volatile __lwpid_t
2475             ("umutex", "m_owner") => true,
2476             // c_has_waiters field is a volatile int32_t
2477             ("ucond", "c_has_waiters") => true,
2478             // is PATH_MAX long but tests can't accept multi array as equivalent.
2479             ("kinfo_vmentry", "kve_path") => true,
2480 
2481             // a_un field is a union
2482             ("Elf32_Auxinfo", "a_un") => true,
2483             ("Elf64_Auxinfo", "a_un") => true,
2484 
2485             // union fields
2486             ("if_data", "__ifi_epoch") => true,
2487             ("if_data", "__ifi_lastchange") => true,
2488             ("ifreq", "ifr_ifru") => true,
2489             ("ifconf", "ifc_ifcu") => true,
2490 
2491             // anonymous struct
2492             ("devstat", "dev_links") => true,
2493 
2494             // FIXME: structs too complicated to bind for now...
2495             ("kinfo_proc", "ki_paddr") => true,
2496             ("kinfo_proc", "ki_addr") => true,
2497             ("kinfo_proc", "ki_tracep") => true,
2498             ("kinfo_proc", "ki_textvp") => true,
2499             ("kinfo_proc", "ki_fd") => true,
2500             ("kinfo_proc", "ki_vmspace") => true,
2501             ("kinfo_proc", "ki_pcb") => true,
2502             ("kinfo_proc", "ki_tdaddr") => true,
2503             ("kinfo_proc", "ki_pd") => true,
2504 
2505             // Anonymous type.
2506             ("filestat", "next") => true,
2507 
2508             // We ignore this field because we needed to use a hack in order to make rust 1.19
2509             // happy...
2510             ("kinfo_proc", "ki_sparestrings") => true,
2511 
2512             // `__sem_base` is a private struct field
2513             ("semid_ds", "__sem_base") => true,
2514 
2515             // `snap_time` is a `long double`, but it's a nightmare to bind correctly in rust
2516             // for the moment, so it's a best effort thing...
2517             ("statinfo", "snap_time") => true,
2518             ("sctp_sndrcvinfo", "__reserve_pad") => true,
2519             ("sctp_extrcvinfo", "__reserve_pad") => true,
2520             // `tcp_snd_wscale` and `tcp_rcv_wscale` are bitfields
2521             ("tcp_info", "tcp_snd_wscale") => true,
2522             ("tcp_info", "tcp_rcv_wscale") => true,
2523 
2524             _ => false,
2525         }
2526     });
2527 
2528     cfg.generate("../src/lib.rs", "main.rs");
2529 }
2530 
2531 fn test_emscripten(target: &str) {
2532     assert!(target.contains("emscripten"));
2533 
2534     let mut cfg = ctest_cfg();
2535     cfg.define("_GNU_SOURCE", None); // FIXME: ??
2536 
2537     headers! { cfg:
2538                "aio.h",
2539                "ctype.h",
2540                "dirent.h",
2541                "dlfcn.h",
2542                "errno.h",
2543                "fcntl.h",
2544                "glob.h",
2545                "grp.h",
2546                "ifaddrs.h",
2547                "langinfo.h",
2548                "limits.h",
2549                "locale.h",
2550                "malloc.h",
2551                "mntent.h",
2552                "mqueue.h",
2553                "net/ethernet.h",
2554                "net/if.h",
2555                "net/if_arp.h",
2556                "net/route.h",
2557                "netdb.h",
2558                "netinet/in.h",
2559                "netinet/ip.h",
2560                "netinet/tcp.h",
2561                "netinet/udp.h",
2562                "netpacket/packet.h",
2563                "poll.h",
2564                "pthread.h",
2565                "pty.h",
2566                "pwd.h",
2567                "resolv.h",
2568                "sched.h",
2569                "sched.h",
2570                "semaphore.h",
2571                "shadow.h",
2572                "signal.h",
2573                "stddef.h",
2574                "stdint.h",
2575                "stdio.h",
2576                "stdlib.h",
2577                "string.h",
2578                "sys/epoll.h",
2579                "sys/eventfd.h",
2580                "sys/file.h",
2581                "sys/ioctl.h",
2582                "sys/ipc.h",
2583                "sys/mman.h",
2584                "sys/mount.h",
2585                "sys/msg.h",
2586                "sys/personality.h",
2587                "sys/prctl.h",
2588                "sys/ptrace.h",
2589                "sys/quota.h",
2590                "sys/reboot.h",
2591                "sys/resource.h",
2592                "sys/sem.h",
2593                "sys/shm.h",
2594                "sys/signalfd.h",
2595                "sys/socket.h",
2596                "sys/stat.h",
2597                "sys/statvfs.h",
2598                "sys/swap.h",
2599                "sys/syscall.h",
2600                "sys/sysctl.h",
2601                "sys/sysinfo.h",
2602                "sys/time.h",
2603                "sys/timerfd.h",
2604                "sys/times.h",
2605                "sys/types.h",
2606                "sys/uio.h",
2607                "sys/un.h",
2608                "sys/user.h",
2609                "sys/utsname.h",
2610                "sys/vfs.h",
2611                "sys/wait.h",
2612                "sys/xattr.h",
2613                "syslog.h",
2614                "termios.h",
2615                "time.h",
2616                "ucontext.h",
2617                "unistd.h",
2618                "utime.h",
2619                "utmp.h",
2620                "utmpx.h",
2621                "wchar.h",
2622     }
2623 
2624     cfg.type_name(move |ty, is_struct, is_union| {
2625         match ty {
2626             // Just pass all these through, no need for a "struct" prefix
2627             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2628 
2629             "os_unfair_lock" => "struct os_unfair_lock_s".to_string(),
2630 
2631             // LFS64 types have been removed in Emscripten 3.1.44+
2632             // https://github.com/emscripten-core/emscripten/pull/19812
2633             "off64_t" => "off_t".to_string(),
2634 
2635             // typedefs don't need any keywords
2636             t if t.ends_with("_t") => t.to_string(),
2637 
2638             // put `struct` in front of all structs:.
2639             t if is_struct => format!("struct {}", t),
2640 
2641             // put `union` in front of all unions:
2642             t if is_union => format!("union {}", t),
2643 
2644             t => t.to_string(),
2645         }
2646     });
2647 
2648     cfg.field_name(move |struct_, field| {
2649         match field {
2650             // Our stat *_nsec fields normally don't actually exist but are part
2651             // of a timeval struct
2652             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2653                 s.replace("e_nsec", ".tv_nsec")
2654             }
2655             // FIXME: appears that `epoll_event.data` is an union
2656             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2657             s => s.to_string(),
2658         }
2659     });
2660 
2661     cfg.skip_type(move |ty| {
2662         match ty {
2663             // sighandler_t is crazy across platforms
2664             // FIXME: is this necessary?
2665             "sighandler_t" => true,
2666 
2667             // FIXME: The size has been changed due to musl's time64
2668             "time_t" => true,
2669 
2670             // LFS64 types have been removed in Emscripten 3.1.44+
2671             // https://github.com/emscripten-core/emscripten/pull/19812
2672             t => t.ends_with("64") || t.ends_with("64_t"),
2673         }
2674     });
2675 
2676     cfg.skip_struct(move |ty| {
2677         match ty {
2678             // This is actually a union, not a struct
2679             // FIXME: is this necessary?
2680             "sigval" => true,
2681 
2682             // FIXME: It was removed in
2683             // emscripten-core/emscripten@953e414
2684             "pthread_mutexattr_t" => true,
2685 
2686             // FIXME: Investigate why the test fails.
2687             // Skip for now to unblock CI.
2688             "pthread_condattr_t" => true,
2689 
2690             // FIXME: The size has been changed when upgraded to musl 1.2.2
2691             "pthread_mutex_t" => true,
2692 
2693             // FIXME: Lowered from 16 to 8 bytes in
2694             // llvm/llvm-project@d1a96e9
2695             "max_align_t" => true,
2696 
2697             // FIXME: The size has been changed due to time64
2698             "utimbuf" | "timeval" | "timespec" | "rusage" | "itimerval" | "sched_param"
2699             | "stat" | "stat64" | "shmid_ds" | "msqid_ds" => true,
2700 
2701             // LFS64 types have been removed in Emscripten 3.1.44+
2702             // https://github.com/emscripten-core/emscripten/pull/19812
2703             ty => ty.ends_with("64") || ty.ends_with("64_t"),
2704         }
2705     });
2706 
2707     cfg.skip_fn(move |name| {
2708         match name {
2709             // Emscripten does not support fork/exec/wait or any kind of multi-process support
2710             // https://github.com/emscripten-core/emscripten/blob/3.1.30/tools/system_libs.py#L973
2711             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" | "wait4" => true,
2712 
2713             // FIXME: Remove after emscripten-core/emscripten#18492 is released (> 3.1.30).
2714             "clearenv" => true,
2715 
2716             _ => false,
2717         }
2718     });
2719 
2720     cfg.skip_const(move |name| {
2721         match name {
2722             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2723             // users should use SIGSYS instead
2724             "SIGUNUSED" => true,
2725 
2726             // FIXME: emscripten uses different constants to constructs these
2727             n if n.contains("__SIZEOF_PTHREAD") => true,
2728 
2729             // FIXME: `SYS_gettid` was removed in
2730             // emscripten-core/emscripten@6d6474e
2731             "SYS_gettid" => true,
2732 
2733             // FIXME: These values have been changed
2734             | "POSIX_MADV_DONTNEED" // to 4
2735             | "RLIMIT_NLIMITS" // to 16
2736             | "RLIM_NLIMITS" // to 16
2737             | "IPPROTO_MAX" // to 263
2738             | "F_GETLK" // to 5
2739             | "F_SETLK" // to 6
2740             | "F_SETLKW" // to 7
2741             | "O_TMPFILE" // to 65
2742             | "SIG_IGN" // -1
2743                 => true,
2744 
2745             // LFS64 types have been removed in Emscripten 3.1.44+
2746             // https://github.com/emscripten-core/emscripten/pull/19812
2747             n if n.starts_with("RLIM64") => true,
2748 
2749             _ => false,
2750         }
2751     });
2752 
2753     cfg.skip_field_type(move |struct_, field| {
2754         // This is a weird union, don't check the type.
2755         // FIXME: is this necessary?
2756         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2757         // sighandler_t type is super weird
2758         // FIXME: is this necessary?
2759         (struct_ == "sigaction" && field == "sa_sigaction") ||
2760         // sigval is actually a union, but we pretend it's a struct
2761         // FIXME: is this necessary?
2762         (struct_ == "sigevent" && field == "sigev_value") ||
2763         // aio_buf is "volatile void*" and Rust doesn't understand volatile
2764         // FIXME: is this necessary?
2765         (struct_ == "aiocb" && field == "aio_buf")
2766     });
2767 
2768     cfg.skip_field(move |struct_, field| {
2769         // this is actually a union on linux, so we can't represent it well and
2770         // just insert some padding.
2771         // FIXME: is this necessary?
2772         (struct_ == "siginfo_t" && field == "_pad") ||
2773         // musl names this __dummy1 but it's still there
2774         // FIXME: is this necessary?
2775         (struct_ == "glob_t" && field == "gl_flags") ||
2776         // musl seems to define this as an *anonymous* bitfield
2777         // FIXME: is this necessary?
2778         (struct_ == "statvfs" && field == "__f_unused") ||
2779         // sigev_notify_thread_id is actually part of a sigev_un union
2780         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2781         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
2782         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2783                                            field == "_pad2" ||
2784                                            field == "ssi_syscall" ||
2785                                            field == "ssi_call_addr" ||
2786                                            field == "ssi_arch")) ||
2787         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2788         // while other fields become reserved.
2789         (struct_ == "sched_param" && [
2790             "sched_ss_low_priority",
2791             "sched_ss_repl_period",
2792             "sched_ss_init_budget",
2793             "sched_ss_max_repl",
2794         ].contains(&field))
2795     });
2796 
2797     // FIXME: test linux like
2798     cfg.generate("../src/lib.rs", "main.rs");
2799 }
2800 
2801 fn test_neutrino(target: &str) {
2802     assert!(target.contains("nto-qnx"));
2803 
2804     let mut cfg = ctest_cfg();
2805 
2806     headers! { cfg:
2807         "ctype.h",
2808         "dirent.h",
2809         "dlfcn.h",
2810         "sys/elf.h",
2811         "fcntl.h",
2812         "glob.h",
2813         "grp.h",
2814         "iconv.h",
2815         "ifaddrs.h",
2816         "limits.h",
2817         "sys/link.h",
2818         "locale.h",
2819         "sys/malloc.h",
2820         "rcheck/malloc.h",
2821         "malloc.h",
2822         "mqueue.h",
2823         "net/if.h",
2824         "net/if_arp.h",
2825         "net/route.h",
2826         "netdb.h",
2827         "netinet/in.h",
2828         "netinet/ip.h",
2829         "netinet/tcp.h",
2830         "netinet/udp.h",
2831         "netinet/ip_var.h",
2832         "sys/poll.h",
2833         "pthread.h",
2834         "pwd.h",
2835         "regex.h",
2836         "resolv.h",
2837         "sys/sched.h",
2838         "sched.h",
2839         "semaphore.h",
2840         "shadow.h",
2841         "signal.h",
2842         "spawn.h",
2843         "stddef.h",
2844         "stdint.h",
2845         "stdio.h",
2846         "stdlib.h",
2847         "string.h",
2848         "sys/sysctl.h",
2849         "sys/file.h",
2850         "sys/inotify.h",
2851         "sys/ioctl.h",
2852         "sys/ipc.h",
2853         "sys/mman.h",
2854         "sys/mount.h",
2855         "sys/msg.h",
2856         "sys/resource.h",
2857         "sys/sem.h",
2858         "sys/socket.h",
2859         "sys/stat.h",
2860         "sys/statvfs.h",
2861         "sys/swap.h",
2862         "sys/termio.h",
2863         "sys/time.h",
2864         "sys/times.h",
2865         "sys/types.h",
2866         "sys/uio.h",
2867         "sys/un.h",
2868         "sys/utsname.h",
2869         "sys/wait.h",
2870         "syslog.h",
2871         "termios.h",
2872         "time.h",
2873         "sys/time.h",
2874         "ucontext.h",
2875         "unistd.h",
2876         "utime.h",
2877         "utmp.h",
2878         "wchar.h",
2879         "aio.h",
2880         "nl_types.h",
2881         "langinfo.h",
2882         "unix.h",
2883         "nbutil.h",
2884         "aio.h",
2885         "net/bpf.h",
2886         "net/if_dl.h",
2887         "sys/syspage.h",
2888 
2889         // TODO: The following header file doesn't appear as part of the default headers
2890         //       found in a standard installation of Neutrino 7.1 SDP.  The structures/
2891         //       functions dependent on it are currently commented out.
2892         //"sys/asyncmsg.h",
2893     }
2894 
2895     // Create and include a header file containing
2896     // items which are not included in any official
2897     // header file.
2898     let internal_header = "internal.h";
2899     let out_dir = env::var("OUT_DIR").unwrap();
2900     cfg.header(internal_header);
2901     cfg.include(&out_dir);
2902     std::fs::write(
2903         out_dir.to_owned() + "/" + internal_header,
2904         "#ifndef __internal_h__
2905         #define __internal_h__
2906         void __my_thread_exit(const void **);
2907         #endif",
2908     )
2909     .unwrap();
2910 
2911     cfg.type_name(move |ty, is_struct, is_union| {
2912         match ty {
2913             // Just pass all these through, no need for a "struct" prefix
2914             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
2915             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
2916             | "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info"
2917             | "syspage_array_info" => ty.to_string(),
2918 
2919             "Ioctl" => "int".to_string(),
2920 
2921             t if is_union => format!("union {}", t),
2922 
2923             t if t.ends_with("_t") => t.to_string(),
2924 
2925             // put `struct` in front of all structs:.
2926             t if is_struct => format!("struct {}", t),
2927 
2928             t => t.to_string(),
2929         }
2930     });
2931 
2932     cfg.field_name(move |_struct_, field| match field {
2933         "type_" => "type".to_string(),
2934 
2935         s => s.to_string(),
2936     });
2937 
2938     cfg.volatile_item(|i| {
2939         use ctest::VolatileItemKind::*;
2940         match i {
2941             // The following fields are volatie but since we cannot express that in
2942             // Rust types, we have to explicitly tell the checker about it here:
2943             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2944             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true,
2945             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true,
2946             StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true,
2947             StructField(ref n, ref f) if n == "intrspin" && f == "value" => true,
2948             _ => false,
2949         }
2950     });
2951 
2952     cfg.skip_type(move |ty| {
2953         match ty {
2954             // FIXME: `sighandler_t` type is incorrect, see:
2955             // https://github.com/rust-lang/libc/issues/1359
2956             "sighandler_t" => true,
2957 
2958             // Does not exist in Neutrino
2959             "locale_t" => true,
2960 
2961             _ => false,
2962         }
2963     });
2964 
2965     cfg.skip_struct(move |ty| {
2966         if ty.starts_with("__c_anonymous_") {
2967             return true;
2968         }
2969         match ty {
2970             "Elf64_Phdr" | "Elf32_Phdr" => true,
2971 
2972             // FIXME: This is actually a union, not a struct
2973             "sigval" => true,
2974 
2975             // union
2976             "_channel_connect_attr" => true,
2977 
2978             _ => false,
2979         }
2980     });
2981 
2982     cfg.skip_const(move |name| {
2983         match name {
2984             // These signal "functions" are actually integer values that are casted to a fn ptr
2985             // This causes the compiler to err because of "illegal cast of int to ptr".
2986             "SIG_DFL" => true,
2987             "SIG_IGN" => true,
2988             "SIG_ERR" => true,
2989 
2990             _ => false,
2991         }
2992     });
2993 
2994     cfg.skip_fn(move |name| {
2995         // skip those that are manually verified
2996         match name {
2997             // FIXME: https://github.com/rust-lang/libc/issues/1272
2998             "execv" | "execve" | "execvp" | "execvpe" => true,
2999 
3000             // wrong signature
3001             "signal" => true,
3002 
3003             // wrong signature of callback ptr
3004             "__cxa_atexit" => true,
3005 
3006             // FIXME: Our API is unsound. The Rust API allows aliasing
3007             // pointers, but the C API requires pointers not to alias.
3008             // We should probably be at least using `&`/`&mut` here, see:
3009             // https://github.com/gnzlbg/ctest/issues/68
3010             "lio_listio" => true,
3011 
3012             // 2 fields are actually unions which we're simply representing
3013             // as structures.
3014             "ChannelConnectAttr" => true,
3015 
3016             // fields contains unions
3017             "SignalKillSigval" => true,
3018             "SignalKillSigval_r" => true,
3019 
3020             // Not defined in any headers.  Defined to work around a
3021             // stack unwinding bug.
3022             "__my_thread_exit" => true,
3023 
3024             _ => false,
3025         }
3026     });
3027 
3028     cfg.skip_field_type(move |struct_, field| {
3029         // sigval is actually a union, but we pretend it's a struct
3030         struct_ == "sigevent" && field == "sigev_value" ||
3031         // Anonymous structures
3032         struct_ == "_idle_hook" && field == "time"
3033     });
3034 
3035     cfg.skip_field(move |struct_, field| {
3036         (struct_ == "__sched_param" && field == "reserved") ||
3037         (struct_ == "sched_param" && field == "reserved") ||
3038         (struct_ == "sigevent" && field == "__padding1") || // ensure alignment
3039         (struct_ == "sigevent" && field == "__padding2") || // union
3040         (struct_ == "sigevent" && field == "__sigev_un2") || // union
3041         // sighandler_t type is super weird
3042         (struct_ == "sigaction" && field == "sa_sigaction") ||
3043         // does not exist
3044         (struct_ == "syspage_entry" && field == "__reserved") ||
3045         false // keep me for smaller diffs when something is added above
3046     });
3047 
3048     cfg.skip_static(move |name| (name == "__dso_handle"));
3049 
3050     cfg.generate("../src/lib.rs", "main.rs");
3051 }
3052 
3053 fn test_vxworks(target: &str) {
3054     assert!(target.contains("vxworks"));
3055 
3056     let mut cfg = ctest::TestGenerator::new();
3057     headers! { cfg:
3058                "vxWorks.h",
3059                "yvals.h",
3060                "nfs/nfsCommon.h",
3061                "rtpLibCommon.h",
3062                "randomNumGen.h",
3063                "taskLib.h",
3064                "sysLib.h",
3065                "ioLib.h",
3066                "inetLib.h",
3067                "socket.h",
3068                "errnoLib.h",
3069                "ctype.h",
3070                "dirent.h",
3071                "dlfcn.h",
3072                "elf.h",
3073                "fcntl.h",
3074                "grp.h",
3075                "sys/poll.h",
3076                "ifaddrs.h",
3077                "langinfo.h",
3078                "limits.h",
3079                "link.h",
3080                "locale.h",
3081                "sys/stat.h",
3082                "netdb.h",
3083                "pthread.h",
3084                "pwd.h",
3085                "sched.h",
3086                "semaphore.h",
3087                "signal.h",
3088                "stddef.h",
3089                "stdint.h",
3090                "stdio.h",
3091                "stdlib.h",
3092                "string.h",
3093                "sys/file.h",
3094                "sys/ioctl.h",
3095                "sys/socket.h",
3096                "sys/time.h",
3097                "sys/times.h",
3098                "sys/types.h",
3099                "sys/uio.h",
3100                "sys/un.h",
3101                "sys/utsname.h",
3102                "sys/wait.h",
3103                "netinet/tcp.h",
3104                "syslog.h",
3105                "termios.h",
3106                "time.h",
3107                "ucontext.h",
3108                "unistd.h",
3109                "utime.h",
3110                "wchar.h",
3111                "errno.h",
3112                "sys/mman.h",
3113                "pathLib.h",
3114                "mqueue.h",
3115     }
3116     // FIXME
3117     cfg.skip_const(move |name| match name {
3118         // sighandler_t weirdness
3119         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
3120         // This is not defined in vxWorks
3121         | "RTLD_DEFAULT"   => true,
3122         _ => false,
3123     });
3124     // FIXME
3125     cfg.skip_type(move |ty| match ty {
3126         "stat64" | "sighandler_t" | "off64_t" => true,
3127         _ => false,
3128     });
3129 
3130     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
3131         ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true,
3132         _ => false,
3133     });
3134 
3135     cfg.skip_roundtrip(move |s| match s {
3136         _ => false,
3137     });
3138 
3139     cfg.type_name(move |ty, is_struct, is_union| match ty {
3140         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
3141         t if is_union => format!("union {}", t),
3142         t if t.ends_with("_t") => t.to_string(),
3143         t if is_struct => format!("struct {}", t),
3144         t => t.to_string(),
3145     });
3146 
3147     // FIXME
3148     cfg.skip_fn(move |name| match name {
3149         // sigval
3150         "sigqueue" | "_sigqueue"
3151         // sighandler_t
3152         | "signal"
3153         // not used in static linking by default
3154         | "dlerror" => true,
3155         _ => false,
3156     });
3157 
3158     cfg.generate("../src/lib.rs", "main.rs");
3159 }
3160 
3161 fn test_linux(target: &str) {
3162     assert!(target.contains("linux"));
3163 
3164     // target_env
3165     let gnu = target.contains("gnu");
3166     let musl = target.contains("musl") || target.contains("ohos");
3167     let uclibc = target.contains("uclibc");
3168 
3169     match (gnu, musl, uclibc) {
3170         (true, false, false) => (),
3171         (false, true, false) => (),
3172         (false, false, true) => (),
3173         (_, _, _) => panic!(
3174             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
3175             gnu, musl, uclibc
3176         ),
3177     }
3178 
3179     let arm = target.contains("arm");
3180     let i686 = target.contains("i686");
3181     let mips = target.contains("mips");
3182     let mips32 = mips && !target.contains("64");
3183     let mips64 = mips && target.contains("64");
3184     let ppc = target.contains("powerpc");
3185     let ppc64 = target.contains("powerpc64");
3186     let s390x = target.contains("s390x");
3187     let sparc64 = target.contains("sparc64");
3188     let x32 = target.contains("x32");
3189     let x86_32 = target.contains("i686");
3190     let x86_64 = target.contains("x86_64");
3191     let aarch64_musl = target.contains("aarch64") && musl;
3192     let gnueabihf = target.contains("gnueabihf");
3193     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
3194     let riscv64 = target.contains("riscv64");
3195     let uclibc = target.contains("uclibc");
3196 
3197     let mut cfg = ctest_cfg();
3198     cfg.define("_GNU_SOURCE", None);
3199     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
3200     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
3201     // glibc versions older than 2.29.
3202     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
3203 
3204     headers! { cfg:
3205                "ctype.h",
3206                "dirent.h",
3207                "dlfcn.h",
3208                "elf.h",
3209                "fcntl.h",
3210                "getopt.h",
3211                "glob.h",
3212                [gnu]: "gnu/libc-version.h",
3213                "grp.h",
3214                "iconv.h",
3215                "ifaddrs.h",
3216                "langinfo.h",
3217                "libgen.h",
3218                "limits.h",
3219                "link.h",
3220                "locale.h",
3221                "malloc.h",
3222                "mntent.h",
3223                "mqueue.h",
3224                "net/ethernet.h",
3225                "net/if.h",
3226                "net/if_arp.h",
3227                "net/route.h",
3228                "netdb.h",
3229                "netinet/in.h",
3230                "netinet/ip.h",
3231                "netinet/tcp.h",
3232                "netinet/udp.h",
3233                "netpacket/packet.h",
3234                "poll.h",
3235                "pthread.h",
3236                "pty.h",
3237                "pwd.h",
3238                "regex.h",
3239                "resolv.h",
3240                "sched.h",
3241                "semaphore.h",
3242                "shadow.h",
3243                "signal.h",
3244                "spawn.h",
3245                "stddef.h",
3246                "stdint.h",
3247                "stdio.h",
3248                "stdlib.h",
3249                "string.h",
3250                "sys/epoll.h",
3251                "sys/eventfd.h",
3252                "sys/file.h",
3253                "sys/fsuid.h",
3254                "sys/inotify.h",
3255                "sys/ioctl.h",
3256                "sys/ipc.h",
3257                "sys/mman.h",
3258                "sys/mount.h",
3259                "sys/msg.h",
3260                "sys/personality.h",
3261                "sys/prctl.h",
3262                "sys/ptrace.h",
3263                "sys/quota.h",
3264                "sys/random.h",
3265                "sys/reboot.h",
3266                "sys/resource.h",
3267                "sys/sem.h",
3268                "sys/sendfile.h",
3269                "sys/shm.h",
3270                "sys/signalfd.h",
3271                "sys/socket.h",
3272                "sys/stat.h",
3273                "sys/statvfs.h",
3274                "sys/swap.h",
3275                "sys/syscall.h",
3276                "sys/time.h",
3277                "sys/timerfd.h",
3278                "sys/times.h",
3279                "sys/timex.h",
3280                "sys/types.h",
3281                "sys/uio.h",
3282                "sys/un.h",
3283                "sys/user.h",
3284                "sys/utsname.h",
3285                "sys/vfs.h",
3286                "sys/wait.h",
3287                "syslog.h",
3288                "termios.h",
3289                "time.h",
3290                "ucontext.h",
3291                "unistd.h",
3292                "utime.h",
3293                "utmp.h",
3294                "utmpx.h",
3295                "wchar.h",
3296                "errno.h",
3297                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
3298                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
3299                // Also unavailable on gnueabihf with glibc 2.30.
3300                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
3301                [(x86_64 || x86_32 || arm) && !gnueabihf]: "sys/io.h",
3302                // `sys/reg.h` is only available on x86 and x86_64
3303                [x86_64 || x86_32]: "sys/reg.h",
3304                // sysctl system call is deprecated and not available on musl
3305                // It is also unsupported in x32, deprecated since glibc 2.30:
3306                [!(x32 || musl || gnu)]: "sys/sysctl.h",
3307                // <execinfo.h> is not supported by musl:
3308                // https://www.openwall.com/lists/musl/2015/04/09/3
3309                // <execinfo.h> is not present on uclibc.
3310                [!(musl || uclibc)]: "execinfo.h",
3311     }
3312 
3313     // Include linux headers at the end:
3314     headers! {
3315         cfg:
3316         "asm/mman.h",
3317         "linux/can.h",
3318         "linux/can/raw.h",
3319         // FIXME: requires kernel headers >= 5.4.1.
3320         [!musl]: "linux/can/j1939.h",
3321         "linux/dccp.h",
3322         "linux/errqueue.h",
3323         "linux/falloc.h",
3324         "linux/filter.h",
3325         "linux/fs.h",
3326         "linux/futex.h",
3327         "linux/genetlink.h",
3328         "linux/if.h",
3329         "linux/if_addr.h",
3330         "linux/if_alg.h",
3331         "linux/if_ether.h",
3332         "linux/if_tun.h",
3333         "linux/input.h",
3334         "linux/ipv6.h",
3335         "linux/kexec.h",
3336         "linux/keyctl.h",
3337         "linux/magic.h",
3338         "linux/memfd.h",
3339         "linux/membarrier.h",
3340         "linux/mempolicy.h",
3341         "linux/mman.h",
3342         "linux/module.h",
3343         // FIXME: requires kernel headers >= 5.1.
3344         [!musl]: "linux/mount.h",
3345         "linux/net_tstamp.h",
3346         "linux/netfilter/nfnetlink.h",
3347         "linux/netfilter/nfnetlink_log.h",
3348         "linux/netfilter/nfnetlink_queue.h",
3349         "linux/netfilter/nf_tables.h",
3350         "linux/netfilter_ipv4.h",
3351         "linux/netfilter_ipv6.h",
3352         "linux/netfilter_ipv6/ip6_tables.h",
3353         "linux/netlink.h",
3354         // FIXME: requires Linux >= 5.6:
3355         [!musl]: "linux/openat2.h",
3356         [!musl]: "linux/ptrace.h",
3357         "linux/quota.h",
3358         "linux/random.h",
3359         "linux/reboot.h",
3360         "linux/rtnetlink.h",
3361         "linux/sched.h",
3362         "linux/sctp.h",
3363         "linux/seccomp.h",
3364         "linux/sock_diag.h",
3365         "linux/sockios.h",
3366         "linux/tls.h",
3367         "linux/uinput.h",
3368         "linux/vm_sockets.h",
3369         "linux/wait.h",
3370         "sys/fanotify.h",
3371         // <sys/auxv.h> is not present on uclibc
3372         [!uclibc]: "sys/auxv.h",
3373     }
3374 
3375     // note: aio.h must be included before sys/mount.h
3376     headers! {
3377         cfg:
3378         "sys/xattr.h",
3379         "sys/sysinfo.h",
3380         // AIO is not supported by uclibc:
3381         [!uclibc]: "aio.h",
3382     }
3383 
3384     cfg.type_name(move |ty, is_struct, is_union| {
3385         match ty {
3386             // Just pass all these through, no need for a "struct" prefix
3387             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
3388             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
3389             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
3390 
3391             "Ioctl" if gnu => "unsigned long".to_string(),
3392             "Ioctl" => "int".to_string(),
3393 
3394             // LFS64 types have been removed in musl 1.2.4+
3395             "off64_t" if musl => "off_t".to_string(),
3396 
3397             // typedefs don't need any keywords
3398             t if t.ends_with("_t") => t.to_string(),
3399             // put `struct` in front of all structs:.
3400             t if is_struct => format!("struct {}", t),
3401             // put `union` in front of all unions:
3402             t if is_union => format!("union {}", t),
3403 
3404             t => t.to_string(),
3405         }
3406     });
3407 
3408     cfg.field_name(move |struct_, field| {
3409         match field {
3410             // Our stat *_nsec fields normally don't actually exist but are part
3411             // of a timeval struct
3412             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
3413                 s.replace("e_nsec", ".tv_nsec")
3414             }
3415             // FIXME: epoll_event.data is actually a union in C, but in Rust
3416             // it is only a u64 because we only expose one field
3417             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
3418             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
3419             // The following structs have a field called `type` in C,
3420             // but `type` is a Rust keyword, so these fields are translated
3421             // to `type_` in Rust.
3422             "type_"
3423                 if struct_ == "input_event"
3424                     || struct_ == "input_mask"
3425                     || struct_ == "ff_effect" =>
3426             {
3427                 "type".to_string()
3428             }
3429 
3430             s => s.to_string(),
3431         }
3432     });
3433 
3434     cfg.skip_type(move |ty| {
3435         match ty {
3436             // FIXME: `sighandler_t` type is incorrect, see:
3437             // https://github.com/rust-lang/libc/issues/1359
3438             "sighandler_t" => true,
3439 
3440             // These cannot be tested when "resolv.h" is included and are tested
3441             // in the `linux_elf.rs` file.
3442             "Elf64_Phdr" | "Elf32_Phdr" => true,
3443 
3444             // This type is private on Linux. It is implemented as a C `enum`
3445             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
3446             // which expect a `c_int` even though both are ABI compatible.
3447             "__rlimit_resource_t" => true,
3448             // on Linux, this is a volatile int
3449             "pthread_spinlock_t" => true,
3450 
3451             // For internal use only, to define architecture specific ioctl constants with a libc
3452             // specific type.
3453             "Ioctl" => true,
3454 
3455             // FIXME: requires >= 5.4.1 kernel headers
3456             "pgn_t" if musl => true,
3457             "priority_t" if musl => true,
3458             "name_t" if musl => true,
3459 
3460             t => {
3461                 if musl {
3462                     // LFS64 types have been removed in musl 1.2.4+
3463                     t.ends_with("64") || t.ends_with("64_t")
3464                 } else {
3465                     false
3466                 }
3467             }
3468         }
3469     });
3470 
3471     cfg.skip_struct(move |ty| {
3472         if ty.starts_with("__c_anonymous_") {
3473             return true;
3474         }
3475         // FIXME: musl CI has old headers
3476         if musl && ty.starts_with("uinput_") {
3477             return true;
3478         }
3479         // LFS64 types have been removed in musl 1.2.4+
3480         if musl && (ty.ends_with("64") || ty.ends_with("64_t")) {
3481             return true;
3482         }
3483         // FIXME: sparc64 CI has old headers
3484         if sparc64 && (ty == "uinput_ff_erase" || ty == "uinput_abs_setup") {
3485             return true;
3486         }
3487         // FIXME(https://github.com/rust-lang/libc/issues/1558): passing by
3488         // value corrupts the value for reasons not understood.
3489         if (gnu && sparc64) && (ty == "ip_mreqn" || ty == "hwtstamp_config") {
3490             return true;
3491         }
3492         match ty {
3493             // These cannot be tested when "resolv.h" is included and are tested
3494             // in the `linux_elf.rs` file.
3495             "Elf64_Phdr" | "Elf32_Phdr" => true,
3496 
3497             // On Linux, the type of `ut_tv` field of `struct utmpx`
3498             // can be an anonymous struct, so an extra struct,
3499             // which is absent in glibc, has to be defined.
3500             "__timeval" => true,
3501 
3502             // FIXME: This is actually a union, not a struct
3503             "sigval" => true,
3504 
3505             // This type is tested in the `linux_termios.rs` file since there
3506             // are header conflicts when including them with all the other
3507             // structs.
3508             "termios2" => true,
3509 
3510             // FIXME: remove once we set minimum supported glibc version.
3511             // ucontext_t added a new field as of glibc 2.28; our struct definition is
3512             // conservative and omits the field, but that means the size doesn't match for newer
3513             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
3514             "ucontext_t" if gnu => true,
3515 
3516             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
3517             // So let's ignore for now and re-visit later.
3518             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
3519             "statx" => true,
3520             "statx_timestamp" => true,
3521 
3522             // On Linux, the type of `ut_exit` field of struct `utmpx`
3523             // can be an anonymous struct, so an extra struct,
3524             // which is absent in musl, has to be defined.
3525             "__exit_status" if musl => true,
3526 
3527             // clone_args might differ b/w libc versions
3528             "clone_args" => true,
3529 
3530             // Might differ between kernel versions
3531             "open_how" => true,
3532 
3533             // FIXME: requires >= 5.4.1 kernel headers
3534             "j1939_filter" if musl => true,
3535 
3536             // FIXME: requires >= 5.4 kernel headers
3537             "sockaddr_can" if musl => true,
3538 
3539             "sctp_initmsg" | "sctp_sndrcvinfo" | "sctp_sndinfo" | "sctp_rcvinfo"
3540             | "sctp_nxtinfo" | "sctp_prinfo" | "sctp_authinfo" => true,
3541 
3542             // FIXME: requires >= 6.1 kernel headers
3543             "canxl_frame" => true,
3544             _ => false,
3545         }
3546     });
3547 
3548     cfg.skip_const(move |name| {
3549         if !gnu {
3550             // Skip definitions from the kernel on non-glibc Linux targets.
3551             // They're libc-independent, so we only need to check them on one
3552             // libc. We don't want to break CI if musl or another libc doesn't
3553             // have the definitions yet. (We do still want to check them on
3554             // every glibc target, though, as some of them can vary by
3555             // architecture.)
3556             //
3557             // This is not an exhaustive list of kernel constants, just a list
3558             // of prefixes of all those that have appeared here or that get
3559             // updated regularly and seem likely to cause breakage.
3560             if name.starts_with("AF_")
3561                 || name.starts_with("ARPHRD_")
3562                 || name.starts_with("EPOLL")
3563                 || name.starts_with("F_")
3564                 || name.starts_with("FALLOC_FL_")
3565                 || name.starts_with("IFLA_")
3566                 || name.starts_with("KEXEC_")
3567                 || name.starts_with("MS_")
3568                 || name.starts_with("MSG_")
3569                 || name.starts_with("OPEN_TREE_")
3570                 || name.starts_with("P_")
3571                 || name.starts_with("PF_")
3572                 || name.starts_with("RLIMIT_")
3573                 || name.starts_with("RTEXT_FILTER_")
3574                 || name.starts_with("SOL_")
3575                 || name.starts_with("STATX_")
3576                 || name.starts_with("SW_")
3577                 || name.starts_with("SYS_")
3578                 || name.starts_with("TCP_")
3579                 || name.starts_with("UINPUT_")
3580                 || name.starts_with("VMADDR_")
3581             {
3582                 return true;
3583             }
3584         }
3585         if musl {
3586             // FIXME: Requires >= 5.4.1 kernel headers
3587             if name.starts_with("J1939")
3588                 || name.starts_with("RTEXT_FILTER_")
3589                 || name.starts_with("SO_J1939")
3590                 || name.starts_with("SCM_J1939")
3591             {
3592                 return true;
3593             }
3594             // FIXME: Requires >= 5.10 kernel headers
3595             if name.starts_with("MEMBARRIER_CMD_REGISTER")
3596                 || name.starts_with("MEMBARRIER_CMD_PRIVATE")
3597             {
3598                 return true;
3599             }
3600             // LFS64 types have been removed in musl 1.2.4+
3601             if name.starts_with("RLIM64") {
3602                 return true;
3603             }
3604         }
3605         match name {
3606             // These constants are not available if gnu headers have been included
3607             // and can therefore not be tested here
3608             //
3609             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
3610             | "IPV6_FLOWINFO"
3611             | "IPV6_FLOWLABEL_MGR"
3612             | "IPV6_FLOWINFO_SEND"
3613             | "IPV6_FLOWINFO_FLOWLABEL"
3614             | "IPV6_FLOWINFO_PRIORITY"
3615             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
3616             | "F_CANCELLK"
3617             | "F_ADD_SEALS"
3618             | "F_GET_SEALS"
3619             | "F_SEAL_SEAL"
3620             | "F_SEAL_SHRINK"
3621             | "F_SEAL_GROW"
3622             | "F_SEAL_WRITE" => true,
3623             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests
3624             // because including `linux/if_arp.h` causes some conflicts:
3625             "ARPHRD_CAN" => true,
3626 
3627             // FIXME: deprecated: not available in any header
3628             // See: https://github.com/rust-lang/libc/issues/1356
3629             "ENOATTR" => true,
3630 
3631             // FIXME: SIGUNUSED was removed in glibc 2.26
3632             // Users should use SIGSYS instead.
3633             "SIGUNUSED" => true,
3634 
3635             // FIXME: conflicts with glibc headers and is tested in
3636             // `linux_termios.rs` below:
3637             | "BOTHER"
3638             | "IBSHIFT"
3639             | "TCGETS2"
3640             | "TCSETS2"
3641             | "TCSETSW2"
3642             | "TCSETSF2" => true,
3643 
3644             // FIXME: on musl the pthread types are defined a little differently
3645             // - these constants are used by the glibc implementation.
3646             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
3647 
3648             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
3649             // We should do so after a while.
3650             "SOMAXCONN" if gnu => true,
3651 
3652             // deprecated: not available from Linux kernel 5.6:
3653             "VMADDR_CID_RESERVED" => true,
3654 
3655             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
3656             | "IPPROTO_MAX"
3657             | "IPPROTO_ETHERNET"
3658             | "IPPROTO_MPTCP" => true,
3659 
3660             // FIXME: Not currently available in headers
3661             "P_PIDFD" if mips => true,
3662             "SYS_pidfd_open" if mips => true,
3663 
3664             // FIXME: Not currently available in headers on MIPS
3665             // Not yet implemented on sparc64
3666             "SYS_clone3" if mips | sparc64 => true,
3667 
3668             // FIXME: Not defined on ARM, gnueabihf, MIPS, musl, PowerPC, riscv64, s390x, and sparc64.
3669             "SYS_memfd_secret" if arm | gnueabihf | mips | musl | ppc | riscv64 | s390x | sparc64 => true,
3670 
3671             // FIXME: Added in Linux 5.16
3672             // https://github.com/torvalds/linux/commit/039c0ec9bb77446d7ada7f55f90af9299b28ca49
3673             "SYS_futex_waitv" => true,
3674 
3675             // FIXME: Added in Linux 5.17
3676             // https://github.com/torvalds/linux/commit/c6018b4b254971863bd0ad36bb5e7d0fa0f0ddb0
3677             "SYS_set_mempolicy_home_node" => true,
3678 
3679             // FIXME: Added in Linux 5.18
3680             // https://github.com/torvalds/linux/commit/8b5413647262dda8d8d0e07e14ea1de9ac7cf0b2
3681             "NFQA_PRIORITY" => true,
3682 
3683             // FIXME: requires more recent kernel headers on CI
3684             | "UINPUT_VERSION"
3685             | "SW_MAX"
3686             | "SW_CNT"
3687                 if mips || ppc64 || riscv64 => true,
3688 
3689             // FIXME: Not currently available in headers on ARM, MIPS and musl.
3690             "NETLINK_GET_STRICT_CHK" if arm || mips || musl => true,
3691 
3692             // kernel constants not available in uclibc 1.0.34
3693             | "EXTPROC"
3694             | "FAN_MARK_FILESYSTEM"
3695             | "FAN_MARK_INODE"
3696             | "IPPROTO_BEETPH"
3697             | "IPPROTO_MPLS"
3698             | "IPV6_HDRINCL"
3699             | "IPV6_MULTICAST_ALL"
3700             | "IPV6_PMTUDISC_INTERFACE"
3701             | "IPV6_PMTUDISC_OMIT"
3702             | "IPV6_ROUTER_ALERT_ISOLATE"
3703             | "PACKET_MR_UNICAST"
3704             | "RUSAGE_THREAD"
3705             | "SHM_EXEC"
3706             | "UDP_GRO"
3707             | "UDP_SEGMENT"
3708                 if uclibc => true,
3709 
3710             // headers conflicts with linux/pidfd.h
3711             "PIDFD_NONBLOCK" => true,
3712 
3713             // is a private value for kernel usage normally
3714             "FUSE_SUPER_MAGIC" => true,
3715 
3716             // linux 5.17 min
3717             "PR_SET_VMA" | "PR_SET_VMA_ANON_NAME" => true,
3718 
3719             // present in recent kernels only
3720             "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,
3721 
3722             // present in recent kernels only >= 5.13
3723             "PR_PAC_SET_ENABLED_KEYS" | "PR_PAC_GET_ENABLED_KEYS" => true,
3724             // present in recent kernels only >= 5.19
3725             "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,
3726 
3727             // Added in Linux 5.14
3728             "FUTEX_LOCK_PI2" => true,
3729 
3730             // Added in  linux 6.1
3731             "STATX_DIOALIGN"
3732             | "CAN_RAW_XL_FRAMES"
3733             | "CANXL_HDR_SIZE"
3734             | "CANXL_MAX_DLC"
3735             | "CANXL_MAX_DLC_MASK"
3736             | "CANXL_MAX_DLEN"
3737             | "CANXL_MAX_MTU"
3738             | "CANXL_MIN_DLC"
3739             | "CANXL_MIN_DLEN"
3740             | "CANXL_MIN_MTU"
3741             | "CANXL_MTU"
3742             | "CANXL_PRIO_BITS"
3743             | "CANXL_PRIO_MASK"
3744             | "CANXL_SEC"
3745             | "CANXL_XLF"
3746              => true,
3747 
3748             // FIXME: Parts of netfilter/nfnetlink*.h require more recent kernel headers:
3749             | "RTNLGRP_MCTP_IFADDR" // linux v5.17+
3750             | "RTNLGRP_TUNNEL" // linux v5.18+
3751             | "RTNLGRP_STATS" // linux v5.18+
3752                 => true,
3753 
3754             // FIXME: The below is no longer const in glibc 2.34:
3755             // https://github.com/bminor/glibc/commit/5d98a7dae955bafa6740c26eaba9c86060ae0344
3756             | "PTHREAD_STACK_MIN"
3757             | "SIGSTKSZ"
3758             | "MINSIGSTKSZ"
3759                 if gnu => true,
3760 
3761             // FIXME: Linux >= 5.16 changed its value:
3762             // https://github.com/torvalds/linux/commit/42df6e1d221dddc0f2acf2be37e68d553ad65f96
3763             "NF_NETDEV_NUMHOOKS" => true,
3764 
3765             // FIXME: requires Linux >= 5.6:
3766             | "RESOLVE_BENEATH"
3767             | "RESOLVE_CACHED"
3768             | "RESOLVE_IN_ROOT"
3769             | "RESOLVE_NO_MAGICLINKS"
3770             | "RESOLVE_NO_SYMLINKS"
3771             | "RESOLVE_NO_XDEV" if musl => true,
3772 
3773             // FIXME: requires Linux >= 5.4:
3774             | "CAN_J1939"
3775             | "CAN_NPROTO" if musl => true,
3776 
3777             // FIXME: requires Linux >= 5.6
3778             "GRND_INSECURE" if musl => true,
3779 
3780             // FIXME: requires Linux >= 5.7:
3781             "MREMAP_DONTUNMAP" if musl => true,
3782 
3783             // FIXME: requires Linux >= v5.8
3784             "IF_LINK_MODE_TESTING" if musl || sparc64 => true,
3785 
3786             // FIXME: Requires more recent kernel headers (5.9 / 5.11):
3787             | "CLOSE_RANGE_UNSHARE"
3788             | "CLOSE_RANGE_CLOEXEC" if musl => true,
3789 
3790             // FIXME: requires Linux >= 5.12:
3791             "MPOL_F_NUMA_BALANCING" if musl => true,
3792 
3793             // FIXME: Requires more recent kernel headers
3794             | "NFNL_SUBSYS_COUNT" // bumped in v5.14
3795             | "NFNL_SUBSYS_HOOK" // v5.14+
3796             | "NFULA_VLAN" // v5.4+
3797             | "NFULA_L2HDR" // v5.4+
3798             | "NFULA_VLAN_PROTO" // v5.4+
3799             | "NFULA_VLAN_TCI" // v5.4+
3800             | "NFULA_VLAN_UNSPEC" // v5.4+
3801             | "RTNLGRP_NEXTHOP" // linux v5.3+
3802             | "RTNLGRP_BRVLAN" // linux v5.6+
3803             if musl => true,
3804 
3805             | "MADV_COLD"
3806             | "MADV_PAGEOUT"
3807             | "MADV_POPULATE_READ"
3808             | "MADV_POPULATE_WRITE"
3809             if musl => true,
3810 
3811             // kernel 6.1 minimum
3812             "MADV_COLLAPSE" => true,
3813 
3814             // FIXME: Requires more recent kernel headers
3815             | "IFLA_PARENT_DEV_NAME"     // linux v5.13+
3816             | "IFLA_PARENT_DEV_BUS_NAME" // linux v5.13+
3817             | "IFLA_GRO_MAX_SIZE"        // linux v5.16+
3818             | "IFLA_TSO_MAX_SIZE"        // linux v5.18+
3819             | "IFLA_TSO_MAX_SEGS"        // linux v5.18+
3820             | "IFLA_ALLMULTI"            // linux v6.0+
3821             | "MADV_DONTNEED_LOCKED"     // linux v5.18+
3822                 => true,
3823             "SCTP_FUTURE_ASSOC" | "SCTP_CURRENT_ASSOC" | "SCTP_ALL_ASSOC" | "SCTP_PEER_ADDR_THLDS_V2" => true, // linux 5.5+
3824 
3825             // FIXME: Requires more recent kernel headers
3826             "HWTSTAMP_TX_ONESTEP_P2P" if musl => true, // linux v5.6+
3827 
3828             _ => false,
3829         }
3830     });
3831 
3832     cfg.skip_fn(move |name| {
3833         // skip those that are manually verified
3834         match name {
3835             // FIXME: https://github.com/rust-lang/libc/issues/1272
3836             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
3837 
3838             // There are two versions of the sterror_r function, see
3839             //
3840             // https://linux.die.net/man/3/strerror_r
3841             //
3842             // An XSI-compliant version provided if:
3843             //
3844             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
3845             //  && ! _GNU_SOURCE
3846             //
3847             // and a GNU specific version provided if _GNU_SOURCE is defined.
3848             //
3849             // libc provides bindings for the XSI-compliant version, which is
3850             // preferred for portable applications.
3851             //
3852             // We skip the test here since here _GNU_SOURCE is defined, and
3853             // test the XSI version below.
3854             "strerror_r" => true,
3855 
3856             // FIXME: Our API is unsound. The Rust API allows aliasing
3857             // pointers, but the C API requires pointers not to alias.
3858             // We should probably be at least using `&`/`&mut` here, see:
3859             // https://github.com/gnzlbg/ctest/issues/68
3860             "lio_listio" if musl => true,
3861 
3862             // Needs glibc 2.34 or later.
3863             "posix_spawn_file_actions_addclosefrom_np" if gnu && sparc64 => true,
3864             // Needs glibc 2.35 or later.
3865             "posix_spawn_file_actions_addtcsetpgrp_np" if gnu && sparc64 => true,
3866 
3867             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
3868             "sysctl" if gnu => true,
3869 
3870             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
3871             "gettimeofday" if gnu => true,
3872 
3873             // These are all implemented as static inline functions in uclibc, so
3874             // they cannot be linked against.
3875             // If implementations are required, they might need to be implemented
3876             // in this crate.
3877             "posix_spawnattr_init" if uclibc => true,
3878             "posix_spawnattr_destroy" if uclibc => true,
3879             "posix_spawnattr_getsigdefault" if uclibc => true,
3880             "posix_spawnattr_setsigdefault" if uclibc => true,
3881             "posix_spawnattr_getsigmask" if uclibc => true,
3882             "posix_spawnattr_setsigmask" if uclibc => true,
3883             "posix_spawnattr_getflags" if uclibc => true,
3884             "posix_spawnattr_setflags" if uclibc => true,
3885             "posix_spawnattr_getpgroup" if uclibc => true,
3886             "posix_spawnattr_setpgroup" if uclibc => true,
3887             "posix_spawnattr_getschedpolicy" if uclibc => true,
3888             "posix_spawnattr_setschedpolicy" if uclibc => true,
3889             "posix_spawnattr_getschedparam" if uclibc => true,
3890             "posix_spawnattr_setschedparam" if uclibc => true,
3891             "posix_spawn_file_actions_init" if uclibc => true,
3892             "posix_spawn_file_actions_destroy" if uclibc => true,
3893 
3894             // uclibc defines the flags type as a uint, but dependent crates
3895             // assume it's a int instead.
3896             "getnameinfo" if uclibc => true,
3897 
3898             // FIXME: This needs musl 1.2.2 or later.
3899             "gettid" if musl => true,
3900 
3901             // Needs glibc 2.33 or later.
3902             "mallinfo2" => true,
3903 
3904             "reallocarray" if musl => true,
3905 
3906             // Not defined in uclibc as of 1.0.34
3907             "gettid" if uclibc => true,
3908 
3909             // Needs musl 1.2.3 or later.
3910             "pthread_getname_np" if musl => true,
3911 
3912             // pthread_sigqueue uses sigval, which was initially declared
3913             // as a struct but should be defined as a union. However due
3914             // to the issues described here: https://github.com/rust-lang/libc/issues/2816
3915             // it can't be changed from struct.
3916             "pthread_sigqueue" => true,
3917 
3918             // There are two versions of basename(3) on Linux with glibc, see
3919             //
3920             // https://man7.org/linux/man-pages/man3/basename.3.html
3921             //
3922             // If libgen.h is included, then the POSIX version will be available;
3923             // If _GNU_SOURCE is defined and string.h is included, then the GNU one
3924             // will be used.
3925             //
3926             // libc exposes both of them, providing a prefix to differentiate between
3927             // them.
3928             //
3929             // Because the name with prefix is not a valid symbol in C, we have to
3930             // skip the tests.
3931             "posix_basename" if gnu => true,
3932             "gnu_basename" if gnu => true,
3933 
3934             _ => false,
3935         }
3936     });
3937 
3938     cfg.skip_field_type(move |struct_, field| {
3939         // This is a weird union, don't check the type.
3940         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
3941         // sighandler_t type is super weird
3942         (struct_ == "sigaction" && field == "sa_sigaction") ||
3943         // __timeval type is a patch which doesn't exist in glibc
3944         (struct_ == "utmpx" && field == "ut_tv") ||
3945         // sigval is actually a union, but we pretend it's a struct
3946         (struct_ == "sigevent" && field == "sigev_value") ||
3947         // this one is an anonymous union
3948         (struct_ == "ff_effect" && field == "u") ||
3949         // `__exit_status` type is a patch which is absent in musl
3950         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
3951         // `can_addr` is an anonymous union
3952         (struct_ == "sockaddr_can" && field == "can_addr")
3953     });
3954 
3955     cfg.volatile_item(|i| {
3956         use ctest::VolatileItemKind::*;
3957         match i {
3958             // aio_buf is a volatile void** but since we cannot express that in
3959             // Rust types, we have to explicitly tell the checker about it here:
3960             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
3961             _ => false,
3962         }
3963     });
3964 
3965     cfg.skip_field(move |struct_, field| {
3966         // this is actually a union on linux, so we can't represent it well and
3967         // just insert some padding.
3968         (struct_ == "siginfo_t" && field == "_pad") ||
3969         // musl names this __dummy1 but it's still there
3970         (musl && struct_ == "glob_t" && field == "gl_flags") ||
3971         // musl seems to define this as an *anonymous* bitfield
3972         (musl && struct_ == "statvfs" && field == "__f_unused") ||
3973         // sigev_notify_thread_id is actually part of a sigev_un union
3974         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
3975         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
3976         // has them yet.
3977         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
3978                                            field == "_pad2" ||
3979                                            field == "ssi_syscall" ||
3980                                            field == "ssi_call_addr" ||
3981                                            field == "ssi_arch")) ||
3982         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
3983         // while other fields become reserved.
3984         (struct_ == "sched_param" && [
3985             "sched_ss_low_priority",
3986             "sched_ss_repl_period",
3987             "sched_ss_init_budget",
3988             "sched_ss_max_repl",
3989         ].contains(&field) && musl) ||
3990         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
3991         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
3992         // glibc uses unnamed fields here and Rust doesn't support that yet
3993         (struct_ == "timex" && field.starts_with("__unused")) ||
3994         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
3995         (struct_ == "ipc_perm" && field == "mode"
3996             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
3997         ) ||
3998         // the `u` field is in fact an anonymous union
3999         (gnu && struct_ == "ptrace_syscall_info" && (field == "u" || field == "pad")) ||
4000         // the vregs field is a `__uint128_t` C's type.
4001         (struct_ == "user_fpsimd_struct" && field == "vregs") ||
4002         // Linux >= 5.11 tweaked the `svm_zero` field of the `sockaddr_vm` struct.
4003         // https://github.com/torvalds/linux/commit/dc8eeef73b63ed8988224ba6b5ed19a615163a7f
4004         (struct_ == "sockaddr_vm" && field == "svm_zero") ||
4005         // the `ifr_ifru` field is an anonymous union
4006         (struct_ == "ifreq" && field == "ifr_ifru") ||
4007         // glibc uses a single array `uregs` instead of individual fields.
4008         (struct_ == "user_regs" && arm)
4009     });
4010 
4011     cfg.skip_roundtrip(move |s| match s {
4012         // FIXME:
4013         "utsname" if mips32 || mips64 => true,
4014         // FIXME:
4015         "mcontext_t" if s390x => true,
4016         // FIXME: This is actually a union.
4017         "fpreg_t" if s390x => true,
4018 
4019         "sockaddr_un" | "sembuf" | "ff_constant_effect" if mips32 && (gnu || musl) => true,
4020 
4021         // The test doesn't work on some env:
4022         "ipv6_mreq"
4023         | "ip_mreq_source"
4024         | "sockaddr_in6"
4025         | "sockaddr_ll"
4026         | "in_pktinfo"
4027         | "arpreq"
4028         | "arpreq_old"
4029         | "sockaddr_un"
4030         | "ff_constant_effect"
4031         | "ff_ramp_effect"
4032         | "ff_condition_effect"
4033         | "Elf32_Ehdr"
4034         | "Elf32_Chdr"
4035         | "ucred"
4036         | "in6_pktinfo"
4037         | "sockaddr_nl"
4038         | "termios"
4039         | "nlmsgerr"
4040             if (mips64 || sparc64) && gnu =>
4041         {
4042             true
4043         }
4044 
4045         // The `inotify_event` and `cmsghdr` types contain Flexible Array Member fields (the
4046         // `name` and `data` fields respectively) which have unspecified calling convention.
4047         // The roundtripping tests deliberately pass the structs by value to check "by value"
4048         // layout consistency, but this would be UB for the these types.
4049         "inotify_event" => true,
4050         "cmsghdr" => true,
4051 
4052         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
4053         "max_align_t" if i686 || mips64 || ppc64 => true,
4054 
4055         _ => false,
4056     });
4057 
4058     cfg.generate("../src/lib.rs", "main.rs");
4059 
4060     test_linux_like_apis(target);
4061 }
4062 
4063 // This function tests APIs that are incompatible to test when other APIs
4064 // are included (e.g. because including both sets of headers clashes)
4065 fn test_linux_like_apis(target: &str) {
4066     let gnu = target.contains("gnu");
4067     let musl = target.contains("musl") || target.contains("ohos");
4068     let linux = target.contains("linux");
4069     let emscripten = target.contains("emscripten");
4070     let android = target.contains("android");
4071     assert!(linux || android || emscripten);
4072 
4073     if linux || android || emscripten {
4074         // test strerror_r from the `string.h` header
4075         let mut cfg = ctest_cfg();
4076         cfg.skip_type(|_| true).skip_static(|_| true);
4077 
4078         headers! { cfg: "string.h" }
4079         cfg.skip_fn(|f| match f {
4080             "strerror_r" => false,
4081             _ => true,
4082         })
4083         .skip_const(|_| true)
4084         .skip_struct(|_| true);
4085         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
4086     }
4087 
4088     if linux || android || emscripten {
4089         // test fcntl - see:
4090         // http://man7.org/linux/man-pages/man2/fcntl.2.html
4091         let mut cfg = ctest_cfg();
4092 
4093         if musl {
4094             cfg.header("fcntl.h");
4095         } else {
4096             cfg.header("linux/fcntl.h");
4097         }
4098 
4099         cfg.skip_type(|_| true)
4100             .skip_static(|_| true)
4101             .skip_struct(|_| true)
4102             .skip_fn(|_| true)
4103             .skip_const(move |name| match name {
4104                 // test fcntl constants:
4105                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK"
4106                 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false,
4107                 _ => true,
4108             })
4109             .type_name(move |ty, is_struct, is_union| match ty {
4110                 t if is_struct => format!("struct {}", t),
4111                 t if is_union => format!("union {}", t),
4112                 t => t.to_string(),
4113             });
4114 
4115         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
4116     }
4117 
4118     if linux || android {
4119         // test termios
4120         let mut cfg = ctest_cfg();
4121         cfg.header("asm/termbits.h");
4122         cfg.header("linux/termios.h");
4123         cfg.skip_type(|_| true)
4124             .skip_static(|_| true)
4125             .skip_fn(|_| true)
4126             .skip_const(|c| match c {
4127                 "BOTHER" | "IBSHIFT" => false,
4128                 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => false,
4129                 _ => true,
4130             })
4131             .skip_struct(|s| s != "termios2")
4132             .type_name(move |ty, is_struct, is_union| match ty {
4133                 "Ioctl" if gnu => "unsigned long".to_string(),
4134                 "Ioctl" => "int".to_string(),
4135                 t if is_struct => format!("struct {}", t),
4136                 t if is_union => format!("union {}", t),
4137                 t => t.to_string(),
4138             });
4139         cfg.generate("../src/lib.rs", "linux_termios.rs");
4140     }
4141 
4142     if linux || android {
4143         // test IPV6_ constants:
4144         let mut cfg = ctest_cfg();
4145         headers! {
4146             cfg:
4147             "linux/in6.h"
4148         }
4149         cfg.skip_type(|_| true)
4150             .skip_static(|_| true)
4151             .skip_fn(|_| true)
4152             .skip_const(|_| true)
4153             .skip_struct(|_| true)
4154             .skip_const(move |name| match name {
4155                 "IPV6_FLOWINFO"
4156                 | "IPV6_FLOWLABEL_MGR"
4157                 | "IPV6_FLOWINFO_SEND"
4158                 | "IPV6_FLOWINFO_FLOWLABEL"
4159                 | "IPV6_FLOWINFO_PRIORITY" => false,
4160                 _ => true,
4161             })
4162             .type_name(move |ty, is_struct, is_union| match ty {
4163                 t if is_struct => format!("struct {}", t),
4164                 t if is_union => format!("union {}", t),
4165                 t => t.to_string(),
4166             });
4167         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
4168     }
4169 
4170     if linux || android {
4171         // Test Elf64_Phdr and Elf32_Phdr
4172         // These types have a field called `p_type`, but including
4173         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
4174         // making the tests for these fails when both are included.
4175         let mut cfg = ctest_cfg();
4176         cfg.header("elf.h");
4177         cfg.skip_fn(|_| true)
4178             .skip_static(|_| true)
4179             .skip_const(|_| true)
4180             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
4181             .skip_struct(move |ty| match ty {
4182                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4183                 _ => true,
4184             })
4185             .skip_type(move |ty| match ty {
4186                 "Elf64_Phdr" | "Elf32_Phdr" => false,
4187                 _ => true,
4188             });
4189         cfg.generate("../src/lib.rs", "linux_elf.rs");
4190     }
4191 
4192     if linux || android {
4193         // Test `ARPHRD_CAN`.
4194         let mut cfg = ctest_cfg();
4195         cfg.header("linux/if_arp.h");
4196         cfg.skip_fn(|_| true)
4197             .skip_static(|_| true)
4198             .skip_const(move |name| match name {
4199                 "ARPHRD_CAN" => false,
4200                 _ => true,
4201             })
4202             .skip_struct(|_| true)
4203             .skip_type(|_| true);
4204         cfg.generate("../src/lib.rs", "linux_if_arp.rs");
4205     }
4206 }
4207 
4208 fn which_freebsd() -> Option<i32> {
4209     let output = std::process::Command::new("freebsd-version")
4210         .output()
4211         .ok()?;
4212     if !output.status.success() {
4213         return None;
4214     }
4215 
4216     let stdout = String::from_utf8(output.stdout).ok()?;
4217 
4218     match &stdout {
4219         s if s.starts_with("10") => Some(10),
4220         s if s.starts_with("11") => Some(11),
4221         s if s.starts_with("12") => Some(12),
4222         s if s.starts_with("13") => Some(13),
4223         s if s.starts_with("14") => Some(14),
4224         _ => None,
4225     }
4226 }
4227 
4228 fn test_haiku(target: &str) {
4229     assert!(target.contains("haiku"));
4230 
4231     let mut cfg = ctest_cfg();
4232     cfg.flag("-Wno-deprecated-declarations");
4233     cfg.define("__USE_GNU", Some("1"));
4234     cfg.define("_GNU_SOURCE", None);
4235     cfg.language(ctest::Lang::CXX);
4236 
4237     // POSIX API
4238     headers! { cfg:
4239                "alloca.h",
4240                "arpa/inet.h",
4241                "arpa/nameser.h",
4242                "arpa/nameser_compat.h",
4243                "assert.h",
4244                "complex.h",
4245                "ctype.h",
4246                "dirent.h",
4247                "div_t.h",
4248                "dlfcn.h",
4249                "endian.h",
4250                "errno.h",
4251                "fcntl.h",
4252                "fenv.h",
4253                "fnmatch.h",
4254                "fts.h",
4255                "ftw.h",
4256                "getopt.h",
4257                "glob.h",
4258                "grp.h",
4259                "inttypes.h",
4260                "iovec.h",
4261                "langinfo.h",
4262                "libgen.h",
4263                "libio.h",
4264                "limits.h",
4265                "locale.h",
4266                "malloc.h",
4267                "malloc_debug.h",
4268                "math.h",
4269                "memory.h",
4270                "monetary.h",
4271                "net/if.h",
4272                "net/if_dl.h",
4273                "net/if_media.h",
4274                "net/if_tun.h",
4275                "net/if_types.h",
4276                "net/route.h",
4277                "netdb.h",
4278                "netinet/in.h",
4279                "netinet/ip.h",
4280                "netinet/ip6.h",
4281                "netinet/ip_icmp.h",
4282                "netinet/ip_var.h",
4283                "netinet/tcp.h",
4284                "netinet/udp.h",
4285                "netinet6/in6.h",
4286                "nl_types.h",
4287                "null.h",
4288                "poll.h",
4289                "pthread.h",
4290                "pwd.h",
4291                "regex.h",
4292                "resolv.h",
4293                "sched.h",
4294                "search.h",
4295                "semaphore.h",
4296                "setjmp.h",
4297                "shadow.h",
4298                "signal.h",
4299                "size_t.h",
4300                "spawn.h",
4301                "stdint.h",
4302                "stdio.h",
4303                "stdlib.h",
4304                "string.h",
4305                "strings.h",
4306                "sys/cdefs.h",
4307                "sys/file.h",
4308                "sys/ioctl.h",
4309                "sys/ipc.h",
4310                "sys/mman.h",
4311                "sys/msg.h",
4312                "sys/param.h",
4313                "sys/poll.h",
4314                "sys/resource.h",
4315                "sys/select.h",
4316                "sys/sem.h",
4317                "sys/socket.h",
4318                "sys/sockio.h",
4319                "sys/stat.h",
4320                "sys/statvfs.h",
4321                "sys/time.h",
4322                "sys/timeb.h",
4323                "sys/times.h",
4324                "sys/types.h",
4325                "sys/uio.h",
4326                "sys/un.h",
4327                "sys/utsname.h",
4328                "sys/wait.h",
4329                "syslog.h",
4330                "tar.h",
4331                "termios.h",
4332                "time.h",
4333                "uchar.h",
4334                "unistd.h",
4335                "utime.h",
4336                "utmpx.h",
4337                "wchar.h",
4338                "wchar_t.h",
4339                "wctype.h"
4340     }
4341 
4342     // BSD Extensions
4343     headers! { cfg:
4344                "ifaddrs.h",
4345                "libutil.h",
4346                "link.h",
4347                "pty.h",
4348                "stringlist.h",
4349                "sys/link_elf.h",
4350     }
4351 
4352     // Native API
4353     headers! { cfg:
4354                "kernel/OS.h",
4355                "kernel/fs_attr.h",
4356                "kernel/fs_index.h",
4357                "kernel/fs_info.h",
4358                "kernel/fs_query.h",
4359                "kernel/fs_volume.h",
4360                "kernel/image.h",
4361                "kernel/scheduler.h",
4362                "storage/FindDirectory.h",
4363                "storage/StorageDefs.h",
4364                "support/Errors.h",
4365                "support/SupportDefs.h",
4366                "support/TypeConstants.h"
4367     }
4368 
4369     cfg.skip_struct(move |ty| {
4370         if ty.starts_with("__c_anonymous_") {
4371             return true;
4372         }
4373         match ty {
4374             // FIXME: actually a union
4375             "sigval" => true,
4376             // FIXME: locale_t does not exist on Haiku
4377             "locale_t" => true,
4378             // FIXME: rusage has a different layout on Haiku
4379             "rusage" => true,
4380             // FIXME?: complains that rust aligns on 4 byte boundary, but
4381             //         Haiku does not align it at all.
4382             "in6_addr" => true,
4383             // The d_name attribute is an array of 1 on Haiku, with the
4384             // intention that the developer allocates a larger or smaller
4385             // piece of memory depending on the expected/actual size of the name.
4386             // Other platforms have sensible defaults. In Rust, the d_name field
4387             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
4388             // newly allocated dirent objects. This breaks the automated tests.
4389             "dirent" => true,
4390             // The following structs contain function pointers, which cannot be initialized
4391             // with mem::zeroed(), so skip the automated test
4392             "image_info" | "thread_info" => true,
4393 
4394             "Elf64_Phdr" => true,
4395 
4396             // is an union
4397             "cpuid_info" => true,
4398 
4399             _ => false,
4400         }
4401     });
4402 
4403     cfg.skip_type(move |ty| {
4404         match ty {
4405             // FIXME: locale_t does not exist on Haiku
4406             "locale_t" => true,
4407             // These cause errors, to be reviewed in the future
4408             "sighandler_t" => true,
4409             "pthread_t" => true,
4410             "pthread_condattr_t" => true,
4411             "pthread_mutexattr_t" => true,
4412             "pthread_rwlockattr_t" => true,
4413             _ => false,
4414         }
4415     });
4416 
4417     cfg.skip_fn(move |name| {
4418         // skip those that are manually verified
4419         match name {
4420             // FIXME: https://github.com/rust-lang/libc/issues/1272
4421             "execv" | "execve" | "execvp" | "execvpe" => true,
4422             // FIXME: does not exist on haiku
4423             "open_wmemstream" => true,
4424             "mlockall" | "munlockall" => true,
4425             "tcgetsid" => true,
4426             "cfsetspeed" => true,
4427             // ignore for now, will be part of Haiku R1 beta 3
4428             "mlock" | "munlock" => true,
4429             // returns const char * on Haiku
4430             "strsignal" => true,
4431             // uses an enum as a parameter argument, which is incorrectly
4432             // translated into a struct argument
4433             "find_path" => true,
4434 
4435             "get_cpuid" => true,
4436 
4437             // uses varargs parameter
4438             "ioctl" => true,
4439 
4440             _ => false,
4441         }
4442     });
4443 
4444     cfg.skip_const(move |name| {
4445         match name {
4446             // FIXME: these constants do not exist on Haiku
4447             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
4448             | "DT_SOCK" => true,
4449             "USRQUOTA" | "GRPQUOTA" => true,
4450             "SIGIOT" => true,
4451             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL"
4452             | "ATF_USETRAILERS" => true,
4453             // Haiku does not have MAP_FILE, but rustc requires it
4454             "MAP_FILE" => true,
4455             // The following does not exist on Haiku but is required by
4456             // several crates
4457             "FIOCLEX" => true,
4458             // just skip this one, it is not defined on Haiku beta 2 but
4459             // since it is meant as a mask and not a parameter it can exist
4460             // here
4461             "LOG_PRIMASK" => true,
4462             // not defined on Haiku, but [get|set]priority is, so they are
4463             // useful
4464             "PRIO_MIN" | "PRIO_MAX" => true,
4465             //
4466             _ => false,
4467         }
4468     });
4469 
4470     cfg.skip_field(move |struct_, field| {
4471         match (struct_, field) {
4472             // FIXME: the stat struct actually has timespec members, whereas
4473             //        the current representation has these unpacked.
4474             ("stat", "st_atime") => true,
4475             ("stat", "st_atime_nsec") => true,
4476             ("stat", "st_mtime") => true,
4477             ("stat", "st_mtime_nsec") => true,
4478             ("stat", "st_ctime") => true,
4479             ("stat", "st_ctime_nsec") => true,
4480             ("stat", "st_crtime") => true,
4481             ("stat", "st_crtime_nsec") => true,
4482 
4483             // these are actually unions, but we cannot represent it well
4484             ("siginfo_t", "sigval") => true,
4485             ("sem_t", "named_sem_id") => true,
4486             ("sigaction", "sa_sigaction") => true,
4487             ("sigevent", "sigev_value") => true,
4488             ("fpu_state", "_fpreg") => true,
4489             // these fields have a simplified data definition in libc
4490             ("fpu_state", "_xmm") => true,
4491             ("savefpu", "_fp_ymm") => true,
4492 
4493             // skip these enum-type fields
4494             ("thread_info", "state") => true,
4495             ("image_info", "image_type") => true,
4496             _ => false,
4497         }
4498     });
4499 
4500     cfg.skip_roundtrip(move |s| match s {
4501         // FIXME: for some reason the roundtrip check fails for cpu_info
4502         "cpu_info" => true,
4503         _ => false,
4504     });
4505 
4506     cfg.type_name(move |ty, is_struct, is_union| {
4507         match ty {
4508             // Just pass all these through, no need for a "struct" prefix
4509             "area_info" | "port_info" | "port_message_info" | "team_info" | "sem_info"
4510             | "team_usage_info" | "thread_info" | "cpu_info" | "system_info"
4511             | "object_wait_info" | "image_info" | "attr_info" | "index_info" | "fs_info"
4512             | "FILE" | "DIR" | "Dl_info" => ty.to_string(),
4513 
4514             // enums don't need a prefix
4515             "directory_which" | "path_base_directory" => ty.to_string(),
4516 
4517             // is actually a union
4518             "sigval" => format!("union sigval"),
4519             t if is_union => format!("union {}", t),
4520             t if t.ends_with("_t") => t.to_string(),
4521             t if is_struct => format!("struct {}", t),
4522             t => t.to_string(),
4523         }
4524     });
4525 
4526     cfg.field_name(move |struct_, field| {
4527         match field {
4528             // Field is named `type` in C but that is a Rust keyword,
4529             // so these fields are translated to `type_` in the bindings.
4530             "type_" if struct_ == "object_wait_info" => "type".to_string(),
4531             "type_" if struct_ == "sem_t" => "type".to_string(),
4532             "type_" if struct_ == "attr_info" => "type".to_string(),
4533             "type_" if struct_ == "index_info" => "type".to_string(),
4534             "image_type" if struct_ == "image_info" => "type".to_string(),
4535             s => s.to_string(),
4536         }
4537     });
4538     cfg.generate("../src/lib.rs", "main.rs");
4539 }
4540