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("linux") 27 || target.contains("android") 28 || target.contains("emscripten") 29 || target.contains("fuchsia") 30 || target.contains("bsd") 31 { 32 cc::Build::new().file("src/makedev.c").compile("makedev"); 33 } 34 } 35 if target.contains("android") || target.contains("linux") { 36 cc::Build::new().file("src/errqueue.c").compile("errqueue"); 37 } 38 if target.contains("linux") 39 || target.contains("l4re") 40 || target.contains("android") 41 || target.contains("emscripten") 42 { 43 cc::Build::new().file("src/sigrt.c").compile("sigrt"); 44 } 45 } 46 47 fn do_ctest() { 48 match &env::var("TARGET").unwrap() { 49 t if t.contains("android") => return test_android(t), 50 t if t.contains("apple") => return test_apple(t), 51 t if t.contains("dragonfly") => return test_dragonflybsd(t), 52 t if t.contains("emscripten") => return test_emscripten(t), 53 t if t.contains("freebsd") => return test_freebsd(t), 54 t if t.contains("haiku") => return test_haiku(t), 55 t if t.contains("linux") => return test_linux(t), 56 t if t.contains("netbsd") => return test_netbsd(t), 57 t if t.contains("openbsd") => return test_openbsd(t), 58 t if t.contains("redox") => return test_redox(t), 59 t if t.contains("solaris") => return test_solarish(t), 60 t if t.contains("illumos") => return test_solarish(t), 61 t if t.contains("wasi") => return test_wasi(t), 62 t if t.contains("windows") => return test_windows(t), 63 t if t.contains("vxworks") => return test_vxworks(t), 64 t if t.contains("nto-qnx") => return test_neutrino(t), 65 t => panic!("unknown target {}", t), 66 } 67 } 68 69 fn ctest_cfg() -> ctest::TestGenerator { 70 let mut cfg = ctest::TestGenerator::new(); 71 let libc_cfgs = [ 72 "libc_priv_mod_use", 73 "libc_union", 74 "libc_const_size_of", 75 "libc_align", 76 "libc_core_cvoid", 77 "libc_packedN", 78 "libc_thread_local", 79 ]; 80 for f in &libc_cfgs { 81 cfg.cfg(f, None); 82 } 83 cfg 84 } 85 86 fn do_semver() { 87 let mut out = PathBuf::from(env::var("OUT_DIR").unwrap()); 88 out.push("semver.rs"); 89 let mut output = BufWriter::new(File::create(&out).unwrap()); 90 91 let family = env::var("CARGO_CFG_TARGET_FAMILY").unwrap(); 92 let vendor = env::var("CARGO_CFG_TARGET_VENDOR").unwrap(); 93 let os = env::var("CARGO_CFG_TARGET_OS").unwrap(); 94 let arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); 95 let target_env = env::var("CARGO_CFG_TARGET_ENV").unwrap(); 96 97 // `libc-test/semver` dir. 98 let mut semver_root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); 99 semver_root.push("semver"); 100 101 // NOTE: Windows has the same `family` as `os`, no point in including it 102 // twice. 103 // NOTE: Android doesn't include the unix file (or the Linux file) because 104 // there are some many definitions missing it's actually easier just to 105 // maintain a file for Android. 106 if family != os && os != "android" { 107 process_semver_file(&mut output, &mut semver_root, &family); 108 } 109 process_semver_file(&mut output, &mut semver_root, &vendor); 110 process_semver_file(&mut output, &mut semver_root, &os); 111 let os_arch = format!("{}-{}", os, arch); 112 process_semver_file(&mut output, &mut semver_root, &os_arch); 113 if target_env != "" { 114 let os_env = format!("{}-{}", os, target_env); 115 process_semver_file(&mut output, &mut semver_root, &os_env); 116 117 let os_env_arch = format!("{}-{}-{}", os, target_env, arch); 118 process_semver_file(&mut output, &mut semver_root, &os_env_arch); 119 } 120 } 121 122 fn process_semver_file<W: Write, P: AsRef<Path>>(output: &mut W, path: &mut PathBuf, file: P) { 123 // NOTE: `path` is reused between calls, so always remove the file again. 124 path.push(file); 125 path.set_extension("txt"); 126 127 println!("cargo:rerun-if-changed={}", path.display()); 128 let input_file = match File::open(&*path) { 129 Ok(file) => file, 130 Err(ref err) if err.kind() == io::ErrorKind::NotFound => { 131 path.pop(); 132 return; 133 } 134 Err(err) => panic!("unexpected error opening file: {}", err), 135 }; 136 let input = BufReader::new(input_file); 137 138 write!(output, "// Source: {}.\n", path.display()).unwrap(); 139 output.write(b"use libc::{\n").unwrap(); 140 for line in input.lines() { 141 let line = line.unwrap().into_bytes(); 142 match line.first() { 143 // Ignore comments and empty lines. 144 Some(b'#') | None => continue, 145 _ => { 146 output.write(b" ").unwrap(); 147 output.write(&line).unwrap(); 148 output.write(b",\n").unwrap(); 149 } 150 } 151 } 152 output.write(b"};\n\n").unwrap(); 153 path.pop(); 154 } 155 156 fn main() { 157 do_cc(); 158 do_ctest(); 159 do_semver(); 160 } 161 162 macro_rules! headers { 163 ($cfg:ident: [$m:expr]: $header:literal) => { 164 if $m { 165 $cfg.header($header); 166 } 167 }; 168 ($cfg:ident: $header:literal) => { 169 $cfg.header($header); 170 }; 171 ($($cfg:ident: $([$c:expr]:)* $header:literal,)*) => { 172 $(headers!($cfg: $([$c]:)* $header);)* 173 }; 174 ($cfg:ident: $( $([$c:expr]:)* $header:literal,)*) => { 175 headers!($($cfg: $([$c]:)* $header,)*); 176 }; 177 ($cfg:ident: $( $([$c:expr]:)* $header:literal),*) => { 178 headers!($($cfg: $([$c]:)* $header,)*); 179 }; 180 } 181 182 fn test_apple(target: &str) { 183 assert!(target.contains("apple")); 184 let x86_64 = target.contains("x86_64"); 185 let i686 = target.contains("i686"); 186 187 let mut cfg = ctest_cfg(); 188 cfg.flag("-Wno-deprecated-declarations"); 189 cfg.define("__APPLE_USE_RFC_3542", None); 190 191 headers! { cfg: 192 "aio.h", 193 "CommonCrypto/CommonCrypto.h", 194 "CommonCrypto/CommonRandom.h", 195 "copyfile.h", 196 "crt_externs.h", 197 "ctype.h", 198 "dirent.h", 199 "dlfcn.h", 200 "errno.h", 201 "execinfo.h", 202 "fcntl.h", 203 "getopt.h", 204 "glob.h", 205 "grp.h", 206 "iconv.h", 207 "ifaddrs.h", 208 "langinfo.h", 209 "libgen.h", 210 "libproc.h", 211 "limits.h", 212 "locale.h", 213 "mach-o/dyld.h", 214 "mach/mach_init.h", 215 "mach/mach.h", 216 "mach/mach_time.h", 217 "mach/mach_types.h", 218 "mach/mach_vm.h", 219 "mach/thread_act.h", 220 "mach/thread_policy.h", 221 "malloc/malloc.h", 222 "net/bpf.h", 223 "net/dlil.h", 224 "net/if.h", 225 "net/if_arp.h", 226 "net/if_dl.h", 227 "net/if_utun.h", 228 "net/if_var.h", 229 "net/ndrv.h", 230 "net/route.h", 231 "netdb.h", 232 "netinet/if_ether.h", 233 "netinet/in.h", 234 "netinet/ip.h", 235 "netinet/tcp.h", 236 "netinet/udp.h", 237 "os/lock.h", 238 "os/signpost.h", 239 "poll.h", 240 "pthread.h", 241 "pthread_spis.h", 242 "pthread/introspection.h", 243 "pthread/spawn.h", 244 "pthread/stack_np.h", 245 "pwd.h", 246 "regex.h", 247 "resolv.h", 248 "sched.h", 249 "semaphore.h", 250 "signal.h", 251 "spawn.h", 252 "stddef.h", 253 "stdint.h", 254 "stdio.h", 255 "stdlib.h", 256 "string.h", 257 "sysdir.h", 258 "sys/appleapiopts.h", 259 "sys/attr.h", 260 "sys/clonefile.h", 261 "sys/event.h", 262 "sys/file.h", 263 "sys/ioctl.h", 264 "sys/ipc.h", 265 "sys/kern_control.h", 266 "sys/mman.h", 267 "sys/mount.h", 268 "sys/proc_info.h", 269 "sys/ptrace.h", 270 "sys/quota.h", 271 "sys/resource.h", 272 "sys/sem.h", 273 "sys/shm.h", 274 "sys/socket.h", 275 "sys/stat.h", 276 "sys/statvfs.h", 277 "sys/sys_domain.h", 278 "sys/sysctl.h", 279 "sys/time.h", 280 "sys/times.h", 281 "sys/timex.h", 282 "sys/types.h", 283 "sys/uio.h", 284 "sys/un.h", 285 "sys/utsname.h", 286 "sys/wait.h", 287 "sys/xattr.h", 288 "syslog.h", 289 "termios.h", 290 "time.h", 291 "unistd.h", 292 "util.h", 293 "utime.h", 294 "utmpx.h", 295 "wchar.h", 296 "xlocale.h", 297 [x86_64]: "crt_externs.h", 298 } 299 300 cfg.skip_struct(move |ty| { 301 match ty { 302 // FIXME: actually a union 303 "sigval" => true, 304 305 // FIXME: The size is changed in recent macOSes. 306 "malloc_zone_t" => true, 307 308 _ => false, 309 } 310 }); 311 312 cfg.skip_type(move |ty| match ty { 313 _ => false, 314 }); 315 316 cfg.skip_const(move |name| { 317 // They're declared via `deprecated_mach` and we don't support it anymore. 318 if name.starts_with("VM_FLAGS_") { 319 return true; 320 } 321 match name { 322 // These OSX constants are removed in Sierra. 323 // https://developer.apple.com/library/content/releasenotes/General/APIDiffsMacOS10_12/Swift/Darwin.html 324 "KERN_KDENABLE_BG_TRACE" | "KERN_KDDISABLE_BG_TRACE" => true, 325 // FIXME: the value has been changed since Catalina (0xffff0000 -> 0x3fff0000). 326 "SF_SETTABLE" => true, 327 328 // FIXME: XCode 13.1 doesn't have it. 329 "TIOCREMOTE" => true, 330 _ => false, 331 } 332 }); 333 334 cfg.skip_fn(move |name| { 335 // skip those that are manually verified 336 match name { 337 // FIXME: https://github.com/rust-lang/libc/issues/1272 338 "execv" | "execve" | "execvp" => true, 339 340 // close calls the close_nocancel system call 341 "close" => true, 342 343 // FIXME: libstd removed libresolv support: https://github.com/rust-lang/rust/pull/102766 344 "res_init" => true, 345 346 // FIXME: remove once the target in CI is updated 347 "pthread_jit_write_freeze_callbacks_np" => true, 348 349 // FIXME: ABI has been changed on recent macOSes. 350 "os_unfair_lock_assert_owner" | "os_unfair_lock_assert_not_owner" => true, 351 352 _ => false, 353 } 354 }); 355 356 cfg.skip_field(move |struct_, field| { 357 match (struct_, field) { 358 // FIXME: the array size has been changed since macOS 10.15 ([8] -> [7]). 359 ("statfs", "f_reserved") => true, 360 ("__darwin_arm_neon_state64", "__v") => true, 361 // MAXPATHLEN is too big for auto-derive traits on arrays. 362 ("vnode_info_path", "vip_path") => true, 363 _ => false, 364 } 365 }); 366 367 cfg.skip_field_type(move |struct_, field| { 368 match (struct_, field) { 369 // FIXME: actually a union 370 ("sigevent", "sigev_value") => true, 371 _ => false, 372 } 373 }); 374 375 cfg.volatile_item(|i| { 376 use ctest::VolatileItemKind::*; 377 match i { 378 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true, 379 _ => false, 380 } 381 }); 382 383 cfg.type_name(move |ty, is_struct, is_union| { 384 match ty { 385 // Just pass all these through, no need for a "struct" prefix 386 "FILE" | "DIR" | "Dl_info" => ty.to_string(), 387 388 // OSX calls this something else 389 "sighandler_t" => "sig_t".to_string(), 390 391 t if is_union => format!("union {}", t), 392 t if t.ends_with("_t") => t.to_string(), 393 t if is_struct => format!("struct {}", t), 394 t => t.to_string(), 395 } 396 }); 397 398 cfg.field_name(move |struct_, field| { 399 match field { 400 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 401 s.replace("e_nsec", "espec.tv_nsec") 402 } 403 // FIXME: sigaction actually contains a union with two variants: 404 // a sa_sigaction with type: (*)(int, struct __siginfo *, void *) 405 // a sa_handler with type sig_t 406 "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(), 407 s => s.to_string(), 408 } 409 }); 410 411 cfg.skip_roundtrip(move |s| match s { 412 // FIXME: this type has the wrong ABI 413 "max_align_t" if i686 => true, 414 // Can't return an array from a C function. 415 "uuid_t" | "vol_capabilities_set_t" => true, 416 _ => false, 417 }); 418 cfg.generate("../src/lib.rs", "main.rs"); 419 } 420 421 fn test_openbsd(target: &str) { 422 assert!(target.contains("openbsd")); 423 424 let mut cfg = ctest_cfg(); 425 cfg.flag("-Wno-deprecated-declarations"); 426 427 let x86_64 = target.contains("x86_64"); 428 429 headers! { cfg: 430 "elf.h", 431 "errno.h", 432 "execinfo.h", 433 "fcntl.h", 434 "getopt.h", 435 "libgen.h", 436 "limits.h", 437 "link.h", 438 "locale.h", 439 "stddef.h", 440 "stdint.h", 441 "stdio.h", 442 "stdlib.h", 443 "sys/stat.h", 444 "sys/types.h", 445 "time.h", 446 "wchar.h", 447 "ctype.h", 448 "dirent.h", 449 "sys/socket.h", 450 [x86_64]:"machine/fpu.h", 451 "net/if.h", 452 "net/route.h", 453 "net/if_arp.h", 454 "netdb.h", 455 "netinet/in.h", 456 "netinet/ip.h", 457 "netinet/tcp.h", 458 "netinet/udp.h", 459 "net/bpf.h", 460 "regex.h", 461 "resolv.h", 462 "pthread.h", 463 "dlfcn.h", 464 "search.h", 465 "signal.h", 466 "string.h", 467 "sys/file.h", 468 "sys/futex.h", 469 "sys/ioctl.h", 470 "sys/ipc.h", 471 "sys/mman.h", 472 "sys/param.h", 473 "sys/resource.h", 474 "sys/shm.h", 475 "sys/socket.h", 476 "sys/time.h", 477 "sys/uio.h", 478 "sys/ktrace.h", 479 "sys/un.h", 480 "sys/wait.h", 481 "unistd.h", 482 "utime.h", 483 "pwd.h", 484 "grp.h", 485 "sys/utsname.h", 486 "sys/ptrace.h", 487 "sys/mount.h", 488 "sys/uio.h", 489 "sched.h", 490 "termios.h", 491 "poll.h", 492 "syslog.h", 493 "semaphore.h", 494 "sys/statvfs.h", 495 "sys/times.h", 496 "glob.h", 497 "ifaddrs.h", 498 "langinfo.h", 499 "sys/sysctl.h", 500 "utmp.h", 501 "sys/event.h", 502 "net/if_dl.h", 503 "util.h", 504 "ufs/ufs/quota.h", 505 "pthread_np.h", 506 "sys/syscall.h", 507 "sys/shm.h", 508 "sys/param.h", 509 } 510 511 cfg.skip_struct(move |ty| { 512 match ty { 513 // FIXME: actually a union 514 "sigval" => true, 515 516 _ => false, 517 } 518 }); 519 520 cfg.skip_const(move |name| { 521 match name { 522 // Removed in OpenBSD 6.0 523 "KERN_USERMOUNT" | "KERN_ARND" => true, 524 // Removed in OpenBSD 7.2 525 "KERN_NSELCOLL" => true, 526 // Good chance it's going to be wrong depending on the host release 527 "KERN_MAXID" | "NET_RT_MAXID" => true, 528 "EV_SYSFLAGS" => true, 529 _ => false, 530 } 531 }); 532 533 cfg.skip_fn(move |name| { 534 match name { 535 // FIXME: https://github.com/rust-lang/libc/issues/1272 536 "execv" | "execve" | "execvp" | "execvpe" => true, 537 538 // Removed in OpenBSD 6.5 539 // https://marc.info/?l=openbsd-cvs&m=154723400730318 540 "mincore" => true, 541 542 // futex() has volatile arguments, but that doesn't exist in Rust. 543 "futex" => true, 544 545 // Available for openBSD 7.3 546 "mimmutable" => true, 547 548 _ => false, 549 } 550 }); 551 552 cfg.type_name(move |ty, is_struct, is_union| { 553 match ty { 554 // Just pass all these through, no need for a "struct" prefix 555 "FILE" | "DIR" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(), 556 557 // OSX calls this something else 558 "sighandler_t" => "sig_t".to_string(), 559 560 t if is_union => format!("union {}", t), 561 t if t.ends_with("_t") => t.to_string(), 562 t if is_struct => format!("struct {}", t), 563 t => t.to_string(), 564 } 565 }); 566 567 cfg.field_name(move |struct_, field| match field { 568 "st_birthtime" if struct_.starts_with("stat") => "__st_birthtime".to_string(), 569 "st_birthtime_nsec" if struct_.starts_with("stat") => "__st_birthtimensec".to_string(), 570 s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.replace("e_nsec", ".tv_nsec"), 571 "sa_sigaction" if struct_ == "sigaction" => "sa_handler".to_string(), 572 s => s.to_string(), 573 }); 574 575 cfg.skip_field_type(move |struct_, field| { 576 // type siginfo_t.si_addr changed from OpenBSD 6.0 to 6.1 577 struct_ == "siginfo_t" && field == "si_addr" 578 }); 579 580 cfg.skip_field(|struct_, field| { 581 match (struct_, field) { 582 // conflicting with `p_type` macro from <resolve.h>. 583 ("Elf32_Phdr", "p_type") => true, 584 ("Elf64_Phdr", "p_type") => true, 585 _ => false, 586 } 587 }); 588 589 cfg.generate("../src/lib.rs", "main.rs"); 590 } 591 592 fn test_windows(target: &str) { 593 assert!(target.contains("windows")); 594 let gnu = target.contains("gnu"); 595 596 let mut cfg = ctest_cfg(); 597 if target.contains("msvc") { 598 cfg.flag("/wd4324"); 599 } 600 cfg.define("_WIN32_WINNT", Some("0x8000")); 601 602 headers! { cfg: 603 "direct.h", 604 "errno.h", 605 "fcntl.h", 606 "io.h", 607 "limits.h", 608 "locale.h", 609 "process.h", 610 "signal.h", 611 "stddef.h", 612 "stdint.h", 613 "stdio.h", 614 "stdlib.h", 615 "sys/stat.h", 616 "sys/types.h", 617 "sys/utime.h", 618 "time.h", 619 "wchar.h", 620 [gnu]: "ws2tcpip.h", 621 [!gnu]: "Winsock2.h", 622 } 623 624 cfg.type_name(move |ty, is_struct, is_union| { 625 match ty { 626 // Just pass all these through, no need for a "struct" prefix 627 "FILE" | "DIR" | "Dl_info" => ty.to_string(), 628 629 // FIXME: these don't exist: 630 "time64_t" => "__time64_t".to_string(), 631 "ssize_t" => "SSIZE_T".to_string(), 632 633 "sighandler_t" if !gnu => "_crt_signal_t".to_string(), 634 "sighandler_t" if gnu => "__p_sig_fn_t".to_string(), 635 636 t if is_union => format!("union {}", t), 637 t if t.ends_with("_t") => t.to_string(), 638 639 // Windows uppercase structs don't have `struct` in front: 640 t if is_struct => { 641 if ty.clone().chars().next().unwrap().is_uppercase() { 642 t.to_string() 643 } else if t == "stat" { 644 "struct __stat64".to_string() 645 } else if t == "utimbuf" { 646 "struct __utimbuf64".to_string() 647 } else { 648 // put `struct` in front of all structs: 649 format!("struct {}", t) 650 } 651 } 652 t => t.to_string(), 653 } 654 }); 655 656 cfg.fn_cname(move |name, cname| cname.unwrap_or(name).to_string()); 657 658 cfg.skip_type(move |name| match name { 659 "SSIZE_T" if !gnu => true, 660 "ssize_t" if !gnu => true, 661 _ => false, 662 }); 663 664 cfg.skip_struct(move |ty| { 665 if ty.starts_with("__c_anonymous_") { 666 return true; 667 } 668 return false; 669 }); 670 671 cfg.skip_const(move |name| { 672 match name { 673 // FIXME: API error: 674 // SIG_ERR type is "void (*)(int)", not "int" 675 "SIG_ERR" | 676 // Similar for SIG_DFL/IGN/GET/SGE/ACK 677 "SIG_DFL" | "SIG_IGN" | "SIG_GET" | "SIG_SGE" | "SIG_ACK" => true, 678 // FIXME: newer windows-gnu environment on CI? 679 "_O_OBTAIN_DIR" if gnu => true, 680 _ => false, 681 } 682 }); 683 684 cfg.skip_field(move |s, field| match s { 685 "CONTEXT" if field == "Fp" => true, 686 _ => false, 687 }); 688 // FIXME: All functions point to the wrong addresses? 689 cfg.skip_fn_ptrcheck(|_| true); 690 691 cfg.skip_signededness(move |c| { 692 match c { 693 // windows-isms 694 n if n.starts_with("P") => true, 695 n if n.starts_with("H") => true, 696 n if n.starts_with("LP") => true, 697 "sighandler_t" if gnu => true, 698 _ => false, 699 } 700 }); 701 702 cfg.skip_fn(move |name| { 703 match name { 704 // FIXME: https://github.com/rust-lang/libc/issues/1272 705 "execv" | "execve" | "execvp" | "execvpe" => true, 706 707 _ => false, 708 } 709 }); 710 711 cfg.generate("../src/lib.rs", "main.rs"); 712 } 713 714 fn test_redox(target: &str) { 715 assert!(target.contains("redox")); 716 717 let mut cfg = ctest_cfg(); 718 cfg.flag("-Wno-deprecated-declarations"); 719 720 headers! { 721 cfg: 722 "ctype.h", 723 "dirent.h", 724 "dlfcn.h", 725 "errno.h", 726 "fcntl.h", 727 "grp.h", 728 "limits.h", 729 "locale.h", 730 "netdb.h", 731 "netinet/in.h", 732 "netinet/ip.h", 733 "netinet/tcp.h", 734 "poll.h", 735 "pwd.h", 736 "semaphore.h", 737 "string.h", 738 "strings.h", 739 "sys/file.h", 740 "sys/ioctl.h", 741 "sys/mman.h", 742 "sys/ptrace.h", 743 "sys/resource.h", 744 "sys/socket.h", 745 "sys/stat.h", 746 "sys/statvfs.h", 747 "sys/time.h", 748 "sys/types.h", 749 "sys/uio.h", 750 "sys/un.h", 751 "sys/utsname.h", 752 "sys/wait.h", 753 "termios.h", 754 "time.h", 755 "unistd.h", 756 "utime.h", 757 "wchar.h", 758 } 759 760 cfg.generate("../src/lib.rs", "main.rs"); 761 } 762 763 fn test_solarish(target: &str) { 764 let is_solaris = target.contains("solaris"); 765 let is_illumos = target.contains("illumos"); 766 assert!(is_solaris || is_illumos); 767 768 // ctest generates arguments supported only by clang, so make sure to run with CC=clang. 769 // While debugging, "CFLAGS=-ferror-limit=<large num>" is useful to get more error output. 770 let mut cfg = ctest_cfg(); 771 cfg.flag("-Wno-deprecated-declarations"); 772 773 cfg.define("_XOPEN_SOURCE", Some("700")); 774 cfg.define("__EXTENSIONS__", None); 775 cfg.define("_LCONV_C99", None); 776 777 headers! { 778 cfg: 779 "ctype.h", 780 "dirent.h", 781 "dlfcn.h", 782 "door.h", 783 "errno.h", 784 "execinfo.h", 785 "fcntl.h", 786 "getopt.h", 787 "glob.h", 788 "grp.h", 789 "ifaddrs.h", 790 "langinfo.h", 791 "limits.h", 792 "link.h", 793 "locale.h", 794 "mqueue.h", 795 "net/if.h", 796 "net/if_arp.h", 797 "net/route.h", 798 "netdb.h", 799 "netinet/in.h", 800 "netinet/ip.h", 801 "netinet/tcp.h", 802 "netinet/udp.h", 803 "poll.h", 804 "port.h", 805 "pthread.h", 806 "pwd.h", 807 "resolv.h", 808 "sched.h", 809 "semaphore.h", 810 "signal.h", 811 "stddef.h", 812 "stdint.h", 813 "stdio.h", 814 "stdlib.h", 815 "string.h", 816 "sys/auxv.h", 817 "sys/epoll.h", 818 "sys/eventfd.h", 819 "sys/file.h", 820 "sys/filio.h", 821 "sys/ioctl.h", 822 "sys/lgrp_user.h", 823 "sys/loadavg.h", 824 "sys/mman.h", 825 "sys/mount.h", 826 "sys/priv.h", 827 "sys/pset.h", 828 "sys/random.h", 829 "sys/resource.h", 830 "sys/sendfile.h", 831 "sys/socket.h", 832 "sys/stat.h", 833 "sys/statvfs.h", 834 "sys/stropts.h", 835 "sys/shm.h", 836 "sys/systeminfo.h", 837 "sys/time.h", 838 "sys/times.h", 839 "sys/timex.h", 840 "sys/types.h", 841 "sys/uio.h", 842 "sys/un.h", 843 "sys/utsname.h", 844 "sys/wait.h", 845 "syslog.h", 846 "termios.h", 847 "thread.h", 848 "time.h", 849 "priv.h", 850 "ucontext.h", 851 "unistd.h", 852 "utime.h", 853 "utmpx.h", 854 "wchar.h", 855 } 856 857 cfg.skip_type(move |ty| match ty { 858 "sighandler_t" => true, 859 _ => false, 860 }); 861 862 cfg.type_name(move |ty, is_struct, is_union| match ty { 863 "FILE" => "__FILE".to_string(), 864 "DIR" | "Dl_info" => ty.to_string(), 865 t if t.ends_with("_t") => t.to_string(), 866 t if is_struct => format!("struct {}", t), 867 t if is_union => format!("union {}", t), 868 t => t.to_string(), 869 }); 870 871 cfg.field_name(move |struct_, field| { 872 match struct_ { 873 // rust struct uses raw u64, rather than union 874 "epoll_event" if field == "u64" => "data.u64".to_string(), 875 // rust struct was committed with typo for Solaris 876 "door_arg_t" if field == "dec_num" => "desc_num".to_string(), 877 "stat" if field.ends_with("_nsec") => { 878 // expose stat.Xtim.tv_nsec fields 879 field.trim_end_matches("e_nsec").to_string() + ".tv_nsec" 880 } 881 _ => field.to_string(), 882 } 883 }); 884 885 cfg.skip_const(move |name| match name { 886 "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" | "DT_SOCK" 887 | "USRQUOTA" | "GRPQUOTA" | "PRIO_MIN" | "PRIO_MAX" => true, 888 889 // skip sighandler_t assignments 890 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, 891 892 "DT_UNKNOWN" => true, 893 894 "_UTX_LINESIZE" | "_UTX_USERSIZE" | "_UTX_PADSIZE" | "_UTX_IDSIZE" | "_UTX_HOSTSIZE" => { 895 true 896 } 897 898 "EADI" | "EXTPROC" | "IPC_SEAT" => true, 899 900 // This evaluates to a sysconf() call rather than a constant 901 "PTHREAD_STACK_MIN" => true, 902 903 // EPOLLEXCLUSIVE is a relatively recent addition to the epoll interface and may not be 904 // defined on older systems. It is, however, safe to use on systems which do not 905 // explicitly support it. (A no-op is an acceptable implementation of EPOLLEXCLUSIVE.) 906 "EPOLLEXCLUSIVE" => true, 907 908 _ => false, 909 }); 910 911 cfg.skip_struct(move |ty| { 912 if ty.starts_with("__c_anonymous_") { 913 return true; 914 } 915 // the union handling is a mess 916 if ty.contains("door_desc_t_") { 917 return true; 918 } 919 match ty { 920 // union, not a struct 921 "sigval" => true, 922 // a bunch of solaris-only fields 923 "utmpx" if is_illumos => true, 924 _ => false, 925 } 926 }); 927 928 cfg.skip_field(move |s, field| { 929 match s { 930 // C99 sizing on this is tough 931 "dirent" if field == "d_name" => true, 932 // the union/macro makes this rough 933 "sigaction" if field == "sa_sigaction" => true, 934 // Missing in illumos 935 "sigevent" if field == "ss_sp" => true, 936 // Avoid sigval union issues 937 "sigevent" if field == "sigev_value" => true, 938 // const issues 939 "sigevent" if field == "sigev_notify_attributes" => true, 940 941 // Avoid const and union issues 942 "door_arg" if field == "desc_ptr" => true, 943 "door_desc_t" if field == "d_data" => true, 944 "door_arg_t" if field.ends_with("_ptr") => true, 945 "door_arg_t" if field.ends_with("rbuf") => true, 946 947 // anonymous union challenges 948 "fpregset_t" if field == "fp_reg_set" => true, 949 950 // The LX brand (integrated into some illumos distros) commandeered several of the 951 // `uc_filler` fields to use for brand-specific state. 952 "ucontext_t" if is_illumos && (field == "uc_filler" || field == "uc_brand_data") => { 953 true 954 } 955 956 _ => false, 957 } 958 }); 959 960 cfg.skip_fn(move |name| { 961 // skip those that are manually verified 962 match name { 963 // const-ness only added recently 964 "dladdr" => true, 965 966 // Definition of those functions as changed since unified headers 967 // from NDK r14b These changes imply some API breaking changes but 968 // are still ABI compatible. We can wait for the next major release 969 // to be compliant with the new API. 970 // 971 // FIXME: unskip these for next major release 972 "setpriority" | "personality" => true, 973 974 // signal is defined in terms of sighandler_t, so ignore 975 "signal" => true, 976 977 // Currently missing 978 "cfmakeraw" | "cfsetspeed" => true, 979 980 // const-ness issues 981 "execv" | "execve" | "execvp" | "settimeofday" | "sethostname" => true, 982 983 // Solaris-different 984 "getpwent_r" | "getgrent_r" | "updwtmpx" if is_illumos => true, 985 "madvise" | "mprotect" if is_illumos => true, 986 "door_call" | "door_return" | "door_create" if is_illumos => true, 987 988 // Not visible when build with _XOPEN_SOURCE=700 989 "mmapobj" | "mmap64" | "meminfo" | "getpagesizes" | "getpagesizes2" => true, 990 991 // These functions may return int or void depending on the exact 992 // configuration of the compilation environment, but the return 993 // value is not useful (always 0) so we can ignore it: 994 "setservent" | "endservent" if is_illumos => true, 995 996 _ => false, 997 } 998 }); 999 1000 cfg.generate("../src/lib.rs", "main.rs"); 1001 } 1002 1003 fn test_netbsd(target: &str) { 1004 assert!(target.contains("netbsd")); 1005 let mut cfg = ctest_cfg(); 1006 1007 cfg.flag("-Wno-deprecated-declarations"); 1008 cfg.define("_NETBSD_SOURCE", Some("1")); 1009 1010 headers! { 1011 cfg: 1012 "elf.h", 1013 "errno.h", 1014 "fcntl.h", 1015 "getopt.h", 1016 "libgen.h", 1017 "limits.h", 1018 "link.h", 1019 "locale.h", 1020 "stddef.h", 1021 "stdint.h", 1022 "stdio.h", 1023 "stdlib.h", 1024 "sys/stat.h", 1025 "sys/types.h", 1026 "time.h", 1027 "wchar.h", 1028 "aio.h", 1029 "ctype.h", 1030 "dirent.h", 1031 "dlfcn.h", 1032 "glob.h", 1033 "grp.h", 1034 "ifaddrs.h", 1035 "langinfo.h", 1036 "net/bpf.h", 1037 "net/if.h", 1038 "net/if_arp.h", 1039 "net/if_dl.h", 1040 "net/route.h", 1041 "netdb.h", 1042 "netinet/in.h", 1043 "netinet/ip.h", 1044 "netinet/tcp.h", 1045 "netinet/udp.h", 1046 "poll.h", 1047 "pthread.h", 1048 "pwd.h", 1049 "regex.h", 1050 "resolv.h", 1051 "sched.h", 1052 "semaphore.h", 1053 "signal.h", 1054 "string.h", 1055 "sys/endian.h", 1056 "sys/exec_elf.h", 1057 "sys/xattr.h", 1058 "sys/extattr.h", 1059 "sys/file.h", 1060 "sys/ioctl.h", 1061 "sys/ioctl_compat.h", 1062 "sys/ipc.h", 1063 "sys/ktrace.h", 1064 "sys/mman.h", 1065 "sys/mount.h", 1066 "sys/ptrace.h", 1067 "sys/resource.h", 1068 "sys/shm.h", 1069 "sys/socket.h", 1070 "sys/statvfs.h", 1071 "sys/sysctl.h", 1072 "sys/time.h", 1073 "sys/times.h", 1074 "sys/timex.h", 1075 "sys/ucontext.h", 1076 "sys/ucred.h", 1077 "sys/uio.h", 1078 "sys/un.h", 1079 "sys/utsname.h", 1080 "sys/wait.h", 1081 "syslog.h", 1082 "termios.h", 1083 "ufs/ufs/quota.h", 1084 "ufs/ufs/quota1.h", 1085 "unistd.h", 1086 "util.h", 1087 "utime.h", 1088 "mqueue.h", 1089 "netinet/dccp.h", 1090 "sys/event.h", 1091 "sys/quota.h", 1092 "sys/shm.h", 1093 "iconv.h", 1094 } 1095 1096 cfg.type_name(move |ty, is_struct, is_union| { 1097 match ty { 1098 // Just pass all these through, no need for a "struct" prefix 1099 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr" 1100 | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" 1101 | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(), 1102 1103 // OSX calls this something else 1104 "sighandler_t" => "sig_t".to_string(), 1105 1106 t if is_union => format!("union {}", t), 1107 1108 t if t.ends_with("_t") => t.to_string(), 1109 1110 // put `struct` in front of all structs:. 1111 t if is_struct => format!("struct {}", t), 1112 1113 t => t.to_string(), 1114 } 1115 }); 1116 1117 cfg.field_name(move |struct_, field| { 1118 match field { 1119 // Our stat *_nsec fields normally don't actually exist but are part 1120 // of a timeval struct 1121 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1122 s.replace("e_nsec", ".tv_nsec") 1123 } 1124 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1125 s => s.to_string(), 1126 } 1127 }); 1128 1129 cfg.skip_type(move |ty| { 1130 if ty.starts_with("__c_anonymous_") { 1131 return true; 1132 } 1133 match ty { 1134 // FIXME: sighandler_t is crazy across platforms 1135 "sighandler_t" => true, 1136 _ => false, 1137 } 1138 }); 1139 1140 cfg.skip_struct(move |ty| { 1141 match ty { 1142 // This is actually a union, not a struct 1143 "sigval" => true, 1144 // These are tested as part of the linux_fcntl tests since there are 1145 // header conflicts when including them with all the other structs. 1146 "termios2" => true, 1147 _ => false, 1148 } 1149 }); 1150 1151 cfg.skip_signededness(move |c| { 1152 match c { 1153 "LARGE_INTEGER" | "float" | "double" => true, 1154 n if n.starts_with("pthread") => true, 1155 // sem_t is a struct or pointer 1156 "sem_t" => true, 1157 _ => false, 1158 } 1159 }); 1160 1161 cfg.skip_const(move |name| { 1162 match name { 1163 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 1164 "SIGUNUSED" => true, // removed in glibc 2.26 1165 1166 // weird signed extension or something like that? 1167 "MS_NOUSER" => true, 1168 "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13 1169 "BOTHER" => true, 1170 "GRND_RANDOM" | "GRND_INSECURE" | "GRND_NONBLOCK" => true, // netbsd 10 minimum 1171 1172 _ => false, 1173 } 1174 }); 1175 1176 cfg.skip_fn(move |name| { 1177 match name { 1178 // FIXME: https://github.com/rust-lang/libc/issues/1272 1179 "execv" | "execve" | "execvp" => true, 1180 // FIXME: netbsd 10 minimum 1181 "getentropy" | "getrandom" => true, 1182 1183 "getrlimit" | "getrlimit64" | // non-int in 1st arg 1184 "setrlimit" | "setrlimit64" | // non-int in 1st arg 1185 "prlimit" | "prlimit64" | // non-int in 2nd arg 1186 1187 _ => false, 1188 } 1189 }); 1190 1191 cfg.skip_field_type(move |struct_, field| { 1192 // This is a weird union, don't check the type. 1193 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1194 // sighandler_t type is super weird 1195 (struct_ == "sigaction" && field == "sa_sigaction") || 1196 // sigval is actually a union, but we pretend it's a struct 1197 (struct_ == "sigevent" && field == "sigev_value") || 1198 // aio_buf is "volatile void*" and Rust doesn't understand volatile 1199 (struct_ == "aiocb" && field == "aio_buf") 1200 }); 1201 1202 cfg.skip_field(|struct_, field| { 1203 match (struct_, field) { 1204 // conflicting with `p_type` macro from <resolve.h>. 1205 ("Elf32_Phdr", "p_type") => true, 1206 ("Elf64_Phdr", "p_type") => true, 1207 // pthread_spin_t is a volatile uchar 1208 ("pthread_spinlock_t", "pts_spin") => true, 1209 _ => false, 1210 } 1211 }); 1212 1213 cfg.generate("../src/lib.rs", "main.rs"); 1214 } 1215 1216 fn test_dragonflybsd(target: &str) { 1217 assert!(target.contains("dragonfly")); 1218 let mut cfg = ctest_cfg(); 1219 cfg.flag("-Wno-deprecated-declarations"); 1220 1221 headers! { 1222 cfg: 1223 "aio.h", 1224 "ctype.h", 1225 "dirent.h", 1226 "dlfcn.h", 1227 "errno.h", 1228 "execinfo.h", 1229 "fcntl.h", 1230 "getopt.h", 1231 "glob.h", 1232 "grp.h", 1233 "ifaddrs.h", 1234 "kvm.h", 1235 "langinfo.h", 1236 "libgen.h", 1237 "limits.h", 1238 "link.h", 1239 "locale.h", 1240 "mqueue.h", 1241 "net/bpf.h", 1242 "net/if.h", 1243 "net/if_arp.h", 1244 "net/if_dl.h", 1245 "net/route.h", 1246 "netdb.h", 1247 "netinet/in.h", 1248 "netinet/ip.h", 1249 "netinet/tcp.h", 1250 "netinet/udp.h", 1251 "poll.h", 1252 "pthread.h", 1253 "pthread_np.h", 1254 "pwd.h", 1255 "regex.h", 1256 "resolv.h", 1257 "sched.h", 1258 "semaphore.h", 1259 "signal.h", 1260 "stddef.h", 1261 "stdint.h", 1262 "stdio.h", 1263 "stdlib.h", 1264 "string.h", 1265 "sys/event.h", 1266 "sys/file.h", 1267 "sys/ioctl.h", 1268 "sys/cpuctl.h", 1269 "sys/eui64.h", 1270 "sys/ipc.h", 1271 "sys/kinfo.h", 1272 "sys/ktrace.h", 1273 "sys/malloc.h", 1274 "sys/mman.h", 1275 "sys/mount.h", 1276 "sys/procctl.h", 1277 "sys/ptrace.h", 1278 "sys/resource.h", 1279 "sys/rtprio.h", 1280 "sys/sched.h", 1281 "sys/shm.h", 1282 "sys/socket.h", 1283 "sys/stat.h", 1284 "sys/statvfs.h", 1285 "sys/sysctl.h", 1286 "sys/time.h", 1287 "sys/times.h", 1288 "sys/timex.h", 1289 "sys/types.h", 1290 "sys/checkpoint.h", 1291 "sys/uio.h", 1292 "sys/un.h", 1293 "sys/utsname.h", 1294 "sys/wait.h", 1295 "syslog.h", 1296 "termios.h", 1297 "time.h", 1298 "ucontext.h", 1299 "unistd.h", 1300 "util.h", 1301 "utime.h", 1302 "utmpx.h", 1303 "vfs/ufs/quota.h", 1304 "vm/vm_map.h", 1305 "wchar.h", 1306 "iconv.h", 1307 } 1308 1309 cfg.type_name(move |ty, is_struct, is_union| { 1310 match ty { 1311 // Just pass all these through, no need for a "struct" prefix 1312 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr" 1313 | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" 1314 | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(), 1315 1316 // FIXME: OSX calls this something else 1317 "sighandler_t" => "sig_t".to_string(), 1318 1319 t if is_union => format!("union {}", t), 1320 1321 t if t.ends_with("_t") => t.to_string(), 1322 1323 // sigval is a struct in Rust, but a union in C: 1324 "sigval" => format!("union sigval"), 1325 1326 // put `struct` in front of all structs:. 1327 t if is_struct => format!("struct {}", t), 1328 1329 t => t.to_string(), 1330 } 1331 }); 1332 1333 cfg.field_name(move |struct_, field| { 1334 match field { 1335 // Our stat *_nsec fields normally don't actually exist but are part 1336 // of a timeval struct 1337 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 1338 s.replace("e_nsec", ".tv_nsec") 1339 } 1340 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1341 // Field is named `type` in C but that is a Rust keyword, 1342 // so these fields are translated to `type_` in the bindings. 1343 "type_" if struct_ == "rtprio" => "type".to_string(), 1344 s => s.to_string(), 1345 } 1346 }); 1347 1348 cfg.skip_type(move |ty| { 1349 match ty { 1350 // sighandler_t is crazy across platforms 1351 "sighandler_t" => true, 1352 1353 _ => false, 1354 } 1355 }); 1356 1357 cfg.skip_struct(move |ty| { 1358 if ty.starts_with("__c_anonymous_") { 1359 return true; 1360 } 1361 match ty { 1362 // FIXME: These are tested as part of the linux_fcntl tests since 1363 // there are header conflicts when including them with all the other 1364 // structs. 1365 "termios2" => true, 1366 1367 _ => false, 1368 } 1369 }); 1370 1371 cfg.skip_signededness(move |c| { 1372 match c { 1373 "LARGE_INTEGER" | "float" | "double" => true, 1374 // uuid_t is a struct, not an integer. 1375 "uuid_t" => true, 1376 n if n.starts_with("pthread") => true, 1377 // sem_t is a struct or pointer 1378 "sem_t" => true, 1379 // mqd_t is a pointer on DragonFly 1380 "mqd_t" => true, 1381 1382 _ => false, 1383 } 1384 }); 1385 1386 cfg.skip_const(move |name| { 1387 match name { 1388 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 1389 1390 // weird signed extension or something like that? 1391 "MS_NOUSER" => true, 1392 "MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13 1393 1394 // These are defined for Solaris 11, but the crate is tested on 1395 // illumos, where they are currently not defined 1396 "EADI" | "PORT_SOURCE_POSTWAIT" | "PORT_SOURCE_SIGNAL" | "PTHREAD_STACK_MIN" => true, 1397 1398 _ => false, 1399 } 1400 }); 1401 1402 cfg.skip_fn(move |name| { 1403 // skip those that are manually verified 1404 match name { 1405 // FIXME: https://github.com/rust-lang/libc/issues/1272 1406 "execv" | "execve" | "execvp" | "fexecve" => true, 1407 1408 "getrlimit" | "getrlimit64" | // non-int in 1st arg 1409 "setrlimit" | "setrlimit64" | // non-int in 1st arg 1410 "prlimit" | "prlimit64" // non-int in 2nd arg 1411 => true, 1412 1413 _ => false, 1414 } 1415 }); 1416 1417 cfg.skip_field_type(move |struct_, field| { 1418 // This is a weird union, don't check the type. 1419 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1420 // sighandler_t type is super weird 1421 (struct_ == "sigaction" && field == "sa_sigaction") || 1422 // sigval is actually a union, but we pretend it's a struct 1423 (struct_ == "sigevent" && field == "sigev_value") || 1424 // aio_buf is "volatile void*" and Rust doesn't understand volatile 1425 (struct_ == "aiocb" && field == "aio_buf") 1426 }); 1427 1428 cfg.skip_field(move |struct_, field| { 1429 // this is actually a union on linux, so we can't represent it well and 1430 // just insert some padding. 1431 (struct_ == "siginfo_t" && field == "_pad") || 1432 // sigev_notify_thread_id is actually part of a sigev_un union 1433 (struct_ == "sigevent" && field == "sigev_notify_thread_id") 1434 }); 1435 1436 cfg.generate("../src/lib.rs", "main.rs"); 1437 } 1438 1439 fn test_wasi(target: &str) { 1440 assert!(target.contains("wasi")); 1441 1442 let mut cfg = ctest_cfg(); 1443 cfg.define("_GNU_SOURCE", None); 1444 1445 headers! { cfg: 1446 "ctype.h", 1447 "dirent.h", 1448 "errno.h", 1449 "fcntl.h", 1450 "limits.h", 1451 "locale.h", 1452 "malloc.h", 1453 "poll.h", 1454 "sched.h", 1455 "stdbool.h", 1456 "stddef.h", 1457 "stdint.h", 1458 "stdio.h", 1459 "stdlib.h", 1460 "string.h", 1461 "sys/resource.h", 1462 "sys/select.h", 1463 "sys/socket.h", 1464 "sys/stat.h", 1465 "sys/times.h", 1466 "sys/types.h", 1467 "sys/uio.h", 1468 "sys/utsname.h", 1469 "sys/ioctl.h", 1470 "time.h", 1471 "unistd.h", 1472 "wasi/api.h", 1473 "wasi/libc.h", 1474 "wasi/libc-find-relpath.h", 1475 "wasi/libc-nocwd.h", 1476 "wchar.h", 1477 } 1478 1479 cfg.type_name(move |ty, is_struct, is_union| match ty { 1480 "FILE" | "fd_set" | "DIR" => ty.to_string(), 1481 t if is_union => format!("union {}", t), 1482 t if t.starts_with("__wasi") && t.ends_with("_u") => format!("union {}", t), 1483 t if t.starts_with("__wasi") && is_struct => format!("struct {}", t), 1484 t if t.ends_with("_t") => t.to_string(), 1485 t if is_struct => format!("struct {}", t), 1486 t => t.to_string(), 1487 }); 1488 1489 cfg.field_name(move |_struct, field| { 1490 match field { 1491 // deal with fields as rust keywords 1492 "type_" => "type".to_string(), 1493 s => s.to_string(), 1494 } 1495 }); 1496 1497 // Looks like LLD doesn't merge duplicate imports, so if the Rust 1498 // code imports from a module and the C code also imports from a 1499 // module we end up with two imports of function pointers which 1500 // import the same thing but have different function pointers 1501 cfg.skip_fn_ptrcheck(|f| f.starts_with("__wasi")); 1502 1503 // d_name is declared as a flexible array in WASI libc, so it 1504 // doesn't support sizeof. 1505 cfg.skip_field(|s, field| s == "dirent" && field == "d_name"); 1506 1507 // Currently Rust/clang disagree on function argument ABI, so skip these 1508 // tests. For more info see WebAssembly/tool-conventions#88 1509 cfg.skip_roundtrip(|_| true); 1510 1511 cfg.generate("../src/lib.rs", "main.rs"); 1512 } 1513 1514 fn test_android(target: &str) { 1515 assert!(target.contains("android")); 1516 let target_pointer_width = match target { 1517 t if t.contains("aarch64") || t.contains("x86_64") => 64, 1518 t if t.contains("i686") || t.contains("arm") => 32, 1519 t => panic!("unsupported target: {}", t), 1520 }; 1521 let x86 = target.contains("i686") || target.contains("x86_64"); 1522 1523 let mut cfg = ctest_cfg(); 1524 cfg.define("_GNU_SOURCE", None); 1525 1526 headers! { cfg: 1527 "arpa/inet.h", 1528 "ctype.h", 1529 "dirent.h", 1530 "dlfcn.h", 1531 "elf.h", 1532 "errno.h", 1533 "fcntl.h", 1534 "getopt.h", 1535 "grp.h", 1536 "ifaddrs.h", 1537 "libgen.h", 1538 "limits.h", 1539 "link.h", 1540 "locale.h", 1541 "malloc.h", 1542 "net/ethernet.h", 1543 "net/if.h", 1544 "net/if_arp.h", 1545 "net/route.h", 1546 "netdb.h", 1547 "netinet/in.h", 1548 "netinet/ip.h", 1549 "netinet/tcp.h", 1550 "netinet/udp.h", 1551 "netpacket/packet.h", 1552 "poll.h", 1553 "pthread.h", 1554 "pty.h", 1555 "pwd.h", 1556 "regex.h", 1557 "resolv.h", 1558 "sched.h", 1559 "semaphore.h", 1560 "signal.h", 1561 "stddef.h", 1562 "stdint.h", 1563 "stdio.h", 1564 "stdlib.h", 1565 "string.h", 1566 "sys/auxv.h", 1567 "sys/epoll.h", 1568 "sys/eventfd.h", 1569 "sys/file.h", 1570 "sys/fsuid.h", 1571 "sys/inotify.h", 1572 "sys/ioctl.h", 1573 "sys/mman.h", 1574 "sys/mount.h", 1575 "sys/personality.h", 1576 "sys/prctl.h", 1577 "sys/ptrace.h", 1578 "sys/random.h", 1579 "sys/reboot.h", 1580 "sys/resource.h", 1581 "sys/sendfile.h", 1582 "sys/signalfd.h", 1583 "sys/socket.h", 1584 "sys/stat.h", 1585 "sys/statvfs.h", 1586 "sys/swap.h", 1587 "sys/syscall.h", 1588 "sys/sysinfo.h", 1589 "sys/system_properties.h", 1590 "sys/time.h", 1591 "sys/timerfd.h", 1592 "sys/times.h", 1593 "sys/types.h", 1594 "sys/ucontext.h", 1595 "sys/uio.h", 1596 "sys/un.h", 1597 "sys/user.h", 1598 "sys/utsname.h", 1599 "sys/vfs.h", 1600 "sys/xattr.h", 1601 "sys/wait.h", 1602 "syslog.h", 1603 "termios.h", 1604 "time.h", 1605 "unistd.h", 1606 "utime.h", 1607 "utmp.h", 1608 "wchar.h", 1609 "xlocale.h", 1610 // time64_t is not defined for 64-bit targets If included it will 1611 // generate the error 'Your time_t is already 64-bit' 1612 [target_pointer_width == 32]: "time64.h", 1613 [x86]: "sys/reg.h", 1614 } 1615 1616 // Include linux headers at the end: 1617 headers! { cfg: 1618 "asm/mman.h", 1619 "linux/auxvec.h", 1620 "linux/dccp.h", 1621 "linux/elf.h", 1622 "linux/errqueue.h", 1623 "linux/falloc.h", 1624 "linux/filter.h", 1625 "linux/futex.h", 1626 "linux/fs.h", 1627 "linux/genetlink.h", 1628 "linux/if_alg.h", 1629 "linux/if_addr.h", 1630 "linux/if_ether.h", 1631 "linux/if_link.h", 1632 "linux/rtnetlink.h", 1633 "linux/if_tun.h", 1634 "linux/magic.h", 1635 "linux/memfd.h", 1636 "linux/mempolicy.h", 1637 "linux/module.h", 1638 "linux/mount.h", 1639 "linux/net_tstamp.h", 1640 "linux/netfilter/nfnetlink.h", 1641 "linux/netfilter/nfnetlink_log.h", 1642 "linux/netfilter/nfnetlink_queue.h", 1643 "linux/netfilter/nf_tables.h", 1644 "linux/netfilter_ipv4.h", 1645 "linux/netfilter_ipv6.h", 1646 "linux/netfilter_ipv6/ip6_tables.h", 1647 "linux/netlink.h", 1648 "linux/quota.h", 1649 "linux/reboot.h", 1650 "linux/seccomp.h", 1651 "linux/sched.h", 1652 "linux/sockios.h", 1653 "linux/uinput.h", 1654 "linux/vm_sockets.h", 1655 "linux/wait.h", 1656 1657 } 1658 1659 // Include Android-specific headers: 1660 headers! { cfg: 1661 "android/set_abort_message.h" 1662 } 1663 1664 cfg.type_name(move |ty, is_struct, is_union| { 1665 match ty { 1666 // Just pass all these through, no need for a "struct" prefix 1667 "FILE" | "fd_set" | "Dl_info" | "Elf32_Phdr" | "Elf64_Phdr" => ty.to_string(), 1668 1669 t if is_union => format!("union {}", t), 1670 1671 t if t.ends_with("_t") => t.to_string(), 1672 1673 // sigval is a struct in Rust, but a union in C: 1674 "sigval" => format!("union sigval"), 1675 1676 // put `struct` in front of all structs:. 1677 t if is_struct => format!("struct {}", t), 1678 1679 t => t.to_string(), 1680 } 1681 }); 1682 1683 cfg.field_name(move |struct_, field| { 1684 match field { 1685 // Our stat *_nsec fields normally don't actually exist but are part 1686 // of a timeval struct 1687 s if s.ends_with("_nsec") && struct_.starts_with("stat") => s.to_string(), 1688 // FIXME: appears that `epoll_event.data` is an union 1689 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 1690 // The following structs have a field called `type` in C, 1691 // but `type` is a Rust keyword, so these fields are translated 1692 // to `type_` in Rust. 1693 "type_" 1694 if struct_ == "input_event" 1695 || struct_ == "input_mask" 1696 || struct_ == "ff_effect" => 1697 { 1698 "type".to_string() 1699 } 1700 1701 s => s.to_string(), 1702 } 1703 }); 1704 1705 cfg.skip_type(move |ty| { 1706 match ty { 1707 // FIXME: `sighandler_t` type is incorrect, see: 1708 // https://github.com/rust-lang/libc/issues/1359 1709 "sighandler_t" => true, 1710 1711 // These are tested in the `linux_elf.rs` file. 1712 "Elf64_Phdr" | "Elf32_Phdr" => true, 1713 _ => false, 1714 } 1715 }); 1716 1717 cfg.skip_struct(move |ty| { 1718 if ty.starts_with("__c_anonymous_") { 1719 return true; 1720 } 1721 match ty { 1722 // These are tested as part of the linux_fcntl tests since there are 1723 // header conflicts when including them with all the other structs. 1724 "termios2" => true, 1725 // uc_sigmask and uc_sigmask64 of ucontext_t are an anonymous union 1726 "ucontext_t" => true, 1727 // 'private' type 1728 "prop_info" => true, 1729 1730 // These are tested in the `linux_elf.rs` file. 1731 "Elf64_Phdr" | "Elf32_Phdr" => true, 1732 1733 _ => false, 1734 } 1735 }); 1736 1737 cfg.skip_const(move |name| { 1738 match name { 1739 // The IPV6 constants are tested in the `linux_ipv6.rs` tests: 1740 | "IPV6_FLOWINFO" 1741 | "IPV6_FLOWLABEL_MGR" 1742 | "IPV6_FLOWINFO_SEND" 1743 | "IPV6_FLOWINFO_FLOWLABEL" 1744 | "IPV6_FLOWINFO_PRIORITY" 1745 // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests: 1746 | "F_CANCELLK" 1747 | "F_ADD_SEALS" 1748 | "F_GET_SEALS" 1749 | "F_SEAL_SEAL" 1750 | "F_SEAL_SHRINK" 1751 | "F_SEAL_GROW" 1752 | "F_SEAL_WRITE" => true, 1753 1754 // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests: 1755 "ARPHRD_CAN" => true, 1756 1757 // FIXME: deprecated: not available in any header 1758 // See: https://github.com/rust-lang/libc/issues/1356 1759 "ENOATTR" => true, 1760 1761 // FIXME: still necessary? 1762 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness 1763 // FIXME: deprecated - removed in glibc 2.26 1764 "SIGUNUSED" => true, 1765 1766 // Needs a newer Android SDK for the definition 1767 "P_PIDFD" => true, 1768 1769 // Requires Linux kernel 5.6 1770 "VMADDR_CID_LOCAL" => true, 1771 1772 // FIXME: conflicts with standard C headers and is tested in 1773 // `linux_termios.rs` below: 1774 "BOTHER" => true, 1775 "IBSHIFT" => true, 1776 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => true, 1777 1778 // is a private value for kernel usage normally 1779 "FUSE_SUPER_MAGIC" => true, 1780 // linux 5.12 min 1781 "MPOL_F_NUMA_BALANCING" => true, 1782 1783 // GRND_INSECURE was added in platform-tools-30.0.0 1784 "GRND_INSECURE" => true, 1785 1786 _ => false, 1787 } 1788 }); 1789 1790 cfg.skip_fn(move |name| { 1791 // skip those that are manually verified 1792 match name { 1793 // FIXME: https://github.com/rust-lang/libc/issues/1272 1794 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 1795 1796 // There are two versions of the sterror_r function, see 1797 // 1798 // https://linux.die.net/man/3/strerror_r 1799 // 1800 // An XSI-compliant version provided if: 1801 // 1802 // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && ! _GNU_SOURCE 1803 // 1804 // and a GNU specific version provided if _GNU_SOURCE is defined. 1805 // 1806 // libc provides bindings for the XSI-compliant version, which is 1807 // preferred for portable applications. 1808 // 1809 // We skip the test here since here _GNU_SOURCE is defined, and 1810 // test the XSI version below. 1811 "strerror_r" => true, 1812 "reallocarray" => true, 1813 "__system_property_wait" => true, 1814 1815 // Added in API level 30, but tests use level 28. 1816 "mlock2" => true, 1817 1818 // Added in glibc 2.25. 1819 "getentropy" => true, 1820 1821 // Added in API level 28, but some tests use level 24. 1822 "getrandom" => true, 1823 1824 // Added in API level 28, but some tests use level 24. 1825 "syncfs" => true, 1826 1827 _ => false, 1828 } 1829 }); 1830 1831 cfg.skip_field_type(move |struct_, field| { 1832 // This is a weird union, don't check the type. 1833 (struct_ == "ifaddrs" && field == "ifa_ifu") || 1834 // sigval is actually a union, but we pretend it's a struct 1835 (struct_ == "sigevent" && field == "sigev_value") || 1836 // this one is an anonymous union 1837 (struct_ == "ff_effect" && field == "u") || 1838 // FIXME: `sa_sigaction` has type `sighandler_t` but that type is 1839 // incorrect, see: https://github.com/rust-lang/libc/issues/1359 1840 (struct_ == "sigaction" && field == "sa_sigaction") || 1841 // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet. 1842 (struct_ == "signalfd_siginfo" && field == "ssi_call_addr") 1843 }); 1844 1845 cfg.skip_field(move |struct_, field| { 1846 // this is actually a union on linux, so we can't represent it well and 1847 // just insert some padding. 1848 (struct_ == "siginfo_t" && field == "_pad") || 1849 // FIXME: `sa_sigaction` has type `sighandler_t` but that type is 1850 // incorrect, see: https://github.com/rust-lang/libc/issues/1359 1851 (struct_ == "sigaction" && field == "sa_sigaction") || 1852 // sigev_notify_thread_id is actually part of a sigev_un union 1853 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 1854 // signalfd had SIGSYS fields added in Android 4.19, but CI does not have that version yet. 1855 (struct_ == "signalfd_siginfo" && (field == "ssi_syscall" || 1856 field == "ssi_call_addr" || 1857 field == "ssi_arch")) 1858 }); 1859 1860 cfg.skip_field(|struct_, field| { 1861 match (struct_, field) { 1862 // conflicting with `p_type` macro from <resolve.h>. 1863 ("Elf32_Phdr", "p_type") => true, 1864 ("Elf64_Phdr", "p_type") => true, 1865 1866 // this is actually a union on linux, so we can't represent it well and 1867 // just insert some padding. 1868 ("siginfo_t", "_pad") => true, 1869 1870 _ => false, 1871 } 1872 }); 1873 1874 cfg.generate("../src/lib.rs", "main.rs"); 1875 1876 test_linux_like_apis(target); 1877 } 1878 1879 fn test_freebsd(target: &str) { 1880 assert!(target.contains("freebsd")); 1881 let mut cfg = ctest_cfg(); 1882 1883 let freebsd_ver = which_freebsd(); 1884 1885 match freebsd_ver { 1886 Some(12) => cfg.cfg("freebsd12", None), 1887 Some(13) => cfg.cfg("freebsd13", None), 1888 Some(14) => cfg.cfg("freebsd14", None), 1889 _ => &mut cfg, 1890 }; 1891 1892 // For sched linux compat fn 1893 cfg.define("_WITH_CPU_SET_T", None); 1894 // Required for `getline`: 1895 cfg.define("_WITH_GETLINE", None); 1896 // Required for making freebsd11_stat available in the headers 1897 cfg.define("_WANT_FREEBSD11_STAT", None); 1898 1899 let freebsd13 = match freebsd_ver { 1900 Some(n) if n >= 13 => true, 1901 _ => false, 1902 }; 1903 1904 headers! { cfg: 1905 "aio.h", 1906 "arpa/inet.h", 1907 "bsm/audit.h", 1908 "ctype.h", 1909 "dirent.h", 1910 "dlfcn.h", 1911 "elf.h", 1912 "errno.h", 1913 "execinfo.h", 1914 "fcntl.h", 1915 "getopt.h", 1916 "glob.h", 1917 "grp.h", 1918 "iconv.h", 1919 "ifaddrs.h", 1920 "langinfo.h", 1921 "libgen.h", 1922 "libutil.h", 1923 "limits.h", 1924 "link.h", 1925 "locale.h", 1926 "machine/elf.h", 1927 "machine/reg.h", 1928 "malloc_np.h", 1929 "memstat.h", 1930 "mqueue.h", 1931 "net/bpf.h", 1932 "net/if.h", 1933 "net/if_arp.h", 1934 "net/if_dl.h", 1935 "net/if_mib.h", 1936 "net/route.h", 1937 "netdb.h", 1938 "netinet/ip.h", 1939 "netinet/in.h", 1940 "netinet/sctp.h", 1941 "netinet/tcp.h", 1942 "netinet/udp.h", 1943 "poll.h", 1944 "pthread.h", 1945 "pthread_np.h", 1946 "pwd.h", 1947 "regex.h", 1948 "resolv.h", 1949 "sched.h", 1950 "semaphore.h", 1951 "signal.h", 1952 "spawn.h", 1953 "stddef.h", 1954 "stdint.h", 1955 "stdio.h", 1956 "stdlib.h", 1957 "string.h", 1958 "sys/capsicum.h", 1959 "sys/auxv.h", 1960 "sys/cpuset.h", 1961 "sys/domainset.h", 1962 "sys/eui64.h", 1963 "sys/event.h", 1964 [freebsd13]:"sys/eventfd.h", 1965 "sys/extattr.h", 1966 "sys/file.h", 1967 "sys/ioctl.h", 1968 "sys/ipc.h", 1969 "sys/jail.h", 1970 "sys/mman.h", 1971 "sys/mount.h", 1972 "sys/msg.h", 1973 "sys/procctl.h", 1974 "sys/procdesc.h", 1975 "sys/ptrace.h", 1976 "sys/queue.h", 1977 "sys/random.h", 1978 "sys/resource.h", 1979 "sys/rtprio.h", 1980 "sys/sem.h", 1981 "sys/shm.h", 1982 "sys/socket.h", 1983 "sys/stat.h", 1984 "sys/statvfs.h", 1985 "sys/sysctl.h", 1986 "sys/thr.h", 1987 "sys/time.h", 1988 "sys/times.h", 1989 "sys/timex.h", 1990 "sys/types.h", 1991 "sys/proc.h", 1992 "kvm.h", // must be after "sys/types.h" 1993 "sys/ucontext.h", 1994 "sys/uio.h", 1995 "sys/ktrace.h", 1996 "sys/umtx.h", 1997 "sys/un.h", 1998 "sys/user.h", 1999 "sys/utsname.h", 2000 "sys/uuid.h", 2001 "sys/vmmeter.h", 2002 "sys/wait.h", 2003 "libprocstat.h", 2004 "devstat.h", 2005 "syslog.h", 2006 "termios.h", 2007 "time.h", 2008 "ufs/ufs/quota.h", 2009 "unistd.h", 2010 "utime.h", 2011 "utmpx.h", 2012 "wchar.h", 2013 } 2014 2015 cfg.type_name(move |ty, is_struct, is_union| { 2016 match ty { 2017 // Just pass all these through, no need for a "struct" prefix 2018 "FILE" 2019 | "fd_set" 2020 | "Dl_info" 2021 | "DIR" 2022 | "Elf32_Phdr" 2023 | "Elf64_Phdr" 2024 | "Elf32_Auxinfo" 2025 | "Elf64_Auxinfo" 2026 | "devstat_select_mode" 2027 | "devstat_support_flags" 2028 | "devstat_type_flags" 2029 | "devstat_match_flags" 2030 | "devstat_priority" => ty.to_string(), 2031 2032 // FIXME: https://github.com/rust-lang/libc/issues/1273 2033 "sighandler_t" => "sig_t".to_string(), 2034 2035 t if is_union => format!("union {}", t), 2036 2037 t if t.ends_with("_t") => t.to_string(), 2038 2039 // sigval is a struct in Rust, but a union in C: 2040 "sigval" => format!("union sigval"), 2041 2042 // put `struct` in front of all structs:. 2043 t if is_struct => format!("struct {}", t), 2044 2045 t => t.to_string(), 2046 } 2047 }); 2048 2049 cfg.field_name(move |struct_, field| { 2050 match field { 2051 // Our stat *_nsec fields normally don't actually exist but are part 2052 // of a timeval struct 2053 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 2054 s.replace("e_nsec", ".tv_nsec") 2055 } 2056 // Field is named `type` in C but that is a Rust keyword, 2057 // so these fields are translated to `type_` in the bindings. 2058 "type_" if struct_ == "rtprio" => "type".to_string(), 2059 "type_" if struct_ == "sockstat" => "type".to_string(), 2060 "type_" if struct_ == "devstat_match_table" => "type".to_string(), 2061 s => s.to_string(), 2062 } 2063 }); 2064 2065 cfg.skip_const(move |name| { 2066 match name { 2067 // These constants were introduced in FreeBSD 13: 2068 "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" | "F_SEAL_GROW" 2069 | "F_SEAL_WRITE" 2070 if Some(13) > freebsd_ver => 2071 { 2072 true 2073 } 2074 2075 // These constants were introduced in FreeBSD 13: 2076 "EFD_CLOEXEC" | "EFD_NONBLOCK" | "EFD_SEMAPHORE" if Some(13) > freebsd_ver => true, 2077 2078 // These constants were introduced in FreeBSD 12: 2079 "AT_RESOLVE_BENEATH" | "O_RESOLVE_BENEATH" if Some(12) > freebsd_ver => true, 2080 2081 // These constants were introduced in FreeBSD 13: 2082 "O_DSYNC" | "O_PATH" | "O_EMPTY_PATH" | "AT_EMPTY_PATH" if Some(13) > freebsd_ver => { 2083 true 2084 } 2085 2086 // These aliases were introduced in FreeBSD 13: 2087 // (note however that the constants themselves work on any version) 2088 "CLOCK_BOOTTIME" | "CLOCK_REALTIME_COARSE" | "CLOCK_MONOTONIC_COARSE" 2089 if Some(13) > freebsd_ver => 2090 { 2091 true 2092 } 2093 2094 // FIXME: These are deprecated - remove in a couple of releases. 2095 // These constants were removed in FreeBSD 11 (svn r273250) but will 2096 // still be accepted and ignored at runtime. 2097 "MAP_RENAME" | "MAP_NORESERVE" => true, 2098 2099 // FIXME: These are deprecated - remove in a couple of releases. 2100 // These constants were removed in FreeBSD 11 (svn r262489), 2101 // and they've never had any legitimate use outside of the 2102 // base system anyway. 2103 "CTL_MAXID" | "KERN_MAXID" | "HW_MAXID" | "USER_MAXID" => true, 2104 2105 // FIXME: This is deprecated - remove in a couple of releases. 2106 // This was removed in FreeBSD 14 (git 1b4701fe1e8) and never 2107 // should've been used anywhere anyway. 2108 "TDF_UNUSED23" => true, 2109 2110 // FIXME: These are deprecated - remove in a couple of releases. 2111 // These symbols are not stable across OS-versions. They were 2112 // changed for FreeBSD 14 in git revisions b62848b0c3f and 2113 // 2cf7870864e. 2114 "PRI_MAX_ITHD" | "PRI_MIN_REALTIME" | "PRI_MAX_REALTIME" | "PRI_MIN_KERN" 2115 | "PRI_MAX_KERN" | "PSWP" | "PVM" | "PINOD" | "PRIBIO" | "PVFS" | "PZERO" | "PSOCK" 2116 | "PWAIT" | "PLOCK" | "PPAUSE" | "PRI_MIN_TIMESHARE" | "PUSER" | "PI_AV" | "PI_NET" 2117 | "PI_DISK" | "PI_TTY" | "PI_DULL" | "PI_SOFT" => true, 2118 2119 // This symbol changed in FreeBSD 14 (git 051e7d78b03), but the new 2120 // version should be safe to use on older releases. 2121 "IFCAP_CANTCHANGE" => true, 2122 2123 // These were removed in FreeBSD 14 (git c6d31b8306e) 2124 "TDF_ASTPENDING" | "TDF_NEEDSUSPCHK" | "TDF_NEEDRESCHED" | "TDF_NEEDSIGCHK" 2125 | "TDF_ALRMPEND" | "TDF_PROFPEND" | "TDF_MACPEND" => true, 2126 2127 // This constant was removed in FreeBSD 13 (svn r363622), and never 2128 // had any legitimate use outside of the base system anyway. 2129 "CTL_P1003_1B_MAXID" => true, 2130 2131 // This was renamed in FreeBSD 12.2 and 13 (r352486). 2132 "CTL_UNSPEC" | "CTL_SYSCTL" => true, 2133 2134 // This was renamed in FreeBSD 12.2 and 13 (r350749). 2135 "IPPROTO_SEP" | "IPPROTO_DCCP" => true, 2136 2137 // This was changed to 96(0x60) in FreeBSD 13: 2138 // https://github.com/freebsd/freebsd/ 2139 // commit/06b00ceaa914a3907e4e27bad924f44612bae1d7 2140 "MINCORE_SUPER" if Some(13) <= freebsd_ver => true, 2141 2142 // Added in FreeBSD 13.0 (r356667) 2143 "GRND_INSECURE" if Some(13) > freebsd_ver => true, 2144 2145 // Added in FreeBSD 13.0 (r349609) 2146 "PROC_PROTMAX_CTL" 2147 | "PROC_PROTMAX_STATUS" 2148 | "PROC_PROTMAX_FORCE_ENABLE" 2149 | "PROC_PROTMAX_FORCE_DISABLE" 2150 | "PROC_PROTMAX_NOFORCE" 2151 | "PROC_PROTMAX_ACTIVE" 2152 | "PROC_NO_NEW_PRIVS_CTL" 2153 | "PROC_NO_NEW_PRIVS_STATUS" 2154 | "PROC_NO_NEW_PRIVS_ENABLE" 2155 | "PROC_NO_NEW_PRIVS_DISABLE" 2156 | "PROC_WXMAP_CTL" 2157 | "PROC_WXMAP_STATUS" 2158 | "PROC_WX_MAPPINGS_PERMIT" 2159 | "PROC_WX_MAPPINGS_DISALLOW_EXEC" 2160 | "PROC_WXORX_ENFORCE" 2161 if Some(13) > freebsd_ver => 2162 { 2163 true 2164 } 2165 2166 // Added in in FreeBSD 13.0 (r367776 and r367287) 2167 "SCM_CREDS2" | "LOCAL_CREDS_PERSISTENT" if Some(13) > freebsd_ver => true, 2168 2169 // Added in FreeBSD 14 2170 "SPACECTL_DEALLOC" if Some(14) > freebsd_ver => true, 2171 2172 // Added in FreeBSD 13. 2173 "KERN_PROC_SIGFASTBLK" 2174 | "USER_LOCALBASE" 2175 | "TDP_SIGFASTBLOCK" 2176 | "TDP_UIOHELD" 2177 | "TDP_SIGFASTPENDING" 2178 | "TDP2_COMPAT32RB" 2179 | "P2_PROTMAX_ENABLE" 2180 | "P2_PROTMAX_DISABLE" 2181 | "CTLFLAG_NEEDGIANT" 2182 | "CTL_SYSCTL_NEXTNOSKIP" 2183 if Some(13) > freebsd_ver => 2184 { 2185 true 2186 } 2187 2188 // Added in freebsd 14. 2189 "IFCAP_MEXTPG" if Some(14) > freebsd_ver => true, 2190 // Added in freebsd 13. 2191 "IFF_KNOWSEPOCH" | "IFCAP_TXTLS4" | "IFCAP_TXTLS6" | "IFCAP_VXLAN_HWCSUM" 2192 | "IFCAP_VXLAN_HWTSO" | "IFCAP_TXTLS_RTLMT" | "IFCAP_TXTLS" 2193 if Some(13) > freebsd_ver => 2194 { 2195 true 2196 } 2197 // Added in FreeBSD 13. 2198 "PS_FST_TYPE_EVENTFD" if Some(13) > freebsd_ver => true, 2199 2200 // Added in FreeBSD 14. 2201 "MNT_RECURSE" | "MNT_DEFERRED" if Some(14) > freebsd_ver => true, 2202 2203 // Added in FreeBSD 13. 2204 "MNT_EXTLS" | "MNT_EXTLSCERT" | "MNT_EXTLSCERTUSER" | "MNT_NOCOVER" 2205 | "MNT_EMPTYDIR" 2206 if Some(13) > freebsd_ver => 2207 { 2208 true 2209 } 2210 2211 // Added in FreeBSD 14. 2212 "PT_COREDUMP" | "PC_ALL" | "PC_COMPRESS" | "PT_GETREGSET" | "PT_SETREGSET" 2213 | "PT_SC_REMOTE" 2214 if Some(14) > freebsd_ver => 2215 { 2216 true 2217 } 2218 2219 // Added in FreeBSD 14. 2220 "F_KINFO" => true, // FIXME: depends how frequent freebsd 14 is updated on CI, this addition went this week only. 2221 "SHM_RENAME_NOREPLACE" 2222 | "SHM_RENAME_EXCHANGE" 2223 | "SHM_LARGEPAGE_ALLOC_DEFAULT" 2224 | "SHM_LARGEPAGE_ALLOC_NOWAIT" 2225 | "SHM_LARGEPAGE_ALLOC_HARD" 2226 | "MFD_CLOEXEC" 2227 | "MFD_ALLOW_SEALING" 2228 | "MFD_HUGETLB" 2229 | "MFD_HUGE_MASK" 2230 | "MFD_HUGE_64KB" 2231 | "MFD_HUGE_512KB" 2232 | "MFD_HUGE_1MB" 2233 | "MFD_HUGE_2MB" 2234 | "MFD_HUGE_8MB" 2235 | "MFD_HUGE_16MB" 2236 | "MFD_HUGE_32MB" 2237 | "MFD_HUGE_256MB" 2238 | "MFD_HUGE_512MB" 2239 | "MFD_HUGE_1GB" 2240 | "MFD_HUGE_2GB" 2241 | "MFD_HUGE_16GB" 2242 if Some(13) > freebsd_ver => 2243 { 2244 true 2245 } 2246 2247 // Flags introduced in FreeBSD 14. 2248 "TCP_MAXUNACKTIME" 2249 | "TCP_MAXPEAKRATE" 2250 | "TCP_IDLE_REDUCE" 2251 | "TCP_REMOTE_UDP_ENCAPS_PORT" 2252 | "TCP_DELACK" 2253 | "TCP_FIN_IS_RST" 2254 | "TCP_LOG_LIMIT" 2255 | "TCP_SHARED_CWND_ALLOWED" 2256 | "TCP_PROC_ACCOUNTING" 2257 | "TCP_USE_CMP_ACKS" 2258 | "TCP_PERF_INFO" 2259 | "TCP_LRD" 2260 if Some(14) > freebsd_ver => 2261 { 2262 true 2263 } 2264 2265 // Added in FreeBSD 14 2266 "LIO_READV" | "LIO_WRITEV" | "LIO_VECTORED" if Some(14) > freebsd_ver => true, 2267 2268 // Added in FreeBSD 13 2269 "FIOSSHMLPGCNF" if Some(13) > freebsd_ver => true, 2270 2271 // Added in FreeBSD 14 2272 "IFCAP_NV" if Some(14) > freebsd_ver => true, 2273 2274 _ => false, 2275 } 2276 }); 2277 2278 cfg.skip_type(move |ty| { 2279 match ty { 2280 // the struct "__kvm" is quite tricky to bind so since we only use a pointer to it 2281 // for now, it doesn't matter too much... 2282 "kvm_t" => true, 2283 2284 _ => false, 2285 } 2286 }); 2287 2288 cfg.skip_struct(move |ty| { 2289 if ty.starts_with("__c_anonymous_") { 2290 return true; 2291 } 2292 match ty { 2293 // `procstat` is a private struct 2294 "procstat" => true, 2295 2296 // `spacectl_range` was introduced in FreeBSD 14 2297 "spacectl_range" if Some(14) > freebsd_ver => true, 2298 2299 // `ptrace_coredump` introduced in FreeBSD 14. 2300 "ptrace_coredump" if Some(14) > freebsd_ver => true, 2301 // `ptrace_sc_remote` introduced in FreeBSD 14. 2302 "ptrace_sc_remote" if Some(14) > freebsd_ver => true, 2303 2304 // `sockcred2` is not available in FreeBSD 12. 2305 "sockcred2" if Some(13) > freebsd_ver => true, 2306 // `shm_largepage_conf` was introduced in FreeBSD 13. 2307 "shm_largepage_conf" if Some(13) > freebsd_ver => true, 2308 2309 // Those are private types 2310 "memory_type" => true, 2311 "memory_type_list" => true, 2312 "pidfh" => true, 2313 "sctp_gen_error_cause" 2314 | "sctp_error_missing_param" 2315 | "sctp_remote_error" 2316 | "sctp_assoc_change" 2317 | "sctp_send_failed_event" 2318 | "sctp_stream_reset_event" => true, 2319 2320 _ => false, 2321 } 2322 }); 2323 2324 cfg.skip_fn(move |name| { 2325 // skip those that are manually verified 2326 match name { 2327 // FIXME: https://github.com/rust-lang/libc/issues/1272 2328 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 2329 2330 // The `uname` function in the `utsname.h` FreeBSD header is a C 2331 // inline function (has no symbol) that calls the `__xuname` symbol. 2332 // Therefore the function pointer comparison does not make sense for it. 2333 "uname" => true, 2334 2335 // FIXME: Our API is unsound. The Rust API allows aliasing 2336 // pointers, but the C API requires pointers not to alias. 2337 // We should probably be at least using `&`/`&mut` here, see: 2338 // https://github.com/gnzlbg/ctest/issues/68 2339 "lio_listio" => true, 2340 2341 // Those are introduced in FreeBSD 12. 2342 "clock_nanosleep" | "getrandom" | "elf_aux_info" | "setproctitle_fast" 2343 | "timingsafe_bcmp" | "timingsafe_memcmp" 2344 if Some(12) > freebsd_ver => 2345 { 2346 true 2347 } 2348 2349 // Those are introduced in FreeBSD 13. 2350 "memfd_create" 2351 | "shm_create_largepage" 2352 | "shm_rename" 2353 | "getentropy" 2354 | "eventfd" 2355 | "SOCKCRED2SIZE" 2356 | "getlocalbase" 2357 | "aio_readv" 2358 | "aio_writev" 2359 | "copy_file_range" 2360 if Some(13) > freebsd_ver => 2361 { 2362 true 2363 } 2364 2365 // Those are introduced in FreeBSD 14. 2366 "sched_getaffinity" | "sched_setaffinity" | "sched_getcpu" | "fspacectl" 2367 if Some(14) > freebsd_ver => 2368 { 2369 true 2370 } 2371 2372 _ => false, 2373 } 2374 }); 2375 2376 cfg.volatile_item(|i| { 2377 use ctest::VolatileItemKind::*; 2378 match i { 2379 // aio_buf is a volatile void** but since we cannot express that in 2380 // Rust types, we have to explicitly tell the checker about it here: 2381 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true, 2382 _ => false, 2383 } 2384 }); 2385 2386 cfg.skip_field(move |struct_, field| { 2387 match (struct_, field) { 2388 // FIXME: `sa_sigaction` has type `sighandler_t` but that type is 2389 // incorrect, see: https://github.com/rust-lang/libc/issues/1359 2390 ("sigaction", "sa_sigaction") => true, 2391 2392 // conflicting with `p_type` macro from <resolve.h>. 2393 ("Elf32_Phdr", "p_type") => true, 2394 ("Elf64_Phdr", "p_type") => true, 2395 2396 // not available until FreeBSD 12, and is an anonymous union there. 2397 ("xucred", "cr_pid__c_anonymous_union") => true, 2398 2399 // m_owner field is a volatile __lwpid_t 2400 ("umutex", "m_owner") => true, 2401 // c_has_waiters field is a volatile int32_t 2402 ("ucond", "c_has_waiters") => true, 2403 // is PATH_MAX long but tests can't accept multi array as equivalent. 2404 ("kinfo_vmentry", "kve_path") => true, 2405 2406 // a_un field is a union 2407 ("Elf32_Auxinfo", "a_un") => true, 2408 ("Elf64_Auxinfo", "a_un") => true, 2409 2410 // union fields 2411 ("if_data", "__ifi_epoch") => true, 2412 ("if_data", "__ifi_lastchange") => true, 2413 ("ifreq", "ifr_ifru") => true, 2414 ("ifconf", "ifc_ifcu") => true, 2415 2416 // anonymous struct 2417 ("devstat", "dev_links") => true, 2418 2419 // FIXME: structs too complicated to bind for now... 2420 ("kinfo_proc", "ki_paddr") => true, 2421 ("kinfo_proc", "ki_addr") => true, 2422 ("kinfo_proc", "ki_tracep") => true, 2423 ("kinfo_proc", "ki_textvp") => true, 2424 ("kinfo_proc", "ki_fd") => true, 2425 ("kinfo_proc", "ki_vmspace") => true, 2426 ("kinfo_proc", "ki_pcb") => true, 2427 ("kinfo_proc", "ki_tdaddr") => true, 2428 ("kinfo_proc", "ki_pd") => true, 2429 2430 // Anonymous type. 2431 ("filestat", "next") => true, 2432 2433 // We ignore this field because we needed to use a hack in order to make rust 1.19 2434 // happy... 2435 ("kinfo_proc", "ki_sparestrings") => true, 2436 2437 // `__sem_base` is a private struct field 2438 ("semid_ds", "__sem_base") => true, 2439 2440 // `snap_time` is a `long double`, but it's a nightmare to bind correctly in rust 2441 // for the moment, so it's a best effort thing... 2442 ("statinfo", "snap_time") => true, 2443 ("sctp_sndrcvinfo", "__reserve_pad") => true, 2444 ("sctp_extrcvinfo", "__reserve_pad") => true, 2445 // `tcp_snd_wscale` and `tcp_rcv_wscale` are bitfields 2446 ("tcp_info", "tcp_snd_wscale") => true, 2447 ("tcp_info", "tcp_rcv_wscale") => true, 2448 2449 _ => false, 2450 } 2451 }); 2452 2453 cfg.generate("../src/lib.rs", "main.rs"); 2454 } 2455 2456 fn test_emscripten(target: &str) { 2457 assert!(target.contains("emscripten")); 2458 2459 let mut cfg = ctest_cfg(); 2460 cfg.define("_GNU_SOURCE", None); // FIXME: ?? 2461 2462 headers! { cfg: 2463 "aio.h", 2464 "ctype.h", 2465 "dirent.h", 2466 "dlfcn.h", 2467 "errno.h", 2468 "fcntl.h", 2469 "glob.h", 2470 "grp.h", 2471 "ifaddrs.h", 2472 "langinfo.h", 2473 "limits.h", 2474 "locale.h", 2475 "malloc.h", 2476 "mntent.h", 2477 "mqueue.h", 2478 "net/ethernet.h", 2479 "net/if.h", 2480 "net/if_arp.h", 2481 "net/route.h", 2482 "netdb.h", 2483 "netinet/in.h", 2484 "netinet/ip.h", 2485 "netinet/tcp.h", 2486 "netinet/udp.h", 2487 "netpacket/packet.h", 2488 "poll.h", 2489 "pthread.h", 2490 "pty.h", 2491 "pwd.h", 2492 "resolv.h", 2493 "sched.h", 2494 "sched.h", 2495 "semaphore.h", 2496 "shadow.h", 2497 "signal.h", 2498 "stddef.h", 2499 "stdint.h", 2500 "stdio.h", 2501 "stdlib.h", 2502 "string.h", 2503 "sys/epoll.h", 2504 "sys/eventfd.h", 2505 "sys/file.h", 2506 "sys/ioctl.h", 2507 "sys/ipc.h", 2508 "sys/mman.h", 2509 "sys/mount.h", 2510 "sys/msg.h", 2511 "sys/personality.h", 2512 "sys/prctl.h", 2513 "sys/ptrace.h", 2514 "sys/quota.h", 2515 "sys/reboot.h", 2516 "sys/resource.h", 2517 "sys/sem.h", 2518 "sys/shm.h", 2519 "sys/signalfd.h", 2520 "sys/socket.h", 2521 "sys/stat.h", 2522 "sys/statvfs.h", 2523 "sys/swap.h", 2524 "sys/syscall.h", 2525 "sys/sysctl.h", 2526 "sys/sysinfo.h", 2527 "sys/time.h", 2528 "sys/timerfd.h", 2529 "sys/times.h", 2530 "sys/types.h", 2531 "sys/uio.h", 2532 "sys/un.h", 2533 "sys/user.h", 2534 "sys/utsname.h", 2535 "sys/vfs.h", 2536 "sys/wait.h", 2537 "sys/xattr.h", 2538 "syslog.h", 2539 "termios.h", 2540 "time.h", 2541 "ucontext.h", 2542 "unistd.h", 2543 "utime.h", 2544 "utmp.h", 2545 "utmpx.h", 2546 "wchar.h", 2547 } 2548 2549 cfg.type_name(move |ty, is_struct, is_union| { 2550 match ty { 2551 // Just pass all these through, no need for a "struct" prefix 2552 "FILE" | "fd_set" | "Dl_info" | "DIR" => ty.to_string(), 2553 2554 "os_unfair_lock" => "struct os_unfair_lock_s".to_string(), 2555 2556 t if is_union => format!("union {}", t), 2557 2558 t if t.ends_with("_t") => t.to_string(), 2559 2560 // put `struct` in front of all structs:. 2561 t if is_struct => format!("struct {}", t), 2562 2563 t => t.to_string(), 2564 } 2565 }); 2566 2567 cfg.field_name(move |struct_, field| { 2568 match field { 2569 // Our stat *_nsec fields normally don't actually exist but are part 2570 // of a timeval struct 2571 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 2572 s.replace("e_nsec", ".tv_nsec") 2573 } 2574 // FIXME: appears that `epoll_event.data` is an union 2575 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 2576 s => s.to_string(), 2577 } 2578 }); 2579 2580 cfg.skip_type(move |ty| { 2581 match ty { 2582 // sighandler_t is crazy across platforms 2583 // FIXME: is this necessary? 2584 "sighandler_t" => true, 2585 2586 // FIXME: The size has been changed due to musl's time64 2587 "time_t" => true, 2588 2589 _ => false, 2590 } 2591 }); 2592 2593 cfg.skip_struct(move |ty| { 2594 match ty { 2595 // This is actually a union, not a struct 2596 // FIXME: is this necessary? 2597 "sigval" => true, 2598 2599 // FIXME: It was removed in 2600 // emscripten-core/emscripten@953e414 2601 "pthread_mutexattr_t" => true, 2602 2603 // FIXME: Investigate why the test fails. 2604 // Skip for now to unblock CI. 2605 "pthread_condattr_t" => true, 2606 2607 // FIXME: The size has been changed when upgraded to musl 1.2.2 2608 "pthread_mutex_t" => true, 2609 2610 // FIXME: Lowered from 16 to 8 bytes in 2611 // llvm/llvm-project@d1a96e9 2612 "max_align_t" => true, 2613 2614 // FIXME: The size has been changed due to time64 2615 "utimbuf" | "timeval" | "timespec" | "rusage" | "itimerval" | "sched_param" 2616 | "stat" | "stat64" | "shmid_ds" | "msqid_ds" => true, 2617 2618 _ => false, 2619 } 2620 }); 2621 2622 cfg.skip_fn(move |name| { 2623 match name { 2624 // Emscripten does not support fork/exec/wait or any kind of multi-process support 2625 // https://github.com/emscripten-core/emscripten/blob/3.1.30/tools/system_libs.py#L973 2626 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" | "wait4" => true, 2627 2628 // FIXME: Remove after emscripten-core/emscripten#18492 is released (> 3.1.30). 2629 "clearenv" => true, 2630 2631 _ => false, 2632 } 2633 }); 2634 2635 cfg.skip_const(move |name| { 2636 match name { 2637 // FIXME: deprecated - SIGNUNUSED was removed in glibc 2.26 2638 // users should use SIGSYS instead 2639 "SIGUNUSED" => true, 2640 2641 // FIXME: emscripten uses different constants to constructs these 2642 n if n.contains("__SIZEOF_PTHREAD") => true, 2643 2644 // FIXME: `SYS_gettid` was removed in 2645 // emscripten-core/emscripten@6d6474e 2646 "SYS_gettid" => true, 2647 2648 // FIXME: These values have been changed 2649 | "POSIX_MADV_DONTNEED" // to 4 2650 | "RLIMIT_NLIMITS" // to 16 2651 | "RLIM_NLIMITS" // to 16 2652 | "IPPROTO_MAX" // to 263 2653 | "F_GETLK" // to 5 2654 | "F_SETLK" // to 6 2655 | "F_SETLKW" // to 7 2656 | "O_TMPFILE" // to 65 2657 | "SIG_IGN" // -1 2658 => true, 2659 2660 _ => false, 2661 } 2662 }); 2663 2664 cfg.skip_field_type(move |struct_, field| { 2665 // This is a weird union, don't check the type. 2666 // FIXME: is this necessary? 2667 (struct_ == "ifaddrs" && field == "ifa_ifu") || 2668 // sighandler_t type is super weird 2669 // FIXME: is this necessary? 2670 (struct_ == "sigaction" && field == "sa_sigaction") || 2671 // sigval is actually a union, but we pretend it's a struct 2672 // FIXME: is this necessary? 2673 (struct_ == "sigevent" && field == "sigev_value") || 2674 // aio_buf is "volatile void*" and Rust doesn't understand volatile 2675 // FIXME: is this necessary? 2676 (struct_ == "aiocb" && field == "aio_buf") 2677 }); 2678 2679 cfg.skip_field(move |struct_, field| { 2680 // this is actually a union on linux, so we can't represent it well and 2681 // just insert some padding. 2682 // FIXME: is this necessary? 2683 (struct_ == "siginfo_t" && field == "_pad") || 2684 // musl names this __dummy1 but it's still there 2685 // FIXME: is this necessary? 2686 (struct_ == "glob_t" && field == "gl_flags") || 2687 // musl seems to define this as an *anonymous* bitfield 2688 // FIXME: is this necessary? 2689 (struct_ == "statvfs" && field == "__f_unused") || 2690 // sigev_notify_thread_id is actually part of a sigev_un union 2691 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 2692 // signalfd had SIGSYS fields added in Linux 4.18, but no libc release has them yet. 2693 (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || 2694 field == "_pad2" || 2695 field == "ssi_syscall" || 2696 field == "ssi_call_addr" || 2697 field == "ssi_arch")) || 2698 // FIXME: After musl 1.1.24, it have only one field `sched_priority`, 2699 // while other fields become reserved. 2700 (struct_ == "sched_param" && [ 2701 "sched_ss_low_priority", 2702 "sched_ss_repl_period", 2703 "sched_ss_init_budget", 2704 "sched_ss_max_repl", 2705 ].contains(&field)) 2706 }); 2707 2708 // FIXME: test linux like 2709 cfg.generate("../src/lib.rs", "main.rs"); 2710 } 2711 2712 fn test_neutrino(target: &str) { 2713 assert!(target.contains("nto-qnx")); 2714 2715 let mut cfg = ctest_cfg(); 2716 2717 headers! { cfg: 2718 "ctype.h", 2719 "dirent.h", 2720 "dlfcn.h", 2721 "sys/elf.h", 2722 "fcntl.h", 2723 "glob.h", 2724 "grp.h", 2725 "iconv.h", 2726 "ifaddrs.h", 2727 "limits.h", 2728 "sys/link.h", 2729 "locale.h", 2730 "sys/malloc.h", 2731 "rcheck/malloc.h", 2732 "malloc.h", 2733 "mqueue.h", 2734 "net/if.h", 2735 "net/if_arp.h", 2736 "net/route.h", 2737 "netdb.h", 2738 "netinet/in.h", 2739 "netinet/ip.h", 2740 "netinet/tcp.h", 2741 "netinet/udp.h", 2742 "netinet/ip_var.h", 2743 "sys/poll.h", 2744 "pthread.h", 2745 "pwd.h", 2746 "regex.h", 2747 "resolv.h", 2748 "sys/sched.h", 2749 "sched.h", 2750 "semaphore.h", 2751 "shadow.h", 2752 "signal.h", 2753 "spawn.h", 2754 "stddef.h", 2755 "stdint.h", 2756 "stdio.h", 2757 "stdlib.h", 2758 "string.h", 2759 "sys/sysctl.h", 2760 "sys/file.h", 2761 "sys/inotify.h", 2762 "sys/ioctl.h", 2763 "sys/ipc.h", 2764 "sys/mman.h", 2765 "sys/mount.h", 2766 "sys/msg.h", 2767 "sys/resource.h", 2768 "sys/sem.h", 2769 "sys/socket.h", 2770 "sys/stat.h", 2771 "sys/statvfs.h", 2772 "sys/swap.h", 2773 "sys/termio.h", 2774 "sys/time.h", 2775 "sys/times.h", 2776 "sys/types.h", 2777 "sys/uio.h", 2778 "sys/un.h", 2779 "sys/utsname.h", 2780 "sys/wait.h", 2781 "syslog.h", 2782 "termios.h", 2783 "time.h", 2784 "sys/time.h", 2785 "ucontext.h", 2786 "unistd.h", 2787 "utime.h", 2788 "utmp.h", 2789 "wchar.h", 2790 "aio.h", 2791 "nl_types.h", 2792 "langinfo.h", 2793 "unix.h", 2794 "nbutil.h", 2795 "aio.h", 2796 "net/bpf.h", 2797 "net/if_dl.h", 2798 "sys/syspage.h", 2799 2800 // TODO: The following header file doesn't appear as part of the default headers 2801 // found in a standard installation of Neutrino 7.1 SDP. The structures/ 2802 // functions dependent on it are currently commented out. 2803 //"sys/asyncmsg.h", 2804 } 2805 2806 // Create and include a header file containing 2807 // items which are not included in any official 2808 // header file. 2809 let internal_header = "internal.h"; 2810 let out_dir = env::var("OUT_DIR").unwrap(); 2811 cfg.header(internal_header); 2812 cfg.include(&out_dir); 2813 std::fs::write( 2814 out_dir.to_owned() + "/" + internal_header, 2815 "#ifndef __internal_h__ 2816 #define __internal_h__ 2817 void __my_thread_exit(const void **); 2818 #endif", 2819 ) 2820 .unwrap(); 2821 2822 cfg.type_name(move |ty, is_struct, is_union| { 2823 match ty { 2824 // Just pass all these through, no need for a "struct" prefix 2825 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr" 2826 | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" 2827 | "Elf32_Chdr" | "Elf64_Chdr" | "aarch64_qreg_t" | "syspage_entry_info" 2828 | "syspage_array_info" => ty.to_string(), 2829 2830 "Ioctl" => "int".to_string(), 2831 2832 t if is_union => format!("union {}", t), 2833 2834 t if t.ends_with("_t") => t.to_string(), 2835 2836 // put `struct` in front of all structs:. 2837 t if is_struct => format!("struct {}", t), 2838 2839 t => t.to_string(), 2840 } 2841 }); 2842 2843 cfg.field_name(move |_struct_, field| match field { 2844 "type_" => "type".to_string(), 2845 2846 s => s.to_string(), 2847 }); 2848 2849 cfg.volatile_item(|i| { 2850 use ctest::VolatileItemKind::*; 2851 match i { 2852 // The following fields are volatie but since we cannot express that in 2853 // Rust types, we have to explicitly tell the checker about it here: 2854 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true, 2855 StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_tod_adjust" => true, 2856 StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec" => true, 2857 StructField(ref n, ref f) if n == "qtime_entry" && f == "nsec_stable" => true, 2858 StructField(ref n, ref f) if n == "intrspin" && f == "value" => true, 2859 _ => false, 2860 } 2861 }); 2862 2863 cfg.skip_type(move |ty| { 2864 match ty { 2865 // FIXME: `sighandler_t` type is incorrect, see: 2866 // https://github.com/rust-lang/libc/issues/1359 2867 "sighandler_t" => true, 2868 2869 // Does not exist in Neutrino 2870 "locale_t" => true, 2871 2872 _ => false, 2873 } 2874 }); 2875 2876 cfg.skip_struct(move |ty| { 2877 if ty.starts_with("__c_anonymous_") { 2878 return true; 2879 } 2880 match ty { 2881 "Elf64_Phdr" | "Elf32_Phdr" => true, 2882 2883 // FIXME: This is actually a union, not a struct 2884 "sigval" => true, 2885 2886 // union 2887 "_channel_connect_attr" => true, 2888 2889 _ => false, 2890 } 2891 }); 2892 2893 cfg.skip_const(move |name| { 2894 match name { 2895 // These signal "functions" are actually integer values that are casted to a fn ptr 2896 // This causes the compiler to err because of "illegal cast of int to ptr". 2897 "SIG_DFL" => true, 2898 "SIG_IGN" => true, 2899 "SIG_ERR" => true, 2900 2901 _ => false, 2902 } 2903 }); 2904 2905 cfg.skip_fn(move |name| { 2906 // skip those that are manually verified 2907 match name { 2908 // FIXME: https://github.com/rust-lang/libc/issues/1272 2909 "execv" | "execve" | "execvp" | "execvpe" => true, 2910 2911 // wrong signature 2912 "signal" => true, 2913 2914 // wrong signature of callback ptr 2915 "__cxa_atexit" => true, 2916 2917 // FIXME: Our API is unsound. The Rust API allows aliasing 2918 // pointers, but the C API requires pointers not to alias. 2919 // We should probably be at least using `&`/`&mut` here, see: 2920 // https://github.com/gnzlbg/ctest/issues/68 2921 "lio_listio" => true, 2922 2923 // 2 fields are actually unions which we're simply representing 2924 // as structures. 2925 "ChannelConnectAttr" => true, 2926 2927 // fields contains unions 2928 "SignalKillSigval" => true, 2929 "SignalKillSigval_r" => true, 2930 2931 // Not defined in any headers. Defined to work around a 2932 // stack unwinding bug. 2933 "__my_thread_exit" => true, 2934 2935 _ => false, 2936 } 2937 }); 2938 2939 cfg.skip_field_type(move |struct_, field| { 2940 // sigval is actually a union, but we pretend it's a struct 2941 struct_ == "sigevent" && field == "sigev_value" || 2942 // Anonymous structures 2943 struct_ == "_idle_hook" && field == "time" 2944 }); 2945 2946 cfg.skip_field(move |struct_, field| { 2947 (struct_ == "__sched_param" && field == "reserved") || 2948 (struct_ == "sched_param" && field == "reserved") || 2949 (struct_ == "sigevent" && field == "__sigev_un1") || // union 2950 (struct_ == "sigevent" && field == "__sigev_un2") || // union 2951 // sighandler_t type is super weird 2952 (struct_ == "sigaction" && field == "sa_sigaction") || 2953 // does not exist 2954 (struct_ == "syspage_entry" && field == "__reserved") || 2955 false // keep me for smaller diffs when something is added above 2956 }); 2957 2958 cfg.skip_static(move |name| (name == "__dso_handle")); 2959 2960 cfg.generate("../src/lib.rs", "main.rs"); 2961 } 2962 2963 fn test_vxworks(target: &str) { 2964 assert!(target.contains("vxworks")); 2965 2966 let mut cfg = ctest::TestGenerator::new(); 2967 headers! { cfg: 2968 "vxWorks.h", 2969 "yvals.h", 2970 "nfs/nfsCommon.h", 2971 "rtpLibCommon.h", 2972 "randomNumGen.h", 2973 "taskLib.h", 2974 "sysLib.h", 2975 "ioLib.h", 2976 "inetLib.h", 2977 "socket.h", 2978 "errnoLib.h", 2979 "ctype.h", 2980 "dirent.h", 2981 "dlfcn.h", 2982 "elf.h", 2983 "fcntl.h", 2984 "grp.h", 2985 "sys/poll.h", 2986 "ifaddrs.h", 2987 "langinfo.h", 2988 "limits.h", 2989 "link.h", 2990 "locale.h", 2991 "sys/stat.h", 2992 "netdb.h", 2993 "pthread.h", 2994 "pwd.h", 2995 "sched.h", 2996 "semaphore.h", 2997 "signal.h", 2998 "stddef.h", 2999 "stdint.h", 3000 "stdio.h", 3001 "stdlib.h", 3002 "string.h", 3003 "sys/file.h", 3004 "sys/ioctl.h", 3005 "sys/socket.h", 3006 "sys/time.h", 3007 "sys/times.h", 3008 "sys/types.h", 3009 "sys/uio.h", 3010 "sys/un.h", 3011 "sys/utsname.h", 3012 "sys/wait.h", 3013 "netinet/tcp.h", 3014 "syslog.h", 3015 "termios.h", 3016 "time.h", 3017 "ucontext.h", 3018 "unistd.h", 3019 "utime.h", 3020 "wchar.h", 3021 "errno.h", 3022 "sys/mman.h", 3023 "pathLib.h", 3024 "mqueue.h", 3025 } 3026 // FIXME 3027 cfg.skip_const(move |name| match name { 3028 // sighandler_t weirdness 3029 "SIG_DFL" | "SIG_ERR" | "SIG_IGN" 3030 // This is not defined in vxWorks 3031 | "RTLD_DEFAULT" => true, 3032 _ => false, 3033 }); 3034 // FIXME 3035 cfg.skip_type(move |ty| match ty { 3036 "stat64" | "sighandler_t" | "off64_t" => true, 3037 _ => false, 3038 }); 3039 3040 cfg.skip_field_type(move |struct_, field| match (struct_, field) { 3041 ("siginfo_t", "si_value") | ("stat", "st_size") | ("sigaction", "sa_u") => true, 3042 _ => false, 3043 }); 3044 3045 cfg.skip_roundtrip(move |s| match s { 3046 _ => false, 3047 }); 3048 3049 cfg.type_name(move |ty, is_struct, is_union| match ty { 3050 "DIR" | "FILE" | "Dl_info" | "RTP_DESC" => ty.to_string(), 3051 t if is_union => format!("union {}", t), 3052 t if t.ends_with("_t") => t.to_string(), 3053 t if is_struct => format!("struct {}", t), 3054 t => t.to_string(), 3055 }); 3056 3057 // FIXME 3058 cfg.skip_fn(move |name| match name { 3059 // sigval 3060 "sigqueue" | "_sigqueue" 3061 // sighandler_t 3062 | "signal" 3063 // not used in static linking by default 3064 | "dlerror" => true, 3065 _ => false, 3066 }); 3067 3068 cfg.generate("../src/lib.rs", "main.rs"); 3069 } 3070 3071 fn test_linux(target: &str) { 3072 assert!(target.contains("linux")); 3073 3074 // target_env 3075 let gnu = target.contains("gnu"); 3076 let musl = target.contains("musl"); 3077 let uclibc = target.contains("uclibc"); 3078 3079 match (gnu, musl, uclibc) { 3080 (true, false, false) => (), 3081 (false, true, false) => (), 3082 (false, false, true) => (), 3083 (_, _, _) => panic!( 3084 "linux target lib is gnu: {}, musl: {}, uclibc: {}", 3085 gnu, musl, uclibc 3086 ), 3087 } 3088 3089 let arm = target.contains("arm"); 3090 let i686 = target.contains("i686"); 3091 let mips = target.contains("mips"); 3092 let mips32 = mips && !target.contains("64"); 3093 let mips64 = mips && target.contains("64"); 3094 let ppc = target.contains("powerpc"); 3095 let ppc64 = target.contains("powerpc64"); 3096 let s390x = target.contains("s390x"); 3097 let sparc64 = target.contains("sparc64"); 3098 let x32 = target.contains("x32"); 3099 let x86_32 = target.contains("i686"); 3100 let x86_64 = target.contains("x86_64"); 3101 let aarch64_musl = target.contains("aarch64") && musl; 3102 let gnueabihf = target.contains("gnueabihf"); 3103 let x86_64_gnux32 = target.contains("gnux32") && x86_64; 3104 let riscv64 = target.contains("riscv64"); 3105 let uclibc = target.contains("uclibc"); 3106 3107 let mut cfg = ctest_cfg(); 3108 cfg.define("_GNU_SOURCE", None); 3109 // This macro re-deifnes fscanf,scanf,sscanf to link to the symbols that are 3110 // deprecated since glibc >= 2.29. This allows Rust binaries to link against 3111 // glibc versions older than 2.29. 3112 cfg.define("__GLIBC_USE_DEPRECATED_SCANF", None); 3113 3114 headers! { cfg: 3115 "ctype.h", 3116 "dirent.h", 3117 "dlfcn.h", 3118 "elf.h", 3119 "fcntl.h", 3120 "getopt.h", 3121 "glob.h", 3122 [gnu]: "gnu/libc-version.h", 3123 "grp.h", 3124 "iconv.h", 3125 "ifaddrs.h", 3126 "langinfo.h", 3127 "libgen.h", 3128 "limits.h", 3129 "link.h", 3130 "locale.h", 3131 "malloc.h", 3132 "mntent.h", 3133 "mqueue.h", 3134 "net/ethernet.h", 3135 "net/if.h", 3136 "net/if_arp.h", 3137 "net/route.h", 3138 "netdb.h", 3139 "netinet/in.h", 3140 "netinet/ip.h", 3141 "netinet/tcp.h", 3142 "netinet/udp.h", 3143 "netpacket/packet.h", 3144 "poll.h", 3145 "pthread.h", 3146 "pty.h", 3147 "pwd.h", 3148 "regex.h", 3149 "resolv.h", 3150 "sched.h", 3151 "semaphore.h", 3152 "shadow.h", 3153 "signal.h", 3154 "spawn.h", 3155 "stddef.h", 3156 "stdint.h", 3157 "stdio.h", 3158 "stdlib.h", 3159 "string.h", 3160 "sys/epoll.h", 3161 "sys/eventfd.h", 3162 "sys/file.h", 3163 "sys/fsuid.h", 3164 "sys/inotify.h", 3165 "sys/ioctl.h", 3166 "sys/ipc.h", 3167 "sys/mman.h", 3168 "sys/mount.h", 3169 "sys/msg.h", 3170 "sys/personality.h", 3171 "sys/prctl.h", 3172 "sys/ptrace.h", 3173 "sys/quota.h", 3174 "sys/random.h", 3175 "sys/reboot.h", 3176 "sys/resource.h", 3177 "sys/sem.h", 3178 "sys/sendfile.h", 3179 "sys/shm.h", 3180 "sys/signalfd.h", 3181 "sys/socket.h", 3182 "sys/stat.h", 3183 "sys/statvfs.h", 3184 "sys/swap.h", 3185 "sys/syscall.h", 3186 "sys/time.h", 3187 "sys/timerfd.h", 3188 "sys/times.h", 3189 "sys/timex.h", 3190 "sys/types.h", 3191 "sys/uio.h", 3192 "sys/un.h", 3193 "sys/user.h", 3194 "sys/utsname.h", 3195 "sys/vfs.h", 3196 "sys/wait.h", 3197 "syslog.h", 3198 "termios.h", 3199 "time.h", 3200 "ucontext.h", 3201 "unistd.h", 3202 "utime.h", 3203 "utmp.h", 3204 "utmpx.h", 3205 "wchar.h", 3206 "errno.h", 3207 // `sys/io.h` is only available on x86*, Alpha, IA64, and 32-bit 3208 // ARM: https://bugzilla.redhat.com/show_bug.cgi?id=1116162 3209 // Also unavailable on gnueabihf with glibc 2.30. 3210 // https://sourceware.org/git/?p=glibc.git;a=commitdiff;h=6b33f373c7b9199e00ba5fbafd94ac9bfb4337b1 3211 [(x86_64 || x86_32 || arm) && !gnueabihf]: "sys/io.h", 3212 // `sys/reg.h` is only available on x86 and x86_64 3213 [x86_64 || x86_32]: "sys/reg.h", 3214 // sysctl system call is deprecated and not available on musl 3215 // It is also unsupported in x32, deprecated since glibc 2.30: 3216 [!(x32 || musl || gnu)]: "sys/sysctl.h", 3217 // <execinfo.h> is not supported by musl: 3218 // https://www.openwall.com/lists/musl/2015/04/09/3 3219 // <execinfo.h> is not present on uclibc. 3220 [!(musl || uclibc)]: "execinfo.h", 3221 } 3222 3223 // Include linux headers at the end: 3224 headers! { 3225 cfg: 3226 "asm/mman.h", 3227 "linux/can.h", 3228 "linux/can/raw.h", 3229 // FIXME: requires kernel headers >= 5.4.1. 3230 [!musl]: "linux/can/j1939.h", 3231 "linux/dccp.h", 3232 "linux/errqueue.h", 3233 "linux/falloc.h", 3234 "linux/filter.h", 3235 "linux/fs.h", 3236 "linux/futex.h", 3237 "linux/genetlink.h", 3238 "linux/if.h", 3239 "linux/if_addr.h", 3240 "linux/if_alg.h", 3241 "linux/if_ether.h", 3242 "linux/if_tun.h", 3243 "linux/input.h", 3244 "linux/ipv6.h", 3245 "linux/keyctl.h", 3246 "linux/magic.h", 3247 "linux/memfd.h", 3248 "linux/mempolicy.h", 3249 "linux/mman.h", 3250 "linux/module.h", 3251 // FIXME: requires kernel headers >= 5.1. 3252 [!musl]: "linux/mount.h", 3253 "linux/net_tstamp.h", 3254 "linux/netfilter/nfnetlink.h", 3255 "linux/netfilter/nfnetlink_log.h", 3256 "linux/netfilter/nfnetlink_queue.h", 3257 "linux/netfilter/nf_tables.h", 3258 "linux/netfilter_ipv4.h", 3259 "linux/netfilter_ipv6.h", 3260 "linux/netfilter_ipv6/ip6_tables.h", 3261 "linux/netlink.h", 3262 // FIXME: requires Linux >= 5.6: 3263 [!musl && !sparc64]: "linux/openat2.h", 3264 [!musl]: "linux/ptrace.h", 3265 "linux/quota.h", 3266 "linux/random.h", 3267 "linux/reboot.h", 3268 "linux/rtnetlink.h", 3269 "linux/sched.h", 3270 "linux/sctp.h", 3271 "linux/seccomp.h", 3272 "linux/sock_diag.h", 3273 "linux/sockios.h", 3274 "linux/uinput.h", 3275 "linux/vm_sockets.h", 3276 "linux/wait.h", 3277 "sys/fanotify.h", 3278 // <sys/auxv.h> is not present on uclibc 3279 [!uclibc]: "sys/auxv.h", 3280 } 3281 3282 // note: aio.h must be included before sys/mount.h 3283 headers! { 3284 cfg: 3285 "sys/xattr.h", 3286 "sys/sysinfo.h", 3287 // AIO is not supported by uclibc: 3288 [!uclibc]: "aio.h", 3289 } 3290 3291 cfg.type_name(move |ty, is_struct, is_union| { 3292 match ty { 3293 // Just pass all these through, no need for a "struct" prefix 3294 "FILE" | "fd_set" | "Dl_info" | "DIR" | "Elf32_Phdr" | "Elf64_Phdr" | "Elf32_Shdr" 3295 | "Elf64_Shdr" | "Elf32_Sym" | "Elf64_Sym" | "Elf32_Ehdr" | "Elf64_Ehdr" 3296 | "Elf32_Chdr" | "Elf64_Chdr" => ty.to_string(), 3297 3298 "Ioctl" if gnu => "unsigned long".to_string(), 3299 "Ioctl" => "int".to_string(), 3300 3301 t if is_union => format!("union {}", t), 3302 3303 t if t.ends_with("_t") => t.to_string(), 3304 3305 // In MUSL `flock64` is a typedef to `flock`. 3306 "flock64" if musl => format!("struct {}", ty), 3307 3308 // put `struct` in front of all structs:. 3309 t if is_struct => format!("struct {}", t), 3310 3311 t => t.to_string(), 3312 } 3313 }); 3314 3315 cfg.field_name(move |struct_, field| { 3316 match field { 3317 // Our stat *_nsec fields normally don't actually exist but are part 3318 // of a timeval struct 3319 s if s.ends_with("_nsec") && struct_.starts_with("stat") => { 3320 s.replace("e_nsec", ".tv_nsec") 3321 } 3322 // FIXME: epoll_event.data is actually a union in C, but in Rust 3323 // it is only a u64 because we only expose one field 3324 // http://man7.org/linux/man-pages/man2/epoll_wait.2.html 3325 "u64" if struct_ == "epoll_event" => "data.u64".to_string(), 3326 // The following structs have a field called `type` in C, 3327 // but `type` is a Rust keyword, so these fields are translated 3328 // to `type_` in Rust. 3329 "type_" 3330 if struct_ == "input_event" 3331 || struct_ == "input_mask" 3332 || struct_ == "ff_effect" => 3333 { 3334 "type".to_string() 3335 } 3336 3337 s => s.to_string(), 3338 } 3339 }); 3340 3341 cfg.skip_type(move |ty| { 3342 match ty { 3343 // FIXME: `sighandler_t` type is incorrect, see: 3344 // https://github.com/rust-lang/libc/issues/1359 3345 "sighandler_t" => true, 3346 3347 // These cannot be tested when "resolv.h" is included and are tested 3348 // in the `linux_elf.rs` file. 3349 "Elf64_Phdr" | "Elf32_Phdr" => true, 3350 3351 // This type is private on Linux. It is implemented as a C `enum` 3352 // (`c_uint`) and this clashes with the type of the `rlimit` APIs 3353 // which expect a `c_int` even though both are ABI compatible. 3354 "__rlimit_resource_t" => true, 3355 // on Linux, this is a volatile int 3356 "pthread_spinlock_t" => true, 3357 3358 // For internal use only, to define architecture specific ioctl constants with a libc specific type. 3359 "Ioctl" => true, 3360 3361 // FIXME: requires >= 5.4.1 kernel headers 3362 "pgn_t" if musl => true, 3363 "priority_t" if musl => true, 3364 "name_t" if musl => true, 3365 3366 _ => false, 3367 } 3368 }); 3369 3370 cfg.skip_struct(move |ty| { 3371 if ty.starts_with("__c_anonymous_") { 3372 return true; 3373 } 3374 // FIXME: musl CI has old headers 3375 if (musl || sparc64) && ty.starts_with("uinput_") { 3376 return true; 3377 } 3378 // FIXME(https://github.com/rust-lang/libc/issues/1558): passing by 3379 // value corrupts the value for reasons not understood. 3380 if (gnu && sparc64) && ty == "ip_mreqn" { 3381 return true; 3382 } 3383 match ty { 3384 // These cannot be tested when "resolv.h" is included and are tested 3385 // in the `linux_elf.rs` file. 3386 "Elf64_Phdr" | "Elf32_Phdr" => true, 3387 3388 // On Linux, the type of `ut_tv` field of `struct utmpx` 3389 // can be an anonymous struct, so an extra struct, 3390 // which is absent in glibc, has to be defined. 3391 "__timeval" => true, 3392 3393 // FIXME: This is actually a union, not a struct 3394 "sigval" => true, 3395 3396 // This type is tested in the `linux_termios.rs` file since there 3397 // are header conflicts when including them with all the other 3398 // structs. 3399 "termios2" => true, 3400 3401 // FIXME: remove once we set minimum supported glibc version. 3402 // ucontext_t added a new field as of glibc 2.28; our struct definition is 3403 // conservative and omits the field, but that means the size doesn't match for newer 3404 // glibcs (see https://github.com/rust-lang/libc/issues/1410) 3405 "ucontext_t" if gnu => true, 3406 3407 // FIXME: Somehow we cannot include headers correctly in glibc 2.30. 3408 // So let's ignore for now and re-visit later. 3409 // Probably related: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91085 3410 "statx" => true, 3411 "statx_timestamp" => true, 3412 3413 // On Linux, the type of `ut_exit` field of struct `utmpx` 3414 // can be an anonymous struct, so an extra struct, 3415 // which is absent in musl, has to be defined. 3416 "__exit_status" if musl => true, 3417 3418 // clone_args might differ b/w libc versions 3419 "clone_args" => true, 3420 3421 // Might differ between kernel versions 3422 "open_how" => true, 3423 3424 // FIXME: requires >= 5.4.1 kernel headers 3425 "j1939_filter" if musl => true, 3426 3427 // FIXME: requires >= 5.4 kernel headers 3428 "sockaddr_can" if musl => true, 3429 3430 // FIXME: Unignore once we update Ubuntu to 22.04 3431 "mallinfo2" if sparc64 => true, 3432 "ptrace_rseq_configuration" if sparc64 => true, 3433 "sctp_initmsg" | "sctp_sndrcvinfo" | "sctp_sndinfo" | "sctp_rcvinfo" 3434 | "sctp_nxtinfo" | "sctp_prinfo" | "sctp_authinfo" => true, 3435 3436 _ => false, 3437 } 3438 }); 3439 3440 cfg.skip_const(move |name| { 3441 if !gnu { 3442 // Skip definitions from the kernel on non-glibc Linux targets. 3443 // They're libc-independent, so we only need to check them on one 3444 // libc. We don't want to break CI if musl or another libc doesn't 3445 // have the definitions yet. (We do still want to check them on 3446 // every glibc target, though, as some of them can vary by 3447 // architecture.) 3448 // 3449 // This is not an exhaustive list of kernel constants, just a list 3450 // of prefixes of all those that have appeared here or that get 3451 // updated regularly and seem likely to cause breakage. 3452 if name.starts_with("AF_") 3453 || name.starts_with("ARPHRD_") 3454 || name.starts_with("EPOLL") 3455 || name.starts_with("F_") 3456 || name.starts_with("FALLOC_FL_") 3457 || name.starts_with("IFLA_") 3458 || name.starts_with("MS_") 3459 || name.starts_with("MSG_") 3460 || name.starts_with("OPEN_TREE_") 3461 || name.starts_with("P_") 3462 || name.starts_with("PF_") 3463 || name.starts_with("RLIMIT_") 3464 || name.starts_with("SOL_") 3465 || name.starts_with("STATX_") 3466 || name.starts_with("SW_") 3467 || name.starts_with("SYS_") 3468 || name.starts_with("TCP_") 3469 || name.starts_with("UINPUT_") 3470 || name.starts_with("VMADDR_") 3471 { 3472 return true; 3473 } 3474 } 3475 if musl || sparc64 { 3476 // FIXME: Requires >= 5.4.1 kernel headers 3477 if name.starts_with("J1939") 3478 || name.starts_with("SO_J1939") 3479 || name.starts_with("SCM_J1939") 3480 { 3481 return true; 3482 } 3483 } 3484 match name { 3485 // These constants are not available if gnu headers have been included 3486 // and can therefore not be tested here 3487 // 3488 // The IPV6 constants are tested in the `linux_ipv6.rs` tests: 3489 | "IPV6_FLOWINFO" 3490 | "IPV6_FLOWLABEL_MGR" 3491 | "IPV6_FLOWINFO_SEND" 3492 | "IPV6_FLOWINFO_FLOWLABEL" 3493 | "IPV6_FLOWINFO_PRIORITY" 3494 // The F_ fnctl constants are tested in the `linux_fnctl.rs` tests: 3495 | "F_CANCELLK" 3496 | "F_ADD_SEALS" 3497 | "F_GET_SEALS" 3498 | "F_SEAL_SEAL" 3499 | "F_SEAL_SHRINK" 3500 | "F_SEAL_GROW" 3501 | "F_SEAL_WRITE" => true, 3502 // The `ARPHRD_CAN` is tested in the `linux_if_arp.rs` tests 3503 // because including `linux/if_arp.h` causes some conflicts: 3504 "ARPHRD_CAN" => true, 3505 3506 // FIXME: deprecated: not available in any header 3507 // See: https://github.com/rust-lang/libc/issues/1356 3508 "ENOATTR" => true, 3509 3510 // FIXME: SIGUNUSED was removed in glibc 2.26 3511 // Users should use SIGSYS instead. 3512 "SIGUNUSED" => true, 3513 3514 // FIXME: conflicts with glibc headers and is tested in 3515 // `linux_termios.rs` below: 3516 | "BOTHER" 3517 | "IBSHIFT" 3518 | "TCGETS2" 3519 | "TCSETS2" 3520 | "TCSETSW2" 3521 | "TCSETSF2" => true, 3522 3523 // FIXME: on musl the pthread types are defined a little differently 3524 // - these constants are used by the glibc implementation. 3525 n if musl && n.contains("__SIZEOF_PTHREAD") => true, 3526 3527 // FIXME: It was extended to 4096 since glibc 2.31 (Linux 5.4). 3528 // We should do so after a while. 3529 "SOMAXCONN" if gnu => true, 3530 3531 // deprecated: not available from Linux kernel 5.6: 3532 "VMADDR_CID_RESERVED" => true, 3533 3534 // IPPROTO_MAX was increased in 5.6 for IPPROTO_MPTCP: 3535 | "IPPROTO_MAX" 3536 | "IPPROTO_MPTCP" => true, 3537 3538 // FIXME: Not currently available in headers 3539 "P_PIDFD" if mips => true, 3540 "SYS_pidfd_open" if mips => true, 3541 3542 // FIXME: Not currently available in headers on MIPS 3543 // Not yet implemented on sparc64 3544 "SYS_clone3" if mips | sparc64 => true, 3545 3546 // FIXME: Not defined on ARM, gnueabihf, MIPS, musl, PowerPC, riscv64, s390x, and sparc64. 3547 "SYS_memfd_secret" if arm | gnueabihf | mips | musl | ppc | riscv64 | s390x | sparc64 => true, 3548 3549 // FIXME: Added in Linux 5.16 3550 // https://github.com/torvalds/linux/commit/039c0ec9bb77446d7ada7f55f90af9299b28ca49 3551 "SYS_futex_waitv" => true, 3552 3553 // FIXME: Added in Linux 5.17 3554 // https://github.com/torvalds/linux/commit/c6018b4b254971863bd0ad36bb5e7d0fa0f0ddb0 3555 "SYS_set_mempolicy_home_node" => true, 3556 3557 // FIXME: Added in Linux 5.18 3558 // https://github.com/torvalds/linux/commit/8b5413647262dda8d8d0e07e14ea1de9ac7cf0b2 3559 "NFQA_PRIORITY" => true, 3560 3561 // FIXME: requires more recent kernel headers on CI 3562 | "UINPUT_VERSION" 3563 | "SW_MAX" 3564 | "SW_CNT" 3565 if mips || ppc64 || riscv64 || sparc64 => true, 3566 3567 // FIXME: Not currently available in headers on ARM, MIPS and musl. 3568 "NETLINK_GET_STRICT_CHK" if arm || mips || musl => true, 3569 3570 // kernel constants not available in uclibc 1.0.34 3571 | "EXTPROC" 3572 | "FAN_MARK_FILESYSTEM" 3573 | "FAN_MARK_INODE" 3574 | "IPPROTO_BEETPH" 3575 | "IPPROTO_MPLS" 3576 | "IPV6_HDRINCL" 3577 | "IPV6_MULTICAST_ALL" 3578 | "IPV6_PMTUDISC_INTERFACE" 3579 | "IPV6_PMTUDISC_OMIT" 3580 | "IPV6_ROUTER_ALERT_ISOLATE" 3581 | "PACKET_MR_UNICAST" 3582 | "RUSAGE_THREAD" 3583 | "SHM_EXEC" 3584 | "UDP_GRO" 3585 | "UDP_SEGMENT" 3586 if uclibc => true, 3587 3588 // headers conflicts with linux/pidfd.h 3589 "PIDFD_NONBLOCK" => true, 3590 3591 // is a private value for kernel usage normally 3592 "FUSE_SUPER_MAGIC" => true, 3593 3594 // linux 5.17 min 3595 "PR_SET_VMA" | "PR_SET_VMA_ANON_NAME" => true, 3596 3597 // present in recent kernels only 3598 "PR_SCHED_CORE" | "PR_SCHED_CORE_CREATE" | "PR_SCHED_CORE_GET" | "PR_SCHED_CORE_MAX" | "PR_SCHED_CORE_SCOPE_PROCESS_GROUP" | "PR_SCHED_CORE_SCOPE_THREAD" | "PR_SCHED_CORE_SCOPE_THREAD_GROUP" | "PR_SCHED_CORE_SHARE_FROM" | "PR_SCHED_CORE_SHARE_TO" => true, 3599 3600 // present in recent kernels only >= 5.13 3601 "PR_PAC_SET_ENABLED_KEYS" | "PR_PAC_GET_ENABLED_KEYS" => true, 3602 // present in recent kernels only >= 5.19 3603 "PR_SME_SET_VL" | "PR_SME_GET_VL" | "PR_SME_VL_LEN_MAX" | "PR_SME_SET_VL_INHERIT" | "PR_SME_SET_VL_ONE_EXEC" => true, 3604 3605 // Added in Linux 5.14 3606 "FUTEX_LOCK_PI2" => true, 3607 3608 // Added in linux 6.1 3609 "STATX_DIOALIGN" => true, 3610 3611 // FIXME: Parts of netfilter/nfnetlink*.h require more recent kernel headers: 3612 | "RTNLGRP_MCTP_IFADDR" // linux v5.17+ 3613 | "RTNLGRP_TUNNEL" // linux v5.18+ 3614 | "RTNLGRP_STATS" // linux v5.18+ 3615 => true, 3616 3617 // FIXME: The below is no longer const in glibc 2.34: 3618 // https://github.com/bminor/glibc/commit/5d98a7dae955bafa6740c26eaba9c86060ae0344 3619 | "PTHREAD_STACK_MIN" 3620 | "SIGSTKSZ" 3621 | "MINSIGSTKSZ" 3622 if gnu => true, 3623 3624 // FIXME: Linux >= 5.16 changed its value: 3625 // https://github.com/torvalds/linux/commit/42df6e1d221dddc0f2acf2be37e68d553ad65f96 3626 "NF_NETDEV_NUMHOOKS" => true, 3627 3628 // FIXME: requires Linux >= 5.6: 3629 | "RESOLVE_BENEATH" 3630 | "RESOLVE_CACHED" 3631 | "RESOLVE_IN_ROOT" 3632 | "RESOLVE_NO_MAGICLINKS" 3633 | "RESOLVE_NO_SYMLINKS" 3634 | "RESOLVE_NO_XDEV" if musl || sparc64 => true, 3635 3636 // FIXME: requires Linux >= 5.4: 3637 | "CAN_J1939" 3638 | "CAN_NPROTO" if musl || sparc64 => true, 3639 3640 // FIXME: requires Linux >= 5.6 3641 "GRND_INSECURE" if musl || sparc64 => true, 3642 3643 // FIXME: requires Linux >= 5.7: 3644 "MREMAP_DONTUNMAP" if musl || sparc64 => true, 3645 3646 // FIXME: Requires more recent kernel headers (5.9 / 5.11): 3647 | "CLOSE_RANGE_UNSHARE" 3648 | "CLOSE_RANGE_CLOEXEC" if musl || sparc64 => true, 3649 3650 // FIXME: requires Linux >= 5.12: 3651 "MPOL_F_NUMA_BALANCING" if musl || sparc64 => true, 3652 3653 // FIXME: Requires more recent kernel headers 3654 | "NFNL_SUBSYS_COUNT" // bumped in v5.14 3655 | "NFNL_SUBSYS_HOOK" // v5.14+ 3656 | "NFULA_VLAN" // v5.4+ 3657 | "NFULA_L2HDR" // v5.4+ 3658 | "NFULA_VLAN_PROTO" // v5.4+ 3659 | "NFULA_VLAN_TCI" // v5.4+ 3660 | "NFULA_VLAN_UNSPEC" // v5.4+ 3661 | "RTNLGRP_NEXTHOP" // linux v5.3+ 3662 | "RTNLGRP_BRVLAN" // linux v5.6+ 3663 if musl || sparc64 => true, 3664 3665 // FIXME: Unignore once we update Ubuntu to 22.04 3666 | "VMADDR_CID_LOCAL" 3667 | "STATX_MNT_ID" 3668 | "SYS_close_range" 3669 | "SYS_openat2" 3670 | "SYS_pidfd_getfd" 3671 | "SYS_faccessat2" 3672 | "SYS_process_madvise" 3673 | "SYS_epoll_pwait2" 3674 | "SYS_mount_setattr" 3675 | "SYS_quotactl_fd" 3676 | "SYS_landlock_create_ruleset" 3677 | "SYS_landlock_add_rule" 3678 | "SYS_landlock_restrict_self" 3679 | "SYS_process_mrelease" 3680 | "IFLA_PROP_LIST" 3681 | "IFLA_ALT_IFNAME" 3682 | "IFLA_PERM_ADDRESS" 3683 | "IFLA_PROTO_DOWN_REASON" 3684 | "STATX_ATTR_MOUNT_ROOT" 3685 | "STATX_ATTR_VERITY" 3686 | "STATX_ATTR_DAX" 3687 if sparc64 => true, 3688 // Added in Linux 5.13 3689 "PTRACE_GET_RSEQ_CONFIGURATION" if sparc64 => true, 3690 3691 // FIXME: Requires more recent kernel headers 3692 | "IFLA_PARENT_DEV_NAME" // linux v5.13+ 3693 | "IFLA_PARENT_DEV_BUS_NAME" // linux v5.13+ 3694 | "IFLA_GRO_MAX_SIZE" // linux v5.16+ 3695 | "IFLA_TSO_MAX_SIZE" // linux v5.18+ 3696 | "IFLA_TSO_MAX_SEGS" // linux v5.18+ 3697 | "IFLA_ALLMULTI" // linux v6.0+ 3698 => true, 3699 "SCTP_FUTURE_ASSOC" | "SCTP_CURRENT_ASSOC" | "SCTP_ALL_ASSOC" | "SCTP_PEER_ADDR_THLDS_V2" => true, // linux 5.5+ 3700 3701 _ => false, 3702 } 3703 }); 3704 3705 cfg.skip_fn(move |name| { 3706 // skip those that are manually verified 3707 match name { 3708 // FIXME: https://github.com/rust-lang/libc/issues/1272 3709 "execv" | "execve" | "execvp" | "execvpe" | "fexecve" => true, 3710 3711 // There are two versions of the sterror_r function, see 3712 // 3713 // https://linux.die.net/man/3/strerror_r 3714 // 3715 // An XSI-compliant version provided if: 3716 // 3717 // (_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) 3718 // && ! _GNU_SOURCE 3719 // 3720 // and a GNU specific version provided if _GNU_SOURCE is defined. 3721 // 3722 // libc provides bindings for the XSI-compliant version, which is 3723 // preferred for portable applications. 3724 // 3725 // We skip the test here since here _GNU_SOURCE is defined, and 3726 // test the XSI version below. 3727 "strerror_r" => true, 3728 3729 // FIXME: Our API is unsound. The Rust API allows aliasing 3730 // pointers, but the C API requires pointers not to alias. 3731 // We should probably be at least using `&`/`&mut` here, see: 3732 // https://github.com/gnzlbg/ctest/issues/68 3733 "lio_listio" if musl => true, 3734 3735 // FIXME: the glibc version used by the Sparc64 build jobs 3736 // which use Debian 10.0 is too old. 3737 "statx" if sparc64 => true, 3738 // Needs glibc 2.34 or later. 3739 "posix_spawn_file_actions_addclosefrom_np" if gnu && sparc64 => true, 3740 // Needs glibc 2.35 or later. 3741 "posix_spawn_file_actions_addtcsetpgrp_np" if gnu && sparc64 => true, 3742 3743 // FIXME: Deprecated since glibc 2.30. Remove fn once upstream does. 3744 "sysctl" if gnu => true, 3745 3746 // FIXME: It now takes c_void instead of timezone since glibc 2.31. 3747 "gettimeofday" if gnu => true, 3748 3749 // These are all implemented as static inline functions in uclibc, so 3750 // they cannot be linked against. 3751 // If implementations are required, they might need to be implemented 3752 // in this crate. 3753 "posix_spawnattr_init" if uclibc => true, 3754 "posix_spawnattr_destroy" if uclibc => true, 3755 "posix_spawnattr_getsigdefault" if uclibc => true, 3756 "posix_spawnattr_setsigdefault" if uclibc => true, 3757 "posix_spawnattr_getsigmask" if uclibc => true, 3758 "posix_spawnattr_setsigmask" if uclibc => true, 3759 "posix_spawnattr_getflags" if uclibc => true, 3760 "posix_spawnattr_setflags" if uclibc => true, 3761 "posix_spawnattr_getpgroup" if uclibc => true, 3762 "posix_spawnattr_setpgroup" if uclibc => true, 3763 "posix_spawnattr_getschedpolicy" if uclibc => true, 3764 "posix_spawnattr_setschedpolicy" if uclibc => true, 3765 "posix_spawnattr_getschedparam" if uclibc => true, 3766 "posix_spawnattr_setschedparam" if uclibc => true, 3767 "posix_spawn_file_actions_init" if uclibc => true, 3768 "posix_spawn_file_actions_destroy" if uclibc => true, 3769 3770 // uclibc defines the flags type as a uint, but dependent crates 3771 // assume it's a int instead. 3772 "getnameinfo" if uclibc => true, 3773 3774 // FIXME: This needs musl 1.2.2 or later. 3775 "gettid" if musl => true, 3776 3777 // Needs glibc 2.33 or later. 3778 "mallinfo2" => true, 3779 3780 "reallocarray" if musl => true, 3781 3782 // Not defined in uclibc as of 1.0.34 3783 "gettid" if uclibc => true, 3784 3785 // Needs musl 1.2.3 or later. 3786 "pthread_getname_np" if musl => true, 3787 3788 // pthread_sigqueue uses sigval, which was initially declared 3789 // as a struct but should be defined as a union. However due 3790 // to the issues described here: https://github.com/rust-lang/libc/issues/2816 3791 // it can't be changed from struct. 3792 "pthread_sigqueue" => true, 3793 3794 // There are two versions of basename(3) on Linux with glibc, see 3795 // 3796 // https://man7.org/linux/man-pages/man3/basename.3.html 3797 // 3798 // If libgen.h is included, then the POSIX version will be available; 3799 // If _GNU_SOURCE is defined and string.h is included, then the GNU one 3800 // will be used. 3801 // 3802 // libc exposes both of them, providing a prefix to differentiate between 3803 // them. 3804 // 3805 // Because the name with prefix is not a valid symbol in C, we have to 3806 // skip the tests. 3807 "posix_basename" if gnu => true, 3808 "gnu_basename" if gnu => true, 3809 3810 _ => false, 3811 } 3812 }); 3813 3814 cfg.skip_field_type(move |struct_, field| { 3815 // This is a weird union, don't check the type. 3816 (struct_ == "ifaddrs" && field == "ifa_ifu") || 3817 // sighandler_t type is super weird 3818 (struct_ == "sigaction" && field == "sa_sigaction") || 3819 // __timeval type is a patch which doesn't exist in glibc 3820 (struct_ == "utmpx" && field == "ut_tv") || 3821 // sigval is actually a union, but we pretend it's a struct 3822 (struct_ == "sigevent" && field == "sigev_value") || 3823 // this one is an anonymous union 3824 (struct_ == "ff_effect" && field == "u") || 3825 // `__exit_status` type is a patch which is absent in musl 3826 (struct_ == "utmpx" && field == "ut_exit" && musl) || 3827 // `can_addr` is an anonymous union 3828 (struct_ == "sockaddr_can" && field == "can_addr") 3829 }); 3830 3831 cfg.volatile_item(|i| { 3832 use ctest::VolatileItemKind::*; 3833 match i { 3834 // aio_buf is a volatile void** but since we cannot express that in 3835 // Rust types, we have to explicitly tell the checker about it here: 3836 StructField(ref n, ref f) if n == "aiocb" && f == "aio_buf" => true, 3837 _ => false, 3838 } 3839 }); 3840 3841 cfg.skip_field(move |struct_, field| { 3842 // this is actually a union on linux, so we can't represent it well and 3843 // just insert some padding. 3844 (struct_ == "siginfo_t" && field == "_pad") || 3845 // musl names this __dummy1 but it's still there 3846 (musl && struct_ == "glob_t" && field == "gl_flags") || 3847 // musl seems to define this as an *anonymous* bitfield 3848 (musl && struct_ == "statvfs" && field == "__f_unused") || 3849 // sigev_notify_thread_id is actually part of a sigev_un union 3850 (struct_ == "sigevent" && field == "sigev_notify_thread_id") || 3851 // signalfd had SIGSYS fields added in Linux 4.18, but no libc release 3852 // has them yet. 3853 (struct_ == "signalfd_siginfo" && (field == "ssi_addr_lsb" || 3854 field == "_pad2" || 3855 field == "ssi_syscall" || 3856 field == "ssi_call_addr" || 3857 field == "ssi_arch")) || 3858 // FIXME: After musl 1.1.24, it have only one field `sched_priority`, 3859 // while other fields become reserved. 3860 (struct_ == "sched_param" && [ 3861 "sched_ss_low_priority", 3862 "sched_ss_repl_period", 3863 "sched_ss_init_budget", 3864 "sched_ss_max_repl", 3865 ].contains(&field) && musl) || 3866 // FIXME: After musl 1.1.24, the type becomes `int` instead of `unsigned short`. 3867 (struct_ == "ipc_perm" && field == "__seq" && aarch64_musl) || 3868 // glibc uses unnamed fields here and Rust doesn't support that yet 3869 (struct_ == "timex" && field.starts_with("__unused")) || 3870 // FIXME: It now takes mode_t since glibc 2.31 on some targets. 3871 (struct_ == "ipc_perm" && field == "mode" 3872 && ((x86_64 || i686 || arm || riscv64) && gnu || x86_64_gnux32) 3873 ) || 3874 // the `u` field is in fact an anonymous union 3875 (gnu && struct_ == "ptrace_syscall_info" && (field == "u" || field == "pad")) || 3876 // the vregs field is a `__uint128_t` C's type. 3877 (struct_ == "user_fpsimd_struct" && field == "vregs") || 3878 // Linux >= 5.11 tweaked the `svm_zero` field of the `sockaddr_vm` struct. 3879 // https://github.com/torvalds/linux/commit/dc8eeef73b63ed8988224ba6b5ed19a615163a7f 3880 (struct_ == "sockaddr_vm" && field == "svm_zero") || 3881 // the `ifr_ifru` field is an anonymous union 3882 (struct_ == "ifreq" && field == "ifr_ifru") || 3883 // glibc uses a single array `uregs` instead of individual fields. 3884 (struct_ == "user_regs" && arm) 3885 }); 3886 3887 cfg.skip_roundtrip(move |s| match s { 3888 // FIXME: 3889 "utsname" if mips32 || mips64 => true, 3890 // FIXME: 3891 "mcontext_t" if s390x => true, 3892 // FIXME: This is actually a union. 3893 "fpreg_t" if s390x => true, 3894 3895 "sockaddr_un" | "sembuf" | "ff_constant_effect" if mips32 && (gnu || musl) => true, 3896 "ipv6_mreq" 3897 | "ip_mreq_source" 3898 | "sockaddr_in6" 3899 | "sockaddr_ll" 3900 | "in_pktinfo" 3901 | "arpreq" 3902 | "arpreq_old" 3903 | "sockaddr_un" 3904 | "ff_constant_effect" 3905 | "ff_ramp_effect" 3906 | "ff_condition_effect" 3907 | "Elf32_Ehdr" 3908 | "Elf32_Chdr" 3909 | "ucred" 3910 | "in6_pktinfo" 3911 | "sockaddr_nl" 3912 | "termios" 3913 | "nlmsgerr" 3914 if (mips64 || sparc64) && gnu => 3915 { 3916 true 3917 } 3918 3919 // FIXME: the call ABI of max_align_t is incorrect on these platforms: 3920 "max_align_t" if i686 || mips64 || ppc64 => true, 3921 3922 _ => false, 3923 }); 3924 3925 cfg.generate("../src/lib.rs", "main.rs"); 3926 3927 test_linux_like_apis(target); 3928 } 3929 3930 // This function tests APIs that are incompatible to test when other APIs 3931 // are included (e.g. because including both sets of headers clashes) 3932 fn test_linux_like_apis(target: &str) { 3933 let gnu = target.contains("gnu"); 3934 let musl = target.contains("musl"); 3935 let linux = target.contains("linux"); 3936 let emscripten = target.contains("emscripten"); 3937 let android = target.contains("android"); 3938 assert!(linux || android || emscripten); 3939 3940 if linux || android || emscripten { 3941 // test strerror_r from the `string.h` header 3942 let mut cfg = ctest_cfg(); 3943 cfg.skip_type(|_| true).skip_static(|_| true); 3944 3945 headers! { cfg: "string.h" } 3946 cfg.skip_fn(|f| match f { 3947 "strerror_r" => false, 3948 _ => true, 3949 }) 3950 .skip_const(|_| true) 3951 .skip_struct(|_| true); 3952 cfg.generate("../src/lib.rs", "linux_strerror_r.rs"); 3953 } 3954 3955 if linux || android || emscripten { 3956 // test fcntl - see: 3957 // http://man7.org/linux/man-pages/man2/fcntl.2.html 3958 let mut cfg = ctest_cfg(); 3959 3960 if musl { 3961 cfg.header("fcntl.h"); 3962 } else { 3963 cfg.header("linux/fcntl.h"); 3964 } 3965 3966 cfg.skip_type(|_| true) 3967 .skip_static(|_| true) 3968 .skip_struct(|_| true) 3969 .skip_fn(|_| true) 3970 .skip_const(move |name| match name { 3971 // test fcntl constants: 3972 "F_CANCELLK" | "F_ADD_SEALS" | "F_GET_SEALS" | "F_SEAL_SEAL" | "F_SEAL_SHRINK" 3973 | "F_SEAL_GROW" | "F_SEAL_WRITE" => false, 3974 _ => true, 3975 }) 3976 .type_name(move |ty, is_struct, is_union| match ty { 3977 t if is_struct => format!("struct {}", t), 3978 t if is_union => format!("union {}", t), 3979 t => t.to_string(), 3980 }); 3981 3982 cfg.generate("../src/lib.rs", "linux_fcntl.rs"); 3983 } 3984 3985 if linux || android { 3986 // test termios 3987 let mut cfg = ctest_cfg(); 3988 cfg.header("asm/termbits.h"); 3989 cfg.header("linux/termios.h"); 3990 cfg.skip_type(|_| true) 3991 .skip_static(|_| true) 3992 .skip_fn(|_| true) 3993 .skip_const(|c| match c { 3994 "BOTHER" | "IBSHIFT" => false, 3995 "TCGETS2" | "TCSETS2" | "TCSETSW2" | "TCSETSF2" => false, 3996 _ => true, 3997 }) 3998 .skip_struct(|s| s != "termios2") 3999 .type_name(move |ty, is_struct, is_union| match ty { 4000 "Ioctl" if gnu => "unsigned long".to_string(), 4001 "Ioctl" => "int".to_string(), 4002 t if is_struct => format!("struct {}", t), 4003 t if is_union => format!("union {}", t), 4004 t => t.to_string(), 4005 }); 4006 cfg.generate("../src/lib.rs", "linux_termios.rs"); 4007 } 4008 4009 if linux || android { 4010 // test IPV6_ constants: 4011 let mut cfg = ctest_cfg(); 4012 headers! { 4013 cfg: 4014 "linux/in6.h" 4015 } 4016 cfg.skip_type(|_| true) 4017 .skip_static(|_| true) 4018 .skip_fn(|_| true) 4019 .skip_const(|_| true) 4020 .skip_struct(|_| true) 4021 .skip_const(move |name| match name { 4022 "IPV6_FLOWINFO" 4023 | "IPV6_FLOWLABEL_MGR" 4024 | "IPV6_FLOWINFO_SEND" 4025 | "IPV6_FLOWINFO_FLOWLABEL" 4026 | "IPV6_FLOWINFO_PRIORITY" => false, 4027 _ => true, 4028 }) 4029 .type_name(move |ty, is_struct, is_union| match ty { 4030 t if is_struct => format!("struct {}", t), 4031 t if is_union => format!("union {}", t), 4032 t => t.to_string(), 4033 }); 4034 cfg.generate("../src/lib.rs", "linux_ipv6.rs"); 4035 } 4036 4037 if linux || android { 4038 // Test Elf64_Phdr and Elf32_Phdr 4039 // These types have a field called `p_type`, but including 4040 // "resolve.h" defines a `p_type` macro that expands to `__p_type` 4041 // making the tests for these fails when both are included. 4042 let mut cfg = ctest_cfg(); 4043 cfg.header("elf.h"); 4044 cfg.skip_fn(|_| true) 4045 .skip_static(|_| true) 4046 .skip_const(|_| true) 4047 .type_name(move |ty, _is_struct, _is_union| ty.to_string()) 4048 .skip_struct(move |ty| match ty { 4049 "Elf64_Phdr" | "Elf32_Phdr" => false, 4050 _ => true, 4051 }) 4052 .skip_type(move |ty| match ty { 4053 "Elf64_Phdr" | "Elf32_Phdr" => false, 4054 _ => true, 4055 }); 4056 cfg.generate("../src/lib.rs", "linux_elf.rs"); 4057 } 4058 4059 if linux || android { 4060 // Test `ARPHRD_CAN`. 4061 let mut cfg = ctest_cfg(); 4062 cfg.header("linux/if_arp.h"); 4063 cfg.skip_fn(|_| true) 4064 .skip_static(|_| true) 4065 .skip_const(move |name| match name { 4066 "ARPHRD_CAN" => false, 4067 _ => true, 4068 }) 4069 .skip_struct(|_| true) 4070 .skip_type(|_| true); 4071 cfg.generate("../src/lib.rs", "linux_if_arp.rs"); 4072 } 4073 } 4074 4075 fn which_freebsd() -> Option<i32> { 4076 let output = std::process::Command::new("freebsd-version") 4077 .output() 4078 .ok()?; 4079 if !output.status.success() { 4080 return None; 4081 } 4082 4083 let stdout = String::from_utf8(output.stdout).ok()?; 4084 4085 match &stdout { 4086 s if s.starts_with("10") => Some(10), 4087 s if s.starts_with("11") => Some(11), 4088 s if s.starts_with("12") => Some(12), 4089 s if s.starts_with("13") => Some(13), 4090 s if s.starts_with("14") => Some(14), 4091 _ => None, 4092 } 4093 } 4094 4095 fn test_haiku(target: &str) { 4096 assert!(target.contains("haiku")); 4097 4098 let mut cfg = ctest_cfg(); 4099 cfg.flag("-Wno-deprecated-declarations"); 4100 cfg.define("__USE_GNU", Some("1")); 4101 cfg.define("_GNU_SOURCE", None); 4102 cfg.language(ctest::Lang::CXX); 4103 4104 // POSIX API 4105 headers! { cfg: 4106 "alloca.h", 4107 "arpa/inet.h", 4108 "arpa/nameser.h", 4109 "arpa/nameser_compat.h", 4110 "assert.h", 4111 "bsd_mem.h", 4112 "complex.h", 4113 "ctype.h", 4114 "dirent.h", 4115 "div_t.h", 4116 "dlfcn.h", 4117 "endian.h", 4118 "errno.h", 4119 "fcntl.h", 4120 "fenv.h", 4121 "fnmatch.h", 4122 "fts.h", 4123 "ftw.h", 4124 "getopt.h", 4125 "glob.h", 4126 "grp.h", 4127 "inttypes.h", 4128 "iovec.h", 4129 "langinfo.h", 4130 "libgen.h", 4131 "libio.h", 4132 "limits.h", 4133 "locale.h", 4134 "malloc.h", 4135 "malloc_debug.h", 4136 "math.h", 4137 "memory.h", 4138 "monetary.h", 4139 "net/if.h", 4140 "net/if_dl.h", 4141 "net/if_media.h", 4142 "net/if_tun.h", 4143 "net/if_types.h", 4144 "net/route.h", 4145 "netdb.h", 4146 "netinet/in.h", 4147 "netinet/ip.h", 4148 "netinet/ip6.h", 4149 "netinet/ip_icmp.h", 4150 "netinet/ip_var.h", 4151 "netinet/tcp.h", 4152 "netinet/udp.h", 4153 "netinet6/in6.h", 4154 "nl_types.h", 4155 "null.h", 4156 "poll.h", 4157 "pthread.h", 4158 "pwd.h", 4159 "regex.h", 4160 "resolv.h", 4161 "sched.h", 4162 "search.h", 4163 "semaphore.h", 4164 "setjmp.h", 4165 "shadow.h", 4166 "signal.h", 4167 "size_t.h", 4168 "spawn.h", 4169 "stdint.h", 4170 "stdio.h", 4171 "stdlib.h", 4172 "string.h", 4173 "strings.h", 4174 "sys/cdefs.h", 4175 "sys/file.h", 4176 "sys/ioctl.h", 4177 "sys/ipc.h", 4178 "sys/mman.h", 4179 "sys/msg.h", 4180 "sys/param.h", 4181 "sys/poll.h", 4182 "sys/resource.h", 4183 "sys/select.h", 4184 "sys/sem.h", 4185 "sys/socket.h", 4186 "sys/sockio.h", 4187 "sys/stat.h", 4188 "sys/statvfs.h", 4189 "sys/time.h", 4190 "sys/timeb.h", 4191 "sys/times.h", 4192 "sys/types.h", 4193 "sys/uio.h", 4194 "sys/un.h", 4195 "sys/utsname.h", 4196 "sys/wait.h", 4197 "syslog.h", 4198 "tar.h", 4199 "termios.h", 4200 "time.h", 4201 "uchar.h", 4202 "unistd.h", 4203 "utime.h", 4204 "utmpx.h", 4205 "wchar.h", 4206 "wchar_t.h", 4207 "wctype.h" 4208 } 4209 4210 // BSD Extensions 4211 headers! { cfg: 4212 "ifaddrs.h", 4213 "libutil.h", 4214 "link.h", 4215 "pty.h", 4216 } 4217 4218 // Native API 4219 headers! { cfg: 4220 "kernel/OS.h", 4221 "kernel/fs_attr.h", 4222 "kernel/fs_index.h", 4223 "kernel/fs_info.h", 4224 "kernel/fs_query.h", 4225 "kernel/fs_volume.h", 4226 "kernel/image.h", 4227 "kernel/scheduler.h", 4228 "storage/FindDirectory.h", 4229 "storage/StorageDefs.h", 4230 "support/Errors.h", 4231 "support/SupportDefs.h", 4232 "support/TypeConstants.h" 4233 } 4234 4235 cfg.skip_struct(move |ty| { 4236 if ty.starts_with("__c_anonymous_") { 4237 return true; 4238 } 4239 match ty { 4240 // FIXME: actually a union 4241 "sigval" => true, 4242 // FIXME: locale_t does not exist on Haiku 4243 "locale_t" => true, 4244 // FIXME: rusage has a different layout on Haiku 4245 "rusage" => true, 4246 // FIXME?: complains that rust aligns on 4 byte boundary, but 4247 // Haiku does not align it at all. 4248 "in6_addr" => true, 4249 // The d_name attribute is an array of 1 on Haiku, with the 4250 // intention that the developer allocates a larger or smaller 4251 // piece of memory depending on the expected/actual size of the name. 4252 // Other platforms have sensible defaults. In Rust, the d_name field 4253 // is sized as the _POSIX_MAX_PATH, so that path names will fit in 4254 // newly allocated dirent objects. This breaks the automated tests. 4255 "dirent" => true, 4256 // The following structs contain function pointers, which cannot be initialized 4257 // with mem::zeroed(), so skip the automated test 4258 "image_info" | "thread_info" => true, 4259 4260 "Elf64_Phdr" => true, 4261 4262 // is an union 4263 "cpuid_info" => true, 4264 4265 _ => false, 4266 } 4267 }); 4268 4269 cfg.skip_type(move |ty| { 4270 match ty { 4271 // FIXME: locale_t does not exist on Haiku 4272 "locale_t" => true, 4273 // These cause errors, to be reviewed in the future 4274 "sighandler_t" => true, 4275 "pthread_t" => true, 4276 "pthread_condattr_t" => true, 4277 "pthread_mutexattr_t" => true, 4278 "pthread_rwlockattr_t" => true, 4279 _ => false, 4280 } 4281 }); 4282 4283 cfg.skip_fn(move |name| { 4284 // skip those that are manually verified 4285 match name { 4286 // FIXME: https://github.com/rust-lang/libc/issues/1272 4287 "execv" | "execve" | "execvp" | "execvpe" => true, 4288 // FIXME: does not exist on haiku 4289 "open_wmemstream" => true, 4290 "mlockall" | "munlockall" => true, 4291 "tcgetsid" => true, 4292 "cfsetspeed" => true, 4293 // ignore for now, will be part of Haiku R1 beta 3 4294 "mlock" | "munlock" => true, 4295 // returns const char * on Haiku 4296 "strsignal" => true, 4297 // uses an enum as a parameter argument, which is incorrectly 4298 // translated into a struct argument 4299 "find_path" => true, 4300 4301 "get_cpuid" => true, 4302 4303 // uses varargs parameter 4304 "ioctl" => true, 4305 4306 _ => false, 4307 } 4308 }); 4309 4310 cfg.skip_const(move |name| { 4311 match name { 4312 // FIXME: these constants do not exist on Haiku 4313 "DT_UNKNOWN" | "DT_FIFO" | "DT_CHR" | "DT_DIR" | "DT_BLK" | "DT_REG" | "DT_LNK" 4314 | "DT_SOCK" => true, 4315 "USRQUOTA" | "GRPQUOTA" => true, 4316 "SIGIOT" => true, 4317 "ARPOP_REQUEST" | "ARPOP_REPLY" | "ATF_COM" | "ATF_PERM" | "ATF_PUBL" 4318 | "ATF_USETRAILERS" => true, 4319 // Haiku does not have MAP_FILE, but rustc requires it 4320 "MAP_FILE" => true, 4321 // The following does not exist on Haiku but is required by 4322 // several crates 4323 "FIOCLEX" => true, 4324 // just skip this one, it is not defined on Haiku beta 2 but 4325 // since it is meant as a mask and not a parameter it can exist 4326 // here 4327 "LOG_PRIMASK" => true, 4328 // not defined on Haiku, but [get|set]priority is, so they are 4329 // useful 4330 "PRIO_MIN" | "PRIO_MAX" => true, 4331 // 4332 _ => false, 4333 } 4334 }); 4335 4336 cfg.skip_field(move |struct_, field| { 4337 match (struct_, field) { 4338 // FIXME: the stat struct actually has timespec members, whereas 4339 // the current representation has these unpacked. 4340 ("stat", "st_atime") => true, 4341 ("stat", "st_atime_nsec") => true, 4342 ("stat", "st_mtime") => true, 4343 ("stat", "st_mtime_nsec") => true, 4344 ("stat", "st_ctime") => true, 4345 ("stat", "st_ctime_nsec") => true, 4346 ("stat", "st_crtime") => true, 4347 ("stat", "st_crtime_nsec") => true, 4348 4349 // these are actually unions, but we cannot represent it well 4350 ("siginfo_t", "sigval") => true, 4351 ("sem_t", "named_sem_id") => true, 4352 ("sigaction", "sa_sigaction") => true, 4353 ("sigevent", "sigev_value") => true, 4354 ("fpu_state", "_fpreg") => true, 4355 // these fields have a simplified data definition in libc 4356 ("fpu_state", "_xmm") => true, 4357 ("savefpu", "_fp_ymm") => true, 4358 4359 // skip these enum-type fields 4360 ("thread_info", "state") => true, 4361 ("image_info", "image_type") => true, 4362 _ => false, 4363 } 4364 }); 4365 4366 cfg.skip_roundtrip(move |s| match s { 4367 // FIXME: for some reason the roundtrip check fails for cpu_info 4368 "cpu_info" => true, 4369 _ => false, 4370 }); 4371 4372 cfg.type_name(move |ty, is_struct, is_union| { 4373 match ty { 4374 // Just pass all these through, no need for a "struct" prefix 4375 "area_info" | "port_info" | "port_message_info" | "team_info" | "sem_info" 4376 | "team_usage_info" | "thread_info" | "cpu_info" | "system_info" 4377 | "object_wait_info" | "image_info" | "attr_info" | "index_info" | "fs_info" 4378 | "FILE" | "DIR" | "Dl_info" => ty.to_string(), 4379 4380 // enums don't need a prefix 4381 "directory_which" | "path_base_directory" => ty.to_string(), 4382 4383 // is actually a union 4384 "sigval" => format!("union sigval"), 4385 t if is_union => format!("union {}", t), 4386 t if t.ends_with("_t") => t.to_string(), 4387 t if is_struct => format!("struct {}", t), 4388 t => t.to_string(), 4389 } 4390 }); 4391 4392 cfg.field_name(move |struct_, field| { 4393 match field { 4394 // Field is named `type` in C but that is a Rust keyword, 4395 // so these fields are translated to `type_` in the bindings. 4396 "type_" if struct_ == "object_wait_info" => "type".to_string(), 4397 "type_" if struct_ == "sem_t" => "type".to_string(), 4398 "type_" if struct_ == "attr_info" => "type".to_string(), 4399 "type_" if struct_ == "index_info" => "type".to_string(), 4400 "image_type" if struct_ == "image_info" => "type".to_string(), 4401 s => s.to_string(), 4402 } 4403 }); 4404 cfg.generate("../src/lib.rs", "main.rs"); 4405 } 4406