xref: /rust-libc-0.2.174/libc-test/build.rs (revision 530b4cae)
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("android") || target.contains("linux") {
27         cc::Build::new().file("src/errqueue.c").compile("errqueue");
28     }
29     if target.contains("linux")
30         || target.contains("l4re")
31         || target.contains("android")
32         || target.contains("emscripten")
33     {
34         cc::Build::new().file("src/sigrt.c").compile("sigrt");
35     }
36 }
37 
38 fn do_ctest() {
39     match &env::var("TARGET").unwrap() {
40         t if t.contains("android") => return test_android(t),
41         t if t.contains("apple") => return test_apple(t),
42         t if t.contains("dragonfly") => return test_dragonflybsd(t),
43         t if t.contains("emscripten") => return test_emscripten(t),
44         t if t.contains("freebsd") => return test_freebsd(t),
45         t if t.contains("haiku") => return test_haiku(t),
46         t if t.contains("linux") => return test_linux(t),
47         t if t.contains("netbsd") => return test_netbsd(t),
48         t if t.contains("openbsd") => return test_openbsd(t),
49         t if t.contains("redox") => return test_redox(t),
50         t if t.contains("solaris") => return test_solarish(t),
51         t if t.contains("illumos") => return test_solarish(t),
52         t if t.contains("wasi") => return test_wasi(t),
53         t if t.contains("windows") => return test_windows(t),
54         t if t.contains("vxworks") => return test_vxworks(t),
55         t => panic!("unknown target {}", t),
56     }
57 }
58 
59 fn ctest_cfg() -> ctest::TestGenerator {
60     let mut cfg = ctest::TestGenerator::new();
61     let libc_cfgs = [
62         "libc_priv_mod_use",
63         "libc_union",
64         "libc_const_size_of",
65         "libc_align",
66         "libc_core_cvoid",
67         "libc_packedN",
68         "libc_thread_local",
69     ];
70     for f in &libc_cfgs {
71         cfg.cfg(f, None);
72     }
73     cfg
74 }
75 
76 fn do_semver() {
77     let mut out = PathBuf::from(env::var("OUT_DIR").unwrap());
78     out.push("semver.rs");
79     let mut output = BufWriter::new(File::create(&out).unwrap());
80 
81     let family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap();
82     let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap();
83     let os = env::var("CARGO_CFG_TARGET_OS").unwrap();
84     let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap();
85     let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap();
86 
87     // `libc-test/semver` dir.
88     let mut semver_root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
89     semver_root.push("semver");
90 
91     // NOTE: Windows has the same `family` as `os`, no point in including it
92     // twice.
93     // NOTE: Android doesn't include the unix file (or the Linux file) because
94     // there are some many definitions missing it's actually easier just to
95     // maintain a file for Android.
96     if family != os && os != "android" {
97         process_semver_file(&mut output, &mut semver_root, &family);
98     }
99     process_semver_file(&mut output, &mut semver_root, &vendor);
100     process_semver_file(&mut output, &mut semver_root, &os);
101     let os_arch = format!("{}-{}", os, arch);
102     process_semver_file(&mut output, &mut semver_root, &os_arch);
103     if target_env != "" {
104         let os_env = format!("{}-{}", os, target_env);
105         process_semver_file(&mut output, &mut semver_root, &os_env);
106 
107         let os_env_arch = format!("{}-{}-{}", os, target_env, arch);
108         process_semver_file(&mut output, &mut semver_root, &os_env_arch);
109     }
110 }
111 
112 fn process_semver_file<W: Write, P: AsRef<Path>>(output: &mut W, path: &mut PathBuf, file: P) {
113     // NOTE: `path` is reused between calls, so always remove the file again.
114     path.push(file);
115     path.set_extension("txt");
116 
117     println!("cargo:rerun-if-changed={}", path.display());
118     let input_file = match File::open(&*path) {
119         Ok(file) => file,
120         Err(ref err) if err.kind() == io::ErrorKind::NotFound => {
121             path.pop();
122             return;
123         }
124         Err(err) => panic!("unexpected error opening file: {}", err),
125     };
126     let input = BufReader::new(input_file);
127 
128     write!(output, "// Source: {}.\n", path.display()).unwrap();
129     output.write(b"use libc::{\n").unwrap();
130     for line in input.lines() {
131         let line = line.unwrap().into_bytes();
132         match line.first() {
133             // Ignore comments and empty lines.
134             Some(b'#') | None => continue,
135             _ => {
136                 output.write(b"    ").unwrap();
137                 output.write(&line).unwrap();
138                 output.write(b",\n").unwrap();
139             }
140         }
141     }
142     output.write(b"};\n\n").unwrap();
143     path.pop();
144 }
145 
146 fn main() {
147     do_cc();
148     do_ctest();
149     do_semver();
150 }
151 
152 macro_rules! headers {
153     ($cfg:ident: [$m:expr]: $header:literal) => {
154         if $m {
155             $cfg.header($header);
156         }
157     };
158     ($cfg:ident: $header:literal) => {
159         $cfg.header($header);
160     };
161     ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => {
162         $(headers!($cfg: $([$c]:)* $header);)*
163     };
164     ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => {
165         headers!($($cfg: $([$c]:)* $header,)*);
166     };
167     ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => {
168         headers!($($cfg: $([$c]:)* $header,)*);
169     };
170 }
171 
172 fn test_apple(target: &str) {
173     assert!(target.contains("apple"));
174     let x86_64 = target.contains("x86_64");
175     let i686 = target.contains("i686");
176 
177     let mut cfg = ctest_cfg();
178     cfg.flag("-Wno-deprecated-declarations");
179     cfg.define("__APPLE_USE_RFC_3542", None);
180 
181     headers! { cfg:
182         "aio.h",
183         "CommonCrypto/CommonCrypto.h",
184         "CommonCrypto/CommonRandom.h",
185         "crt_externs.h",
186         "ctype.h",
187         "dirent.h",
188         "dlfcn.h",
189         "errno.h",
190         "execinfo.h",
191         "fcntl.h",
192         "glob.h",
193         "grp.h",
194         "iconv.h",
195         "ifaddrs.h",
196         "langinfo.h",
197         "libproc.h",
198         "limits.h",
199         "locale.h",
200         "mach-o/dyld.h",
201         "mach/mach_init.h",
202         "mach/mach_time.h",
203         "mach/mach_types.h",
204         "mach/mach_vm.h",
205         "mach/thread_act.h",
206         "mach/thread_policy.h",
207         "malloc/malloc.h",
208         "net/bpf.h",
209         "net/if.h",
210         "net/if_arp.h",
211         "net/if_dl.h",
212         "net/if_utun.h",
213         "net/route.h",
214         "net/route.h",
215         "netdb.h",
216         "netinet/if_ether.h",
217         "netinet/in.h",
218         "netinet/in.h",
219         "netinet/ip.h",
220         "netinet/tcp.h",
221         "netinet/udp.h",
222         "poll.h",
223         "pthread.h",
224         "pwd.h",
225         "regex.h",
226         "resolv.h",
227         "sched.h",
228         "semaphore.h",
229         "signal.h",
230         "spawn.h",
231         "stddef.h",
232         "stdint.h",
233         "stdio.h",
234         "stdlib.h",
235         "string.h",
236         "sys/attr.h",
237         "sys/clonefile.h",
238         "sys/event.h",
239         "sys/file.h",
240         "sys/ioctl.h",
241         "sys/ipc.h",
242         "sys/kern_control.h",
243         "sys/mman.h",
244         "sys/mount.h",
245         "sys/proc_info.h",
246         "sys/ptrace.h",
247         "sys/quota.h",
248         "sys/resource.h",
249         "sys/sem.h",
250         "sys/shm.h",
251         "sys/socket.h",
252         "sys/stat.h",
253         "sys/statvfs.h",
254         "sys/sys_domain.h",
255         "sys/sysctl.h",
256         "sys/time.h",
257         "sys/times.h",
258         "sys/timex.h",
259         "sys/types.h",
260         "sys/uio.h",
261         "sys/un.h",
262         "sys/utsname.h",
263         "sys/wait.h",
264         "sys/xattr.h",
265         "syslog.h",
266         "termios.h",
267         "time.h",
268         "unistd.h",
269         "util.h",
270         "utime.h",
271         "utmpx.h",
272         "wchar.h",
273         "xlocale.h",
274         [x86_64]: "crt_externs.h",
275     }
276 
277     cfg.skip_struct(move |ty| {
278         match ty {
279             // FIXME: actually a union
280             "sigval" => true,
281 
282             _ => false,
283         }
284     });
285 
286     cfg.skip_const(move |name| {
287         // They're declared via `deprecated_mach` and we don't support it anymore.
288         if name.starts_with("VM_FLAGS_") {
289             return true;
290         }
291         match name {
292             // These OSX constants are removed in Sierra.
293             // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html
294             "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true,
295             // FIXME: the value has been changed since Catalina (0xffff0000 -> 0x3fff0000).
296             "SF_SETTABLE" => true,
297             // FIXME: the values have been changed since Big Sur
298             "HW_TARGET" | "HW_PRODUCT" | "HW_MAXID" => true,
299             _ => false,
300         }
301     });
302 
303     cfg.skip_fn(move |name| {
304         // skip those that are manually verified
305         match name {
306             // FIXME: https://github.com/rust-lang/libc/issues/1272
307             "execv" | "execve" | "execvp" => true,
308 
309             // close calls the close_nocancel system call
310             "close" => true,
311 
312             // these calls require macOS 11.0 or higher
313             "preadv" | "pwritev" => true,
314 
315             _ => false,
316         }
317     });
318 
319     cfg.skip_field(move |struct_, field| {
320         match (struct_, field) {
321             // FIXME: the array size has been changed since macOS 10.15 ([8] -> [7]).
322             ("statfs", "f_reserved") => true,
323             ("__darwin_arm_neon_state64", "__v") => true,
324             // MAXPATHLEN is too big for auto-derive traits on arrays.
325             ("vnode_info_path", "vip_path") => true,
326             _ => false,
327         }
328     });
329 
330     cfg.skip_field_type(move |struct_, field| {
331         match (struct_, field) {
332             // FIXME: actually a union
333             ("sigevent", "sigev_value") => true,
334             _ => false,
335         }
336     });
337 
338     cfg.volatile_item(|i| {
339         use ctest::VolatileItemKind::*;
340         match i {
341             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
342             _ => false,
343         }
344     });
345 
346     cfg.type_name(move |ty, is_struct, is_union| {
347         match ty {
348             // Just pass all these through, no need for a "struct" prefix
349             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
350 
351             // OSX calls this something else
352             "sighandler_t" => "sig_t".to_string(),
353 
354             t if is_union => format!("union {}", t),
355             t if t.ends_with("_t") => t.to_string(),
356             t if is_struct => format!("struct {}", t),
357             t => t.to_string(),
358         }
359     });
360 
361     cfg.field_name(move |struct_, field| {
362         match field {
363             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
364                 s.replace("e_nsec", "espec.tv_nsec")
365             }
366             // FIXME: sigaction actually contains a union with two variants:
367             // a sa_sigaction with type: (*)(int, struct __siginfo *, void *)
368             // a sa_handler with type sig_t
369             "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
370             s => s.to_string(),
371         }
372     });
373 
374     cfg.skip_roundtrip(move |s| match s {
375         // FIXME: this type has the wrong ABI
376         "max_align_t" if i686 => true,
377         // Can't return an array from a C function.
378         "uuid_t" => true,
379         _ => false,
380     });
381     cfg.generate("../src/lib.rs", "main.rs");
382 }
383 
384 fn test_openbsd(target: &str) {
385     assert!(target.contains("openbsd"));
386 
387     let mut cfg = ctest_cfg();
388     cfg.flag("-Wno-deprecated-declarations");
389 
390     headers! { cfg:
391         "elf.h",
392         "errno.h",
393         "fcntl.h",
394         "limits.h",
395         "link.h",
396         "locale.h",
397         "stddef.h",
398         "stdint.h",
399         "stdio.h",
400         "stdlib.h",
401         "sys/stat.h",
402         "sys/types.h",
403         "time.h",
404         "wchar.h",
405         "ctype.h",
406         "dirent.h",
407         "sys/socket.h",
408         "net/if.h",
409         "net/route.h",
410         "net/if_arp.h",
411         "netdb.h",
412         "netinet/in.h",
413         "netinet/ip.h",
414         "netinet/tcp.h",
415         "netinet/udp.h",
416         "net/bpf.h",
417         "regex.h",
418         "resolv.h",
419         "pthread.h",
420         "dlfcn.h",
421         "signal.h",
422         "string.h",
423         "sys/file.h",
424         "sys/ioctl.h",
425         "sys/mman.h",
426         "sys/resource.h",
427         "sys/shm.h",
428         "sys/socket.h",
429         "sys/time.h",
430         "sys/un.h",
431         "sys/wait.h",
432         "unistd.h",
433         "utime.h",
434         "pwd.h",
435         "grp.h",
436         "sys/utsname.h",
437         "sys/ptrace.h",
438         "sys/mount.h",
439         "sys/uio.h",
440         "sched.h",
441         "termios.h",
442         "poll.h",
443         "syslog.h",
444         "semaphore.h",
445         "sys/statvfs.h",
446         "sys/times.h",
447         "glob.h",
448         "ifaddrs.h",
449         "langinfo.h",
450         "sys/sysctl.h",
451         "utmp.h",
452         "sys/event.h",
453         "net/if_dl.h",
454         "util.h",
455         "ufs/ufs/quota.h",
456         "pthread_np.h",
457         "sys/syscall.h",
458         "sys/shm.h",
459     }
460 
461     cfg.skip_struct(move |ty| {
462         match ty {
463             // FIXME: actually a union
464             "sigval" => true,
465 
466             _ => false,
467         }
468     });
469 
470     cfg.skip_const(move |name| {
471         match name {
472             // Removed in OpenBSD 6.0
473             "KERN_USERMOUNT" | "KERN_ARND" => true,
474             _ => false,
475         }
476     });
477 
478     cfg.skip_fn(move |name| {
479         match name {
480             // FIXME: https://github.com/rust-lang/libc/issues/1272
481             "execv" | "execve" | "execvp" | "execvpe" => true,
482 
483             // Removed in OpenBSD 6.5
484             // https://marc.info/?l=openbsd-cvs&m=154723400730318
485             "mincore" => true,
486 
487             _ => false,
488         }
489     });
490 
491     cfg.type_name(move |ty, is_struct, is_union| {
492         match ty {
493             // Just pass all these through, no need for a "struct" prefix
494             "FILE" | "DIR" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
495 
496             // OSX calls this something else
497             "sighandler_t" => "sig_t".to_string(),
498 
499             t if is_union => format!("union {}", t),
500             t if t.ends_with("_t") => t.to_string(),
501             t if is_struct => format!("struct {}", t),
502             t => t.to_string(),
503         }
504     });
505 
506     cfg.field_name(move |struct_, field| match field {
507         "st_birthtime" if struct_.starts_with("stat") => "__st_birthtime".to_string(),
508         "st_birthtime_nsec" if struct_.starts_with("stat") => "__st_birthtimensec".to_string(),
509         s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.replace("e_nsec", ".tv_nsec"),
510         "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(),
511         s => s.to_string(),
512     });
513 
514     cfg.skip_field_type(move |struct_, field| {
515         // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1
516         struct_ == "siginfo_t" && field == "si_addr"
517     });
518 
519     cfg.skip_field(|struct_, field| {
520         match (struct_, field) {
521             // conflicting with `p_type` macro from <resolve.h>.
522             ("Elf32_Phdr", "p_type") => true,
523             ("Elf64_Phdr", "p_type") => true,
524             _ => false,
525         }
526     });
527 
528     cfg.generate("../src/lib.rs", "main.rs");
529 }
530 
531 fn test_windows(target: &str) {
532     assert!(target.contains("windows"));
533     let gnu = target.contains("gnu");
534 
535     let mut cfg = ctest_cfg();
536     cfg.define("_WIN32_WINNT", Some("0x8000"));
537 
538     headers! { cfg:
539         "direct.h",
540         "errno.h",
541         "fcntl.h",
542         "io.h",
543         "limits.h",
544         "locale.h",
545         "process.h",
546         "signal.h",
547         "stddef.h",
548         "stdint.h",
549         "stdio.h",
550         "stdlib.h",
551         "sys/stat.h",
552         "sys/types.h",
553         "sys/utime.h",
554         "time.h",
555         "wchar.h",
556         [gnu]: "ws2tcpip.h",
557         [!gnu]: "Winsock2.h",
558     }
559 
560     cfg.type_name(move |ty, is_struct, is_union| {
561         match ty {
562             // Just pass all these through, no need for a "struct" prefix
563             "FILE" | "DIR" | "Dl_info" => ty.to_string(),
564 
565             // FIXME: these don't exist:
566             "time64_t" => "__time64_t".to_string(),
567             "ssize_t" => "SSIZE_T".to_string(),
568 
569             "sighandler_t" if !gnu => "_crt_signal_t".to_string(),
570             "sighandler_t" if gnu => "__p_sig_fn_t".to_string(),
571 
572             t if is_union => format!("union {}", t),
573             t if t.ends_with("_t") => t.to_string(),
574 
575             // Windows uppercase structs don't have `struct` in front:
576             t if is_struct => {
577                 if ty.clone().chars().next().unwrap().is_uppercase() {
578                     t.to_string()
579                 } else if t == "stat" {
580                     "struct __stat64".to_string()
581                 } else if t == "utimbuf" {
582                     "struct __utimbuf64".to_string()
583                 } else {
584                     // put `struct` in front of all structs:
585                     format!("struct {}", t)
586                 }
587             }
588             t => t.to_string(),
589         }
590     });
591 
592     cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string());
593 
594     cfg.skip_type(move |name| match name {
595         "SSIZE_T" if !gnu => true,
596         "ssize_t" if !gnu => true,
597         _ => false,
598     });
599 
600     cfg.skip_const(move |name| {
601         match name {
602             // FIXME: API error:
603             // SIG_ERR type is "void (*)(int)", not "int"
604             "SIG_ERR" |
605             // Similar for SIG_DFL/IGN/GET/SGE/ACK
606             "SIG_DFL" | "SIG_IGN" | "SIG_GET" | "SIG_SGE" | "SIG_ACK" => true,
607             // FIXME: newer windows-gnu environment on CI?
608             "_O_OBTAIN_DIR" if gnu => true,
609             _ => false,
610         }
611     });
612 
613     // FIXME: All functions point to the wrong addresses?
614     cfg.skip_fn_ptrcheck(|_| true);
615 
616     cfg.skip_signededness(move |c| {
617         match c {
618             // windows-isms
619             n if n.starts_with("P") => true,
620             n if n.starts_with("H") => true,
621             n if n.starts_with("LP") => true,
622             "sighandler_t" if gnu => true,
623             _ => false,
624         }
625     });
626 
627     cfg.skip_fn(move |name| {
628         match name {
629             // FIXME: https://github.com/rust-lang/libc/issues/1272
630             "execv" | "execve" | "execvp" | "execvpe" => true,
631 
632             _ => false,
633         }
634     });
635 
636     cfg.generate("../src/lib.rs", "main.rs");
637 }
638 
639 fn test_redox(target: &str) {
640     assert!(target.contains("redox"));
641 
642     let mut cfg = ctest_cfg();
643     cfg.flag("-Wno-deprecated-declarations");
644 
645     headers! {
646         cfg:
647         "ctype.h",
648         "dirent.h",
649         "dlfcn.h",
650         "errno.h",
651         "fcntl.h",
652         "grp.h",
653         "limits.h",
654         "locale.h",
655         "netdb.h",
656         "netinet/in.h",
657         "netinet/ip.h",
658         "netinet/tcp.h",
659         "poll.h",
660         "pwd.h",
661         "semaphore.h",
662         "string.h",
663         "strings.h",
664         "sys/file.h",
665         "sys/ioctl.h",
666         "sys/mman.h",
667         "sys/ptrace.h",
668         "sys/resource.h",
669         "sys/socket.h",
670         "sys/stat.h",
671         "sys/statvfs.h",
672         "sys/time.h",
673         "sys/types.h",
674         "sys/uio.h",
675         "sys/un.h",
676         "sys/utsname.h",
677         "sys/wait.h",
678         "termios.h",
679         "time.h",
680         "unistd.h",
681         "utime.h",
682         "wchar.h",
683     }
684 
685     cfg.generate("../src/lib.rs", "main.rs");
686 }
687 
688 fn test_solarish(target: &str) {
689     let is_solaris = target.contains("solaris");
690     let is_illumos = target.contains("illumos");
691     assert!(is_solaris || is_illumos);
692 
693     // ctest generates arguments supported only by clang, so make sure to run with CC=clang.
694     // While debugging, "CFLAGS=-ferror-limit=<large num>" is useful to get more error output.
695     let mut cfg = ctest_cfg();
696     cfg.flag("-Wno-deprecated-declarations");
697 
698     cfg.define("_XOPEN_SOURCE", Some("700"));
699     cfg.define("__EXTENSIONS__", None);
700     cfg.define("_LCONV_C99", None);
701 
702     headers! {
703         cfg:
704         "ctype.h",
705         "dirent.h",
706         "dlfcn.h",
707         "door.h",
708         "errno.h",
709         "execinfo.h",
710         "fcntl.h",
711         "glob.h",
712         "grp.h",
713         "ifaddrs.h",
714         "langinfo.h",
715         "limits.h",
716         "locale.h",
717         "mqueue.h",
718         "net/if.h",
719         "net/if_arp.h",
720         "net/route.h",
721         "netdb.h",
722         "netinet/in.h",
723         "netinet/ip.h",
724         "netinet/tcp.h",
725         "netinet/udp.h",
726         "poll.h",
727         "port.h",
728         "pthread.h",
729         "pwd.h",
730         "resolv.h",
731         "sched.h",
732         "semaphore.h",
733         "signal.h",
734         "stddef.h",
735         "stdint.h",
736         "stdio.h",
737         "stdlib.h",
738         "string.h",
739         "sys/epoll.h",
740         "sys/eventfd.h",
741         "sys/file.h",
742         "sys/filio.h",
743         "sys/ioctl.h",
744         "sys/loadavg.h",
745         "sys/mman.h",
746         "sys/mount.h",
747         "sys/pset.h",
748         "sys/resource.h",
749         "sys/socket.h",
750         "sys/stat.h",
751         "sys/statvfs.h",
752         "sys/stropts.h",
753         "sys/shm.h",
754         "sys/time.h",
755         "sys/times.h",
756         "sys/timex.h",
757         "sys/types.h",
758         "sys/uio.h",
759         "sys/un.h",
760         "sys/utsname.h",
761         "sys/wait.h",
762         "syslog.h",
763         "termios.h",
764         "time.h",
765         "ucontext.h",
766         "unistd.h",
767         "utime.h",
768         "utmpx.h",
769         "wchar.h",
770     }
771 
772     cfg.skip_type(move |ty| {
773         match ty {
774             // sighandler_t is not present here
775             "sighandler_t" => true,
776             _ => false,
777         }
778     });
779 
780     cfg.type_name(move |ty, is_struct, is_union| match ty {
781         "FILE" => "__FILE".to_string(),
782         "DIR" | "Dl_info" => ty.to_string(),
783         t if t.ends_with("_t") => t.to_string(),
784         t if is_struct => format!("struct {}", t),
785         t if is_union => format!("union {}", t),
786         t => t.to_string(),
787     });
788 
789     cfg.field_name(move |struct_, field| {
790         match struct_ {
791             // rust struct uses raw u64, rather than union
792             "epoll_event" if field == "u64" => "data.u64".to_string(),
793             // rust struct was committed with typo for Solaris
794             "door_arg_t" if field == "dec_num" => "desc_num".to_string(),
795             "stat" if field.ends_with("_nsec") => {
796                 // expose stat.Xtim.tv_nsec fields
797                 field.trim_end_matches("e_nsec").to_string() + ".tv_nsec"
798             }
799             _ => field.to_string(),
800         }
801     });
802 
803     cfg.skip_const(move |name| match name {
804         "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK"
805         | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => true,
806 
807         // skip sighandler_t assignments
808         "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true,
809 
810         "DT_UNKNOWN" => true,
811 
812         "_UTX_LINESIZE" | "_UTX_USERSIZE" | "_UTX_PADSIZE" | "_UTX_IDSIZE" | "_UTX_HOSTSIZE" => {
813             true
814         }
815 
816         "EADI" | "EXTPROC" | "IPC_SEAT" => true,
817 
818         // This evaluates to a sysconf() call rather than a constant
819         "PTHREAD_STACK_MIN" => true,
820 
821         // EPOLLEXCLUSIVE is a relatively recent addition to the epoll interface and may not be
822         // defined on older systems.  It is, however, safe to use on systems which do not
823         // explicitly support it. (A no-op is an acceptable implementation of EPOLLEXCLUSIVE.)
824         "EPOLLEXCLUSIVE" => true,
825 
826         _ => false,
827     });
828 
829     cfg.skip_struct(move |ty| {
830         // the union handling is a mess
831         if ty.contains("door_desc_t_") {
832             return true;
833         }
834         match ty {
835             // union, not a struct
836             "sigval" => true,
837             // a bunch of solaris-only fields
838             "utmpx" if is_illumos => true,
839             _ => false,
840         }
841     });
842 
843     cfg.skip_field(move |s, field| {
844         match s {
845             // C99 sizing on this is tough
846             "dirent" if field == "d_name" => true,
847             // the union/macro makes this rough
848             "sigaction" if field == "sa_sigaction" => true,
849             // Missing in illumos
850             "sigevent" if field == "ss_sp" => true,
851             // Avoid sigval union issues
852             "sigevent" if field == "sigev_value" => true,
853             // const issues
854             "sigevent" if field == "sigev_notify_attributes" => true,
855 
856             // Avoid const and union issues
857             "door_arg" if field == "desc_ptr" => true,
858             "door_desc_t" if field == "d_data" => true,
859             "door_arg_t" if field.ends_with("_ptr") => true,
860             "door_arg_t" if field.ends_with("rbuf") => true,
861 
862             _ => false,
863         }
864     });
865 
866     cfg.skip_fn(move |name| {
867         // skip those that are manually verified
868         match name {
869             // const-ness only added recently
870             "dladdr" => true,
871 
872             // Definition of those functions as changed since unified headers
873             // from NDK r14b These changes imply some API breaking changes but
874             // are still ABI compatible. We can wait for the next major release
875             // to be compliant with the new API.
876             //
877             // FIXME: unskip these for next major release
878             "setpriority" | "personality" => true,
879 
880             // signal is defined in terms of sighandler_t, so ignore
881             "signal" => true,
882 
883             // Currently missing
884             "cfmakeraw" | "cfsetspeed" => true,
885 
886             // const-ness issues
887             "execv" | "execve" | "execvp" | "settimeofday" | "sethostname" => true,
888 
889             // Solaris-different
890             "getpwent_r" | "getgrent_r" | "updwtmpx" if is_illumos => true,
891             "madvise" | "mprotect" if is_illumos => true,
892             "door_call" | "door_return" | "door_create" if is_illumos => true,
893 
894             // These functions may return int or void depending on the exact
895             // configuration of the compilation environment, but the return
896             // value is not useful (always 0) so we can ignore it:
897             "setservent" | "endservent" if is_illumos => true,
898 
899             _ => false,
900         }
901     });
902 
903     cfg.generate("../src/lib.rs", "main.rs");
904 }
905 
906 fn test_netbsd(target: &str) {
907     assert!(target.contains("netbsd"));
908     let rumprun = target.contains("rumprun");
909     let mut cfg = ctest_cfg();
910 
911     cfg.flag("-Wno-deprecated-declarations");
912     cfg.define("_NETBSD_SOURCE", Some("1"));
913 
914     headers! {
915         cfg:
916         "elf.h",
917         "errno.h",
918         "fcntl.h",
919         "limits.h",
920         "link.h",
921         "locale.h",
922         "stddef.h",
923         "stdint.h",
924         "stdio.h",
925         "stdlib.h",
926         "sys/stat.h",
927         "sys/types.h",
928         "time.h",
929         "wchar.h",
930         "aio.h",
931         "ctype.h",
932         "dirent.h",
933         "dlfcn.h",
934         "glob.h",
935         "grp.h",
936         "ifaddrs.h",
937         "langinfo.h",
938         "net/bpf.h",
939         "net/if.h",
940         "net/if_arp.h",
941         "net/if_dl.h",
942         "net/route.h",
943         "netdb.h",
944         "netinet/in.h",
945         "netinet/ip.h",
946         "netinet/tcp.h",
947         "netinet/udp.h",
948         "poll.h",
949         "pthread.h",
950         "pwd.h",
951         "regex.h",
952         "resolv.h",
953         "sched.h",
954         "semaphore.h",
955         "signal.h",
956         "string.h",
957         "sys/endian.h",
958         "sys/extattr.h",
959         "sys/file.h",
960         "sys/ioctl.h",
961         "sys/ioctl_compat.h",
962         "sys/mman.h",
963         "sys/mount.h",
964         "sys/ptrace.h",
965         "sys/resource.h",
966         "sys/shm.h",
967         "sys/socket.h",
968         "sys/statvfs.h",
969         "sys/sysctl.h",
970         "sys/time.h",
971         "sys/times.h",
972         "sys/timex.h",
973         "sys/ucontext.h",
974         "sys/uio.h",
975         "sys/un.h",
976         "sys/utsname.h",
977         "sys/wait.h",
978         "syslog.h",
979         "termios.h",
980         "ufs/ufs/quota.h",
981         "ufs/ufs/quota1.h",
982         "unistd.h",
983         "util.h",
984         "utime.h",
985         "mqueue.h",
986         "netinet/dccp.h",
987         "sys/event.h",
988         "sys/quota.h",
989         "sys/shm.h",
990         "iconv.h",
991     }
992 
993     cfg.type_name(move |ty, is_struct, is_union| {
994         match ty {
995             // Just pass all these through, no need for a "struct" prefix
996             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
997             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
998             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
999 
1000             // OSX calls this something else
1001             "sighandler_t" => "sig_t".to_string(),
1002 
1003             t if is_union => format!("union {}", t),
1004 
1005             t if t.ends_with("_t") => t.to_string(),
1006 
1007             // put `struct` in front of all structs:.
1008             t if is_struct => format!("struct {}", t),
1009 
1010             t => t.to_string(),
1011         }
1012     });
1013 
1014     cfg.field_name(move |struct_, field| {
1015         match field {
1016             // Our stat *_nsec fields normally don't actually exist but are part
1017             // of a timeval struct
1018             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1019                 s.replace("e_nsec", ".tv_nsec")
1020             }
1021             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1022             s => s.to_string(),
1023         }
1024     });
1025 
1026     cfg.skip_type(move |ty| {
1027         match ty {
1028             // FIXME: sighandler_t is crazy across platforms
1029             "sighandler_t" => true,
1030             _ => false,
1031         }
1032     });
1033 
1034     cfg.skip_struct(move |ty| {
1035         match ty {
1036             // This is actually a union, not a struct
1037             "sigval" => true,
1038             // These are tested as part of the linux_fcntl tests since there are
1039             // header conflicts when including them with all the other structs.
1040             "termios2" => true,
1041             _ => false,
1042         }
1043     });
1044 
1045     cfg.skip_signededness(move |c| {
1046         match c {
1047             "LARGE_INTEGER" | "float" | "double" => true,
1048             n if n.starts_with("pthread") => true,
1049             // sem_t is a struct or pointer
1050             "sem_t" => true,
1051             _ => false,
1052         }
1053     });
1054 
1055     cfg.skip_const(move |name| {
1056         match name {
1057             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1058             "SIGUNUSED" => true,                       // removed in glibc 2.26
1059 
1060             // weird signed extension or something like that?
1061             "MS_NOUSER" => true,
1062             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1063             "BOTHER" => true,
1064 
1065             _ => false,
1066         }
1067     });
1068 
1069     cfg.skip_fn(move |name| {
1070         match name {
1071             // FIXME: https://github.com/rust-lang/libc/issues/1272
1072             "execv" | "execve" | "execvp" => true,
1073 
1074             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1075             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1076             "prlimit" | "prlimit64" |        // non-int in 2nd arg
1077 
1078             // These functions presumably exist on netbsd but don't look like
1079             // they're implemented on rumprun yet, just let them slide for now.
1080             // Some of them look like they have headers but then don't have
1081             // corresponding actual definitions either...
1082             "shm_open" |
1083             "shm_unlink" |
1084             "syscall" |
1085             "mq_open" |
1086             "mq_close" |
1087             "mq_getattr" |
1088             "mq_notify" |
1089             "mq_receive" |
1090             "mq_send" |
1091             "mq_setattr" |
1092             "mq_timedreceive" |
1093             "mq_timedsend" |
1094             "mq_unlink" |
1095             "ptrace" |
1096             "sigaltstack" if rumprun => true,
1097 
1098             _ => false,
1099         }
1100     });
1101 
1102     cfg.skip_field_type(move |struct_, field| {
1103         // This is a weird union, don't check the type.
1104         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1105         // sighandler_t type is super weird
1106         (struct_ == "sigaction" && field == "sa_sigaction") ||
1107         // sigval is actually a union, but we pretend it's a struct
1108         (struct_ == "sigevent" && field == "sigev_value") ||
1109         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1110         (struct_ == "aiocb" && field == "aio_buf")
1111     });
1112 
1113     cfg.skip_field(|struct_, field| {
1114         match (struct_, field) {
1115             // conflicting with `p_type` macro from <resolve.h>.
1116             ("Elf32_Phdr", "p_type") => true,
1117             ("Elf64_Phdr", "p_type") => true,
1118             // pthread_spin_t is a volatile uchar
1119             ("pthread_spinlock_t", "pts_spin") => true,
1120             _ => false,
1121         }
1122     });
1123 
1124     cfg.generate("../src/lib.rs", "main.rs");
1125 }
1126 
1127 fn test_dragonflybsd(target: &str) {
1128     assert!(target.contains("dragonfly"));
1129     let mut cfg = ctest_cfg();
1130     cfg.flag("-Wno-deprecated-declarations");
1131 
1132     headers! {
1133         cfg:
1134         "aio.h",
1135         "ctype.h",
1136         "dirent.h",
1137         "dlfcn.h",
1138         "errno.h",
1139         "execinfo.h",
1140         "fcntl.h",
1141         "glob.h",
1142         "grp.h",
1143         "ifaddrs.h",
1144         "langinfo.h",
1145         "limits.h",
1146         "locale.h",
1147         "mqueue.h",
1148         "net/if.h",
1149         "net/if_arp.h",
1150         "net/if_dl.h",
1151         "net/route.h",
1152         "netdb.h",
1153         "netinet/in.h",
1154         "netinet/ip.h",
1155         "netinet/tcp.h",
1156         "netinet/udp.h",
1157         "poll.h",
1158         "pthread.h",
1159         "pthread_np.h",
1160         "pwd.h",
1161         "regex.h",
1162         "resolv.h",
1163         "sched.h",
1164         "semaphore.h",
1165         "signal.h",
1166         "stddef.h",
1167         "stdint.h",
1168         "stdio.h",
1169         "stdlib.h",
1170         "string.h",
1171         "sys/event.h",
1172         "sys/file.h",
1173         "sys/ioctl.h",
1174         "sys/ipc.h",
1175         "sys/mman.h",
1176         "sys/mount.h",
1177         "sys/ptrace.h",
1178         "sys/resource.h",
1179         "sys/rtprio.h",
1180         "sys/sched.h",
1181         "sys/shm.h",
1182         "sys/socket.h",
1183         "sys/stat.h",
1184         "sys/statvfs.h",
1185         "sys/sysctl.h",
1186         "sys/time.h",
1187         "sys/times.h",
1188         "sys/types.h",
1189         "sys/uio.h",
1190         "sys/un.h",
1191         "sys/utsname.h",
1192         "sys/wait.h",
1193         "syslog.h",
1194         "termios.h",
1195         "time.h",
1196         "ucontext.h",
1197         "ufs/ufs/quota.h",
1198         "unistd.h",
1199         "util.h",
1200         "utime.h",
1201         "utmpx.h",
1202         "wchar.h",
1203         "iconv.h",
1204     }
1205 
1206     cfg.type_name(move |ty, is_struct, is_union| {
1207         match ty {
1208             // Just pass all these through, no need for a "struct" prefix
1209             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
1210             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
1211             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
1212 
1213             // FIXME: OSX calls this something else
1214             "sighandler_t" => "sig_t".to_string(),
1215 
1216             t if is_union => format!("union {}", t),
1217 
1218             t if t.ends_with("_t") => t.to_string(),
1219 
1220             // put `struct` in front of all structs:.
1221             t if is_struct => format!("struct {}", t),
1222 
1223             t => t.to_string(),
1224         }
1225     });
1226 
1227     cfg.field_name(move |struct_, field| {
1228         match field {
1229             // Our stat *_nsec fields normally don't actually exist but are part
1230             // of a timeval struct
1231             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1232                 s.replace("e_nsec", ".tv_nsec")
1233             }
1234             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1235             // Field is named `type` in C but that is a Rust keyword,
1236             // so these fields are translated to `type_` in the bindings.
1237             "type_" if struct_ == "rtprio" => "type".to_string(),
1238             s => s.to_string(),
1239         }
1240     });
1241 
1242     cfg.skip_type(move |ty| {
1243         match ty {
1244             // sighandler_t is crazy across platforms
1245             "sighandler_t" => true,
1246 
1247             _ => false,
1248         }
1249     });
1250 
1251     cfg.skip_struct(move |ty| {
1252         match ty {
1253             // This is actually a union, not a struct
1254             "sigval" => true,
1255 
1256             // FIXME: These are tested as part of the linux_fcntl tests since
1257             // there are header conflicts when including them with all the other
1258             // structs.
1259             "termios2" => true,
1260 
1261             _ => false,
1262         }
1263     });
1264 
1265     cfg.skip_signededness(move |c| {
1266         match c {
1267             "LARGE_INTEGER" | "float" | "double" => true,
1268             // uuid_t is a struct, not an integer.
1269             "uuid_t" => true,
1270             n if n.starts_with("pthread") => true,
1271             // sem_t is a struct or pointer
1272             "sem_t" => true,
1273             // mqd_t is a pointer on DragonFly
1274             "mqd_t" => true,
1275 
1276             _ => false,
1277         }
1278     });
1279 
1280     cfg.skip_const(move |name| {
1281         match name {
1282             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1283 
1284             // weird signed extension or something like that?
1285             "MS_NOUSER" => true,
1286             "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
1287 
1288             // These are defined for Solaris 11, but the crate is tested on
1289             // illumos, where they are currently not defined
1290             "EADI" | "PORT_SOURCE_POSTWAIT" | "PORT_SOURCE_SIGNAL" | "PTHREAD_STACK_MIN" => true,
1291 
1292             _ => false,
1293         }
1294     });
1295 
1296     cfg.skip_fn(move |name| {
1297         // skip those that are manually verified
1298         match name {
1299             // FIXME: https://github.com/rust-lang/libc/issues/1272
1300             "execv" | "execve" | "execvp" => true,
1301 
1302             "getrlimit" | "getrlimit64" |    // non-int in 1st arg
1303             "setrlimit" | "setrlimit64" |    // non-int in 1st arg
1304             "prlimit" | "prlimit64"        // non-int in 2nd arg
1305              => true,
1306 
1307             _ => false,
1308         }
1309     });
1310 
1311     cfg.skip_field_type(move |struct_, field| {
1312         // This is a weird union, don't check the type.
1313         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1314         // sighandler_t type is super weird
1315         (struct_ == "sigaction" && field == "sa_sigaction") ||
1316         // sigval is actually a union, but we pretend it's a struct
1317         (struct_ == "sigevent" && field == "sigev_value") ||
1318         // aio_buf is "volatile void*" and Rust doesn't understand volatile
1319         (struct_ == "aiocb" && field == "aio_buf")
1320     });
1321 
1322     cfg.skip_field(move |struct_, field| {
1323         // this is actually a union on linux, so we can't represent it well and
1324         // just insert some padding.
1325         (struct_ == "siginfo_t" && field == "_pad") ||
1326         // sigev_notify_thread_id is actually part of a sigev_un union
1327         (struct_ == "sigevent" && field == "sigev_notify_thread_id")
1328     });
1329 
1330     cfg.generate("../src/lib.rs", "main.rs");
1331 }
1332 
1333 fn test_wasi(target: &str) {
1334     assert!(target.contains("wasi"));
1335 
1336     let mut cfg = ctest_cfg();
1337     cfg.define("_GNU_SOURCE", None);
1338 
1339     headers! { cfg:
1340         "ctype.h",
1341         "dirent.h",
1342         "errno.h",
1343         "fcntl.h",
1344         "limits.h",
1345         "locale.h",
1346         "malloc.h",
1347         "poll.h",
1348         "sched.h",
1349         "stdbool.h",
1350         "stddef.h",
1351         "stdint.h",
1352         "stdio.h",
1353         "stdlib.h",
1354         "string.h",
1355         "sys/resource.h",
1356         "sys/select.h",
1357         "sys/socket.h",
1358         "sys/stat.h",
1359         "sys/times.h",
1360         "sys/types.h",
1361         "sys/uio.h",
1362         "sys/utsname.h",
1363         "sys/ioctl.h",
1364         "time.h",
1365         "unistd.h",
1366         "wasi/api.h",
1367         "wasi/libc.h",
1368         "wasi/libc-find-relpath.h",
1369         "wasi/libc-nocwd.h",
1370         "wchar.h",
1371     }
1372 
1373     cfg.type_name(move |ty, is_struct, is_union| match ty {
1374         "FILE" | "fd_set" | "DIR" => ty.to_string(),
1375         t if is_union => format!("union {}", t),
1376         t if t.starts_with("__wasi") && t.ends_with("_u") => {
1377             format!("union {}", t)
1378         }
1379         t if t.starts_with("__wasi") && is_struct => format!("struct {}", t),
1380         t if t.ends_with("_t") => t.to_string(),
1381         t if is_struct => format!("struct {}", t),
1382         t => t.to_string(),
1383     });
1384 
1385     cfg.field_name(move |_struct, field| {
1386         match field {
1387             // deal with fields as rust keywords
1388             "type_" => "type".to_string(),
1389             s => s.to_string(),
1390         }
1391     });
1392 
1393     // Looks like LLD doesn't merge duplicate imports, so if the Rust
1394     // code imports from a module and the C code also imports from a
1395     // module we end up with two imports of function pointers which
1396     // import the same thing but have different function pointers
1397     cfg.skip_fn_ptrcheck(|f| f.starts_with("__wasi"));
1398 
1399     // d_name is declared as a flexible array in WASI libc, so it
1400     // doesn't support sizeof.
1401     cfg.skip_field(|s, field| s == "dirent" && field == "d_name");
1402 
1403     // Currently Rust/clang disagree on function argument ABI, so skip these
1404     // tests. For more info see WebAssembly/tool-conventions#88
1405     cfg.skip_roundtrip(|_| true);
1406 
1407     cfg.generate("../src/lib.rs", "main.rs");
1408 }
1409 
1410 fn test_android(target: &str) {
1411     assert!(target.contains("android"));
1412     let target_pointer_width = match target {
1413         t if t.contains("aarch64") || t.contains("x86_64") => 64,
1414         t if t.contains("i686") || t.contains("arm") => 32,
1415         t => panic!("unsupported target: {}", t),
1416     };
1417     let x86 = target.contains("i686") || target.contains("x86_64");
1418 
1419     let mut cfg = ctest_cfg();
1420     cfg.define("_GNU_SOURCE", None);
1421 
1422     headers! { cfg:
1423                "arpa/inet.h",
1424                "ctype.h",
1425                "dirent.h",
1426                "dlfcn.h",
1427                "elf.h",
1428                "errno.h",
1429                "fcntl.h",
1430                "grp.h",
1431                "ifaddrs.h",
1432                "limits.h",
1433                "link.h",
1434                "locale.h",
1435                "malloc.h",
1436                "net/ethernet.h",
1437                "net/if.h",
1438                "net/if_arp.h",
1439                "net/route.h",
1440                "netdb.h",
1441                "netinet/in.h",
1442                "netinet/ip.h",
1443                "netinet/tcp.h",
1444                "netinet/udp.h",
1445                "netpacket/packet.h",
1446                "poll.h",
1447                "pthread.h",
1448                "pty.h",
1449                "pwd.h",
1450                "regex.h",
1451                "resolv.h",
1452                "sched.h",
1453                "semaphore.h",
1454                "signal.h",
1455                "stddef.h",
1456                "stdint.h",
1457                "stdio.h",
1458                "stdlib.h",
1459                "string.h",
1460                "sys/auxv.h",
1461                "sys/epoll.h",
1462                "sys/eventfd.h",
1463                "sys/file.h",
1464                "sys/fsuid.h",
1465                "sys/inotify.h",
1466                "sys/ioctl.h",
1467                "sys/mman.h",
1468                "sys/mount.h",
1469                "sys/personality.h",
1470                "sys/prctl.h",
1471                "sys/ptrace.h",
1472                "sys/random.h",
1473                "sys/reboot.h",
1474                "sys/resource.h",
1475                "sys/sendfile.h",
1476                "sys/signalfd.h",
1477                "sys/socket.h",
1478                "sys/stat.h",
1479                "sys/statvfs.h",
1480                "sys/swap.h",
1481                "sys/syscall.h",
1482                "sys/sysinfo.h",
1483                "sys/system_properties.h",
1484                "sys/time.h",
1485                "sys/timerfd.h",
1486                "sys/times.h",
1487                "sys/types.h",
1488                "sys/ucontext.h",
1489                "sys/uio.h",
1490                "sys/un.h",
1491                "sys/utsname.h",
1492                "sys/vfs.h",
1493                "sys/xattr.h",
1494                "sys/wait.h",
1495                "syslog.h",
1496                "termios.h",
1497                "time.h",
1498                "unistd.h",
1499                "utime.h",
1500                "utmp.h",
1501                "wchar.h",
1502                "xlocale.h",
1503                // time64_t is not defined for 64-bit targets If included it will
1504                // generate the error 'Your time_t is already 64-bit'
1505                [target_pointer_width == 32]: "time64.h",
1506                [x86]: "sys/reg.h",
1507     }
1508 
1509     // Include linux headers at the end:
1510     headers! { cfg:
1511                 "asm/mman.h",
1512                 "linux/auxvec.h",
1513                 "linux/dccp.h",
1514                 "linux/elf.h",
1515                 "linux/errqueue.h",
1516                 "linux/falloc.h",
1517                 "linux/futex.h",
1518                 "linux/fs.h",
1519                 "linux/genetlink.h",
1520                 "linux/if_alg.h",
1521                 "linux/if_ether.h",
1522                 "linux/if_tun.h",
1523                 "linux/magic.h",
1524                 "linux/memfd.h",
1525                 "linux/module.h",
1526                 "linux/net_tstamp.h",
1527                 "linux/netfilter/nfnetlink.h",
1528                 "linux/netfilter/nfnetlink_log.h",
1529                 "linux/netfilter/nfnetlink_queue.h",
1530                 "linux/netfilter/nf_tables.h",
1531                 "linux/netfilter_ipv4.h",
1532                 "linux/netfilter_ipv6.h",
1533                 "linux/netfilter_ipv6/ip6_tables.h",
1534                 "linux/netlink.h",
1535                 "linux/quota.h",
1536                 "linux/reboot.h",
1537                 "linux/seccomp.h",
1538                 "linux/sched.h",
1539                 "linux/sockios.h",
1540                 "linux/vm_sockets.h",
1541                 "linux/wait.h",
1542 
1543     }
1544 
1545     // Include Android-specific headers:
1546     headers! { cfg:
1547                 "android/set_abort_message.h"
1548     }
1549 
1550     cfg.type_name(move |ty, is_struct, is_union| {
1551         match ty {
1552             // Just pass all these through, no need for a "struct" prefix
1553             "FILE" | "fd_set" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
1554 
1555             t if is_union => format!("union {}", t),
1556 
1557             t if t.ends_with("_t") => t.to_string(),
1558 
1559             // sigval is a struct in Rust, but a union in C:
1560             "sigval" => format!("union sigval"),
1561 
1562             // put `struct` in front of all structs:.
1563             t if is_struct => format!("struct {}", t),
1564 
1565             t => t.to_string(),
1566         }
1567     });
1568 
1569     cfg.field_name(move |struct_, field| {
1570         match field {
1571             // Our stat *_nsec fields normally don't actually exist but are part
1572             // of a timeval struct
1573             s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.to_string(),
1574             // FIXME: appears that `epoll_event.data` is an union
1575             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
1576             s => s.to_string(),
1577         }
1578     });
1579 
1580     cfg.skip_type(move |ty| {
1581         match ty {
1582             // FIXME: `sighandler_t` type is incorrect, see:
1583             // https://github.com/rust-lang/libc/issues/1359
1584             "sighandler_t" => true,
1585             _ => false,
1586         }
1587     });
1588 
1589     cfg.skip_struct(move |ty| {
1590         if ty.starts_with("__c_anonymous_") {
1591             return true;
1592         }
1593         match ty {
1594             // These are tested as part of the linux_fcntl tests since there are
1595             // header conflicts when including them with all the other structs.
1596             "termios2" => true,
1597             // uc_sigmask and uc_sigmask64 of ucontext_t are an anonymous union
1598             "ucontext_t" => true,
1599 
1600             _ => false,
1601         }
1602     });
1603 
1604     cfg.skip_const(move |name| {
1605         match name {
1606             // FIXME: deprecated: not available in any header
1607             // See: https://github.com/rust-lang/libc/issues/1356
1608             "ENOATTR" => true,
1609 
1610             // FIXME: still necessary?
1611             "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness
1612             // FIXME: deprecated - removed in glibc 2.26
1613             "SIGUNUSED" => true,
1614 
1615             // Needs a newer Android SDK for the definition
1616             "P_PIDFD" => true,
1617 
1618             // Requires Linux kernel 5.6
1619             "VMADDR_CID_LOCAL" => true,
1620 
1621             _ => false,
1622         }
1623     });
1624 
1625     cfg.skip_fn(move |name| {
1626         // skip those that are manually verified
1627         match name {
1628             // FIXME: https://github.com/rust-lang/libc/issues/1272
1629             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1630 
1631             // There are two versions of the sterror_r function, see
1632             //
1633             // https://linux.die.net/man/3/strerror_r
1634             //
1635             // An XSI-compliant version provided if:
1636             //
1637             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE
1638             //
1639             // and a GNU specific version provided if _GNU_SOURCE is defined.
1640             //
1641             // libc provides bindings for the XSI-compliant version, which is
1642             // preferred for portable applications.
1643             //
1644             // We skip the test here since here _GNU_SOURCE is defined, and
1645             // test the XSI version below.
1646             "strerror_r" => true,
1647             "reallocarray" => true,
1648 
1649             _ => false,
1650         }
1651     });
1652 
1653     cfg.skip_field_type(move |struct_, field| {
1654         // This is a weird union, don't check the type.
1655         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
1656         // sigval is actually a union, but we pretend it's a struct
1657         (struct_ == "sigevent" && field == "sigev_value") ||
1658         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1659         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1660         (struct_ == "sigaction" && field == "sa_sigaction") ||
1661         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1662         (struct_ == "signalfd_siginfo" && field == "ssi_call_addr")
1663     });
1664 
1665     cfg.skip_field(move |struct_, field| {
1666         // this is actually a union on linux, so we can't represent it well and
1667         // just insert some padding.
1668         (struct_ == "siginfo_t" && field == "_pad") ||
1669         // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
1670         // incorrect, see: https://github.com/rust-lang/libc/issues/1359
1671         (struct_ == "sigaction" && field == "sa_sigaction") ||
1672         // sigev_notify_thread_id is actually part of a sigev_un union
1673         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
1674         // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet.
1675         (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" ||
1676                                            field == "ssi_call_addr" ||
1677                                            field == "ssi_arch"))
1678     });
1679 
1680     cfg.skip_field(|struct_, field| {
1681         match (struct_, field) {
1682             // conflicting with `p_type` macro from <resolve.h>.
1683             ("Elf32_Phdr", "p_type") => true,
1684             ("Elf64_Phdr", "p_type") => true,
1685 
1686             // this is actually a union on linux, so we can't represent it well and
1687             // just insert some padding.
1688             ("siginfo_t", "_pad") => true,
1689 
1690             _ => false,
1691         }
1692     });
1693 
1694     cfg.generate("../src/lib.rs", "main.rs");
1695 
1696     test_linux_like_apis(target);
1697 }
1698 
1699 fn test_freebsd(target: &str) {
1700     assert!(target.contains("freebsd"));
1701     let mut cfg = ctest_cfg();
1702 
1703     let freebsd_ver = which_freebsd();
1704 
1705     match freebsd_ver {
1706         Some(10) => cfg.cfg("freebsd10", None),
1707         Some(11) => cfg.cfg("freebsd11", None),
1708         Some(12) => cfg.cfg("freebsd12", None),
1709         Some(13) => cfg.cfg("freebsd13", None),
1710         _ => &mut cfg,
1711     };
1712 
1713     // Required for `getline`:
1714     cfg.define("_WITH_GETLINE", None);
1715     // Required for making freebsd11_stat available in the headers
1716     match freebsd_ver {
1717         Some(10) => &mut cfg,
1718         _ => cfg.define("_WANT_FREEBSD11_STAT", None),
1719     };
1720 
1721     let freebsdlast = match freebsd_ver {
1722         Some(12) | Some(13) => true,
1723         _ => false,
1724     };
1725 
1726     headers! { cfg:
1727                 "aio.h",
1728                 "arpa/inet.h",
1729                 "ctype.h",
1730                 "dirent.h",
1731                 "dlfcn.h",
1732                 "elf.h",
1733                 "errno.h",
1734                 "execinfo.h",
1735                 "fcntl.h",
1736                 "glob.h",
1737                 "grp.h",
1738                 "iconv.h",
1739                 "ifaddrs.h",
1740                 "langinfo.h",
1741                 "libutil.h",
1742                 "limits.h",
1743                 "link.h",
1744                 "locale.h",
1745                 "machine/reg.h",
1746                 "malloc_np.h",
1747                 "mqueue.h",
1748                 "net/bpf.h",
1749                 "net/if.h",
1750                 "net/if_arp.h",
1751                 "net/if_dl.h",
1752                 "net/route.h",
1753                 "netdb.h",
1754                 "netinet/ip.h",
1755                 "netinet/in.h",
1756                 "netinet/tcp.h",
1757                 "netinet/udp.h",
1758                 "poll.h",
1759                 "pthread.h",
1760                 "pthread_np.h",
1761                 "pwd.h",
1762                 "regex.h",
1763                 "resolv.h",
1764                 "sched.h",
1765                 "semaphore.h",
1766                 "signal.h",
1767                 "spawn.h",
1768                 "stddef.h",
1769                 "stdint.h",
1770                 "stdio.h",
1771                 "stdlib.h",
1772                 "string.h",
1773                 "sys/capsicum.h",
1774                 [freebsdlast]:"sys/auxv.h",
1775                 "sys/cpuset.h",
1776                 "sys/event.h",
1777                 "sys/extattr.h",
1778                 "sys/file.h",
1779                 "sys/ioctl.h",
1780                 "sys/ipc.h",
1781                 "sys/jail.h",
1782                 "sys/mman.h",
1783                 "sys/mount.h",
1784                 "sys/msg.h",
1785                 "sys/procdesc.h",
1786                 "sys/ptrace.h",
1787                 "sys/random.h",
1788                 "sys/resource.h",
1789                 "sys/rtprio.h",
1790                 "sys/shm.h",
1791                 "sys/socket.h",
1792                 "sys/stat.h",
1793                 "sys/statvfs.h",
1794                 "sys/sysctl.h",
1795                 "sys/time.h",
1796                 "sys/times.h",
1797                 "sys/timex.h",
1798                 "sys/types.h",
1799                 "sys/ucontext.h",
1800                 "sys/uio.h",
1801                 "sys/un.h",
1802                 "sys/user.h",
1803                 "sys/utsname.h",
1804                 "sys/wait.h",
1805                 "syslog.h",
1806                 "termios.h",
1807                 "time.h",
1808                 "ufs/ufs/quota.h",
1809                 "unistd.h",
1810                 "utime.h",
1811                 "utmpx.h",
1812                 "wchar.h",
1813     }
1814 
1815     cfg.type_name(move |ty, is_struct, is_union| {
1816         match ty {
1817             // Just pass all these through, no need for a "struct" prefix
1818             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(),
1819 
1820             // FIXME: https://github.com/rust-lang/libc/issues/1273
1821             "sighandler_t" => "sig_t".to_string(),
1822 
1823             t if is_union => format!("union {}", t),
1824 
1825             t if t.ends_with("_t") => t.to_string(),
1826 
1827             // sigval is a struct in Rust, but a union in C:
1828             "sigval" => format!("union sigval"),
1829 
1830             // put `struct` in front of all structs:.
1831             t if is_struct => format!("struct {}", t),
1832 
1833             t => t.to_string(),
1834         }
1835     });
1836 
1837     cfg.field_name(move |struct_, field| {
1838         match field {
1839             // Our stat *_nsec fields normally don't actually exist but are part
1840             // of a timeval struct
1841             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
1842                 s.replace("e_nsec", ".tv_nsec")
1843             }
1844             // Field is named `type` in C but that is a Rust keyword,
1845             // so these fields are translated to `type_` in the bindings.
1846             "type_" if struct_ == "rtprio" => "type".to_string(),
1847             s => s.to_string(),
1848         }
1849     });
1850 
1851     cfg.skip_const(move |name| {
1852         match name {
1853             // These constants are to be introduced in yet-unreleased FreeBSD 12.2.
1854             "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW"
1855             | "F_SEAL_WRITE"
1856                 if Some(12) <= freebsd_ver =>
1857             {
1858                 true
1859             }
1860 
1861             // These constants were introduced in FreeBSD 12:
1862             "SF_USER_READAHEAD"
1863             | "EVFILT_EMPTY"
1864             | "SO_REUSEPORT_LB"
1865             | "IP_ORIGDSTADDR"
1866             | "IP_RECVORIGDSTADDR"
1867             | "IPV6_ORIGDSTADDR"
1868             | "IPV6_RECVORIGDSTADDR"
1869             | "NI_NUMERICSCOPE"
1870                 if Some(11) == freebsd_ver =>
1871             {
1872                 true
1873             }
1874 
1875             // These constants were introduced in FreeBSD 11:
1876             "SF_USER_READAHEAD"
1877             | "SF_NOCACHE"
1878             | "RLIMIT_KQUEUES"
1879             | "RLIMIT_UMTXP"
1880             | "EVFILT_PROCDESC"
1881             | "EVFILT_SENDFILE"
1882             | "EVFILT_EMPTY"
1883             | "SO_REUSEPORT_LB"
1884             | "TCP_CCALGOOPT"
1885             | "TCP_PCAP_OUT"
1886             | "TCP_PCAP_IN"
1887             | "IP_BINDMULTI"
1888             | "IP_ORIGDSTADDR"
1889             | "IP_RECVORIGDSTADDR"
1890             | "IPV6_ORIGDSTADDR"
1891             | "IPV6_RECVORIGDSTADDR"
1892             | "PD_CLOEXEC"
1893             | "PD_ALLOWED_AT_FORK"
1894             | "IP_RSS_LISTEN_BUCKET"
1895             | "NI_NUMERICSCOPE"
1896                 if Some(10) == freebsd_ver =>
1897             {
1898                 true
1899             }
1900 
1901             // FIXME: This constant has a different value in FreeBSD 10:
1902             "RLIM_NLIMITS" if Some(10) == freebsd_ver => true,
1903 
1904             // FIXME: There are deprecated - remove in a couple of releases.
1905             // These constants were removed in FreeBSD 11 (svn r273250) but will
1906             // still be accepted and ignored at runtime.
1907             "MAP_RENAME" | "MAP_NORESERVE" if Some(10) != freebsd_ver => true,
1908 
1909             // FIXME: There are deprecated - remove in a couple of releases.
1910             // These constants were removed in FreeBSD 11 (svn r262489),
1911             // and they've never had any legitimate use outside of the
1912             // base system anyway.
1913             "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true,
1914 
1915             // This constant was removed in FreeBSD 13 (svn r363622), and never
1916             // had any legitimate use outside of the base system anyway.
1917             "CTL_P1003_1B_MAXID" => true,
1918 
1919             // This was renamed in FreeBSD 12.2 and 13 (r352486).
1920             "CTL_UNSPEC" | "CTL_SYSCTL" => true,
1921 
1922             // These were added in FreeBSD 12.2 and 13 (r352486),
1923             // but they are just names for magic numbers that existed for ages.
1924             "CTL_SYSCTL_DEBUG"
1925             | "CTL_SYSCTL_NAME"
1926             | "CTL_SYSCTL_NEXT"
1927             | "CTL_SYSCTL_NAME2OID"
1928             | "CTL_SYSCTL_OIDFMT"
1929             | "CTL_SYSCTL_OIDDESCR"
1930             | "CTL_SYSCTL_OIDLABEL" => true,
1931 
1932             // This was renamed in FreeBSD 12.2 and 13 (r350749).
1933             "IPPROTO_SEP" | "IPPROTO_DCCP" => true,
1934 
1935             // This was changed to 96(0x60) in FreeBSD 13:
1936             // https://github.com/freebsd/freebsd/
1937             // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7
1938             "MINCORE_SUPER" if Some(13) == freebsd_ver => true,
1939 
1940             // This was increased to 97 in FreeBSD 12.2 and 13.
1941             // https://github.com/freebsd/freebsd/
1942             // commit/72a21ba0f62da5e86a1c0b462aeb3f5ff849a1b7
1943             "ELAST" if Some(12) == freebsd_ver => true,
1944 
1945             _ => false,
1946         }
1947     });
1948 
1949     cfg.skip_struct(move |ty| {
1950         if ty.starts_with("__c_anonymous_") {
1951             return true;
1952         }
1953         match ty {
1954             // `mmsghdr` is not available in FreeBSD 10
1955             "mmsghdr" if Some(10) == freebsd_ver => true,
1956 
1957             // `max_align_t` is not available in FreeBSD 10
1958             "max_align_t" if Some(10) == freebsd_ver => true,
1959 
1960             _ => false,
1961         }
1962     });
1963 
1964     cfg.skip_fn(move |name| {
1965         // skip those that are manually verified
1966         match name {
1967             // FIXME: https://github.com/rust-lang/libc/issues/1272
1968             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
1969 
1970             // These functions were added in FreeBSD 11:
1971             "fdatasync" | "mq_getfd_np" | "sendmmsg" | "recvmmsg" if Some(10) == freebsd_ver => {
1972                 true
1973             }
1974 
1975             // This function changed its return type from `int` in FreeBSD10 to
1976             // `ssize_t` in FreeBSD11:
1977             "aio_waitcomplete" if Some(10) == freebsd_ver => true,
1978 
1979             // The `uname` function in the `utsname.h` FreeBSD header is a C
1980             // inline function (has no symbol) that calls the `__xuname` symbol.
1981             // Therefore the function pointer comparison does not make sense for it.
1982             "uname" => true,
1983 
1984             // FIXME: Our API is unsound. The Rust API allows aliasing
1985             // pointers, but the C API requires pointers not to alias.
1986             // We should probably be at least using `&`/`&mut` here, see:
1987             // https://github.com/gnzlbg/ctest/issues/68
1988             "lio_listio" => true,
1989 
1990             _ => false,
1991         }
1992     });
1993 
1994     cfg.skip_signededness(move |c| {
1995         match c {
1996             // FIXME: has a different sign in FreeBSD10
1997             "blksize_t" if Some(10) == freebsd_ver => true,
1998             _ => false,
1999         }
2000     });
2001 
2002     cfg.volatile_item(|i| {
2003         use ctest::VolatileItemKind::*;
2004         match i {
2005             // aio_buf is a volatile void** but since we cannot express that in
2006             // Rust types, we have to explicitly tell the checker about it here:
2007             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2008             _ => false,
2009         }
2010     });
2011 
2012     cfg.skip_field(move |struct_, field| {
2013         match (struct_, field) {
2014             // FIXME: `sa_sigaction` has type `sighandler_t` but that type is
2015             // incorrect, see: https://github.com/rust-lang/libc/issues/1359
2016             ("sigaction", "sa_sigaction") => true,
2017 
2018             // FIXME: in FreeBSD10 this field has type `char*` instead of
2019             // `void*`:
2020             ("stack_t", "ss_sp") if Some(10) == freebsd_ver => true,
2021 
2022             // conflicting with `p_type` macro from <resolve.h>.
2023             ("Elf32_Phdr", "p_type") => true,
2024             ("Elf64_Phdr", "p_type") => true,
2025 
2026             // not available until FreeBSD 12, and is an anonymous union there.
2027             ("xucred", "cr_pid__c_anonymous_union") => true,
2028 
2029             // m_owner field is a volatile __lwpid_t
2030             ("umutex", "m_owner") => true,
2031             // c_has_waiters field is a volatile int32_t
2032             ("ucond", "c_has_waiters") => true,
2033             // is PATH_MAX long but tests can't accept multi array as equivalent.
2034             ("kinfo_vmentry", "kve_path") => true,
2035 
2036             _ => false,
2037         }
2038     });
2039 
2040     cfg.generate("../src/lib.rs", "main.rs");
2041 }
2042 
2043 fn test_emscripten(target: &str) {
2044     assert!(target.contains("emscripten"));
2045 
2046     let mut cfg = ctest_cfg();
2047     cfg.define("_GNU_SOURCE", None); // FIXME: ??
2048 
2049     headers! { cfg:
2050                "aio.h",
2051                "ctype.h",
2052                "dirent.h",
2053                "dlfcn.h",
2054                "errno.h",
2055                "fcntl.h",
2056                "glob.h",
2057                "grp.h",
2058                "ifaddrs.h",
2059                "langinfo.h",
2060                "limits.h",
2061                "locale.h",
2062                "malloc.h",
2063                "mntent.h",
2064                "mqueue.h",
2065                "net/ethernet.h",
2066                "net/if.h",
2067                "net/if_arp.h",
2068                "net/route.h",
2069                "netdb.h",
2070                "netinet/in.h",
2071                "netinet/ip.h",
2072                "netinet/tcp.h",
2073                "netinet/udp.h",
2074                "netpacket/packet.h",
2075                "poll.h",
2076                "pthread.h",
2077                "pty.h",
2078                "pwd.h",
2079                "resolv.h",
2080                "sched.h",
2081                "sched.h",
2082                "semaphore.h",
2083                "shadow.h",
2084                "signal.h",
2085                "stddef.h",
2086                "stdint.h",
2087                "stdio.h",
2088                "stdlib.h",
2089                "string.h",
2090                "sys/epoll.h",
2091                "sys/eventfd.h",
2092                "sys/file.h",
2093                "sys/ioctl.h",
2094                "sys/ipc.h",
2095                "sys/mman.h",
2096                "sys/mount.h",
2097                "sys/msg.h",
2098                "sys/personality.h",
2099                "sys/prctl.h",
2100                "sys/ptrace.h",
2101                "sys/quota.h",
2102                "sys/reboot.h",
2103                "sys/resource.h",
2104                "sys/sem.h",
2105                "sys/sendfile.h",
2106                "sys/shm.h",
2107                "sys/signalfd.h",
2108                "sys/socket.h",
2109                "sys/stat.h",
2110                "sys/statvfs.h",
2111                "sys/swap.h",
2112                "sys/syscall.h",
2113                "sys/sysctl.h",
2114                "sys/sysinfo.h",
2115                "sys/time.h",
2116                "sys/timerfd.h",
2117                "sys/times.h",
2118                "sys/types.h",
2119                "sys/uio.h",
2120                "sys/un.h",
2121                "sys/user.h",
2122                "sys/utsname.h",
2123                "sys/vfs.h",
2124                "sys/wait.h",
2125                "sys/xattr.h",
2126                "syslog.h",
2127                "termios.h",
2128                "time.h",
2129                "ucontext.h",
2130                "unistd.h",
2131                "utime.h",
2132                "utmp.h",
2133                "utmpx.h",
2134                "wchar.h",
2135     }
2136 
2137     cfg.type_name(move |ty, is_struct, is_union| {
2138         match ty {
2139             // Just pass all these through, no need for a "struct" prefix
2140             "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(),
2141 
2142             t if is_union => format!("union {}", t),
2143 
2144             t if t.ends_with("_t") => t.to_string(),
2145 
2146             // put `struct` in front of all structs:.
2147             t if is_struct => format!("struct {}", t),
2148 
2149             t => t.to_string(),
2150         }
2151     });
2152 
2153     cfg.field_name(move |struct_, field| {
2154         match field {
2155             // Our stat *_nsec fields normally don't actually exist but are part
2156             // of a timeval struct
2157             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2158                 s.replace("e_nsec", ".tv_nsec")
2159             }
2160             // FIXME: appears that `epoll_event.data` is an union
2161             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2162             s => s.to_string(),
2163         }
2164     });
2165 
2166     cfg.skip_type(move |ty| {
2167         match ty {
2168             // sighandler_t is crazy across platforms
2169             // FIXME: is this necessary?
2170             "sighandler_t" => true,
2171 
2172             _ => false,
2173         }
2174     });
2175 
2176     cfg.skip_struct(move |ty| {
2177         match ty {
2178             // This is actually a union, not a struct
2179             // FIXME: is this necessary?
2180             "sigval" => true,
2181 
2182             // FIXME: It was removed in
2183             // emscripten-core/emscripten@953e414
2184             "pthread_mutexattr_t" => true,
2185 
2186             // FIXME: Investigate why the test fails.
2187             // Skip for now to unblock CI.
2188             "pthread_condattr_t" => true,
2189 
2190             _ => false,
2191         }
2192     });
2193 
2194     cfg.skip_fn(move |name| {
2195         match name {
2196             // FIXME: https://github.com/rust-lang/libc/issues/1272
2197             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2198 
2199             // FIXME: Investigate why CI is missing it.
2200             "clearenv" => true,
2201 
2202             _ => false,
2203         }
2204     });
2205 
2206     cfg.skip_const(move |name| {
2207         match name {
2208             // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26
2209             // users should use SIGSYS instead
2210             "SIGUNUSED" => true,
2211 
2212             // FIXME: emscripten uses different constants to constructs these
2213             n if n.contains("__SIZEOF_PTHREAD") => true,
2214 
2215             // FIXME: `SYS_gettid` was removed in
2216             // emscripten-core/emscripten@6d6474e
2217             "SYS_gettid" => true,
2218 
2219             _ => false,
2220         }
2221     });
2222 
2223     cfg.skip_field_type(move |struct_, field| {
2224         // This is a weird union, don't check the type.
2225         // FIXME: is this necessary?
2226         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2227         // sighandler_t type is super weird
2228         // FIXME: is this necessary?
2229         (struct_ == "sigaction" && field == "sa_sigaction") ||
2230         // sigval is actually a union, but we pretend it's a struct
2231         // FIXME: is this necessary?
2232         (struct_ == "sigevent" && field == "sigev_value") ||
2233         // aio_buf is "volatile void*" and Rust doesn't understand volatile
2234         // FIXME: is this necessary?
2235         (struct_ == "aiocb" && field == "aio_buf")
2236     });
2237 
2238     cfg.skip_field(move |struct_, field| {
2239         // this is actually a union on linux, so we can't represent it well and
2240         // just insert some padding.
2241         // FIXME: is this necessary?
2242         (struct_ == "siginfo_t" && field == "_pad") ||
2243         // musl names this __dummy1 but it's still there
2244         // FIXME: is this necessary?
2245         (struct_ == "glob_t" && field == "gl_flags") ||
2246         // musl seems to define this as an *anonymous* bitfield
2247         // FIXME: is this necessary?
2248         (struct_ == "statvfs" && field == "__f_unused") ||
2249         // sigev_notify_thread_id is actually part of a sigev_un union
2250         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2251         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet.
2252         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2253                                            field == "_pad2" ||
2254                                            field == "ssi_syscall" ||
2255                                            field == "ssi_call_addr" ||
2256                                            field == "ssi_arch"))
2257     });
2258 
2259     // FIXME: test linux like
2260     cfg.generate("../src/lib.rs", "main.rs");
2261 }
2262 
2263 fn test_vxworks(target: &str) {
2264     assert!(target.contains("vxworks"));
2265 
2266     let mut cfg = ctest::TestGenerator::new();
2267     headers! { cfg:
2268                "vxWorks.h",
2269                "yvals.h",
2270                "nfs/nfsCommon.h",
2271                "rtpLibCommon.h",
2272                "randomNumGen.h",
2273                "taskLib.h",
2274                "sysLib.h",
2275                "ioLib.h",
2276                "inetLib.h",
2277                "socket.h",
2278                "errnoLib.h",
2279                "ctype.h",
2280                "dirent.h",
2281                "dlfcn.h",
2282                "elf.h",
2283                "fcntl.h",
2284                "grp.h",
2285                "sys/poll.h",
2286                "ifaddrs.h",
2287                "langinfo.h",
2288                "limits.h",
2289                "link.h",
2290                "locale.h",
2291                "sys/stat.h",
2292                "netdb.h",
2293                "pthread.h",
2294                "pwd.h",
2295                "sched.h",
2296                "semaphore.h",
2297                "signal.h",
2298                "stddef.h",
2299                "stdint.h",
2300                "stdio.h",
2301                "stdlib.h",
2302                "string.h",
2303                "sys/file.h",
2304                "sys/ioctl.h",
2305                "sys/socket.h",
2306                "sys/time.h",
2307                "sys/times.h",
2308                "sys/types.h",
2309                "sys/uio.h",
2310                "sys/un.h",
2311                "sys/utsname.h",
2312                "sys/wait.h",
2313                "netinet/tcp.h",
2314                "syslog.h",
2315                "termios.h",
2316                "time.h",
2317                "ucontext.h",
2318                "unistd.h",
2319                "utime.h",
2320                "wchar.h",
2321                "errno.h",
2322                "sys/mman.h",
2323                "pathLib.h",
2324                "mqueue.h",
2325     }
2326     // FIXME
2327     cfg.skip_const(move |name| match name {
2328         // sighandler_t weirdness
2329         "SIG_DFL" | "SIG_ERR" | "SIG_IGN"
2330         // This is not defined in vxWorks
2331         | "RTLD_DEFAULT"   => true,
2332         _ => false,
2333     });
2334     // FIXME
2335     cfg.skip_type(move |ty| match ty {
2336         "stat64" | "sighandler_t" | "off64_t" => true,
2337         _ => false,
2338     });
2339 
2340     cfg.skip_field_type(move |struct_, field| match (struct_, field) {
2341         ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true,
2342         _ => false,
2343     });
2344 
2345     cfg.skip_roundtrip(move |s| match s {
2346         _ => false,
2347     });
2348 
2349     cfg.type_name(move |ty, is_struct, is_union| match ty {
2350         "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(),
2351         t if is_union => format!("union {}", t),
2352         t if t.ends_with("_t") => t.to_string(),
2353         t if is_struct => format!("struct {}", t),
2354         t => t.to_string(),
2355     });
2356 
2357     // FIXME
2358     cfg.skip_fn(move |name| match name {
2359         // sigval
2360         "sigqueue" | "_sigqueue"
2361         // sighandler_t
2362         | "signal"
2363         // not used in static linking by default
2364         | "dlerror" => true,
2365         _ => false,
2366     });
2367 
2368     cfg.generate("../src/lib.rs", "main.rs");
2369 }
2370 
2371 fn test_linux(target: &str) {
2372     assert!(target.contains("linux"));
2373 
2374     // target_env
2375     let gnu = target.contains("gnu");
2376     let musl = target.contains("musl");
2377     let uclibc = target.contains("uclibc");
2378 
2379     match (gnu, musl, uclibc) {
2380         (true, false, false) => (),
2381         (false, true, false) => (),
2382         (false, false, true) => (),
2383         (_, _, _) => panic!(
2384             "linux target lib is gnu: {}, musl: {}, uclibc: {}",
2385             gnu, musl, uclibc
2386         ),
2387     }
2388 
2389     let arm = target.contains("arm");
2390     let i686 = target.contains("i686");
2391     let mips = target.contains("mips");
2392     let mips32 = mips && !target.contains("64");
2393     let mips64 = mips && target.contains("64");
2394     let ppc64 = target.contains("powerpc64");
2395     let s390x = target.contains("s390x");
2396     let sparc64 = target.contains("sparc64");
2397     let x32 = target.contains("x32");
2398     let x86_32 = target.contains("i686");
2399     let x86_64 = target.contains("x86_64");
2400     let aarch64_musl = target.contains("aarch64") && musl;
2401     let gnuabihf = target.contains("gnueabihf");
2402     let x86_64_gnux32 = target.contains("gnux32") && x86_64;
2403     let riscv64 = target.contains("riscv64");
2404     let uclibc = target.contains("uclibc");
2405 
2406     let mut cfg = ctest_cfg();
2407     cfg.define("_GNU_SOURCE", None);
2408     // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are
2409     // deprecated since glibc >= 2.29. This allows Rust binaries to link against
2410     // glibc versions older than 2.29.
2411     cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None);
2412 
2413     headers! { cfg:
2414                "ctype.h",
2415                "dirent.h",
2416                "dlfcn.h",
2417                "elf.h",
2418                "fcntl.h",
2419                "glob.h",
2420                "grp.h",
2421                "iconv.h",
2422                "ifaddrs.h",
2423                "langinfo.h",
2424                "limits.h",
2425                "link.h",
2426                "locale.h",
2427                "malloc.h",
2428                "mntent.h",
2429                "mqueue.h",
2430                "net/ethernet.h",
2431                "net/if.h",
2432                "net/if_arp.h",
2433                "net/route.h",
2434                "netdb.h",
2435                "netinet/in.h",
2436                "netinet/ip.h",
2437                "netinet/tcp.h",
2438                "netinet/udp.h",
2439                "netpacket/packet.h",
2440                "poll.h",
2441                "pthread.h",
2442                "pty.h",
2443                "pwd.h",
2444                "regex.h",
2445                "resolv.h",
2446                "sched.h",
2447                "semaphore.h",
2448                "shadow.h",
2449                "signal.h",
2450                "spawn.h",
2451                "stddef.h",
2452                "stdint.h",
2453                "stdio.h",
2454                "stdlib.h",
2455                "string.h",
2456                "sys/epoll.h",
2457                "sys/eventfd.h",
2458                "sys/file.h",
2459                "sys/fsuid.h",
2460                "sys/inotify.h",
2461                "sys/ioctl.h",
2462                "sys/ipc.h",
2463                "sys/mman.h",
2464                "sys/mount.h",
2465                "sys/msg.h",
2466                "sys/personality.h",
2467                "sys/prctl.h",
2468                "sys/ptrace.h",
2469                "sys/quota.h",
2470                "sys/random.h",
2471                "sys/reboot.h",
2472                "sys/resource.h",
2473                "sys/sem.h",
2474                "sys/sendfile.h",
2475                "sys/shm.h",
2476                "sys/signalfd.h",
2477                "sys/socket.h",
2478                "sys/stat.h",
2479                "sys/statvfs.h",
2480                "sys/swap.h",
2481                "sys/syscall.h",
2482                "sys/time.h",
2483                "sys/timerfd.h",
2484                "sys/times.h",
2485                "sys/timex.h",
2486                "sys/types.h",
2487                "sys/uio.h",
2488                "sys/un.h",
2489                "sys/user.h",
2490                "sys/utsname.h",
2491                "sys/vfs.h",
2492                "sys/wait.h",
2493                "syslog.h",
2494                "termios.h",
2495                "time.h",
2496                "ucontext.h",
2497                "unistd.h",
2498                "utime.h",
2499                "utmp.h",
2500                "utmpx.h",
2501                "wchar.h",
2502                "errno.h",
2503                // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit
2504                // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162
2505                // Also unavailable on gnuabihf with glibc 2.30.
2506                // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1
2507                [(x86_64 || x86_32 || arm) && !gnuabihf]: "sys/io.h",
2508                // `sys/reg.h` is only available on x86 and x86_64
2509                [x86_64 || x86_32]: "sys/reg.h",
2510                // sysctl system call is deprecated and not available on musl
2511                // It is also unsupported in x32, deprecated since glibc 2.30:
2512                [!(x32 || musl || gnu)]: "sys/sysctl.h",
2513                // <execinfo.h> is not supported by musl:
2514                // https://www.openwall.com/lists/musl/2015/04/09/3
2515                // <execinfo.h> is not present on uclibc.
2516                [!(musl || uclibc)]: "execinfo.h",
2517     }
2518 
2519     // Include linux headers at the end:
2520     headers! {
2521         cfg:
2522         "asm/mman.h",
2523         "linux/can.h",
2524         "linux/dccp.h",
2525         "linux/errqueue.h",
2526         "linux/falloc.h",
2527         "linux/fs.h",
2528         "linux/futex.h",
2529         "linux/genetlink.h",
2530         "linux/if.h",
2531         "linux/if_addr.h",
2532         "linux/if_alg.h",
2533         "linux/if_ether.h",
2534         "linux/if_tun.h",
2535         "linux/input.h",
2536         "linux/keyctl.h",
2537         "linux/magic.h",
2538         "linux/memfd.h",
2539         "linux/mman.h",
2540         "linux/module.h",
2541         "linux/net_tstamp.h",
2542         "linux/netfilter/nfnetlink.h",
2543         "linux/netfilter/nfnetlink_log.h",
2544         "linux/netfilter/nfnetlink_queue.h",
2545         "linux/netfilter/nf_tables.h",
2546         "linux/netfilter_ipv4.h",
2547         "linux/netfilter_ipv6.h",
2548         "linux/netfilter_ipv6/ip6_tables.h",
2549         "linux/netlink.h",
2550         "linux/quota.h",
2551         "linux/random.h",
2552         "linux/reboot.h",
2553         "linux/rtnetlink.h",
2554         "linux/seccomp.h",
2555         "linux/sockios.h",
2556         "linux/uinput.h",
2557         "linux/vm_sockets.h",
2558         "linux/wait.h",
2559         "sys/fanotify.h",
2560         // <sys/auxv.h> is not present on uclibc
2561         [!uclibc]: "sys/auxv.h",
2562     }
2563 
2564     // note: aio.h must be included before sys/mount.h
2565     headers! {
2566         cfg:
2567         "sys/xattr.h",
2568         "sys/sysinfo.h",
2569         // AIO is not supported by uclibc:
2570         [!uclibc]: "aio.h",
2571     }
2572 
2573     cfg.type_name(move |ty, is_struct, is_union| {
2574         match ty {
2575             // Just pass all these through, no need for a "struct" prefix
2576             "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr"
2577             | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr"
2578             | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(),
2579 
2580             t if is_union => format!("union {}", t),
2581 
2582             t if t.ends_with("_t") => t.to_string(),
2583 
2584             // In MUSL `flock64` is a typedef to `flock`.
2585             "flock64" if musl => format!("struct {}", ty),
2586 
2587             // put `struct` in front of all structs:.
2588             t if is_struct => format!("struct {}", t),
2589 
2590             t => t.to_string(),
2591         }
2592     });
2593 
2594     cfg.field_name(move |struct_, field| {
2595         match field {
2596             // Our stat *_nsec fields normally don't actually exist but are part
2597             // of a timeval struct
2598             s if s.ends_with("_nsec") && struct_.starts_with("stat") => {
2599                 s.replace("e_nsec", ".tv_nsec")
2600             }
2601             // FIXME: epoll_event.data is actually a union in C, but in Rust
2602             // it is only a u64 because we only expose one field
2603             // http://man7.org/linux/man-pages/man2/epoll_wait.2.html
2604             "u64" if struct_ == "epoll_event" => "data.u64".to_string(),
2605             // The following structs have a field called `type` in C,
2606             // but `type` is a Rust keyword, so these fields are translated
2607             // to `type_` in Rust.
2608             "type_"
2609                 if struct_ == "input_event"
2610                     || struct_ == "input_mask"
2611                     || struct_ == "ff_effect" =>
2612             {
2613                 "type".to_string()
2614             }
2615 
2616             s => s.to_string(),
2617         }
2618     });
2619 
2620     cfg.skip_type(move |ty| {
2621         match ty {
2622             // FIXME: `sighandler_t` type is incorrect, see:
2623             // https://github.com/rust-lang/libc/issues/1359
2624             "sighandler_t" => true,
2625 
2626             // These cannot be tested when "resolv.h" is included and are tested
2627             // in the `linux_elf.rs` file.
2628             "Elf64_Phdr" | "Elf32_Phdr" => true,
2629 
2630             // This type is private on Linux. It is implemented as a C `enum`
2631             // (`c_uint`) and this clashes with the type of the `rlimit` APIs
2632             // which expect a `c_int` even though both are ABI compatible.
2633             "__rlimit_resource_t" => true,
2634             // on Linux, this is a volatile int
2635             "pthread_spinlock_t" => true,
2636 
2637             _ => false,
2638         }
2639     });
2640 
2641     cfg.skip_struct(move |ty| {
2642         if ty.starts_with("__c_anonymous_") {
2643             return true;
2644         }
2645         // FIXME: musl CI has old headers
2646         if (musl || sparc64) && ty.starts_with("uinput_") {
2647             return true;
2648         }
2649         match ty {
2650             // These cannot be tested when "resolv.h" is included and are tested
2651             // in the `linux_elf.rs` file.
2652             "Elf64_Phdr" | "Elf32_Phdr" => true,
2653 
2654             // On Linux, the type of `ut_tv` field of `struct utmpx`
2655             // can be an anonymous struct, so an extra struct,
2656             // which is absent in glibc, has to be defined.
2657             "__timeval" => true,
2658 
2659             // FIXME: This is actually a union, not a struct
2660             "sigval" => true,
2661 
2662             // This type is tested in the `linux_termios.rs` file since there
2663             // are header conflicts when including them with all the other
2664             // structs.
2665             "termios2" => true,
2666 
2667             // FIXME: remove once we set minimum supported glibc version.
2668             // ucontext_t added a new field as of glibc 2.28; our struct definition is
2669             // conservative and omits the field, but that means the size doesn't match for newer
2670             // glibcs (see https://github.com/rust-lang/libc/issues/1410)
2671             "ucontext_t" if gnu => true,
2672 
2673             // FIXME: Somehow we cannot include headers correctly in glibc 2.30.
2674             // So let's ignore for now and re-visit later.
2675             // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085
2676             "statx" => true,
2677             "statx_timestamp" => true,
2678 
2679             // On Linux, the type of `ut_exit` field of struct `utmpx`
2680             // can be an anonymous struct, so an extra struct,
2681             // which is absent in musl, has to be defined.
2682             "__exit_status" if musl => true,
2683 
2684             // FIXME: CI's kernel header version is old.
2685             "sockaddr_can" => true,
2686 
2687             // Requires glibc 2.33 or newer.
2688             "mallinfo2" => true,
2689 
2690             _ => false,
2691         }
2692     });
2693 
2694     cfg.skip_const(move |name| {
2695         if !gnu {
2696             // Skip definitions from the kernel on non-glibc Linux targets.
2697             // They're libc-independent, so we only need to check them on one
2698             // libc. We don't want to break CI if musl or another libc doesn't
2699             // have the definitions yet. (We do still want to check them on
2700             // every glibc target, though, as some of them can vary by
2701             // architecture.)
2702             //
2703             // This is not an exhaustive list of kernel constants, just a list
2704             // of prefixes of all those that have appeared here or that get
2705             // updated regularly and seem likely to cause breakage.
2706             if name.starts_with("AF_")
2707                 || name.starts_with("ARPHRD_")
2708                 || name.starts_with("EPOLL")
2709                 || name.starts_with("F_")
2710                 || name.starts_with("FALLOC_FL_")
2711                 || name.starts_with("IFLA_")
2712                 || name.starts_with("MS_")
2713                 || name.starts_with("MSG_")
2714                 || name.starts_with("P_")
2715                 || name.starts_with("PF_")
2716                 || name.starts_with("RLIMIT_")
2717                 || name.starts_with("SOL_")
2718                 || name.starts_with("STATX_")
2719                 || name.starts_with("SW_")
2720                 || name.starts_with("SYS_")
2721                 || name.starts_with("TCP_")
2722                 || name.starts_with("UINPUT_")
2723                 || name.starts_with("VMADDR_")
2724             {
2725                 return true;
2726             }
2727         }
2728         match name {
2729             // These constants are not available if gnu headers have been included
2730             // and can therefore not be tested here
2731             //
2732             // The IPV6 constants are tested in the `linux_ipv6.rs` tests:
2733             | "IPV6_FLOWINFO"
2734             | "IPV6_FLOWLABEL_MGR"
2735             | "IPV6_FLOWINFO_SEND"
2736             | "IPV6_FLOWINFO_FLOWLABEL"
2737             | "IPV6_FLOWINFO_PRIORITY"
2738             // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests:
2739             | "F_CANCELLK"
2740             | "F_ADD_SEALS"
2741             | "F_GET_SEALS"
2742             | "F_SEAL_SEAL"
2743             | "F_SEAL_SHRINK"
2744             | "F_SEAL_GROW"
2745             | "F_SEAL_WRITE" => true,
2746             // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests
2747             // because including `linux/if_arp.h` causes some conflicts:
2748             "ARPHRD_CAN" => true,
2749 
2750             // Require Linux kernel 5.1:
2751             "F_SEAL_FUTURE_WRITE" => true,
2752 
2753             // FIXME: deprecated: not available in any header
2754             // See: https://github.com/rust-lang/libc/issues/1356
2755             "ENOATTR" => true,
2756 
2757             // FIXME: SIGUNUSED was removed in glibc 2.26
2758             // Users should use SIGSYS instead.
2759             "SIGUNUSED" => true,
2760 
2761             // FIXME: conflicts with glibc headers and is tested in
2762             // `linux_termios.rs` below:
2763             "BOTHER" => true,
2764 
2765             // FIXME: on musl the pthread types are defined a little differently
2766             // - these constants are used by the glibc implementation.
2767             n if musl && n.contains("__SIZEOF_PTHREAD") => true,
2768 
2769             // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4).
2770             // We should do so after a while.
2771             "SOMAXCONN" if gnu => true,
2772 
2773             // deprecated: not available from Linux kernel 5.6:
2774             "VMADDR_CID_RESERVED" => true,
2775 
2776             // Require Linux kernel 5.6:
2777             "VMADDR_CID_LOCAL" => true,
2778 
2779             // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP:
2780             | "IPPROTO_MAX"
2781             | "IPPROTO_MPTCP" => true,
2782 
2783             // FIXME: Not currently available in headers
2784             "P_PIDFD" if mips => true,
2785             "SYS_pidfd_open" if mips => true,
2786 
2787             // FIXME: Not currently available in headers on MIPS
2788             // Not yet implemented on sparc64
2789             "SYS_clone3" if mips | sparc64 => true,
2790 
2791             // FIXME: these syscalls were added in Linux 5.9 or later
2792             // and are currently not included in the glibc headers.
2793             | "SYS_close_range"
2794             | "SYS_openat2"
2795             | "SYS_pidfd_getfd"
2796             | "SYS_faccessat2"
2797             | "SYS_process_madvise"
2798             | "SYS_epoll_pwait2"
2799             | "SYS_mount_setattr" => true,
2800 
2801             // Requires more recent kernel headers:
2802             | "IFLA_PROP_LIST"
2803             | "IFLA_ALT_IFNAME"
2804             | "IFLA_PERM_ADDRESS"
2805             | "IFLA_PROTO_DOWN_REASON" => true,
2806 
2807             // FIXME: They require recent kernel header:
2808             | "CAN_J1939"
2809             | "CAN_RAW_FILTER_MAX"
2810             | "CAN_NPROTO" => true,
2811 
2812             // FIXME: Requires recent kernel headers (5.8):
2813             "STATX_MNT_ID" => true,
2814 
2815             // FIXME: requires more recent kernel headers on CI
2816             | "UINPUT_VERSION"
2817             | "SW_MAX"
2818             | "SW_CNT"
2819                 if mips || ppc64 || riscv64 || sparc64 => true,
2820 
2821             _ => false,
2822         }
2823     });
2824 
2825     cfg.skip_fn(move |name| {
2826         // skip those that are manually verified
2827         match name {
2828             // FIXME: https://github.com/rust-lang/libc/issues/1272
2829             "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true,
2830 
2831             // There are two versions of the sterror_r function, see
2832             //
2833             // https://linux.die.net/man/3/strerror_r
2834             //
2835             // An XSI-compliant version provided if:
2836             //
2837             // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600)
2838             //  && ! _GNU_SOURCE
2839             //
2840             // and a GNU specific version provided if _GNU_SOURCE is defined.
2841             //
2842             // libc provides bindings for the XSI-compliant version, which is
2843             // preferred for portable applications.
2844             //
2845             // We skip the test here since here _GNU_SOURCE is defined, and
2846             // test the XSI version below.
2847             "strerror_r" => true,
2848 
2849             // FIXME: Our API is unsound. The Rust API allows aliasing
2850             // pointers, but the C API requires pointers not to alias.
2851             // We should probably be at least using `&`/`&mut` here, see:
2852             // https://github.com/gnzlbg/ctest/issues/68
2853             "lio_listio" if musl => true,
2854 
2855             // FIXME: the glibc version used by the Sparc64 build jobs
2856             // which use Debian 10.0 is too old.
2857             "statx" if sparc64 => true,
2858 
2859             // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does.
2860             "sysctl" if gnu => true,
2861 
2862             // FIXME: It now takes c_void instead of timezone since glibc 2.31.
2863             "gettimeofday" if gnu => true,
2864 
2865             // These are all implemented as static inline functions in uclibc, so
2866             // they cannot be linked against.
2867             // If implementations are required, they might need to be implemented
2868             // in this crate.
2869             "posix_spawnattr_init" if uclibc => true,
2870             "posix_spawnattr_destroy" if uclibc => true,
2871             "posix_spawnattr_getsigdefault" if uclibc => true,
2872             "posix_spawnattr_setsigdefault" if uclibc => true,
2873             "posix_spawnattr_getsigmask" if uclibc => true,
2874             "posix_spawnattr_setsigmask" if uclibc => true,
2875             "posix_spawnattr_getflags" if uclibc => true,
2876             "posix_spawnattr_setflags" if uclibc => true,
2877             "posix_spawnattr_getpgroup" if uclibc => true,
2878             "posix_spawnattr_setpgroup" if uclibc => true,
2879             "posix_spawnattr_getschedpolicy" if uclibc => true,
2880             "posix_spawnattr_setschedpolicy" if uclibc => true,
2881             "posix_spawnattr_getschedparam" if uclibc => true,
2882             "posix_spawnattr_setschedparam" if uclibc => true,
2883             "posix_spawn_file_actions_init" if uclibc => true,
2884             "posix_spawn_file_actions_destroy" if uclibc => true,
2885 
2886             // uclibc defines the flags type as a uint, but dependent crates
2887             // assume it's a int instead.
2888             "getnameinfo" if uclibc => true,
2889 
2890             // FIXME: This needs musl 1.2.2 or later.
2891             "gettid" if musl => true,
2892 
2893             // Needs glibc 2.33 or later.
2894             "mallinfo2" => true,
2895 
2896             "reallocarray" if musl => true,
2897 
2898             _ => false,
2899         }
2900     });
2901 
2902     cfg.skip_field_type(move |struct_, field| {
2903         // This is a weird union, don't check the type.
2904         (struct_ == "ifaddrs" && field == "ifa_ifu") ||
2905         // sighandler_t type is super weird
2906         (struct_ == "sigaction" && field == "sa_sigaction") ||
2907         // __timeval type is a patch which doesn't exist in glibc
2908         (struct_ == "utmpx" && field == "ut_tv") ||
2909         // sigval is actually a union, but we pretend it's a struct
2910         (struct_ == "sigevent" && field == "sigev_value") ||
2911         // this one is an anonymous union
2912         (struct_ == "ff_effect" && field == "u") ||
2913         // `__exit_status` type is a patch which is absent in musl
2914         (struct_ == "utmpx" && field == "ut_exit" && musl) ||
2915         // `can_addr` is an anonymous union
2916         (struct_ == "sockaddr_can" && field == "can_addr")
2917     });
2918 
2919     cfg.volatile_item(|i| {
2920         use ctest::VolatileItemKind::*;
2921         match i {
2922             // aio_buf is a volatile void** but since we cannot express that in
2923             // Rust types, we have to explicitly tell the checker about it here:
2924             StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true,
2925             _ => false,
2926         }
2927     });
2928 
2929     cfg.skip_field(move |struct_, field| {
2930         // this is actually a union on linux, so we can't represent it well and
2931         // just insert some padding.
2932         (struct_ == "siginfo_t" && field == "_pad") ||
2933         // musl names this __dummy1 but it's still there
2934         (musl && struct_ == "glob_t" && field == "gl_flags") ||
2935         // musl seems to define this as an *anonymous* bitfield
2936         (musl && struct_ == "statvfs" && field == "__f_unused") ||
2937         // sigev_notify_thread_id is actually part of a sigev_un union
2938         (struct_ == "sigevent" && field == "sigev_notify_thread_id") ||
2939         // signalfd had SIGSYS fields added in Linux 4.18, but no libc release
2940         // has them yet.
2941         (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" ||
2942                                            field == "_pad2" ||
2943                                            field == "ssi_syscall" ||
2944                                            field == "ssi_call_addr" ||
2945                                            field == "ssi_arch")) ||
2946         // FIXME: After musl 1.1.24, it have only one field `sched_priority`,
2947         // while other fields become reserved.
2948         (struct_ == "sched_param" && [
2949             "sched_ss_low_priority",
2950             "sched_ss_repl_period",
2951             "sched_ss_init_budget",
2952             "sched_ss_max_repl",
2953         ].contains(&field) && musl) ||
2954         // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`.
2955         (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) ||
2956         // glibc uses unnamed fields here and Rust doesn't support that yet
2957         (struct_ == "timex" && field.starts_with("__unused")) ||
2958         // FIXME: It now takes mode_t since glibc 2.31 on some targets.
2959         (struct_ == "ipc_perm" && field == "mode"
2960             && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32)
2961         )
2962     });
2963 
2964     cfg.skip_roundtrip(move |s| match s {
2965         // FIXME:
2966         "utsname" if mips32 || mips64 => true,
2967         // FIXME:
2968         "mcontext_t" if s390x => true,
2969         // FIXME: This is actually a union.
2970         "fpreg_t" if s390x => true,
2971 
2972         "sockaddr_un" | "sembuf" | "ff_constant_effect" if mips32 && (gnu || musl) => true,
2973         "ipv6_mreq"
2974         | "ip_mreq_source"
2975         | "sockaddr_in6"
2976         | "sockaddr_ll"
2977         | "in_pktinfo"
2978         | "arpreq"
2979         | "arpreq_old"
2980         | "sockaddr_un"
2981         | "ff_constant_effect"
2982         | "ff_ramp_effect"
2983         | "ff_condition_effect"
2984         | "Elf32_Ehdr"
2985         | "Elf32_Chdr"
2986         | "ucred"
2987         | "in6_pktinfo"
2988         | "sockaddr_nl"
2989         | "termios"
2990         | "nlmsgerr"
2991             if (mips64 || sparc64) && gnu =>
2992         {
2993             true
2994         }
2995 
2996         // FIXME: the call ABI of max_align_t is incorrect on these platforms:
2997         "max_align_t" if i686 || mips64 || ppc64 => true,
2998 
2999         _ => false,
3000     });
3001 
3002     cfg.generate("../src/lib.rs", "main.rs");
3003 
3004     test_linux_like_apis(target);
3005 }
3006 
3007 // This function tests APIs that are incompatible to test when other APIs
3008 // are included (e.g. because including both sets of headers clashes)
3009 fn test_linux_like_apis(target: &str) {
3010     let musl = target.contains("musl");
3011     let linux = target.contains("linux");
3012     let emscripten = target.contains("emscripten");
3013     let android = target.contains("android");
3014     assert!(linux || android || emscripten);
3015 
3016     if linux || android || emscripten {
3017         // test strerror_r from the `string.h` header
3018         let mut cfg = ctest_cfg();
3019         cfg.skip_type(|_| true).skip_static(|_| true);
3020 
3021         headers! { cfg: "string.h" }
3022         cfg.skip_fn(|f| match f {
3023             "strerror_r" => false,
3024             _ => true,
3025         })
3026         .skip_const(|_| true)
3027         .skip_struct(|_| true);
3028         cfg.generate("../src/lib.rs", "linux_strerror_r.rs");
3029     }
3030 
3031     if linux || android || emscripten {
3032         // test fcntl - see:
3033         // http://man7.org/linux/man-pages/man2/fcntl.2.html
3034         let mut cfg = ctest_cfg();
3035 
3036         if musl {
3037             cfg.header("fcntl.h");
3038         } else {
3039             cfg.header("linux/fcntl.h");
3040         }
3041 
3042         cfg.skip_type(|_| true)
3043             .skip_static(|_| true)
3044             .skip_struct(|_| true)
3045             .skip_fn(|_| true)
3046             .skip_const(move |name| match name {
3047                 // test fcntl constants:
3048                 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK"
3049                 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false,
3050                 _ => true,
3051             })
3052             .type_name(move |ty, is_struct, is_union| match ty {
3053                 t if is_struct => format!("struct {}", t),
3054                 t if is_union => format!("union {}", t),
3055                 t => t.to_string(),
3056             });
3057 
3058         cfg.generate("../src/lib.rs", "linux_fcntl.rs");
3059     }
3060 
3061     if linux || android {
3062         // test termios
3063         let mut cfg = ctest_cfg();
3064         cfg.header("asm/termbits.h");
3065         cfg.skip_type(|_| true)
3066             .skip_static(|_| true)
3067             .skip_fn(|_| true)
3068             .skip_const(|c| c != "BOTHER")
3069             .skip_struct(|s| s != "termios2")
3070             .type_name(move |ty, is_struct, is_union| match ty {
3071                 t if is_struct => format!("struct {}", t),
3072                 t if is_union => format!("union {}", t),
3073                 t => t.to_string(),
3074             });
3075         cfg.generate("../src/lib.rs", "linux_termios.rs");
3076     }
3077 
3078     if linux || android {
3079         // test IPV6_ constants:
3080         let mut cfg = ctest_cfg();
3081         headers! {
3082             cfg:
3083             "linux/in6.h"
3084         }
3085         cfg.skip_type(|_| true)
3086             .skip_static(|_| true)
3087             .skip_fn(|_| true)
3088             .skip_const(|_| true)
3089             .skip_struct(|_| true)
3090             .skip_const(move |name| match name {
3091                 "IPV6_FLOWINFO"
3092                 | "IPV6_FLOWLABEL_MGR"
3093                 | "IPV6_FLOWINFO_SEND"
3094                 | "IPV6_FLOWINFO_FLOWLABEL"
3095                 | "IPV6_FLOWINFO_PRIORITY" => false,
3096                 _ => true,
3097             })
3098             .type_name(move |ty, is_struct, is_union| match ty {
3099                 t if is_struct => format!("struct {}", t),
3100                 t if is_union => format!("union {}", t),
3101                 t => t.to_string(),
3102             });
3103         cfg.generate("../src/lib.rs", "linux_ipv6.rs");
3104     }
3105 
3106     if linux || android {
3107         // Test Elf64_Phdr and Elf32_Phdr
3108         // These types have a field called `p_type`, but including
3109         // "resolve.h" defines a `p_type` macro that expands to `__p_type`
3110         // making the tests for these fails when both are included.
3111         let mut cfg = ctest_cfg();
3112         cfg.header("elf.h");
3113         cfg.skip_fn(|_| true)
3114             .skip_static(|_| true)
3115             .skip_const(|_| true)
3116             .type_name(move |ty, _is_struct, _is_union| ty.to_string())
3117             .skip_struct(move |ty| match ty {
3118                 "Elf64_Phdr" | "Elf32_Phdr" => false,
3119                 _ => true,
3120             })
3121             .skip_type(move |ty| match ty {
3122                 "Elf64_Phdr" | "Elf32_Phdr" => false,
3123                 _ => true,
3124             });
3125         cfg.generate("../src/lib.rs", "linux_elf.rs");
3126     }
3127 
3128     if linux || android {
3129         // Test `ARPHRD_CAN`.
3130         let mut cfg = ctest_cfg();
3131         cfg.header("linux/if_arp.h");
3132         cfg.skip_fn(|_| true)
3133             .skip_static(|_| true)
3134             .skip_const(move |name| match name {
3135                 "ARPHRD_CAN" => false,
3136                 _ => true,
3137             })
3138             .skip_struct(|_| true)
3139             .skip_type(|_| true);
3140         cfg.generate("../src/lib.rs", "linux_if_arp.rs");
3141     }
3142 }
3143 
3144 fn which_freebsd() -> Option<i32> {
3145     let output = std::process::Command::new("freebsd-version")
3146         .output()
3147         .ok()?;
3148     if !output.status.success() {
3149         return None;
3150     }
3151 
3152     let stdout = String::from_utf8(output.stdout).ok()?;
3153 
3154     match &stdout {
3155         s if s.starts_with("10") => Some(10),
3156         s if s.starts_with("11") => Some(11),
3157         s if s.starts_with("12") => Some(12),
3158         s if s.starts_with("13") => Some(13),
3159         _ => None,
3160     }
3161 }
3162 
3163 fn test_haiku(target: &str) {
3164     assert!(target.contains("haiku"));
3165 
3166     let mut cfg = ctest_cfg();
3167     cfg.flag("-Wno-deprecated-declarations");
3168     cfg.define("__USE_GNU", Some("1"));
3169 
3170     // POSIX API
3171     headers! { cfg:
3172                "alloca.h",
3173                "arpa/inet.h",
3174                "arpa/nameser.h",
3175                "arpa/nameser_compat.h",
3176                "assert.h",
3177                "bsd_mem.h",
3178                "complex.h",
3179                "ctype.h",
3180                "dirent.h",
3181                "div_t.h",
3182                "dlfcn.h",
3183                "endian.h",
3184                "errno.h",
3185                "fcntl.h",
3186                "fenv.h",
3187                "fnmatch.h",
3188                "fts.h",
3189                "ftw.h",
3190                "getopt.h",
3191                "glob.h",
3192                "grp.h",
3193                "inttypes.h",
3194                "iovec.h",
3195                "langinfo.h",
3196                "libgen.h",
3197                "libio.h",
3198                "limits.h",
3199                "locale.h",
3200                "malloc.h",
3201                "malloc_debug.h",
3202                "math.h",
3203                "memory.h",
3204                "monetary.h",
3205                "net/if.h",
3206                "net/if_dl.h",
3207                "net/if_media.h",
3208                "net/if_tun.h",
3209                "net/if_types.h",
3210                "net/route.h",
3211                "netdb.h",
3212                "netinet/icmp6.h",
3213                "netinet/in.h",
3214                "netinet/ip.h",
3215                "netinet/ip6.h",
3216                "netinet/ip_icmp.h",
3217                "netinet/ip_var.h",
3218                "netinet/tcp.h",
3219                "netinet/udp.h",
3220                "netinet6/in6.h",
3221                "nl_types.h",
3222                "null.h",
3223                "poll.h",
3224                "pthread.h",
3225                "pwd.h",
3226                "regex.h",
3227                "resolv.h",
3228                "sched.h",
3229                "search.h",
3230                "semaphore.h",
3231                "setjmp.h",
3232                "shadow.h",
3233                "signal.h",
3234                "size_t.h",
3235                "spawn.h",
3236                "stdint.h",
3237                "stdio.h",
3238                "stdlib.h",
3239                "string.h",
3240                "strings.h",
3241                "sys/cdefs.h",
3242                "sys/file.h",
3243                "sys/ioctl.h",
3244                "sys/ipc.h",
3245                "sys/mman.h",
3246                "sys/msg.h",
3247                "sys/param.h",
3248                "sys/poll.h",
3249                "sys/resource.h",
3250                "sys/select.h",
3251                "sys/sem.h",
3252                "sys/socket.h",
3253                "sys/sockio.h",
3254                "sys/stat.h",
3255                "sys/statvfs.h",
3256                "sys/time.h",
3257                "sys/timeb.h",
3258                "sys/times.h",
3259                "sys/types.h",
3260                "sys/uio.h",
3261                "sys/un.h",
3262                "sys/utsname.h",
3263                "sys/wait.h",
3264                "syslog.h",
3265                "tar.h",
3266                "termios.h",
3267                "time.h",
3268                "uchar.h",
3269                "unistd.h",
3270                "utime.h",
3271                "wchar.h",
3272                "wchar_t.h",
3273                "wctype.h"
3274     }
3275 
3276     // BSD Extensions
3277     headers! { cfg:
3278                "pty.h",
3279     }
3280 
3281     // Native API
3282     headers! { cfg:
3283                "kernel/OS.h",
3284                "kernel/fs_attr.h",
3285                "kernel/fs_index.h",
3286                "kernel/fs_info.h",
3287                "kernel/fs_query.h",
3288                "kernel/fs_volume.h",
3289                "kernel/image.h",
3290                "kernel/scheduler.h",
3291                "storage/FindDirectory.h",
3292                "storage/StorageDefs.h",
3293                "support/Errors.h",
3294                "support/SupportDefs.h",
3295                "support/TypeConstants.h"
3296     }
3297 
3298     cfg.skip_struct(move |ty| {
3299         match ty {
3300             // FIXME: actually a union
3301             "sigval" => true,
3302             // FIXME: locale_t does not exist on Haiku
3303             "locale_t" => true,
3304             // FIXME: rusage has a different layout on Haiku
3305             "rusage" => true,
3306             // FIXME?: complains that rust aligns on 4 byte boundary, but
3307             //         Haiku does not align it at all.
3308             "in6_addr" => true,
3309             // The d_name attribute is an array of 1 on Haiku, with the
3310             // intention that the developer allocates a larger or smaller
3311             // piece of memory depending on the expected/actual size of the name.
3312             // Other platforms have sensible defaults. In Rust, the d_name field
3313             // is sized as the _POSIX_MAX_PATH, so that path names will fit in
3314             // newly allocated dirent objects. This breaks the automated tests.
3315             "dirent" => true,
3316 
3317             _ => false,
3318         }
3319     });
3320 
3321     cfg.skip_type(move |ty| {
3322         match ty {
3323             // FIXME: locale_t does not exist on Haiku
3324             "locale_t" => true,
3325             // These cause errors, to be reviewed in the future
3326             "sighandler_t" => true,
3327             "pthread_t" => true,
3328             "pthread_condattr_t" => true,
3329             "pthread_mutexattr_t" => true,
3330             "pthread_rwlockattr_t" => true,
3331             _ => false,
3332         }
3333     });
3334 
3335     cfg.skip_fn(move |name| {
3336         // skip those that are manually verified
3337         match name {
3338             // FIXME: https://github.com/rust-lang/libc/issues/1272
3339             "execv" | "execve" | "execvp" | "execvpe" => true,
3340             // FIXME: does not exist on haiku
3341             "open_wmemstream" => true,
3342             "mlockall" | "munlockall" => true,
3343             "tcgetsid" => true,
3344             "cfsetspeed" => true,
3345             // ignore for now, will be part of Haiku R1 beta 3
3346             "mlock" | "munlock" => true,
3347             // returns const char * on Haiku
3348             "strsignal" => true,
3349 
3350             _ => false,
3351         }
3352     });
3353 
3354     cfg.skip_const(move |name| {
3355         match name {
3356             // FIXME: these constants do not exist on Haiku
3357             "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK"
3358             | "DT_SOCK" => true,
3359             "USRQUOTA" | "GRPQUOTA" => true,
3360             "SIGIOT" => true,
3361             "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL"
3362             | "ATF_USETRAILERS" => true,
3363             // Haiku does not have MAP_FILE, but rustc requires it
3364             "MAP_FILE" => true,
3365             // The following does not exist on Haiku but is required by
3366             // several crates
3367             "FIOCLEX" => true,
3368             // just skip this one, it is not defined on Haiku beta 2 but
3369             // since it is meant as a mask and not a parameter it can exist
3370             // here
3371             "LOG_PRIMASK" => true,
3372             // not defined on Haiku, but [get|set]priority is, so they are
3373             // useful
3374             "PRIO_MIN" | "PRIO_MAX" => true,
3375             //
3376             _ => false,
3377         }
3378     });
3379 
3380     cfg.skip_field(move |struct_, field| {
3381         match (struct_, field) {
3382             // FIXME: the stat struct actually has timespec members, whereas
3383             //        the current representation has these unpacked.
3384             ("stat", "st_atime") => true,
3385             ("stat", "st_atime_nsec") => true,
3386             ("stat", "st_mtime") => true,
3387             ("stat", "st_mtime_nsec") => true,
3388             ("stat", "st_ctime") => true,
3389             ("stat", "st_ctime_nsec") => true,
3390             ("stat", "st_crtime") => true,
3391             ("stat", "st_crtime_nsec") => true,
3392 
3393             // these are actually unions, but we cannot represent it well
3394             ("siginfo_t", "sigval") => true,
3395             ("sem_t", "named_sem_id") => true,
3396             ("sigaction", "sa_sigaction") => true,
3397             ("sigevent", "sigev_value") => true,
3398 
3399             // skip these enum-type fields
3400             ("thread_info", "state") => true,
3401             ("image_info", "image_type") => true,
3402             _ => false,
3403         }
3404     });
3405 
3406     cfg.skip_roundtrip(move |s| match s {
3407         // FIXME: for some reason the roundtrip check fails for cpu_info
3408         "cpu_info" => true,
3409         _ => false,
3410     });
3411 
3412     cfg.type_name(move |ty, is_struct, is_union| {
3413         match ty {
3414             // Just pass all these through, no need for a "struct" prefix
3415             "area_info" | "port_info" | "port_message_info" | "team_info" | "sem_info"
3416             | "team_usage_info" | "thread_info" | "cpu_info" | "system_info"
3417             | "object_wait_info" | "image_info" | "attr_info" | "index_info" | "fs_info"
3418             | "FILE" | "DIR" | "Dl_info" => ty.to_string(),
3419 
3420             // is actually a union
3421             "sigval" => format!("union sigval"),
3422             t if is_union => format!("union {}", t),
3423             t if t.ends_with("_t") => t.to_string(),
3424             t if is_struct => format!("struct {}", t),
3425             t => t.to_string(),
3426         }
3427     });
3428 
3429     cfg.field_name(move |struct_, field| {
3430         match field {
3431             // Field is named `type` in C but that is a Rust keyword,
3432             // so these fields are translated to `type_` in the bindings.
3433             "type_" if struct_ == "object_wait_info" => "type".to_string(),
3434             "type_" if struct_ == "sem_t" => "type".to_string(),
3435             "type_" if struct_ == "attr_info" => "type".to_string(),
3436             "type_" if struct_ == "index_info" => "type".to_string(),
3437             "image_type" if struct_ == "image_info" => "type".to_string(),
3438             s => s.to_string(),
3439         }
3440     });
3441     cfg.generate("../src/lib.rs", "main.rs");
3442 }
3443