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