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