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