Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ctest/src/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub struct TestGenerator {
pub(crate) includes: Vec<PathBuf>,
out_dir: Option<PathBuf>,
pub(crate) flags: Vec<String>,
pub(crate) flags_if_supported: Vec<String>,
pub(crate) global_defines: Vec<(String, Option<String>)>,
cfg: Vec<(String, Option<String>)>,
mapped_names: Vec<MappedName>,
Expand Down Expand Up @@ -670,6 +671,15 @@ impl TestGenerator {
self
}

/// Add a flag to the C compiler invocation if the compiler supports it.
///
/// This is useful for warning suppressions that are only available on some
/// compiler families or versions.
pub fn flag_if_supported(&mut self, flag: &str) -> &mut Self {
self.flags_if_supported.push(flag.to_string());
self
}

/// Set a `-D` flag for the C compiler being called.
///
/// This can be used to define various global variables to configure how header
Expand Down
18 changes: 12 additions & 6 deletions ctest/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub fn generate_test(
let mut cfg = cc::Build::new();
cfg.file(output_file_path.with_extension(generator.language.extension()));
cfg.host(&host);
cfg.target(&target);

if target.contains("msvc") {
cfg.flag("/W3")
Expand All @@ -63,10 +64,12 @@ pub fn generate_test(
.flag("-Werror")
.flag("-Wno-unused-parameter")
.flag("-Wno-type-limits")
// allow taking address of packed struct members:
.flag("-Wno-address-of-packed-member")
.flag("-Wno-unknown-warning-option")
.flag("-Wno-deprecated-declarations"); // allow deprecated items
.flag("-Wno-deprecated-declarations") // allow deprecated items
// Probe support instead of assuming a compiler family. This keeps
// the suppression on newer GCC/Clang while avoiding regressions on
// older toolchains that reject these flags.
.flag_if_supported("-Wno-address-of-packed-member")
.flag_if_supported("-Wno-unknown-warning-option");
}

for p in &generator.includes {
Expand All @@ -77,15 +80,18 @@ pub fn generate_test(
cfg.flag(flag);
}

for flag in &generator.flags_if_supported {
cfg.flag_if_supported(flag);
}

for (k, v) in &generator.global_defines {
cfg.define(k, v.as_ref().map(|s| &s[..]));
}

cfg.cpp(matches!(generator.language, Language::CXX));

let stem: &str = output_file_path.file_stem().unwrap().to_str().unwrap();
cfg.target(&target)
.out_dir(output_file_path.parent().unwrap())
cfg.out_dir(output_file_path.parent().unwrap())
.compile(stem);

Ok(output_file_path)
Expand Down
174 changes: 164 additions & 10 deletions libc-test/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ fn do_cc() {
|| target.contains("android")
|| target.contains("emscripten")
|| target.contains("fuchsia")
|| target.contains("dragonfly")
|| target.contains("bsd")
|| target.contains("cygwin")
{
Expand Down Expand Up @@ -1504,7 +1505,17 @@ fn test_netbsd(target: &str) {
fn test_dragonflybsd(target: &str) {
assert!(target.contains("dragonfly"));
let mut cfg = ctest_cfg();
cfg.flag("-Wno-deprecated-declarations");
cfg.flag("-Wno-deprecated-declarations")
.flag_if_supported("-Wno-address-of-packed-member")
.flag_if_supported("-Wno-unknown-warning-option");

let dragonfly_version = if let Ok(version) = env::var("RUST_LIBC_UNSTABLE_DRAGONFLY_VERSION") {
let vers = parse_dragonfly_version(&version).unwrap();
println!("cargo:warning=setting DragonFly version to {vers}");
vers
} else {
which_dragonfly().unwrap_or(600_200)
};

headers!(
cfg,
Expand Down Expand Up @@ -1547,6 +1558,7 @@ fn test_dragonflybsd(target: &str) {
"sched.h",
"semaphore.h",
"signal.h",
"spawn.h",
"stddef.h",
"stdint.h",
"stdio.h",
Expand All @@ -1557,6 +1569,7 @@ fn test_dragonflybsd(target: &str) {
"sys/ioctl.h",
"sys/cpuctl.h",
"sys/eui64.h",
"sys/extattr.h",
"sys/ipc.h",
"sys/kinfo.h",
"sys/ktrace.h",
Expand All @@ -1566,6 +1579,7 @@ fn test_dragonflybsd(target: &str) {
"sys/procctl.h",
"sys/ptrace.h",
"sys/reboot.h",
"sys/random.h",
"sys/resource.h",
"sys/rtprio.h",
"sys/sched.h",
Expand All @@ -1591,8 +1605,8 @@ fn test_dragonflybsd(target: &str) {
"util.h",
"utime.h",
"utmpx.h",
"vm/vm.h",
"vfs/ufs/quota.h",
"vm/vm_map.h",
"wchar.h",
"iconv.h",
);
Expand All @@ -1613,6 +1627,12 @@ fn test_dragonflybsd(target: &str) {
match ty {
// FIXME(dragonflybsd): OSX calls this something else
"sighandler_t" => Some("sig_t".to_string()),
"lwpstat" => Some("enum lwpstat".to_string()),
"procstat" => Some("enum procstat".to_string()),
"vm_map_t" => Some("struct vm_map *".to_string()),
"vm_map_entry_t" => Some("struct vm_map_entry *".to_string()),
"vm_eflags_t" => Some("unsigned int".to_string()),
"vm_subsys_t" => Some("int".to_string()),
_ => None,
}
});
Expand Down Expand Up @@ -1663,11 +1683,59 @@ fn test_dragonflybsd(target: &str) {
_ => false,
}
});
cfg.alias_is_c_enum(move |e| matches!(e, "lwpstat" | "procstat"));

cfg.skip_const(move |constant| {
match constant.ident() {
"SIG_DFL" | "SIG_ERR" | "SIG_IGN" => true, // sighandler_t weirdness

// Kernel-only symbols in DragonFly headers.
"DTYPE_VNODE" | "DTYPE_SOCKET" | "DTYPE_PIPE" | "DTYPE_FIFO" | "DTYPE_KQUEUE"
| "DTYPE_CRYPTO" | "DTYPE_MQUEUE" | "DTYPE_DMABUF" => true,

// Not exposed by current DragonFly userland headers.
"REG_DUMP"
| "REG_ASSERT"
| "REG_ATOI"
| "REG_ITOA"
| "REG_TRACE"
| "REG_LARGE"
| "IP_ADD_SOURCE_MEMBERSHIP"
| "IP_DROP_SOURCE_MEMBERSHIP"
| "IP_BLOCK_SOURCE"
| "IP_UNBLOCK_SOURCE"
| "MAP_RENAME"
| "MAP_NORESERVE"
| "CTL_UNSPEC"
| "KERN_PROF"
| "CTL_P1003_1B_UNUSED1"
| "CTL_P1003_1B_SEM_VALUE_MAX"
| "DOWNTIME"
| "SF_CACHE" => true,

// libc exposes the 6.0-compatible value for this version-dependent mask.
"KERN_PROC_FLAGMASK" => true,

// Renamed in current DragonFly headers.
"CPUCTL_RSMSR" | "UTX_DB_LASTLOG" => true,

// Introduced after DragonFly 5.8.
"AF_ARP"
| "PF_ARP"
| "IP_SENDSRCADDR"
| "F_GETPATH"
| "ENOTRECOVERABLE"
| "EOWNERDEAD"
| "SO_PASSCRED"
| "PROC_PDEATHSIG_CTL"
| "PROC_PDEATHSIG_STATUS"
| "KERN_STATIC_TLS_EXTRA"
| "KERN_MAXID"
if dragonfly_version < 600_000 =>
{
true
}

// weird signed extension or something like that?
"MS_NOUSER" => true,
"MS_RMT_MASK" => true, // updated in glibc 2.22 and musl 1.1.13
Expand All @@ -1688,18 +1756,40 @@ fn test_dragonflybsd(target: &str) {
"prlimit" | "prlimit64" // non-int in 2nd arg
=> true,

// These are exposed unconditionally by libc, but older DragonFly
// headers cannot validate them.
"clock_nanosleep" | "fexecve" | "pthread_getname_np" | "pthread_setname_np"
if dragonfly_version < 600_000 => true,
"pthread_getaffinity_np" | "pthread_setaffinity_np" if dragonfly_version < 600_000 => {
true
},
"fdatasync" | "getentropy" | "posix_fallocate" if dragonfly_version < 600_200 => true,
"malloc_usable_size" if dragonfly_version < 600_400 => true,
_ => false,
}
});

cfg.skip_struct_field_type(move |struct_, field| {
match (struct_.ident(), field.ident()) {
// This is a weird union, don't check the type.
("ifaddrs", "ifa_ifu") => true,
// sighandler_t type is super weird
("sigaction", "sa_sigaction") => true,
// aio_buf is "volatile void*" and Rust doesn't understand volatile
("aiocb", "aio_buf") => true,
cfg.skip_alias(move |ty| {
match ty.ident() {
// sighandler_t is crazy across platforms
"sighandler_t" => true,
// Kernel-only or opaque types in userland.
"kvm_t" | "pmap" | "umtx_t" | "Elf32_Lword" => true,
_ => false,
}
});

cfg.skip_struct(move |struct_| {
match struct_.ident() {
// FIXME(dragonflybsd): These are tested as part of the linux_fcntl tests since
// there are header conflicts when including them with all the other
// structs.
"termios2" => true,

// Not available as userland-complete structs on DragonFly.
"ip_mreq_source" | "vm_map_entry" | "vmspace" => true,
"ip_mreqn" if dragonfly_version < 600_000 => true,

_ => false,
}
});
Expand All @@ -1711,13 +1801,77 @@ fn test_dragonflybsd(target: &str) {
("siginfo_t", "_pad") => true,
// sigev_notify_thread_id is actually part of a sigev_un union
("sigevent", "sigev_notify_thread_id") => true,
// Current DragonFly headers use these names instead.
("kinfo_cputime", "cp_idel") => true,
// conflicting with `p_type` macro from <resolve.h>.
("Elf32_Phdr", "p_type") | ("Elf64_Phdr", "p_type") => true,
("kinfo_lwp", "kl_stat") | ("kinfo_proc", "kp_stat") => true,
("utmpx", "ut_type") => true,
("mcontext_t", "mc_fpregs") => true,
_ => false,
}
});

cfg.skip_struct_field_type(move |struct_, field| {
match (struct_.ident(), field.ident()) {
// This is a weird union, don't check the type.
("ifaddrs", "ifa_ifu") => true,
// sighandler_t type is super weird
("sigaction", "sa_sigaction") => true,
// aio_buf is "volatile void*" and Rust doesn't understand volatile
("aiocb", "aio_buf") => true,
_ => false,
}
});

cfg.skip_roundtrip(move |ty| {
matches!(
ty,
"kvm_t"
| "posix_spawnattr_t"
| "posix_spawn_file_actions_t"
| "umtx_t"
| "pmap"
| "ip_mreq_source"
)
});

ctest::generate_test(&mut cfg, "../src/lib.rs", "ctest_output.rs").unwrap();
}

fn parse_dragonfly_version(version: &str) -> Option<u32> {
let version = version.trim();

if let Ok(version) = version.parse::<u32>() {
// DragonFly's __DragonFly_version uses major * 100_000 + minor * 100.
// Accept compact test override spellings like 58, 60, 62, and 602.
return Some(match version {
0..=9 => version * 100_000,
10..=99 => (version / 10) * 100_000 + (version % 10) * 100,
100..=999 => (version / 100) * 100_000 + (version % 100) * 100,
_ => version,
});
}

let mut pieces = version.split(['.', '-']);
let major = pieces.next()?.parse::<u32>().ok()?;
let minor = pieces.next()?.parse::<u32>().ok()?;
Some(major * 100_000 + minor * 100)
}

fn which_dragonfly() -> Option<u32> {
if env::var("CARGO_CFG_TARGET_OS").ok()?.as_str() != "dragonfly" {
return None;
}

if try_command_output("uname", &["-s"])?.trim() != "DragonFly" {
return None;
}

let stdout = try_command_output("uname", &["-r"])?;
parse_dragonfly_version(stdout.trim())
}

fn test_wasi(target: &str) {
assert!(target.contains("wasi"));
let p2 = target.contains("wasip2");
Expand Down
Loading
Loading