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