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