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