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