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