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
170 changes: 161 additions & 9 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 @@ -1506,6 +1507,14 @@ fn test_dragonflybsd(target: &str) {
let mut cfg = ctest_cfg();
cfg.flag("-Wno-deprecated-declarations");

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,
"aio.h",
Expand Down Expand Up @@ -1547,6 +1556,7 @@ fn test_dragonflybsd(target: &str) {
"sched.h",
"semaphore.h",
"signal.h",
"spawn.h",
"stddef.h",
"stdint.h",
"stdio.h",
Expand All @@ -1557,6 +1567,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 +1577,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 +1603,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 +1625,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 +1681,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,
Comment on lines +1690 to +1718

@tgross35 tgross35 Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since dragonfly is tier 3, you can make the relevant updates in the library (change or delete as needed) rather than adding test skips.

View changes since the review


// 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
}

@tgross35 tgross35 Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is 5.x still in active enough use that this is worth worrying about? Per https://www.dragonflybsd.org/releases/ 5.8 is from 2020 and 6.0 was in 2021.

View changes since the review


// 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 +1754,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 +1799,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"
)
});
Comment on lines +1825 to +1835

@tgross35 tgross35 Jun 18, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add a note for why these are skipped?

The match ty { ... } syntax is preferable over matches! since it's easier to add things like version-specific cases if needed.

View changes since the review


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