1 #![deny(warnings)] 2 3 extern crate cc; 4 extern crate ctest; 5 6 use std::env; 7 8 fn do_cc() { 9 let target = env::var("TARGET").unwrap(); 10 if cfg!(unix) && !target.contains("wasi") { 11 cc::Build::new().file("src/cmsg.c").compile("cmsg"); 12 } 13 if target.contains("android") || target.contains("linux") { 14 cc::Build::new().file("src/errqueue.c").compile("errqueue"); 15 } 16 } 17 18 fn do_ctest() { 19 match &env::var("TARGET").unwrap() { 20 t if t.contains("android") => return test_android(t), 21 t if t.contains("apple") => return test_apple(t), 22 t if t.contains("cloudabi") => return test_cloudabi(t), 23 t if t.contains("dragonfly") => return test_dragonflybsd(t), 24 t if t.contains("emscripten") => return test_emscripten(t), 25 t if t.contains("freebsd") => return test_freebsd(t), 26 t if t.contains("linux") => return test_linux(t), 27 t if t.contains("netbsd") => return test_netbsd(t), 28 t if t.contains("openbsd") => return test_openbsd(t), 29 t if t.contains("redox") => return test_redox(t), 30 t if t.contains("solaris") => return test_solaris(t), 31 t if t.contains("wasi") => return test_wasi(t), 32 t if t.contains("windows") => return test_windows(t), 33 t if t.contains("vxworks") => return test_vxworks(t), 34 t => panic!("unknown target {}", t), 35 } 36 } 37 38 fn ctest_cfg() -> ctest::TestGenerator { 39 let mut cfg = ctest::TestGenerator::new(); 40 let libc_cfgs = [ 41 "libc_priv_mod_use", 42 "libc_union", 43 "libc_const_size_of", 44 "libc_align", 45 "libc_core_cvoid", 46 "libc_packedN", 47 "libc_thread_local", 48 ]; 49 for f in &libc_cfgs { 50 cfg.cfg(f, None); 51 } 52 cfg 53 } 54 55 fn main() { 56 do_cc(); 57 do_ctest(); 58 } 59 60 macro_rules! headers { 61 ($cfg:ident: [$m:expr]: $header:literal) => { 62 if $m { 63 $cfg.header($header); 64 } 65 }; 66 ($cfg:ident: $header:literal) => { 67 $cfg.header($header); 68 }; 69 ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => { 70 $(headers!($cfg: $([$c]:)* $header);)* 71 }; 72 ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => { 73 headers!($($cfg: $([$c]:)* $header,)*); 74 }; 75 ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => { 76 headers!($($cfg: $([$c]:)* $header,)*); 77 }; 78 } 79 80 fn test_apple(target: &str) { 81 assert!(target.contains("apple")); 82 let x86_64 = target.contains("x86_64"); 83 let i686 = target.contains("i686"); 84 85 let mut cfg = ctest_cfg(); 86 cfg.flag("-Wno-deprecated-declarations"); 87 cfg.define("__APPLE_USE_RFC_3542", None); 88 89 headers! { cfg: 90 "aio.h", 91 "ctype.h", 92 "dirent.h", 93 "dlfcn.h", 94 "errno.h", 95 "execinfo.h", 96 "fcntl.h", 97 "glob.h", 98 "grp.h", 99 "ifaddrs.h", 100 "langinfo.h", 101 "limits.h", 102 "locale.h", 103 "mach-o/dyld.h", 104 "mach/mach_time.h", 105 "malloc/malloc.h", 106 "net/bpf.h", 107 "net/if.h", 108 "net/if_arp.h", 109 "net/if_dl.h", 110 "net/if_utun.h", 111 "net/route.h", 112 "net/route.h", 113 "netdb.h", 114 "netinet/if_ether.h", 115 "netinet/in.h", 116 "netinet/in.h", 117 "netinet/ip.h", 118 "netinet/tcp.h", 119 "netinet/udp.h", 120 "poll.h", 121 "pthread.h", 122 "pwd.h", 123 "resolv.h", 124 "sched.h", 125 "semaphore.h", 126 "signal.h", 127 "spawn.h", 128 "stddef.h", 129 "stdint.h", 130 "stdio.h", 131 "stdlib.h", 132 "string.h", 133 "sys/event.h", 134 "sys/file.h", 135 "sys/ioctl.h", 136 "sys/ipc.h", 137 "sys/kern_control.h", 138 "sys/mman.h", 139 "sys/mount.h", 140 "sys/proc_info.h", 141 "sys/ptrace.h", 142 "sys/quota.h", 143 "sys/resource.h", 144 "sys/sem.h", 145 "sys/shm.h", 146 "sys/socket.h", 147 "sys/stat.h", 148 "sys/statvfs.h", 149 "sys/sys_domain.h", 150 "sys/sysctl.h", 151 "sys/time.h", 152 "sys/times.h", 153 "sys/types.h", 154 "sys/uio.h", 155 "sys/un.h", 156 "sys/utsname.h", 157 "sys/wait.h", 158 "sys/xattr.h", 159 "syslog.h", 160 "termios.h", 161 "time.h", 162 "unistd.h", 163 "util.h", 164 "utime.h", 165 "utmpx.h", 166 "wchar.h", 167 "xlocale.h", 168 [x86_64]: "crt_externs.h", 169 } 170 171 cfg.skip_struct(move |ty| { 172 match ty { 173 // FIXME: actually a union 174 "sigval" => true, 175 176 _ => false, 177 } 178 }); 179 180 cfg.skip_const(move |name| { 181 match name { 182 // These OSX constants are removed in Sierra. 183 // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html 184 "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true, 185 _ => false, 186 } 187 }); 188 189 cfg.skip_fn(move |name| { 190 // skip those that are manually verified 191 match name { 192 // FIXME: https://github.com/rust-lang/libc/issues/1272 193 "execv" | "execve" | "execvp" => true, 194 195 // close calls the close_nocancel system call 196 "close" => true, 197 198 _ => false, 199 } 200 }); 201 202 cfg.skip_field_type(move |struct_, field| { 203 match (struct_, field) { 204 // FIXME: actually a union 205 ("sigevent", "sigev_value") => true, 206 _ => false, 207 } 208 }); 209 210 cfg.volatile_item(|i| { 211 use ctest::VolatileItemKind::*; 212 match i { 213 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => { 214 true 215 } 216 _ => false, 217 } 218 }); 219 220 cfg.type_name(move |ty, is_struct, is_union| { 221 match ty { 222 // Just pass all these through, no need for a "struct" prefix 223 "FILE" | "DIR" | "Dl_info" => ty.to_string(), 224 225 // OSX calls this something else 226 "sighandler_t" => "sig_t".to_string(), 227 228 t if is_union => format!("union {}", t), 229 t if t.ends_with("_t") => t.to_string(), 230 t if is_struct => format!("struct {}", t), 231 t => t.to_string(), 232 } 233 }); 234 235 cfg.field_name(move |struct_, field| { 236 match field { 237 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 238 s.replace("e_nsec", "espec.tv_nsec") 239 } 240 // FIXME: sigaction actually contains a union with two variants: 241 // a sa_sigaction with type: (*)(int, struct __siginfo *, void *) 242 // a sa_handler with type sig_t 243 "sa_sigaction" if struct_ == "sigaction" => { 244 "sa_handler".to_string() 245 } 246 s => s.to_string(), 247 } 248 }); 249 250 cfg.skip_roundtrip(move |s| match s { 251 // FIXME: this type has the wrong ABI 252 "max_align_t" if i686 => true, 253 _ => false, 254 }); 255 cfg.generate("../src/lib.rs", "main.rs"); 256 } 257 258 fn test_openbsd(target: &str) { 259 assert!(target.contains("openbsd")); 260 261 let mut cfg = ctest_cfg(); 262 cfg.flag("-Wno-deprecated-declarations"); 263 264 headers! { cfg: 265 "errno.h", 266 "fcntl.h", 267 "limits.h", 268 "locale.h", 269 "stddef.h", 270 "stdint.h", 271 "stdio.h", 272 "stdlib.h", 273 "sys/stat.h", 274 "sys/types.h", 275 "time.h", 276 "wchar.h", 277 "ctype.h", 278 "dirent.h", 279 "sys/socket.h", 280 "net/if.h", 281 "net/route.h", 282 "net/if_arp.h", 283 "netdb.h", 284 "netinet/in.h", 285 "netinet/ip.h", 286 "netinet/tcp.h", 287 "netinet/udp.h", 288 "net/bpf.h", 289 "resolv.h", 290 "pthread.h", 291 "dlfcn.h", 292 "signal.h", 293 "string.h", 294 "sys/file.h", 295 "sys/ioctl.h", 296 "sys/mman.h", 297 "sys/resource.h", 298 "sys/socket.h", 299 "sys/time.h", 300 "sys/un.h", 301 "sys/wait.h", 302 "unistd.h", 303 "utime.h", 304 "pwd.h", 305 "grp.h", 306 "sys/utsname.h", 307 "sys/ptrace.h", 308 "sys/mount.h", 309 "sys/uio.h", 310 "sched.h", 311 "termios.h", 312 "poll.h", 313 "syslog.h", 314 "semaphore.h", 315 "sys/statvfs.h", 316 "sys/times.h", 317 "glob.h", 318 "ifaddrs.h", 319 "langinfo.h", 320 "sys/sysctl.h", 321 "utmp.h", 322 "sys/event.h", 323 "net/if_dl.h", 324 "util.h", 325 "ufs/ufs/quota.h", 326 "pthread_np.h", 327 "sys/syscall.h", 328 "sys/shm.h", 329 } 330 331 cfg.skip_struct(move |ty| { 332 match ty { 333 // FIXME: actually a union 334 "sigval" => true, 335 336 _ => false, 337 } 338 }); 339 340 cfg.skip_const(move |name| { 341 match name { 342 // Removed in OpenBSD 6.0 343 "KERN_USERMOUNT" | "KERN_ARND" => true, 344 _ => false, 345 } 346 }); 347 348 cfg.skip_fn(move |name| { 349 match name { 350 // FIXME: https://github.com/rust-lang/libc/issues/1272 351 "execv" | "execve" | "execvp" | "execvpe" => true, 352 353 // Removed in OpenBSD 6.5 354 // https://marc.info/?l=openbsd-cvs&m=154723400730318 355 "mincore" => true, 356 357 _ => false, 358 } 359 }); 360 361 cfg.type_name(move |ty, is_struct, is_union| { 362 match ty { 363 // Just pass all these through, no need for a "struct" prefix 364 "FILE" | "DIR" | "Dl_info" => ty.to_string(), 365 366 // OSX calls this something else 367 "sighandler_t" => "sig_t".to_string(), 368 369 t if is_union => format!("union {}", t), 370 t if t.ends_with("_t") => t.to_string(), 371 t if is_struct => format!("struct {}", t), 372 t => t.to_string(), 373 } 374 }); 375 376 cfg.field_name(move |struct_, field| match field { 377 "st_birthtime" if struct_.starts_with("stat") => { 378 "__st_birthtime".to_string() 379 } 380 "st_birthtime_nsec" if struct_.starts_with("stat") => { 381 "__st_birthtimensec".to_string() 382 } 383 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 384 s.replace("e_nsec", ".tv_nsec") 385 } 386 "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(), 387 s => s.to_string(), 388 }); 389 390 cfg.skip_field_type(move |struct_, field| { 391 // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1 392 struct_ == "siginfo_t" && field == "si_addr" 393 }); 394 395 cfg.generate("../src/lib.rs", "main.rs"); 396 } 397 398 fn test_windows(target: &str) { 399 assert!(target.contains("windows")); 400 let gnu = target.contains("gnu"); 401 402 let mut cfg = ctest_cfg(); 403 cfg.define("_WIN32_WINNT", Some("0x8000")); 404 405 headers! { cfg: 406 "direct.h", 407 "errno.h", 408 "fcntl.h", 409 "io.h", 410 "limits.h", 411 "locale.h", 412 "process.h", 413 "signal.h", 414 "stddef.h", 415 "stdint.h", 416 "stdio.h", 417 "stdlib.h", 418 "sys/stat.h", 419 "sys/types.h", 420 "sys/utime.h", 421 "time.h", 422 "wchar.h", 423 [gnu]: "ws2tcpip.h", 424 [!gnu]: "Winsock2.h", 425 } 426 427 cfg.type_name(move |ty, is_struct, is_union| { 428 match ty { 429 // Just pass all these through, no need for a "struct" prefix 430 "FILE" | "DIR" | "Dl_info" => ty.to_string(), 431 432 // FIXME: these don't exist: 433 "time64_t" => "__time64_t".to_string(), 434 "ssize_t" => "SSIZE_T".to_string(), 435 436 "sighandler_t" if !gnu => "_crt_signal_t".to_string(), 437 "sighandler_t" if gnu => "__p_sig_fn_t".to_string(), 438 439 t if is_union => format!("union {}", t), 440 t if t.ends_with("_t") => t.to_string(), 441 442 // Windows uppercase structs don't have `struct` in front: 443 t if is_struct => { 444 if ty.clone().chars().next().unwrap().is_uppercase() { 445 t.to_string() 446 } else if t == "stat" { 447 "struct __stat64".to_string() 448 } else if t == "utimbuf" { 449 "struct __utimbuf64".to_string() 450 } else { 451 // put `struct` in front of all structs: 452 format!("struct {}", t) 453 } 454 } 455 t => t.to_string(), 456 } 457 }); 458 459 cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string()); 460 461 cfg.skip_type(move |name| match name { 462 "SSIZE_T" if !gnu => true, 463 "ssize_t" if !gnu => true, 464 _ => false, 465 }); 466 467 cfg.skip_const(move |name| { 468 match name { 469 // FIXME: API error: 470 // SIG_ERR type is "void (*)(int)", not "int" 471 "SIG_ERR" => true, 472 _ => false, 473 } 474 }); 475 476 // FIXME: All functions point to the wrong addresses? 477 cfg.skip_fn_ptrcheck(|_| true); 478 479 cfg.skip_signededness(move |c| { 480 match c { 481 // windows-isms 482 n if n.starts_with("P") => true, 483 n if n.starts_with("H") => true, 484 n if n.starts_with("LP") => true, 485 "sighandler_t" if gnu => true, 486 _ => false, 487 } 488 }); 489 490 cfg.skip_fn(move |name| { 491 match name { 492 // FIXME: https://github.com/rust-lang/libc/issues/1272 493 "execv" | "execve" | "execvp" | "execvpe" => true, 494 495 _ => false, 496 } 497 }); 498 499 cfg.generate("../src/lib.rs", "main.rs"); 500 } 501 502 fn test_redox(target: &str) { 503 assert!(target.contains("redox")); 504 505 let mut cfg = ctest_cfg(); 506 cfg.flag("-Wno-deprecated-declarations"); 507 508 headers! { 509 cfg: 510 "ctype.h", 511 "dirent.h", 512 "dlfcn.h", 513 "errno.h", 514 "execinfo.h", 515 "fcntl.h", 516 "glob.h", 517 "grp.h", 518 "ifaddrs.h", 519 "langinfo.h", 520 "limits.h", 521 "locale.h", 522 "net/if.h", 523 "net/if_arp.h", 524 "net/route.h", 525 "netdb.h", 526 "netinet/in.h", 527 "netinet/ip.h", 528 "netinet/tcp.h", 529 "netinet/udp.h", 530 "poll.h", 531 "pthread.h", 532 "pwd.h", 533 "resolv.h", 534 "sched.h", 535 "semaphore.h", 536 "string.h", 537 "strings.h", 538 "sys/file.h", 539 "sys/ioctl.h", 540 "sys/mman.h", 541 "sys/mount.h", 542 "sys/ptrace.h", 543 "sys/quota.h", 544 "sys/resource.h", 545 "sys/socket.h", 546 "sys/stat.h", 547 "sys/statvfs.h", 548 "sys/sysctl.h", 549 "sys/time.h", 550 "sys/times.h", 551 "sys/types.h", 552 "sys/uio.h", 553 "sys/un.h", 554 "sys/utsname.h", 555 "sys/wait.h", 556 "syslog.h", 557 "termios.h", 558 "time.h", 559 "unistd.h", 560 "utime.h", 561 "utmpx.h", 562 "wchar.h", 563 } 564 565 cfg.generate("../src/lib.rs", "main.rs"); 566 } 567 568 fn test_cloudabi(target: &str) { 569 assert!(target.contains("cloudabi")); 570 571 let mut cfg = ctest_cfg(); 572 cfg.flag("-Wno-deprecated-declarations"); 573 574 headers! { 575 cfg: 576 "execinfo.h", 577 "glob.h", 578 "ifaddrs.h", 579 "langinfo.h", 580 "sys/ptrace.h", 581 "sys/quota.h", 582 "sys/sysctl.h", 583 "utmpx.h", 584 "ctype.h", 585 "dirent.h", 586 "dlfcn.h", 587 "errno.h", 588 "fcntl.h", 589 "grp.h", 590 "limits.h", 591 "locale.h", 592 "net/if.h", 593 "net/if_arp.h", 594 "net/route.h", 595 "netdb.h", 596 "netinet/in.h", 597 "netinet/ip.h", 598 "netinet/tcp.h", 599 "netinet/udp.h", 600 "poll.h", 601 "pthread.h", 602 "pwd.h", 603 "resolv.h", 604 "sched.h", 605 "semaphore.h", 606 "signal.h", 607 "stddef.h", 608 "stdint.h", 609 "stdio.h", 610 "stdlib.h", 611 "string.h", 612 "strings.h", 613 "sys/file.h", 614 "sys/ioctl.h", 615 "sys/mman.h", 616 "sys/mount.h", 617 "sys/resource.h", 618 "sys/socket.h", 619 "sys/stat.h", 620 "sys/statvfs.h", 621 "sys/time.h", 622 "sys/times.h", 623 "sys/types.h", 624 "sys/uio.h", 625 "sys/un.h", 626 "sys/utsname.h", 627 "sys/wait.h", 628 "syslog.h", 629 "termios.h", 630 "time.h", 631 "unistd.h", 632 "utime.h", 633 "wchar.h", 634 } 635 636 cfg.generate("../src/lib.rs", "main.rs"); 637 } 638 639 fn test_solaris(target: &str) { 640 assert!(target.contains("solaris")); 641 642 let mut cfg = ctest_cfg(); 643 cfg.flag("-Wno-deprecated-declarations"); 644 645 cfg.define("_XOPEN_SOURCE", Some("700")); 646 cfg.define("__EXTENSIONS__", None); 647 cfg.define("_LCONV_C99", None); 648 649 headers! { 650 cfg: 651 "ctype.h", 652 "dirent.h", 653 "dlfcn.h", 654 "errno.h", 655 "execinfo.h", 656 "fcntl.h", 657 "glob.h", 658 "grp.h", 659 "ifaddrs.h", 660 "langinfo.h", 661 "limits.h", 662 "locale.h", 663 "net/if.h", 664 "net/if_arp.h", 665 "net/route.h", 666 "netdb.h", 667 "netinet/in.h", 668 "netinet/ip.h", 669 "netinet/tcp.h", 670 "netinet/udp.h", 671 "poll.h", 672 "port.h", 673 "pthread.h", 674 "pwd.h", 675 "resolv.h", 676 "sched.h", 677 "semaphore.h", 678 "signal.h", 679 "stddef.h", 680 "stdint.h", 681 "stdio.h", 682 "stdlib.h", 683 "string.h", 684 "sys/epoll.h", 685 "sys/file.h", 686 "sys/filio.h", 687 "sys/ioctl.h", 688 "sys/loadavg.h", 689 "sys/mman.h", 690 "sys/mount.h", 691 "sys/resource.h", 692 "sys/socket.h", 693 "sys/stat.h", 694 "sys/statvfs.h", 695 "sys/time.h", 696 "sys/times.h", 697 "sys/types.h", 698 "sys/uio.h", 699 "sys/un.h", 700 "sys/utsname.h", 701 "sys/wait.h", 702 "syslog.h", 703 "termios.h", 704 "time.h", 705 "ucontext.h", 706 "unistd.h", 707 "utime.h", 708 "utmpx.h", 709 "wchar.h", 710 } 711 712 cfg.skip_const(move |name| match name { 713 "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" 714 | "DT_SOCK" | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => { 715 true 716 } 717 718 _ => false, 719 }); 720 721 cfg.skip_fn(move |name| { 722 // skip those that are manually verified 723 match name { 724 // const-ness only added recently 725 "dladdr" => true, 726 727 // Definition of those functions as changed since unified headers 728 // from NDK r14b These changes imply some API breaking changes but 729 // are still ABI compatible. We can wait for the next major release 730 // to be compliant with the new API. 731 // 732 // FIXME: unskip these for next major release 733 "setpriority" | "personality" => true, 734 735 // signal is defined with sighandler_t, so ignore 736 "signal" => true, 737 738 "cfmakeraw" | "cfsetspeed" => true, 739 740 // FIXME: mincore is defined with caddr_t on Solaris. 741 "mincore" => true, 742 743 _ => false, 744 } 745 }); 746 747 cfg.generate("../src/lib.rs", "main.rs"); 748 } 749 750 fn test_netbsd(target: &str) { 751 assert!(target.contains("netbsd")); 752 let rumprun = target.contains("rumprun"); 753 let mut cfg = ctest_cfg(); 754 755 cfg.flag("-Wno-deprecated-declarations"); 756 cfg.define("_NETBSD_SOURCE", Some("1")); 757 758 headers! { 759 cfg: 760 "errno.h", 761 "fcntl.h", 762 "limits.h", 763 "locale.h", 764 "stddef.h", 765 "stdint.h", 766 "stdio.h", 767 "stdlib.h", 768 "sys/stat.h", 769 "sys/types.h", 770 "time.h", 771 "wchar.h", 772 "aio.h", 773 "ctype.h", 774 "dirent.h", 775 "dlfcn.h", 776 "glob.h", 777 "grp.h", 778 "ifaddrs.h", 779 "langinfo.h", 780 "net/if.h", 781 "net/if_arp.h", 782 "net/if_dl.h", 783 "net/route.h", 784 "netdb.h", 785 "netinet/in.h", 786 "netinet/ip.h", 787 "netinet/tcp.h", 788 "netinet/udp.h", 789 "poll.h", 790 "pthread.h", 791 "pwd.h", 792 "resolv.h", 793 "sched.h", 794 "semaphore.h", 795 "signal.h", 796 "string.h", 797 "sys/extattr.h", 798 "sys/file.h", 799 "sys/ioctl.h", 800 "sys/ioctl_compat.h", 801 "sys/mman.h", 802 "sys/mount.h", 803 "sys/ptrace.h", 804 "sys/resource.h", 805 "sys/socket.h", 806 "sys/statvfs.h", 807 "sys/sysctl.h", 808 "sys/time.h", 809 "sys/times.h", 810 "sys/uio.h", 811 "sys/un.h", 812 "sys/utsname.h", 813 "sys/wait.h", 814 "syslog.h", 815 "termios.h", 816 "ufs/ufs/quota.h", 817 "ufs/ufs/quota1.h", 818 "unistd.h", 819 "util.h", 820 "utime.h", 821 "mqueue.h", 822 "netinet/dccp.h", 823 "sys/event.h", 824 "sys/quota.h", 825 "sys/shm.h", 826 } 827 828 cfg.type_name(move |ty, is_struct, is_union| { 829 match ty { 830 // Just pass all these through, no need for a "struct" prefix 831 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" 832 | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym" 833 | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr" 834 | "Elf64_Chdr" => ty.to_string(), 835 836 // OSX calls this something else 837 "sighandler_t" => "sig_t".to_string(), 838 839 t if is_union => format!("union {}", t), 840 841 t if t.ends_with("_t") => t.to_string(), 842 843 // put `struct` in front of all structs:. 844 t if is_struct => format!("struct {}", t), 845 846 t => t.to_string(), 847 } 848 }); 849 850 cfg.field_name(move |struct_, field| { 851 match field { 852 // Our stat *_nsec fields normally don't actually exist but are part 853 // of a timeval struct 854 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 855 s.replace("e_nsec", ".tv_nsec") 856 } 857 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 858 s => s.to_string(), 859 } 860 }); 861 862 cfg.skip_type(move |ty| { 863 match ty { 864 // FIXME: sighandler_t is crazy across platforms 865 "sighandler_t" => true, 866 _ => false, 867 } 868 }); 869 870 cfg.skip_struct(move |ty| { 871 match ty { 872 // This is actually a union, not a struct 873 "sigval" => true, 874 // These are tested as part of the linux_fcntl tests since there are 875 // header conflicts when including them with all the other structs. 876 "termios2" => true, 877 _ => false, 878 } 879 }); 880 881 cfg.skip_signededness(move |c| { 882 match c { 883 "LARGE_INTEGER" | "float" | "double" => true, 884 n if n.starts_with("pthread") => true, 885 // sem_t is a struct or pointer 886 "sem_t" => true, 887 _ => false, 888 } 889 }); 890 891 cfg.skip_const(move |name| { 892 match name { 893 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 894 "SIGUNUSED" => true, // removed in glibc 2.26 895 896 // weird signed extension or something like that? 897 "MS_NOUSER" => true, 898 "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13 899 "BOTHER" => true, 900 901 _ => false, 902 } 903 }); 904 905 cfg.skip_fn(move |name| { 906 match name { 907 // FIXME: https://github.com/rust-lang/libc/issues/1272 908 "execv" | "execve" | "execvp" => true, 909 910 "getrlimit" | "getrlimit64" | // non-int in 1st arg 911 "setrlimit" | "setrlimit64" | // non-int in 1st arg 912 "prlimit" | "prlimit64" | // non-int in 2nd arg 913 914 // These functions presumably exist on netbsd but don't look like 915 // they're implemented on rumprun yet, just let them slide for now. 916 // Some of them look like they have headers but then don't have 917 // corresponding actual definitions either... 918 "shm_open" | 919 "shm_unlink" | 920 "syscall" | 921 "mq_open" | 922 "mq_close" | 923 "mq_getattr" | 924 "mq_notify" | 925 "mq_receive" | 926 "mq_send" | 927 "mq_setattr" | 928 "mq_timedreceive" | 929 "mq_timedsend" | 930 "mq_unlink" | 931 "ptrace" | 932 "sigaltstack" if rumprun => true, 933 934 _ => false, 935 } 936 }); 937 938 cfg.skip_field_type(move |struct_, field| { 939 // This is a weird union, don't check the type. 940 (struct_ == "ifaddrs" && field == "ifa_ifu") || 941 // sighandler_t type is super weird 942 (struct_ == "sigaction" && field == "sa_sigaction") || 943 // sigval is actually a union, but we pretend it's a struct 944 (struct_ == "sigevent" && field == "sigev_value") || 945 // aio_buf is "volatile void*" and Rust doesn't understand volatile 946 (struct_ == "aiocb" && field == "aio_buf") 947 }); 948 949 cfg.generate("../src/lib.rs", "main.rs"); 950 } 951 952 fn test_dragonflybsd(target: &str) { 953 assert!(target.contains("dragonfly")); 954 let mut cfg = ctest_cfg(); 955 cfg.flag("-Wno-deprecated-declarations"); 956 957 headers! { 958 cfg: 959 "aio.h", 960 "ctype.h", 961 "dirent.h", 962 "dlfcn.h", 963 "errno.h", 964 "execinfo.h", 965 "fcntl.h", 966 "glob.h", 967 "grp.h", 968 "ifaddrs.h", 969 "langinfo.h", 970 "limits.h", 971 "locale.h", 972 "mqueue.h", 973 "net/if.h", 974 "net/if_arp.h", 975 "net/if_dl.h", 976 "net/route.h", 977 "netdb.h", 978 "netinet/in.h", 979 "netinet/ip.h", 980 "netinet/tcp.h", 981 "netinet/udp.h", 982 "poll.h", 983 "pthread.h", 984 "pthread_np.h", 985 "pwd.h", 986 "resolv.h", 987 "sched.h", 988 "semaphore.h", 989 "signal.h", 990 "stddef.h", 991 "stdint.h", 992 "stdio.h", 993 "stdlib.h", 994 "string.h", 995 "sys/event.h", 996 "sys/file.h", 997 "sys/ioctl.h", 998 "sys/mman.h", 999 "sys/mount.h", 1000 "sys/ptrace.h", 1001 "sys/resource.h", 1002 "sys/rtprio.h", 1003 "sys/socket.h", 1004 "sys/stat.h", 1005 "sys/statvfs.h", 1006 "sys/sysctl.h", 1007 "sys/time.h", 1008 "sys/times.h", 1009 "sys/types.h", 1010 "sys/uio.h", 1011 "sys/un.h", 1012 "sys/utsname.h", 1013 "sys/wait.h", 1014 "syslog.h", 1015 "termios.h", 1016 "time.h", 1017 "ufs/ufs/quota.h", 1018 "unistd.h", 1019 "util.h", 1020 "utime.h", 1021 "utmpx.h", 1022 "wchar.h", 1023 } 1024 1025 cfg.type_name(move |ty, is_struct, is_union| { 1026 match ty { 1027 // Just pass all these through, no need for a "struct" prefix 1028 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" 1029 | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym" 1030 | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr" 1031 | "Elf64_Chdr" => ty.to_string(), 1032 1033 // FIXME: OSX calls this something else 1034 "sighandler_t" => "sig_t".to_string(), 1035 1036 t if is_union => format!("union {}", t), 1037 1038 t if t.ends_with("_t") => t.to_string(), 1039 1040 // put `struct` in front of all structs:. 1041 t if is_struct => format!("struct {}", t), 1042 1043 t => t.to_string(), 1044 } 1045 }); 1046 1047 cfg.field_name(move |struct_, field| { 1048 match field { 1049 // Our stat *_nsec fields normally don't actually exist but are part 1050 // of a timeval struct 1051 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1052 s.replace("e_nsec", ".tv_nsec") 1053 } 1054 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1055 // Field is named `type` in C but that is a Rust keyword, 1056 // so these fields are translated to `type_` in the bindings. 1057 "type_" if struct_ == "rtprio" => "type".to_string(), 1058 s => s.to_string(), 1059 } 1060 }); 1061 1062 cfg.skip_type(move |ty| { 1063 match ty { 1064 // sighandler_t is crazy across platforms 1065 "sighandler_t" => true, 1066 1067 _ => false, 1068 } 1069 }); 1070 1071 cfg.skip_struct(move |ty| { 1072 match ty { 1073 // This is actually a union, not a struct 1074 "sigval" => true, 1075 1076 // FIXME: These are tested as part of the linux_fcntl tests since 1077 // there are header conflicts when including them with all the other 1078 // structs. 1079 "termios2" => true, 1080 1081 _ => false, 1082 } 1083 }); 1084 1085 cfg.skip_signededness(move |c| { 1086 match c { 1087 "LARGE_INTEGER" | "float" | "double" => true, 1088 // uuid_t is a struct, not an integer. 1089 "uuid_t" => true, 1090 n if n.starts_with("pthread") => true, 1091 // sem_t is a struct or pointer 1092 "sem_t" => true, 1093 // mqd_t is a pointer on DragonFly 1094 "mqd_t" => true, 1095 1096 _ => false, 1097 } 1098 }); 1099 1100 cfg.skip_const(move |name| { 1101 match name { 1102 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 1103 1104 // weird signed extension or something like that? 1105 "MS_NOUSER" => true, 1106 "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13 1107 1108 // These are defined for Solaris 11, but the crate is tested on 1109 // illumos, where they are currently not defined 1110 "EADI" 1111 | "PORT_SOURCE_POSTWAIT" 1112 | "PORT_SOURCE_SIGNAL" 1113 | "PTHREAD_STACK_MIN" => true, 1114 1115 _ => false, 1116 } 1117 }); 1118 1119 cfg.skip_fn(move |name| { 1120 // skip those that are manually verified 1121 match name { 1122 // FIXME: https://github.com/rust-lang/libc/issues/1272 1123 "execv" | "execve" | "execvp" => true, 1124 1125 "getrlimit" | "getrlimit64" | // non-int in 1st arg 1126 "setrlimit" | "setrlimit64" | // non-int in 1st arg 1127 "prlimit" | "prlimit64" // non-int in 2nd arg 1128 => true, 1129 1130 _ => false, 1131 } 1132 }); 1133 1134 cfg.skip_field_type(move |struct_, field| { 1135 // This is a weird union, don't check the type. 1136 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1137 // sighandler_t type is super weird 1138 (struct_ == "sigaction" && field == "sa_sigaction") || 1139 // sigval is actually a union, but we pretend it's a struct 1140 (struct_ == "sigevent" && field == "sigev_value") || 1141 // aio_buf is "volatile void*" and Rust doesn't understand volatile 1142 (struct_ == "aiocb" && field == "aio_buf") 1143 }); 1144 1145 cfg.skip_field(move |struct_, field| { 1146 // this is actually a union on linux, so we can't represent it well and 1147 // just insert some padding. 1148 (struct_ == "siginfo_t" && field == "_pad") || 1149 // sigev_notify_thread_id is actually part of a sigev_un union 1150 (struct_ == "sigevent" && field == "sigev_notify_thread_id") 1151 }); 1152 1153 cfg.generate("../src/lib.rs", "main.rs"); 1154 } 1155 1156 fn test_wasi(target: &str) { 1157 assert!(target.contains("wasi")); 1158 1159 let mut cfg = ctest_cfg(); 1160 cfg.define("_GNU_SOURCE", None); 1161 1162 headers! { cfg: 1163 "ctype.h", 1164 "dirent.h", 1165 "errno.h", 1166 "fcntl.h", 1167 "limits.h", 1168 "locale.h", 1169 "malloc.h", 1170 "poll.h", 1171 "sched.h", 1172 "stdbool.h", 1173 "stddef.h", 1174 "stdint.h", 1175 "stdio.h", 1176 "stdlib.h", 1177 "string.h", 1178 "sys/resource.h", 1179 "sys/select.h", 1180 "sys/socket.h", 1181 "sys/stat.h", 1182 "sys/times.h", 1183 "sys/types.h", 1184 "sys/uio.h", 1185 "sys/utsname.h", 1186 "time.h", 1187 "unistd.h", 1188 "wasi/api.h", 1189 "wasi/libc.h", 1190 "wasi/libc-find-relpath.h", 1191 "wchar.h", 1192 } 1193 1194 cfg.type_name(move |ty, is_struct, is_union| match ty { 1195 "FILE" | "fd_set" | "DIR" => ty.to_string(), 1196 t if is_union => format!("union {}", t), 1197 t if t.starts_with("__wasi") && t.ends_with("_u") => { 1198 format!("union {}", t) 1199 } 1200 t if t.starts_with("__wasi") && is_struct => format!("struct {}", t), 1201 t if t.ends_with("_t") => t.to_string(), 1202 t if is_struct => format!("struct {}", t), 1203 t => t.to_string(), 1204 }); 1205 1206 cfg.field_name(move |_struct, field| { 1207 match field { 1208 // deal with fields as rust keywords 1209 "type_" => "type".to_string(), 1210 s => s.to_string(), 1211 } 1212 }); 1213 1214 // Looks like LLD doesn't merge duplicate imports, so if the Rust 1215 // code imports from a module and the C code also imports from a 1216 // module we end up with two imports of function pointers which 1217 // import the same thing but have different function pointers 1218 cfg.skip_fn_ptrcheck(|f| f.starts_with("__wasi")); 1219 1220 // d_name is declared as a flexible array in WASI libc, so it 1221 // doesn't support sizeof. 1222 cfg.skip_field(|s, field| s == "dirent" && field == "d_name"); 1223 1224 // Currently Rust/clang disagree on function argument ABI, so skip these 1225 // tests. For more info see WebAssembly/tool-conventions#88 1226 cfg.skip_roundtrip(|_| true); 1227 1228 cfg.generate("../src/lib.rs", "main.rs"); 1229 } 1230 1231 fn test_android(target: &str) { 1232 assert!(target.contains("android")); 1233 let target_pointer_width = match target { 1234 t if t.contains("aarch64") || t.contains("x86_64") => 64, 1235 t if t.contains("i686") || t.contains("arm") => 32, 1236 t => panic!("unsupported target: {}", t), 1237 }; 1238 let x86 = target.contains("i686") || target.contains("x86_64"); 1239 1240 let mut cfg = ctest_cfg(); 1241 cfg.define("_GNU_SOURCE", None); 1242 1243 headers! { cfg: 1244 "arpa/inet.h", 1245 "ctype.h", 1246 "dirent.h", 1247 "dlfcn.h", 1248 "errno.h", 1249 "fcntl.h", 1250 "grp.h", 1251 "ifaddrs.h", 1252 "limits.h", 1253 "locale.h", 1254 "malloc.h", 1255 "net/ethernet.h", 1256 "net/if.h", 1257 "net/if_arp.h", 1258 "net/route.h", 1259 "netdb.h", 1260 "netinet/in.h", 1261 "netinet/ip.h", 1262 "netinet/tcp.h", 1263 "netinet/udp.h", 1264 "netpacket/packet.h", 1265 "poll.h", 1266 "pthread.h", 1267 "pty.h", 1268 "pwd.h", 1269 "resolv.h", 1270 "sched.h", 1271 "semaphore.h", 1272 "signal.h", 1273 "stddef.h", 1274 "stdint.h", 1275 "stdio.h", 1276 "stdlib.h", 1277 "string.h", 1278 "sys/epoll.h", 1279 "sys/eventfd.h", 1280 "sys/file.h", 1281 "sys/fsuid.h", 1282 "sys/inotify.h", 1283 "sys/ioctl.h", 1284 "sys/mman.h", 1285 "sys/mount.h", 1286 "sys/personality.h", 1287 "sys/prctl.h", 1288 "sys/ptrace.h", 1289 "sys/random.h", 1290 "sys/reboot.h", 1291 "sys/resource.h", 1292 "sys/sendfile.h", 1293 "sys/signalfd.h", 1294 "sys/socket.h", 1295 "sys/stat.h", 1296 "sys/statvfs.h", 1297 "sys/swap.h", 1298 "sys/syscall.h", 1299 "sys/sysinfo.h", 1300 "sys/time.h", 1301 "sys/times.h", 1302 "sys/types.h", 1303 "sys/uio.h", 1304 "sys/un.h", 1305 "sys/utsname.h", 1306 "sys/vfs.h", 1307 "sys/xattr.h", 1308 "sys/wait.h", 1309 "syslog.h", 1310 "termios.h", 1311 "time.h", 1312 "unistd.h", 1313 "utime.h", 1314 "utmp.h", 1315 "wchar.h", 1316 "xlocale.h", 1317 // time64_t is not defined for 64-bit targets If included it will 1318 // generate the error 'Your time_t is already 64-bit' 1319 [target_pointer_width == 32]: "time64.h", 1320 [x86]: "sys/reg.h", 1321 } 1322 1323 // Include linux headers at the end: 1324 headers! { cfg: 1325 "asm/mman.h", 1326 "linux/dccp.h", 1327 "linux/errqueue.h", 1328 "linux/futex.h", 1329 "linux/fs.h", 1330 "linux/genetlink.h", 1331 "linux/if_alg.h", 1332 "linux/if_ether.h", 1333 "linux/if_tun.h", 1334 "linux/magic.h", 1335 "linux/memfd.h", 1336 "linux/module.h", 1337 "linux/net_tstamp.h", 1338 "linux/netfilter/nfnetlink.h", 1339 "linux/netfilter/nfnetlink_log.h", 1340 "linux/netfilter/nf_tables.h", 1341 "linux/netfilter_ipv4.h", 1342 "linux/netfilter_ipv6.h", 1343 "linux/netlink.h", 1344 "linux/quota.h", 1345 "linux/reboot.h", 1346 "linux/seccomp.h", 1347 "linux/sockios.h", 1348 1349 } 1350 1351 cfg.type_name(move |ty, is_struct, is_union| { 1352 match ty { 1353 // Just pass all these through, no need for a "struct" prefix 1354 "FILE" | "fd_set" | "Dl_info" => ty.to_string(), 1355 1356 t if is_union => format!("union {}", t), 1357 1358 t if t.ends_with("_t") => t.to_string(), 1359 1360 // sigval is a struct in Rust, but a union in C: 1361 "sigval" => format!("union sigval"), 1362 1363 // put `struct` in front of all structs:. 1364 t if is_struct => format!("struct {}", t), 1365 1366 t => t.to_string(), 1367 } 1368 }); 1369 1370 cfg.field_name(move |struct_, field| { 1371 match field { 1372 // Our stat *_nsec fields normally don't actually exist but are part 1373 // of a timeval struct 1374 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1375 s.to_string() 1376 } 1377 // FIXME: appears that `epoll_event.data` is an union 1378 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1379 s => s.to_string(), 1380 } 1381 }); 1382 1383 cfg.skip_type(move |ty| { 1384 match ty { 1385 // FIXME: `sighandler_t` type is incorrect, see: 1386 // https://github.com/rust-lang/libc/issues/1359 1387 "sighandler_t" => true, 1388 _ => false, 1389 } 1390 }); 1391 1392 cfg.skip_struct(move |ty| { 1393 match ty { 1394 // These are tested as part of the linux_fcntl tests since there are 1395 // header conflicts when including them with all the other structs. 1396 "termios2" => true, 1397 1398 _ => false, 1399 } 1400 }); 1401 1402 cfg.skip_const(move |name| { 1403 match name { 1404 // FIXME: deprecated: not available in any header 1405 // See: https://github.com/rust-lang/libc/issues/1356 1406 "ENOATTR" => true, 1407 1408 // FIXME: still necessary? 1409 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 1410 // FIXME: deprecated - removed in glibc 2.26 1411 "SIGUNUSED" => true, 1412 1413 _ => false, 1414 } 1415 }); 1416 1417 cfg.skip_fn(move |name| { 1418 // skip those that are manually verified 1419 match name { 1420 // FIXME: https://github.com/rust-lang/libc/issues/1272 1421 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 1422 1423 // There are two versions of the sterror_r function, see 1424 // 1425 // https://linux.die.net/man/3/strerror_r 1426 // 1427 // An XSI-compliant version provided if: 1428 // 1429 // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE 1430 // 1431 // and a GNU specific version provided if _GNU_SOURCE is defined. 1432 // 1433 // libc provides bindings for the XSI-compliant version, which is 1434 // preferred for portable applications. 1435 // 1436 // We skip the test here since here _GNU_SOURCE is defined, and 1437 // test the XSI version below. 1438 "strerror_r" => true, 1439 1440 _ => false, 1441 } 1442 }); 1443 1444 cfg.skip_field_type(move |struct_, field| { 1445 // This is a weird union, don't check the type. 1446 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1447 // sigval is actually a union, but we pretend it's a struct 1448 (struct_ == "sigevent" && field == "sigev_value") 1449 }); 1450 1451 cfg.skip_field(move |struct_, field| { 1452 // this is actually a union on linux, so we can't represent it well and 1453 // just insert some padding. 1454 (struct_ == "siginfo_t" && field == "_pad") || 1455 // FIXME: `sa_sigaction` has type `sighandler_t` but that type is 1456 // incorrect, see: https://github.com/rust-lang/libc/issues/1359 1457 (struct_ == "sigaction" && field == "sa_sigaction") || 1458 // sigev_notify_thread_id is actually part of a sigev_un union 1459 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 1460 // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet. 1461 (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" || 1462 field == "ssi_call_addr" || 1463 field == "ssi_arch")) 1464 }); 1465 1466 cfg.generate("../src/lib.rs", "main.rs"); 1467 1468 test_linux_like_apis(target); 1469 } 1470 1471 fn test_freebsd(target: &str) { 1472 assert!(target.contains("freebsd")); 1473 let mut cfg = ctest_cfg(); 1474 1475 let freebsd_ver = which_freebsd(); 1476 1477 match freebsd_ver { 1478 Some(10) => cfg.cfg("freebsd10", None), 1479 Some(11) => cfg.cfg("freebsd11", None), 1480 Some(12) => cfg.cfg("freebsd12", None), 1481 Some(13) => cfg.cfg("freebsd13", None), 1482 _ => &mut cfg, 1483 }; 1484 1485 // Required for `getline`: 1486 cfg.define("_WITH_GETLINE", None); 1487 // Required for making freebsd11_stat available in the headers 1488 match freebsd_ver { 1489 Some(10) => &mut cfg, 1490 _ => cfg.define("_WANT_FREEBSD11_STAT", None), 1491 }; 1492 1493 headers! { cfg: 1494 "aio.h", 1495 "arpa/inet.h", 1496 "ctype.h", 1497 "dirent.h", 1498 "dlfcn.h", 1499 "errno.h", 1500 "fcntl.h", 1501 "glob.h", 1502 "grp.h", 1503 "ifaddrs.h", 1504 "langinfo.h", 1505 "libutil.h", 1506 "limits.h", 1507 "locale.h", 1508 "mqueue.h", 1509 "net/bpf.h", 1510 "net/if.h", 1511 "net/if_arp.h", 1512 "net/if_dl.h", 1513 "net/route.h", 1514 "netdb.h", 1515 "netinet/ip.h", 1516 "netinet/in.h", 1517 "netinet/tcp.h", 1518 "netinet/udp.h", 1519 "poll.h", 1520 "pthread.h", 1521 "pthread_np.h", 1522 "pwd.h", 1523 "resolv.h", 1524 "sched.h", 1525 "semaphore.h", 1526 "signal.h", 1527 "spawn.h", 1528 "stddef.h", 1529 "stdint.h", 1530 "stdio.h", 1531 "stdlib.h", 1532 "string.h", 1533 "sys/event.h", 1534 "sys/extattr.h", 1535 "sys/file.h", 1536 "sys/ioctl.h", 1537 "sys/ipc.h", 1538 "sys/jail.h", 1539 "sys/mman.h", 1540 "sys/mount.h", 1541 "sys/msg.h", 1542 "sys/procdesc.h", 1543 "sys/ptrace.h", 1544 "sys/resource.h", 1545 "sys/rtprio.h", 1546 "sys/shm.h", 1547 "sys/socket.h", 1548 "sys/stat.h", 1549 "sys/statvfs.h", 1550 "sys/sysctl.h", 1551 "sys/time.h", 1552 "sys/times.h", 1553 "sys/types.h", 1554 "sys/uio.h", 1555 "sys/un.h", 1556 "sys/utsname.h", 1557 "sys/wait.h", 1558 "syslog.h", 1559 "termios.h", 1560 "time.h", 1561 "ufs/ufs/quota.h", 1562 "unistd.h", 1563 "utime.h", 1564 "utmpx.h", 1565 "wchar.h", 1566 } 1567 1568 cfg.type_name(move |ty, is_struct, is_union| { 1569 match ty { 1570 // Just pass all these through, no need for a "struct" prefix 1571 "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(), 1572 1573 // FIXME: https://github.com/rust-lang/libc/issues/1273 1574 "sighandler_t" => "sig_t".to_string(), 1575 1576 t if is_union => format!("union {}", t), 1577 1578 t if t.ends_with("_t") => t.to_string(), 1579 1580 // sigval is a struct in Rust, but a union in C: 1581 "sigval" => format!("union sigval"), 1582 1583 // put `struct` in front of all structs:. 1584 t if is_struct => format!("struct {}", t), 1585 1586 t => t.to_string(), 1587 } 1588 }); 1589 1590 cfg.field_name(move |struct_, field| { 1591 match field { 1592 // Our stat *_nsec fields normally don't actually exist but are part 1593 // of a timeval struct 1594 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1595 s.replace("e_nsec", ".tv_nsec") 1596 } 1597 // Field is named `type` in C but that is a Rust keyword, 1598 // so these fields are translated to `type_` in the bindings. 1599 "type_" if struct_ == "rtprio" => "type".to_string(), 1600 s => s.to_string(), 1601 } 1602 }); 1603 1604 cfg.skip_const(move |name| { 1605 match name { 1606 // These constants were introduced in FreeBSD 12: 1607 "SF_USER_READAHEAD" 1608 | "EVFILT_EMPTY" 1609 | "SO_REUSEPORT_LB" 1610 | "IP_ORIGDSTADDR" 1611 | "IP_RECVORIGDSTADDR" 1612 | "IPV6_ORIGDSTADDR" 1613 | "IPV6_RECVORIGDSTADDR" 1614 if Some(11) == freebsd_ver => 1615 { 1616 true 1617 } 1618 1619 // These constants were introduced in FreeBSD 11: 1620 "SF_USER_READAHEAD" 1621 | "SF_NOCACHE" 1622 | "RLIMIT_KQUEUES" 1623 | "RLIMIT_UMTXP" 1624 | "EVFILT_PROCDESC" 1625 | "EVFILT_SENDFILE" 1626 | "EVFILT_EMPTY" 1627 | "SO_REUSEPORT_LB" 1628 | "TCP_CCALGOOPT" 1629 | "TCP_PCAP_OUT" 1630 | "TCP_PCAP_IN" 1631 | "IP_BINDMULTI" 1632 | "IP_ORIGDSTADDR" 1633 | "IP_RECVORIGDSTADDR" 1634 | "IPV6_ORIGDSTADDR" 1635 | "IPV6_RECVORIGDSTADDR" 1636 | "PD_CLOEXEC" 1637 | "PD_ALLOWED_AT_FORK" 1638 | "IP_RSS_LISTEN_BUCKET" 1639 if Some(10) == freebsd_ver => 1640 { 1641 true 1642 } 1643 1644 // FIXME: This constant has a different value in FreeBSD 10: 1645 "RLIM_NLIMITS" if Some(10) == freebsd_ver => true, 1646 1647 // FIXME: There are deprecated - remove in a couple of releases. 1648 // These constants were removed in FreeBSD 11 (svn r273250) but will 1649 // still be accepted and ignored at runtime. 1650 "MAP_RENAME" | "MAP_NORESERVE" if Some(10) != freebsd_ver => true, 1651 1652 // FIXME: There are deprecated - remove in a couple of releases. 1653 // These constants were removed in FreeBSD 11 (svn r262489), 1654 // and they've never had any legitimate use outside of the 1655 // base system anyway. 1656 "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true, 1657 1658 _ => false, 1659 } 1660 }); 1661 1662 cfg.skip_struct(move |ty| { 1663 match ty { 1664 // `mmsghdr` is not available in FreeBSD 10 1665 "mmsghdr" if Some(10) == freebsd_ver => true, 1666 1667 // `max_align_t` is not available in FreeBSD 10 1668 "max_align_t" if Some(10) == freebsd_ver => true, 1669 1670 _ => false, 1671 } 1672 }); 1673 1674 cfg.skip_fn(move |name| { 1675 // skip those that are manually verified 1676 match name { 1677 // FIXME: https://github.com/rust-lang/libc/issues/1272 1678 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 1679 1680 // These functions were added in FreeBSD 11: 1681 "fdatasync" | "mq_getfd_np" | "sendmmsg" | "recvmmsg" 1682 if Some(10) == freebsd_ver => 1683 { 1684 true 1685 } 1686 1687 // This function changed its return type from `int` in FreeBSD10 to 1688 // `ssize_t` in FreeBSD11: 1689 "aio_waitcomplete" if Some(10) == freebsd_ver => true, 1690 1691 // The `uname` function in the `utsname.h` FreeBSD header is a C 1692 // inline function (has no symbol) that calls the `__xuname` symbol. 1693 // Therefore the function pointer comparison does not make sense for it. 1694 "uname" => true, 1695 1696 // FIXME: Our API is unsound. The Rust API allows aliasing 1697 // pointers, but the C API requires pointers not to alias. 1698 // We should probably be at least using `&`/`&mut` here, see: 1699 // https://github.com/gnzlbg/ctest/issues/68 1700 "lio_listio" => true, 1701 1702 _ => false, 1703 } 1704 }); 1705 1706 cfg.skip_signededness(move |c| { 1707 match c { 1708 // FIXME: has a different sign in FreeBSD10 1709 "blksize_t" if Some(10) == freebsd_ver => true, 1710 _ => false, 1711 } 1712 }); 1713 1714 cfg.volatile_item(|i| { 1715 use ctest::VolatileItemKind::*; 1716 match i { 1717 // aio_buf is a volatile void** but since we cannot express that in 1718 // Rust types, we have to explicitly tell the checker about it here: 1719 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => { 1720 true 1721 } 1722 _ => false, 1723 } 1724 }); 1725 1726 cfg.skip_field(move |struct_, field| { 1727 match (struct_, field) { 1728 // FIXME: `sa_sigaction` has type `sighandler_t` but that type is 1729 // incorrect, see: https://github.com/rust-lang/libc/issues/1359 1730 ("sigaction", "sa_sigaction") => true, 1731 1732 // FIXME: in FreeBSD10 this field has type `char*` instead of 1733 // `void*`: 1734 ("stack_t", "ss_sp") if Some(10) == freebsd_ver => true, 1735 1736 _ => false, 1737 } 1738 }); 1739 1740 cfg.generate("../src/lib.rs", "main.rs"); 1741 } 1742 1743 fn test_emscripten(target: &str) { 1744 assert!(target.contains("emscripten")); 1745 1746 let mut cfg = ctest_cfg(); 1747 cfg.define("_GNU_SOURCE", None); // FIXME: ?? 1748 1749 headers! { cfg: 1750 "aio.h", 1751 "ctype.h", 1752 "dirent.h", 1753 "dlfcn.h", 1754 "errno.h", 1755 "fcntl.h", 1756 "glob.h", 1757 "grp.h", 1758 "ifaddrs.h", 1759 "langinfo.h", 1760 "limits.h", 1761 "locale.h", 1762 "malloc.h", 1763 "mntent.h", 1764 "mqueue.h", 1765 "net/ethernet.h", 1766 "net/if.h", 1767 "net/if_arp.h", 1768 "net/route.h", 1769 "netdb.h", 1770 "netinet/in.h", 1771 "netinet/ip.h", 1772 "netinet/tcp.h", 1773 "netinet/udp.h", 1774 "netpacket/packet.h", 1775 "poll.h", 1776 "pthread.h", 1777 "pty.h", 1778 "pwd.h", 1779 "resolv.h", 1780 "sched.h", 1781 "sched.h", 1782 "semaphore.h", 1783 "shadow.h", 1784 "signal.h", 1785 "stddef.h", 1786 "stdint.h", 1787 "stdio.h", 1788 "stdlib.h", 1789 "string.h", 1790 "sys/epoll.h", 1791 "sys/eventfd.h", 1792 "sys/file.h", 1793 "sys/ioctl.h", 1794 "sys/ipc.h", 1795 "sys/mman.h", 1796 "sys/mount.h", 1797 "sys/msg.h", 1798 "sys/personality.h", 1799 "sys/prctl.h", 1800 "sys/ptrace.h", 1801 "sys/quota.h", 1802 "sys/reboot.h", 1803 "sys/resource.h", 1804 "sys/sem.h", 1805 "sys/sendfile.h", 1806 "sys/shm.h", 1807 "sys/signalfd.h", 1808 "sys/socket.h", 1809 "sys/stat.h", 1810 "sys/statvfs.h", 1811 "sys/swap.h", 1812 "sys/syscall.h", 1813 "sys/sysctl.h", 1814 "sys/sysinfo.h", 1815 "sys/time.h", 1816 "sys/timerfd.h", 1817 "sys/times.h", 1818 "sys/types.h", 1819 "sys/uio.h", 1820 "sys/un.h", 1821 "sys/user.h", 1822 "sys/utsname.h", 1823 "sys/vfs.h", 1824 "sys/wait.h", 1825 "sys/xattr.h", 1826 "syslog.h", 1827 "termios.h", 1828 "time.h", 1829 "ucontext.h", 1830 "unistd.h", 1831 "utime.h", 1832 "utmp.h", 1833 "utmpx.h", 1834 "wchar.h", 1835 } 1836 1837 cfg.type_name(move |ty, is_struct, is_union| { 1838 match ty { 1839 // Just pass all these through, no need for a "struct" prefix 1840 "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(), 1841 1842 t if is_union => format!("union {}", t), 1843 1844 t if t.ends_with("_t") => t.to_string(), 1845 1846 // put `struct` in front of all structs:. 1847 t if is_struct => format!("struct {}", t), 1848 1849 t => t.to_string(), 1850 } 1851 }); 1852 1853 cfg.field_name(move |struct_, field| { 1854 match field { 1855 // Our stat *_nsec fields normally don't actually exist but are part 1856 // of a timeval struct 1857 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1858 s.replace("e_nsec", ".tv_nsec") 1859 } 1860 // FIXME: appears that `epoll_event.data` is an union 1861 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1862 s => s.to_string(), 1863 } 1864 }); 1865 1866 cfg.skip_type(move |ty| { 1867 match ty { 1868 // sighandler_t is crazy across platforms 1869 // FIXME: is this necessary? 1870 "sighandler_t" => true, 1871 1872 _ => false, 1873 } 1874 }); 1875 1876 cfg.skip_struct(move |ty| { 1877 match ty { 1878 // This is actually a union, not a struct 1879 // FIXME: is this necessary? 1880 "sigval" => true, 1881 1882 _ => false, 1883 } 1884 }); 1885 1886 cfg.skip_fn(move |name| { 1887 match name { 1888 // FIXME: https://github.com/rust-lang/libc/issues/1272 1889 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 1890 1891 _ => false, 1892 } 1893 }); 1894 1895 cfg.skip_const(move |name| { 1896 match name { 1897 // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26 1898 // users should use SIGSYS instead 1899 "SIGUNUSED" => true, 1900 1901 // FIXME: emscripten uses different constants to constructs these 1902 n if n.contains("__SIZEOF_PTHREAD") => true, 1903 1904 _ => false, 1905 } 1906 }); 1907 1908 cfg.skip_field_type(move |struct_, field| { 1909 // This is a weird union, don't check the type. 1910 // FIXME: is this necessary? 1911 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1912 // sighandler_t type is super weird 1913 // FIXME: is this necessary? 1914 (struct_ == "sigaction" && field == "sa_sigaction") || 1915 // sigval is actually a union, but we pretend it's a struct 1916 // FIXME: is this necessary? 1917 (struct_ == "sigevent" && field == "sigev_value") || 1918 // aio_buf is "volatile void*" and Rust doesn't understand volatile 1919 // FIXME: is this necessary? 1920 (struct_ == "aiocb" && field == "aio_buf") 1921 }); 1922 1923 cfg.skip_field(move |struct_, field| { 1924 // this is actually a union on linux, so we can't represent it well and 1925 // just insert some padding. 1926 // FIXME: is this necessary? 1927 (struct_ == "siginfo_t" && field == "_pad") || 1928 // musl names this __dummy1 but it's still there 1929 // FIXME: is this necessary? 1930 (struct_ == "glob_t" && field == "gl_flags") || 1931 // musl seems to define this as an *anonymous* bitfield 1932 // FIXME: is this necessary? 1933 (struct_ == "statvfs" && field == "__f_unused") || 1934 // sigev_notify_thread_id is actually part of a sigev_un union 1935 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 1936 // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet. 1937 (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || 1938 field == "_pad2" || 1939 field == "ssi_syscall" || 1940 field == "ssi_call_addr" || 1941 field == "ssi_arch")) 1942 }); 1943 1944 // FIXME: test linux like 1945 cfg.generate("../src/lib.rs", "main.rs"); 1946 } 1947 1948 fn test_vxworks(target: &str) { 1949 assert!(target.contains("vxworks")); 1950 1951 let mut cfg = ctest::TestGenerator::new(); 1952 headers! { cfg: 1953 "vxWorks.h", 1954 "yvals.h", 1955 "nfs/nfsCommon.h", 1956 "rtpLibCommon.h", 1957 "randomNumGen.h", 1958 "taskLib.h", 1959 "sysLib.h", 1960 "ioLib.h", 1961 "inetLib.h", 1962 "socket.h", 1963 "errnoLib.h", 1964 "ctype.h", 1965 "dirent.h", 1966 "dlfcn.h", 1967 "elf.h", 1968 "fcntl.h", 1969 "grp.h", 1970 "sys/poll.h", 1971 "ifaddrs.h", 1972 "langinfo.h", 1973 "limits.h", 1974 "link.h", 1975 "locale.h", 1976 "sys/stat.h", 1977 "netdb.h", 1978 "pthread.h", 1979 "pwd.h", 1980 "sched.h", 1981 "semaphore.h", 1982 "signal.h", 1983 "stddef.h", 1984 "stdint.h", 1985 "stdio.h", 1986 "stdlib.h", 1987 "string.h", 1988 "sys/file.h", 1989 "sys/ioctl.h", 1990 "sys/socket.h", 1991 "sys/time.h", 1992 "sys/times.h", 1993 "sys/types.h", 1994 "sys/uio.h", 1995 "sys/un.h", 1996 "sys/utsname.h", 1997 "sys/wait.h", 1998 "netinet/tcp.h", 1999 "syslog.h", 2000 "termios.h", 2001 "time.h", 2002 "ucontext.h", 2003 "unistd.h", 2004 "utime.h", 2005 "wchar.h", 2006 "errno.h", 2007 "sys/mman.h", 2008 "pathLib.h", 2009 "mqueue.h", 2010 } 2011 /* Fix me */ 2012 cfg.skip_const(move |name| match name { 2013 // sighandler_t weirdness 2014 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" 2015 // This is not defined in vxWorks 2016 | "RTLD_DEFAULT" => true, 2017 _ => false, 2018 }); 2019 /* Fix me */ 2020 cfg.skip_type(move |ty| match ty { 2021 "stat64" | "sighandler_t" | "off64_t" => true, 2022 _ => false, 2023 }); 2024 2025 cfg.skip_field_type(move |struct_, field| match (struct_, field) { 2026 ("siginfo_t", "si_value") 2027 | ("stat", "st_size") 2028 | ("sigaction", "sa_u") => true, 2029 _ => false, 2030 }); 2031 2032 cfg.skip_roundtrip(move |s| match s { 2033 _ => false, 2034 }); 2035 2036 cfg.type_name(move |ty, is_struct, is_union| match ty { 2037 "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(), 2038 t if is_union => format!("union {}", t), 2039 t if t.ends_with("_t") => t.to_string(), 2040 t if is_struct => format!("struct {}", t), 2041 t => t.to_string(), 2042 }); 2043 2044 /* Fix me */ 2045 cfg.skip_fn(move |name| match name { 2046 /* sigval */ 2047 "sigqueue" | "_sigqueue" 2048 /* sighandler_t*/ 2049 | "signal" 2050 /* not used in static linking by default */ 2051 | "dlerror" => true, 2052 _ => false, 2053 }); 2054 2055 cfg.generate("../src/lib.rs", "main.rs"); 2056 } 2057 2058 fn test_linux(target: &str) { 2059 assert!(target.contains("linux")); 2060 2061 // target_env 2062 let gnu = target.contains("gnu"); 2063 let musl = target.contains("musl"); 2064 let uclibc = target.contains("uclibc"); 2065 2066 match (gnu, musl, uclibc) { 2067 (true, false, false) => (), 2068 (false, true, false) => (), 2069 (false, false, true) => (), 2070 (_, _, _) => panic!( 2071 "linux target lib is gnu: {}, musl: {}, uclibc: {}", 2072 gnu, musl, uclibc 2073 ), 2074 } 2075 2076 let arm = target.contains("arm"); 2077 let i686 = target.contains("i686"); 2078 let mips = target.contains("mips"); 2079 let mips32 = mips && !target.contains("64"); 2080 let mips64 = mips && target.contains("64"); 2081 let ppc64 = target.contains("powerpc64"); 2082 let s390x = target.contains("s390x"); 2083 let sparc64 = target.contains("sparc64"); 2084 let x32 = target.contains("x32"); 2085 let x86_32 = target.contains("i686"); 2086 let x86_64 = target.contains("x86_64"); 2087 let aarch64_musl = target.contains("aarch64") && musl; 2088 let gnuabihf = target.contains("gnueabihf"); 2089 2090 let mut cfg = ctest_cfg(); 2091 cfg.define("_GNU_SOURCE", None); 2092 // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are 2093 // deprecated since glibc >= 2.29. This allows Rust binaries to link against 2094 // glibc versions older than 2.29. 2095 cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None); 2096 2097 headers! { cfg: 2098 "ctype.h", 2099 "dirent.h", 2100 "dlfcn.h", 2101 "elf.h", 2102 "fcntl.h", 2103 "glob.h", 2104 "grp.h", 2105 "ifaddrs.h", 2106 "langinfo.h", 2107 "limits.h", 2108 "link.h", 2109 "locale.h", 2110 "malloc.h", 2111 "mntent.h", 2112 "mqueue.h", 2113 "net/ethernet.h", 2114 "net/if.h", 2115 "net/if_arp.h", 2116 "net/route.h", 2117 "netdb.h", 2118 "netinet/in.h", 2119 "netinet/ip.h", 2120 "netinet/tcp.h", 2121 "netinet/udp.h", 2122 "netpacket/packet.h", 2123 "poll.h", 2124 "pthread.h", 2125 "pty.h", 2126 "pwd.h", 2127 "resolv.h", 2128 "sched.h", 2129 "semaphore.h", 2130 "shadow.h", 2131 "signal.h", 2132 "spawn.h", 2133 "stddef.h", 2134 "stdint.h", 2135 "stdio.h", 2136 "stdlib.h", 2137 "string.h", 2138 "sys/epoll.h", 2139 "sys/eventfd.h", 2140 "sys/file.h", 2141 "sys/fsuid.h", 2142 "sys/inotify.h", 2143 "sys/ioctl.h", 2144 "sys/ipc.h", 2145 "sys/mman.h", 2146 "sys/mount.h", 2147 "sys/msg.h", 2148 "sys/personality.h", 2149 "sys/prctl.h", 2150 "sys/ptrace.h", 2151 "sys/quota.h", 2152 "sys/random.h", 2153 "sys/reboot.h", 2154 "sys/resource.h", 2155 "sys/sem.h", 2156 "sys/sendfile.h", 2157 "sys/shm.h", 2158 "sys/signalfd.h", 2159 "sys/socket.h", 2160 "sys/stat.h", 2161 "sys/statvfs.h", 2162 "sys/swap.h", 2163 "sys/syscall.h", 2164 "sys/time.h", 2165 "sys/timerfd.h", 2166 "sys/times.h", 2167 "sys/types.h", 2168 "sys/uio.h", 2169 "sys/un.h", 2170 "sys/user.h", 2171 "sys/utsname.h", 2172 "sys/vfs.h", 2173 "sys/wait.h", 2174 "syslog.h", 2175 "termios.h", 2176 "time.h", 2177 "ucontext.h", 2178 "unistd.h", 2179 "utime.h", 2180 "utmp.h", 2181 "utmpx.h", 2182 "wchar.h", 2183 "errno.h", 2184 // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit 2185 // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162 2186 // Also unavailable on gnuabihf with glibc 2.30. 2187 // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1 2188 [(x86_64 || x86_32 || arm) && !gnuabihf]: "sys/io.h", 2189 // `sys/reg.h` is only available on x86 and x86_64 2190 [x86_64 || x86_32]: "sys/reg.h", 2191 // sysctl system call is deprecated and not available on musl 2192 // It is also unsupported in x32, deprecated since glibc 2.30: 2193 [!(x32 || musl || gnu)]: "sys/sysctl.h", 2194 // <execinfo.h> is not supported by musl: 2195 // https://www.openwall.com/lists/musl/2015/04/09/3 2196 [!musl]: "execinfo.h", 2197 } 2198 2199 // Include linux headers at the end: 2200 headers! { 2201 cfg: 2202 "asm/mman.h", 2203 "linux/dccp.h", 2204 "linux/errqueue.h", 2205 "linux/falloc.h", 2206 "linux/fs.h", 2207 "linux/futex.h", 2208 "linux/genetlink.h", 2209 "linux/if.h", 2210 "linux/if_addr.h", 2211 "linux/if_alg.h", 2212 "linux/if_ether.h", 2213 "linux/if_tun.h", 2214 "linux/input.h", 2215 "linux/magic.h", 2216 "linux/memfd.h", 2217 "linux/module.h", 2218 "linux/net_tstamp.h", 2219 "linux/netfilter/nfnetlink.h", 2220 "linux/netfilter/nfnetlink_log.h", 2221 "linux/netfilter/nf_tables.h", 2222 "linux/netfilter_ipv4.h", 2223 "linux/netfilter_ipv6.h", 2224 "linux/netlink.h", 2225 "linux/quota.h", 2226 "linux/random.h", 2227 "linux/reboot.h", 2228 "linux/rtnetlink.h", 2229 "linux/seccomp.h", 2230 "linux/sockios.h", 2231 "linux/vm_sockets.h", 2232 "sys/auxv.h", 2233 } 2234 2235 // note: aio.h must be included before sys/mount.h 2236 headers! { 2237 cfg: 2238 "sys/xattr.h", 2239 "sys/sysinfo.h", 2240 "aio.h", 2241 } 2242 2243 cfg.type_name(move |ty, is_struct, is_union| { 2244 match ty { 2245 // Just pass all these through, no need for a "struct" prefix 2246 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" 2247 | "Elf64_Phdr" | "Elf32_Shdr" | "Elf64_Shdr" | "Elf32_Sym" 2248 | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" | "Elf32_Chdr" 2249 | "Elf64_Chdr" => ty.to_string(), 2250 2251 t if is_union => format!("union {}", t), 2252 2253 t if t.ends_with("_t") => t.to_string(), 2254 2255 // In MUSL `flock64` is a typedef to `flock`. 2256 "flock64" if musl => format!("struct {}", ty), 2257 2258 // put `struct` in front of all structs:. 2259 t if is_struct => format!("struct {}", t), 2260 2261 t => t.to_string(), 2262 } 2263 }); 2264 2265 cfg.field_name(move |struct_, field| { 2266 match field { 2267 // Our stat *_nsec fields normally don't actually exist but are part 2268 // of a timeval struct 2269 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 2270 s.replace("e_nsec", ".tv_nsec") 2271 } 2272 // FIXME: epoll_event.data is actuall a union in C, but in Rust 2273 // it is only a u64 because we only expose one field 2274 // http://man7.org/linux/man-pages/man2/epoll_wait.2.html 2275 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 2276 // The following structs have a field called `type` in C, 2277 // but `type` is a Rust keyword, so these fields are translated 2278 // to `type_` in Rust. 2279 "type_" 2280 if struct_ == "input_event" 2281 || struct_ == "input_mask" 2282 || struct_ == "ff_effect" => 2283 { 2284 "type".to_string() 2285 } 2286 2287 s => s.to_string(), 2288 } 2289 }); 2290 2291 cfg.skip_type(move |ty| { 2292 match ty { 2293 // FIXME: `sighandler_t` type is incorrect, see: 2294 // https://github.com/rust-lang/libc/issues/1359 2295 "sighandler_t" => true, 2296 2297 // These cannot be tested when "resolv.h" is included and are tested 2298 // in the `linux_elf.rs` file. 2299 "Elf64_Phdr" | "Elf32_Phdr" => true, 2300 2301 // This type is private on Linux. It is implemented as a C `enum` 2302 // (`c_uint`) and this clashes with the type of the `rlimit` APIs 2303 // which expect a `c_int` even though both are ABI compatible. 2304 "__rlimit_resource_t" => true, 2305 2306 _ => false, 2307 } 2308 }); 2309 2310 cfg.skip_struct(move |ty| { 2311 match ty { 2312 // These cannot be tested when "resolv.h" is included and are tested 2313 // in the `linux_elf.rs` file. 2314 "Elf64_Phdr" | "Elf32_Phdr" => true, 2315 2316 // On Linux, the type of `ut_tv` field of `struct utmpx` 2317 // can be an anonymous struct, so an extra struct, 2318 // which is absent in glibc, has to be defined. 2319 "__timeval" => true, 2320 2321 // FIXME: This is actually a union, not a struct 2322 "sigval" => true, 2323 2324 // This type is tested in the `linux_termios.rs` file since there 2325 // are header conflicts when including them with all the other 2326 // structs. 2327 "termios2" => true, 2328 2329 // FIXME: remove once Ubuntu 20.04 LTS is released, somewhere in 2020. 2330 // ucontext_t added a new field as of glibc 2.28; our struct definition is 2331 // conservative and omits the field, but that means the size doesn't match for newer 2332 // glibcs (see https://github.com/rust-lang/libc/issues/1410) 2333 "ucontext_t" if gnu => true, 2334 2335 // FIXME: Somehow we cannot include headers correctly in glibc 2.30. 2336 // So let's ignore for now and re-visit later. 2337 // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085 2338 "statx" => true, 2339 "statx_timestamp" => true, 2340 2341 _ => false, 2342 } 2343 }); 2344 2345 cfg.skip_const(move |name| { 2346 match name { 2347 // These constants are not available if gnu headers have been included 2348 // and can therefore not be tested here 2349 // 2350 // The IPV6 constants are tested in the `linux_ipv6.rs` tests: 2351 | "IPV6_FLOWINFO" 2352 | "IPV6_FLOWLABEL_MGR" 2353 | "IPV6_FLOWINFO_SEND" 2354 | "IPV6_FLOWINFO_FLOWLABEL" 2355 | "IPV6_FLOWINFO_PRIORITY" 2356 // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests: 2357 | "F_CANCELLK" 2358 | "F_ADD_SEALS" 2359 | "F_GET_SEALS" 2360 | "F_SEAL_SEAL" 2361 | "F_SEAL_SHRINK" 2362 | "F_SEAL_GROW" 2363 | "F_SEAL_WRITE" => true, 2364 2365 // The musl-sanitized kernel headers used in CI 2366 // target the Linux kernel 4.4 and do not contain the 2367 // following constants: 2368 // 2369 // Requires Linux kernel 4.9 2370 | "FALLOC_FL_UNSHARE_RANGE" 2371 // 2372 // Require Linux kernel 5.x: 2373 | "MSG_COPY" 2374 if musl => true, 2375 // Require Linux kernel 5.1: 2376 "F_SEAL_FUTURE_WRITE" => true, 2377 2378 // The musl version 1.1.24 used in CI does not 2379 // contain these glibc constants yet: 2380 | "RLIMIT_RTTIME" // should be in `resource.h` 2381 | "TCP_COOKIE_TRANSACTIONS" // should be in the `netinet/tcp.h` header 2382 if musl => true, 2383 2384 // FIXME: deprecated: not available in any header 2385 // See: https://github.com/rust-lang/libc/issues/1356 2386 "ENOATTR" => true, 2387 2388 // FIXME: SIGUNUSED was removed in glibc 2.26 2389 // Users should use SIGSYS instead. 2390 "SIGUNUSED" => true, 2391 2392 // FIXME: conflicts with glibc headers and is tested in 2393 // `linux_termios.rs` below: 2394 "BOTHER" => true, 2395 2396 // FIXME: on musl the pthread types are defined a little differently 2397 // - these constants are used by the glibc implementation. 2398 n if musl && n.contains("__SIZEOF_PTHREAD") => true, 2399 2400 _ => false, 2401 } 2402 }); 2403 2404 cfg.skip_fn(move |name| { 2405 // skip those that are manually verified 2406 match name { 2407 // FIXME: https://github.com/rust-lang/libc/issues/1272 2408 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 2409 2410 // There are two versions of the sterror_r function, see 2411 // 2412 // https://linux.die.net/man/3/strerror_r 2413 // 2414 // An XSI-compliant version provided if: 2415 // 2416 // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) 2417 // && ! _GNU_SOURCE 2418 // 2419 // and a GNU specific version provided if _GNU_SOURCE is defined. 2420 // 2421 // libc provides bindings for the XSI-compliant version, which is 2422 // preferred for portable applications. 2423 // 2424 // We skip the test here since here _GNU_SOURCE is defined, and 2425 // test the XSI version below. 2426 "strerror_r" => true, 2427 2428 // FIXME: Our API is unsound. The Rust API allows aliasing 2429 // pointers, but the C API requires pointers not to alias. 2430 // We should probably be at least using `&`/`&mut` here, see: 2431 // https://github.com/gnzlbg/ctest/issues/68 2432 "lio_listio" if musl => true, 2433 2434 // FIXME: the glibc version used by the Sparc64 build jobs 2435 // which use Debian 10.0 is too old. 2436 "statx" if sparc64 => true, 2437 2438 // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does. 2439 "sysctl" if gnu => true, 2440 2441 _ => false, 2442 } 2443 }); 2444 2445 cfg.skip_field_type(move |struct_, field| { 2446 // This is a weird union, don't check the type. 2447 (struct_ == "ifaddrs" && field == "ifa_ifu") || 2448 // sighandler_t type is super weird 2449 (struct_ == "sigaction" && field == "sa_sigaction") || 2450 // __timeval type is a patch which doesn't exist in glibc 2451 (struct_ == "utmpx" && field == "ut_tv") || 2452 // sigval is actually a union, but we pretend it's a struct 2453 (struct_ == "sigevent" && field == "sigev_value") || 2454 // this one is an anonymous union 2455 (struct_ == "ff_effect" && field == "u") 2456 }); 2457 2458 cfg.volatile_item(|i| { 2459 use ctest::VolatileItemKind::*; 2460 match i { 2461 // aio_buf is a volatile void** but since we cannot express that in 2462 // Rust types, we have to explicitly tell the checker about it here: 2463 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => { 2464 true 2465 } 2466 _ => false, 2467 } 2468 }); 2469 2470 cfg.skip_field(move |struct_, field| { 2471 // this is actually a union on linux, so we can't represent it well and 2472 // just insert some padding. 2473 (struct_ == "siginfo_t" && field == "_pad") || 2474 // musl names this __dummy1 but it's still there 2475 (musl && struct_ == "glob_t" && field == "gl_flags") || 2476 // musl seems to define this as an *anonymous* bitfield 2477 (musl && struct_ == "statvfs" && field == "__f_unused") || 2478 // sigev_notify_thread_id is actually part of a sigev_un union 2479 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 2480 // signalfd had SIGSYS fields added in Linux 4.18, but no libc release 2481 // has them yet. 2482 (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || 2483 field == "_pad2" || 2484 field == "ssi_syscall" || 2485 field == "ssi_call_addr" || 2486 field == "ssi_arch")) || 2487 // FIXME: After musl 1.1.24, it have only one field `sched_priority`, 2488 // while other fields become reserved. 2489 (struct_ == "sched_param" && [ 2490 "sched_ss_low_priority", 2491 "sched_ss_repl_period", 2492 "sched_ss_init_budget", 2493 "sched_ss_max_repl", 2494 ].contains(&field) && musl) || 2495 // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`. 2496 (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) 2497 }); 2498 2499 cfg.skip_roundtrip(move |s| match s { 2500 // FIXME: 2501 "utsname" if mips32 || mips64 => true, 2502 // FIXME: 2503 "mcontext_t" if s390x => true, 2504 2505 "sockaddr_un" | "sembuf" | "ff_constant_effect" 2506 if mips32 && (gnu || musl) => 2507 { 2508 true 2509 } 2510 "ipv6_mreq" 2511 | "ip_mreq_source" 2512 | "sockaddr_in6" 2513 | "sockaddr_ll" 2514 | "in_pktinfo" 2515 | "arpreq" 2516 | "arpreq_old" 2517 | "sockaddr_un" 2518 | "ff_constant_effect" 2519 | "ff_ramp_effect" 2520 | "ff_condition_effect" 2521 | "Elf32_Ehdr" 2522 | "Elf32_Chdr" 2523 | "ucred" 2524 | "in6_pktinfo" 2525 | "sockaddr_nl" 2526 | "termios" 2527 | "nlmsgerr" 2528 if (mips64 || sparc64) && gnu => 2529 { 2530 true 2531 } 2532 2533 // FIXME: the call ABI of max_align_t is incorrect on these platforms: 2534 "max_align_t" if i686 || mips64 || ppc64 => true, 2535 2536 _ => false, 2537 }); 2538 2539 cfg.generate("../src/lib.rs", "main.rs"); 2540 2541 test_linux_like_apis(target); 2542 } 2543 2544 // This function tests APIs that are incompatible to test when other APIs 2545 // are included (e.g. because including both sets of headers clashes) 2546 fn test_linux_like_apis(target: &str) { 2547 let musl = target.contains("musl"); 2548 let linux = target.contains("linux"); 2549 let emscripten = target.contains("emscripten"); 2550 let android = target.contains("android"); 2551 assert!(linux || android || emscripten); 2552 2553 if linux || android || emscripten { 2554 // test strerror_r from the `string.h` header 2555 let mut cfg = ctest_cfg(); 2556 cfg.skip_type(|_| true).skip_static(|_| true); 2557 2558 headers! { cfg: "string.h" } 2559 cfg.skip_fn(|f| match f { 2560 "strerror_r" => false, 2561 _ => true, 2562 }) 2563 .skip_const(|_| true) 2564 .skip_struct(|_| true); 2565 cfg.generate("../src/lib.rs", "linux_strerror_r.rs"); 2566 } 2567 2568 if linux || android || emscripten { 2569 // test fcntl - see: 2570 // http://man7.org/linux/man-pages/man2/fcntl.2.html 2571 let mut cfg = ctest_cfg(); 2572 2573 if musl { 2574 cfg.header("fcntl.h"); 2575 } else { 2576 cfg.header("linux/fcntl.h"); 2577 } 2578 2579 cfg.skip_type(|_| true) 2580 .skip_static(|_| true) 2581 .skip_struct(|_| true) 2582 .skip_fn(|_| true) 2583 .skip_const(move |name| match name { 2584 // test fcntl constants: 2585 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" 2586 | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW" 2587 | "F_SEAL_WRITE" => false, 2588 _ => true, 2589 }) 2590 .type_name(move |ty, is_struct, is_union| match ty { 2591 t if is_struct => format!("struct {}", t), 2592 t if is_union => format!("union {}", t), 2593 t => t.to_string(), 2594 }); 2595 2596 cfg.generate("../src/lib.rs", "linux_fcntl.rs"); 2597 } 2598 2599 if linux || android { 2600 // test termios 2601 let mut cfg = ctest_cfg(); 2602 cfg.header("asm/termbits.h"); 2603 cfg.skip_type(|_| true) 2604 .skip_static(|_| true) 2605 .skip_fn(|_| true) 2606 .skip_const(|c| c != "BOTHER") 2607 .skip_struct(|s| s != "termios2") 2608 .type_name(move |ty, is_struct, is_union| match ty { 2609 t if is_struct => format!("struct {}", t), 2610 t if is_union => format!("union {}", t), 2611 t => t.to_string(), 2612 }); 2613 cfg.generate("../src/lib.rs", "linux_termios.rs"); 2614 } 2615 2616 if linux || android { 2617 // test IPV6_ constants: 2618 let mut cfg = ctest_cfg(); 2619 headers! { 2620 cfg: 2621 "linux/in6.h" 2622 } 2623 cfg.skip_type(|_| true) 2624 .skip_static(|_| true) 2625 .skip_fn(|_| true) 2626 .skip_const(|_| true) 2627 .skip_struct(|_| true) 2628 .skip_const(move |name| match name { 2629 "IPV6_FLOWINFO" 2630 | "IPV6_FLOWLABEL_MGR" 2631 | "IPV6_FLOWINFO_SEND" 2632 | "IPV6_FLOWINFO_FLOWLABEL" 2633 | "IPV6_FLOWINFO_PRIORITY" => false, 2634 _ => true, 2635 }) 2636 .type_name(move |ty, is_struct, is_union| match ty { 2637 t if is_struct => format!("struct {}", t), 2638 t if is_union => format!("union {}", t), 2639 t => t.to_string(), 2640 }); 2641 cfg.generate("../src/lib.rs", "linux_ipv6.rs"); 2642 } 2643 2644 if linux || android { 2645 // Test Elf64_Phdr and Elf32_Phdr 2646 // These types have a field called `p_type`, but including 2647 // "resolve.h" defines a `p_type` macro that expands to `__p_type` 2648 // making the tests for these fails when both are included. 2649 let mut cfg = ctest_cfg(); 2650 cfg.header("elf.h"); 2651 cfg.skip_fn(|_| true) 2652 .skip_static(|_| true) 2653 .skip_fn(|_| true) 2654 .skip_const(|_| true) 2655 .type_name(move |ty, _is_struct, _is_union| ty.to_string()) 2656 .skip_struct(move |ty| match ty { 2657 "Elf64_Phdr" | "Elf32_Phdr" => false, 2658 _ => true, 2659 }) 2660 .skip_type(move |ty| match ty { 2661 "Elf64_Phdr" | "Elf32_Phdr" => false, 2662 _ => true, 2663 }); 2664 cfg.generate("../src/lib.rs", "linux_elf.rs"); 2665 } 2666 } 2667 2668 fn which_freebsd() -> Option<i32> { 2669 let output = std::process::Command::new("freebsd-version") 2670 .output() 2671 .ok()?; 2672 if !output.status.success() { 2673 return None; 2674 } 2675 2676 let stdout = String::from_utf8(output.stdout).ok()?; 2677 2678 match &stdout { 2679 s if s.starts_with("10") => Some(10), 2680 s if s.starts_with("11") => Some(11), 2681 s if s.starts_with("12") => Some(12), 2682 s if s.starts_with("13") => Some(13), 2683 _ => None, 2684 } 2685 } 2686