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