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