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