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