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