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