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