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