@@ -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 ;
1419use std:: {
1520 env,
1621 io,
1722} ;
1823
24+ use regex:: Regex ;
25+
1926fn 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
176183fn 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`.
60966233fn 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