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