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