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