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