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