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