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