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