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