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