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