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