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