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