Skip to content

Commit 76aae41

Browse files
committed
test: Set version-dependent skips based on header defines
When there are additions or updates that are made that are only available with newer platform versions, we don't have a great way to handle testing. The only thing we can really do is unconditionally skip anything that doesn't pass on the oldest versions we have on CI. Check these versions using an expand-only compiler invocation;
1 parent 6300136 commit 76aae41

1 file changed

Lines changed: 169 additions & 32 deletions

File tree

libc-test/build.rs

Lines changed: 169 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,18 @@ use std::path::{
1111
Path,
1212
PathBuf,
1313
};
14+
use std::process::{
15+
Command,
16+
Stdio,
17+
};
18+
use std::sync::LazyLock;
1419
use std::{
1520
env,
1621
io,
1722
};
1823

24+
use regex::Regex;
25+
1926
fn do_cc() {
2027
let target = env::var("TARGET").unwrap();
2128
if cfg!(unix) || target.contains("cygwin") {
@@ -176,6 +183,8 @@ fn process_semver_file<W: Write, P: AsRef<Path>>(output: &mut W, path: &mut Path
176183
fn main() {
177184
// Avoid unnecessary re-building.
178185
println!("cargo:rerun-if-changed=build.rs");
186+
// Ensure version checking works, even if we don't use it.
187+
LazyLock::force(&VERSIONS);
179188

180189
do_cc();
181190
do_ctest();
@@ -1201,10 +1210,7 @@ fn test_netbsd(target: &str) {
12011210
let mut cfg = ctest_cfg();
12021211

12031212
// Assume netbsd10 but check for netbsd9 for test config.
1204-
let netbsd9 = match try_command_output("uname", &["-sr"]) {
1205-
Some(s) if s.starts_with("NetBSD 9.") => true,
1206-
_ => false,
1207-
};
1213+
let netbsd9 = matches!(VERSIONS.netbsd, Some((9, _)));
12081214

12091215
cfg.flag("-Wno-deprecated-declarations");
12101216
cfg.define("_NETBSD_SOURCE", Some("1"));
@@ -3731,6 +3737,8 @@ fn test_linux(target: &str) {
37313737
let mips = target.contains("mips");
37323738
let mips64 = target.contains("mips64");
37333739
let mips32 = mips && !mips64;
3740+
let kernel = VERSIONS.linux.unwrap_or((0, 0));
3741+
println!("cargo:warning=kernel version: {kernel:?}");
37343742

37353743
let musl_v1_2_3 = env::var("RUST_LIBC_UNSTABLE_MUSL_V1_2_3").is_ok();
37363744
if musl_v1_2_3 {
@@ -4183,13 +4191,13 @@ fn test_linux(target: &str) {
41834191
"sched_attr" => true,
41844192

41854193
// FIXME(linux): Requires >= 6.9 kernel headers.
4186-
"epoll_params" => true,
4194+
"epoll_params" => kernel < (6,9),
41874195

41884196
// FIXME(linux): Requires >= 6.12 kernel headers.
4189-
"dmabuf_cmsg" | "dmabuf_token" => true,
4197+
"dmabuf_cmsg" | "dmabuf_token" => kernel < (6, 12),
41904198

41914199
// FIXME(linux): Requires >= 6.12 kernel headers.
4192-
"mnt_ns_info" => true,
4200+
"mnt_ns_info" => kernel < (6,12),
41934201

41944202
// FIXME(musl): Struct has changed for new musl versions
41954203
"tcp_info" if musl => true,
@@ -4596,7 +4604,7 @@ fn test_linux(target: &str) {
45964604
"PIDFD_THREAD"
45974605
| "PIDFD_SIGNAL_THREAD"
45984606
| "PIDFD_SIGNAL_THREAD_GROUP"
4599-
| "PIDFD_SIGNAL_PROCESS_GROUP" => true,
4607+
| "PIDFD_SIGNAL_PROCESS_GROUP" => kernel < (6, 9),
46004608
// Linux >= 6.11
46014609
"PIDFD_GET_CGROUP_NAMESPACE"
46024610
| "PIDFD_GET_IPC_NAMESPACE"
@@ -4607,15 +4615,15 @@ fn test_linux(target: &str) {
46074615
| "PIDFD_GET_TIME_NAMESPACE"
46084616
| "PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE"
46094617
| "PIDFD_GET_USER_NAMESPACE"
4610-
| "PIDFD_GET_UTS_NAMESPACE" => true,
4618+
| "PIDFD_GET_UTS_NAMESPACE" => kernel < (6, 11),
46114619
// Linux >= 6.13
46124620
"PIDFD_GET_INFO"
46134621
| "PIDFD_INFO_PID"
46144622
| "PIDFD_INFO_CREDS"
46154623
| "PIDFD_INFO_CGROUPID"
4616-
| "PIDFD_INFO_SIZE_VER0" => true,
4624+
| "PIDFD_INFO_SIZE_VER0" => kernel < (6, 13),
46174625
// Linux >= 6.15
4618-
"PIDFD_INFO_EXIT" | "PIDFD_SELF" | "PIDFD_SELF_PROCESS" => true,
4626+
"PIDFD_INFO_EXIT" | "PIDFD_SELF" | "PIDFD_SELF_PROCESS" => kernel < (6, 15),
46194627

46204628
// is a private value for kernel usage normally
46214629
"FUSE_SUPER_MAGIC" => true,
@@ -4635,13 +4643,13 @@ fn test_linux(target: &str) {
46354643
"NF_NETDEV_NUMHOOKS" | "RLIM_NLIMITS" | "NFT_MSG_MAX" if uclibc => true,
46364644

46374645
// kernel 6.9 minimum
4638-
"RWF_NOAPPEND" => true,
4646+
"RWF_NOAPPEND" => kernel < (6, 9),
46394647

46404648
// kernel 6.11 minimum
4641-
"RWF_ATOMIC" => true,
4649+
"RWF_ATOMIC" => kernel < (6, 11),
46424650

46434651
// kernel 6.14 minimum
4644-
"RWF_DONTCACHE" => true,
4652+
"RWF_DONTCACHE" => kernel < (6, 14),
46454653

46464654
// musl doesn't use <linux/fanotify.h> in <sys/fanotify.h>
46474655
"FAN_REPORT_PIDFD"
@@ -4659,7 +4667,7 @@ fn test_linux(target: &str) {
46594667
}
46604668

46614669
// FIXME(linux32): Requires >= 6.6 kernel headers.
4662-
"XDP_USE_SG" | "XDP_PKT_CONTD" if pointer_width == 32 => true,
4670+
"XDP_USE_SG" | "XDP_PKT_CONTD" if pointer_width == 32 => kernel < (6, 6),
46634671

46644672
// FIXME(linux): Missing only on this platform for some reason
46654673
"PR_MDWE_NO_INHERIT" if gnueabihf => true,
@@ -4672,25 +4680,25 @@ fn test_linux(target: &str) {
46724680
| "XDP_TX_METADATA"
46734681
if musl || pointer_width == 32 =>
46744682
{
4675-
true
4683+
musl || kernel < (6, 8)
46764684
}
46774685

46784686
// FIXME(linux): Requires >= 6.11 kernel headers.
4679-
"XDP_UMEM_TX_METADATA_LEN" => true,
4687+
"XDP_UMEM_TX_METADATA_LEN" => kernel < (6, 11),
46804688

46814689
// FIXME(linux): Requires >= 6.11 kernel headers.
46824690
"NS_GET_MNTNS_ID"
46834691
| "NS_GET_PID_FROM_PIDNS"
46844692
| "NS_GET_TGID_FROM_PIDNS"
46854693
| "NS_GET_PID_IN_PIDNS"
4686-
| "NS_GET_TGID_IN_PIDNS" => true,
4694+
| "NS_GET_TGID_IN_PIDNS" => kernel < (6, 11),
46874695
// FIXME(linux): Requires >= 6.12 kernel headers.
46884696
"MNT_NS_INFO_SIZE_VER0" | "NS_MNT_GET_INFO" | "NS_MNT_GET_NEXT" | "NS_MNT_GET_PREV" => {
4689-
true
4697+
kernel < (6, 12)
46904698
}
46914699

46924700
// FIXME(linux): Requires >= 6.10 kernel headers.
4693-
"SYS_mseal" => true,
4701+
"SYS_mseal" => kernel < (6, 10),
46944702

46954703
// FIXME(linux): seems to not be available all the time (from <include/linux/sched.h>:
46964704
"PF_VCPU" | "PF_IDLE" | "PF_EXITING" | "PF_POSTCOREDUMP" | "PF_IO_WORKER"
@@ -4702,42 +4710,42 @@ fn test_linux(target: &str) {
47024710
| "PF_BLOCK_TS" | "PF_SUSPEND_TASK" => true,
47034711

47044712
// FIXME(linux): Requires >= 6.9 kernel headers.
4705-
"EPIOCSPARAMS" | "EPIOCGPARAMS" => true,
4713+
"EPIOCSPARAMS" | "EPIOCGPARAMS" => kernel < (6, 9),
47064714

47074715
// FIXME(linux): Requires >= 6.11 kernel headers.
4708-
"MAP_DROPPABLE" => true,
4716+
"MAP_DROPPABLE" => kernel < (6, 11),
47094717

47104718
// FIXME(linux): Requires >= 6.12 kernel headers.
4711-
"SOF_TIMESTAMPING_OPT_RX_FILTER" => true,
4719+
"SOF_TIMESTAMPING_OPT_RX_FILTER" => kernel < (6, 12),
47124720

47134721
// FIXME(linux): Requires >= 6.12 kernel headers.
47144722
"SO_DEVMEM_LINEAR" | "SO_DEVMEM_DMABUF" | "SO_DEVMEM_DONTNEED"
4715-
| "SCM_DEVMEM_LINEAR" | "SCM_DEVMEM_DMABUF" => true,
4723+
| "SCM_DEVMEM_LINEAR" | "SCM_DEVMEM_DMABUF" => kernel < (6, 12),
47164724

47174725
// FIXME(linux): Requires >= 6.14 kernel headers.
47184726
"SECBIT_EXEC_DENY_INTERACTIVE"
47194727
| "SECBIT_EXEC_DENY_INTERACTIVE_LOCKED"
47204728
| "SECBIT_EXEC_RESTRICT_FILE"
47214729
| "SECBIT_EXEC_RESTRICT_FILE_LOCKED"
4722-
| "SECURE_ALL_UNPRIVILEGED" => true,
4730+
| "SECURE_ALL_UNPRIVILEGED" => kernel < (6, 14),
47234731

47244732
// FIXME(linux): Value changed in 6.14
4725-
"SECURE_ALL_BITS" | "SECURE_ALL_LOCKS" => true,
4733+
"SECURE_ALL_BITS" | "SECURE_ALL_LOCKS" => kernel < (6, 14),
47264734

47274735
// FIXME(linux): Requires >= 6.9 kernel headers.
4728-
"AT_HWCAP3" | "AT_HWCAP4" => true,
4736+
"AT_HWCAP3" | "AT_HWCAP4" => kernel < (6, 9),
47294737

47304738
// Linux 6.14
4731-
"AT_EXECVE_CHECK" => true,
4739+
"AT_EXECVE_CHECK" => kernel < (6, 14),
47324740

47334741
// FIXME(linux): Requires >= 6.16 kernel headers.
4734-
"PTRACE_SET_SYSCALL_INFO" => true,
4742+
"PTRACE_SET_SYSCALL_INFO" => kernel < (6, 16),
47354743

47364744
// FIXME(linux): Requires >= 6.13 kernel headers.
4737-
"AT_HANDLE_CONNECTABLE" => true,
4745+
"AT_HANDLE_CONNECTABLE" => kernel < (6, 13),
47384746

47394747
// FIXME(linux): Requires >= 6.12 kernel headers.
4740-
"AT_HANDLE_MNT_ID_UNIQUE" => true,
4748+
"AT_HANDLE_MNT_ID_UNIQUE" => kernel < (6, 12),
47414749

47424750
// FIXME(musl): This value is not yet in musl.
47434751
// eabihf targets are tested using an older version of glibc
@@ -6091,10 +6099,139 @@ fn test_qurt(target: &str) {
60916099
ctest::generate_test(&mut cfg, "../src/lib.rs", "ctest_output.rs").unwrap();
60926100
}
60936101

6102+
/// Platform versions for checking expected support. These are extracted from headers so should be
6103+
/// accurate for the target we are building, rather than the host (which `uname` would provide).
6104+
static VERSIONS: LazyLock<Versions> = LazyLock::new(Versions::init_from_cc);
6105+
6106+
#[derive(Clone, Copy, Debug, Default)]
6107+
struct Versions {
6108+
linux: Option<(u32, u32)>,
6109+
glibc: Option<(u32, u32)>,
6110+
freebsd: Option<(u32, u32)>,
6111+
openbsd: Option<(u32, u32)>,
6112+
netbsd: Option<(u32, u32)>,
6113+
macos: Option<(u32, u32)>,
6114+
}
6115+
6116+
impl Versions {
6117+
fn init_from_cc() -> Self {
6118+
let src = r#"
6119+
#ifdef __linux__
6120+
/* Defines LINUX_VERSION_MAJOR, LINUX_VERSION_PATCHLEVEL (integers) */
6121+
#include "linux/version.h"
6122+
#endif
6123+
6124+
/* Including a libc header will define __GLIBC__ */
6125+
#include <stdio.h>
6126+
6127+
#ifdef __GLIBC__
6128+
/* Provides __GLIBC__, __GLIBC_MINOR__ (integers) */
6129+
#include "gnu/libc-version.h"
6130+
#endif
6131+
6132+
#if defined(__FreeBSD__) \
6133+
|| defined(__NetBSD__) \
6134+
|| defined(__OpenBSD__) \
6135+
|| defined(__APPLE__)
6136+
/* FreeBSD: __FreeBSD_version (MMmmRxx string, e.g. 1600018)
6137+
* NetBSD: __NetBSD_Version__ (MMmmrrpp00 string, e.g. 1001000000)
6138+
* OpenBSD: OpenBSD (release date, e.g. 202510) and OpenBSDM_m (e.g. OpenBSD7_8)
6139+
* Apple: __MAC_OS_X_VERSION_MAX_ALLOWED __MAC_M_m (e.g. __MAC_26_5)
6140+
*/
6141+
#include "sys/param.h"
6142+
#endif
6143+
"#;
6144+
6145+
let mut ret = Versions::default();
6146+
6147+
let compiler = cc::Build::new().get_compiler();
6148+
6149+
println!("cargo:warning={compiler:?}");
6150+
6151+
if !compiler.is_like_gnu() {
6152+
println!("cargo:warning=non-gcc-like compiler, skipping versions");
6153+
return ret;
6154+
}
6155+
6156+
// `-dM -E` invokes the preprocessor and prints all `#define`s. `cc` automatically
6157+
// sets target-specific flags.
6158+
let mut cmd = compiler.to_command();
6159+
cmd.stdin(Stdio::piped())
6160+
.stdout(Stdio::piped())
6161+
.args(["-dM", "-E", "-"]);
6162+
6163+
println!("cargo:warning=invocation: {cmd:?}");
6164+
let mut child = cmd.spawn().expect("failed to spawn compiler");
6165+
child
6166+
.stdin
6167+
.take()
6168+
.unwrap()
6169+
.write_all(src.as_bytes())
6170+
.expect("failed to send stdin");
6171+
let out = child.wait_with_output().expect("failed to wait on child");
6172+
let out = String::from_utf8_lossy(&out.stdout);
6173+
6174+
// Allow spaces everywhere so we match things like `\n # define foo bar \n`.
6175+
let re = Regex::new(r"^\s*#\s*define\s+(\w+)\s+(.*?)\s*$").unwrap();
6176+
let obsd_re = Regex::new(r"^OpenBSD(\d+)_(\d+)$").unwrap();
6177+
let mac_re = Regex::new(r"^__MAC_(\d+)_(\d+)").unwrap();
6178+
6179+
for line in out.lines() {
6180+
let Some(caps) = re.captures(line) else {
6181+
continue;
6182+
};
6183+
let name = &caps[1];
6184+
let value = &caps[2];
6185+
println!("cargo:warning=#define {name} {value}");
6186+
6187+
match name {
6188+
"LINUX_VERSION_MAJOR" => {
6189+
ret.linux.get_or_insert_default().0 = value.parse().unwrap()
6190+
}
6191+
"LINUX_VERSION_PATCHLEVEL" => {
6192+
ret.linux.get_or_insert_default().1 = value.parse().unwrap()
6193+
}
6194+
"__GLIBC__" => ret.glibc.get_or_insert_default().0 = value.parse().unwrap(),
6195+
"__GLIBC_MINOR__" => ret.glibc.get_or_insert_default().0 = value.parse().unwrap(),
6196+
"__MAC_OS_X_VERSION_MAX_ALLOWED" => {
6197+
let caps = mac_re.captures(value).unwrap();
6198+
let major: u32 = caps[1].parse().unwrap();
6199+
let minor: u32 = caps[2].parse().unwrap();
6200+
ret.macos = Some((major, minor));
6201+
}
6202+
"__FreeBSD_version" => {
6203+
// Format: MmmRxx where M is major (possibly multi-digit), mm is minor, R
6204+
// indicates release status, xx is some sequence.
6205+
let major: u32 = value[..(value.len() - 5)].parse().unwrap();
6206+
let minor: u32 = value[(value.len() - 5)..(value.len() - 3)].parse().unwrap();
6207+
ret.freebsd = Some((major, minor));
6208+
}
6209+
"__NetBSD_Version__" => {
6210+
// Format: MMmmrrpp00 where M is major (possibly multi-digit), mm is minor, r
6211+
// and p are patch level.
6212+
let major: u32 = value[..(value.len() - 8)].parse().unwrap();
6213+
let minor: u32 = value[(value.len() - 8)..(value.len() - 6)].parse().unwrap();
6214+
ret.netbsd = Some((major, minor));
6215+
}
6216+
x if obsd_re.is_match(x) => {
6217+
let caps = obsd_re.captures(name).expect("is_match checked");
6218+
let major: u32 = caps[1].parse().unwrap();
6219+
let minor: u32 = caps[2].parse().unwrap();
6220+
ret.openbsd = Some((major, minor));
6221+
}
6222+
_ => (),
6223+
}
6224+
}
6225+
6226+
println!("cargo:warning=detected versions: {ret:?}");
6227+
ret
6228+
}
6229+
}
6230+
60946231
/// Attempt to execute a command and collect its output, If the command fails for whatever
60956232
/// reason, return `None`.
60966233
fn try_command_output(cmd: &str, args: &[&str]) -> Option<String> {
6097-
let output = std::process::Command::new(cmd).args(args).output().ok()?;
6234+
let output = Command::new(cmd).args(args).output().ok()?;
60986235

60996236
if !output.status.success() {
61006237
return None;

0 commit comments

Comments
 (0)