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