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