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