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