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