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