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