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