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