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