diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 570b59c7a6d93..0c77bd3c880d4 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -128,6 +128,11 @@ jobs: # env: { ARCH_BITS: 32, ARCH: i686 } - target: i686-pc-windows-msvc os: windows-2025 + env: { ARCH_BITS: 32, ARCH: i686 } + - target: i686-pc-windows-msvc + os: windows-2025 + env: { ARCH_BITS: 32, ARCH: i686, RUST_LIBC_UNSTABLE_WINDOWS_TIME32: 1 } + artifact-tag: windows_time32 - target: i686-unknown-linux-gnu - target: x86_64-pc-windows-gnu os: windows-2025 diff --git a/build.rs b/build.rs index 18004edfeb1b4..1df5a700bbb97 100644 --- a/build.rs +++ b/build.rs @@ -31,6 +31,8 @@ const ALLOWED_CFGS: &[&str] = &[ // Corresponds to `_REDIR_TIME64` in musl "musl32_time64", "vxworks_lt_25_09", + // Corresponds to `_USE_32BIT_TIME_T` in Windows CRT + "windows_use_time32", ]; // Extra values to allow for check-cfg. @@ -123,6 +125,11 @@ fn main() { } } + let windows_use_time32 = env::var("RUST_LIBC_UNSTABLE_WINDOWS_TIME32").is_ok(); + println!("cargo:rerun-if-env-changed=RUST_LIBC_UNSTABLE_WINDOWS_TIME32"); + if windows_use_time32 { + set_cfg("windows_use_time32"); + } let linux_time_bits64 = env::var("RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64").is_ok(); println!("cargo:rerun-if-env-changed=RUST_LIBC_UNSTABLE_LINUX_TIME_BITS64"); if linux_time_bits64 { diff --git a/ci/install-rust.sh b/ci/install-rust.sh index 69e8ba97a5d3d..70a9a7f5d83a5 100755 --- a/ci/install-rust.sh +++ b/ci/install-rust.sh @@ -38,7 +38,7 @@ if [ -n "${INSTALL_RUST_SRC:-}" ]; then fi if [ "$os" = "windows" ]; then - if [ "${ARCH_BITS:-}" = "i686" ]; then + if [ "${ARCH:-}" = "i686" ]; then echo "Install MinGW32" choco install mingw --x86 --force fi diff --git a/libc-test/build.rs b/libc-test/build.rs index cb577fd5e363e..748289710b0a0 100644 --- a/libc-test/build.rs +++ b/libc-test/build.rs @@ -735,7 +735,6 @@ fn test_cygwin(target: &str) { fn test_windows(target: &str) { assert!(target.contains("windows")); let gnu = target.contains("gnu"); - let i686 = target.contains("i686"); let mut cfg = ctest_cfg(); @@ -744,6 +743,11 @@ fn test_windows(target: &str) { } cfg.define("_WIN32_WINNT", Some("0x8000")); + if env::var("RUST_LIBC_UNSTABLE_WINDOWS_TIME32").is_ok() { + cfg.define("_USE_32BIT_TIME_T", None); + cfg.cfg("windows_use_time32", None); + } + headers!( cfg, "direct.h", @@ -772,23 +776,18 @@ fn test_windows(target: &str) { // Just pass all these through, no need for a "struct" prefix "FILE" | "DIR" | "Dl_info" => ty.to_string().into(), t if t.ends_with("_t") => t.to_string().into(), - // Windows uppercase structs don't have `struct` in fr.into()ont: + // Windows uppercase structs don't have `struct` in front: t if ty.chars().next().unwrap().is_uppercase() => t.to_string().into(), - "stat" => "struct __stat64".to_string().into(), - "utimbuf" => "struct __utimbuf64".to_string().into(), _ => None, } }); - cfg.rename_type(move |ty| { - match ty { - // FIXME(windows): these don't exist: - "time64_t" => "__time64_t".to_string().into(), - "ssize_t" => "SSIZE_T".to_string().into(), - "sighandler_t" if !gnu => "_crt_signal_t".to_string().into(), - "sighandler_t" if gnu => "__p_sig_fn_t".to_string().into(), - _ => None, - } + cfg.rename_type(move |ty| match ty { + "time64_t" => "__time64_t".to_string().into(), + "ssize_t" => "SSIZE_T".to_string().into(), + "sighandler_t" if !gnu => "_crt_signal_t".to_string().into(), + "sighandler_t" if gnu => "__p_sig_fn_t".to_string().into(), + _ => None, }); cfg.rename_fn(move |func| { @@ -800,19 +799,9 @@ fn test_windows(target: &str) { cfg.skip_alias(move |alias| match alias.ident() { "SSIZE_T" if !gnu => true, "ssize_t" if !gnu => true, - // FIXME(windows): The size and alignment of this type are incorrect - "time_t" if gnu && i686 => true, _ => false, }); - cfg.skip_struct(move |struct_| { - match struct_.ident() { - // FIXME(windows): The size and alignment of this struct are incorrect - "timespec" if gnu && i686 => true, - _ => false, - } - }); - cfg.skip_const(move |constant| { match constant.ident() { // FIXME(windows): API error: @@ -1380,7 +1369,6 @@ fn test_netbsd(target: &str) { }); cfg.skip_fn(move |func| { - #[expect(clippy::wildcard_in_or_patterns)] match func.ident() { // FIXME(netbsd): Look into setting `_POSIX_C_SOURCE` to enable this "qsort_r" => true, @@ -3085,11 +3073,8 @@ fn test_emscripten(target: &str) { }); cfg.skip_alias(|ty| { - match ty.ident() { - // LFS64 types have been removed in Emscripten 3.1.44 - // https://github.com/emscripten-core/emscripten/pull/19812 - ty => ty.ends_with("64") || ty.ends_with("64_t"), - } + let ty = ty.ident(); + ty.ends_with("64") || ty.ends_with("64_t") }); cfg.skip_struct(move |struct_| { diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 9bb3bcdaa9fe2..d2cb60b4a8975 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -19,7 +19,12 @@ pub type clock_t = i32; pub type errno_t = c_int; cfg_if! { - if #[cfg(all(target_arch = "x86", target_env = "gnu"))] { + if #[cfg(windows_use_time32)] { + pub type time_t = c_long; + } else if #[cfg(all( + any(target_arch = "x86", target_arch = "arm"), + target_env = "gnu" + ))] { pub type time_t = i32; } else { pub type time_t = i64; @@ -39,7 +44,6 @@ pub type time64_t = i64; pub type SOCKET = crate::uintptr_t; s! { - // note this is the struct called stat64 in Windows. Not stat, nor stati64. pub struct stat { pub st_dev: dev_t, pub st_ino: ino_t, @@ -48,16 +52,15 @@ s! { pub st_uid: c_short, pub st_gid: c_short, pub st_rdev: dev_t, - pub st_size: i64, - pub st_atime: time64_t, - pub st_mtime: time64_t, - pub st_ctime: time64_t, + pub st_size: off_t, + pub st_atime: time_t, + pub st_mtime: time_t, + pub st_ctime: time_t, } - // note that this is called utimbuf64 in Windows pub struct utimbuf { - pub actime: time64_t, - pub modtime: time64_t, + pub actime: time_t, + pub modtime: time_t, } pub struct tm { @@ -379,9 +382,32 @@ extern "C" { pub fn raise(signum: c_int) -> c_int; pub fn clock() -> clock_t; + #[cfg_attr(windows_use_time32, link_name = "_ctime32")] + #[cfg_attr(not(windows_use_time32), link_name = "_ctime64")] + #[cfg_attr( + target_env = "msvc", + deprecated( + since = "0.2.184", + note = "This function, among other older functions in the CRT, were marked deprecated \ + and have more secure variants with the same symbols but `_s`-suffixed. If you \ + use this specific routine, we're requesting comments at #PENDING." + ) + )] pub fn ctime(sourceTime: *const time_t) -> *mut c_char; + #[cfg_attr(windows_use_time32, link_name = "_difftime32")] + #[cfg_attr(not(windows_use_time32), link_name = "_difftime64")] + #[cfg_attr( + target_env = "msvc", + deprecated( + since = "0.2.184", + note = "This function, among other older functions in the CRT, were marked deprecated \ + and have more secure variants with the same symbols but `_s`-suffixed. If you \ + use this specific routine, we're requesting comments at #PENDING." + ) + )] pub fn difftime(timeEnd: time_t, timeStart: time_t) -> c_double; - #[link_name = "_gmtime64_s"] + #[cfg_attr(windows_use_time32, link_name = "_gmtime32_s")] + #[cfg_attr(not(windows_use_time32), link_name = "_gmtime64_s")] pub fn gmtime_s(destTime: *mut tm, srcTime: *const time_t) -> c_int; #[link_name = "_get_daylight"] pub fn get_daylight(hours: *mut c_int) -> errno_t; @@ -396,9 +422,11 @@ extern "C" { size_in_bytes: size_t, index: c_int, ) -> errno_t; - #[link_name = "_localtime64_s"] + #[cfg_attr(windows_use_time32, link_name = "_localtime32_s")] + #[cfg_attr(not(windows_use_time32), link_name = "_localtime64_s")] pub fn localtime_s(tmDest: *mut tm, sourceTime: *const time_t) -> crate::errno_t; - #[link_name = "_time64"] + #[cfg_attr(windows_use_time32, link_name = "_time32")] + #[cfg_attr(not(windows_use_time32), link_name = "_time64")] pub fn time(destTime: *mut time_t) -> time_t; #[link_name = "_tzset"] pub fn tzset(); @@ -410,13 +438,13 @@ extern "C" { pub fn mkdir(path: *const c_char) -> c_int; #[link_name = "_wrmdir"] pub fn wrmdir(path: *const wchar_t) -> c_int; - #[link_name = "_fstat64"] + #[link_name = "_fstat"] pub fn fstat(fildes: c_int, buf: *mut stat) -> c_int; - #[link_name = "_stat64"] + #[link_name = "_stat"] pub fn stat(path: *const c_char, buf: *mut stat) -> c_int; - #[link_name = "_wstat64"] + #[link_name = "_wstat"] pub fn wstat(path: *const wchar_t, buf: *mut stat) -> c_int; - #[link_name = "_wutime64"] + #[link_name = "_wutime"] pub fn wutime(file: *const wchar_t, buf: *mut utimbuf) -> c_int; #[link_name = "_popen"] pub fn popen(command: *const c_char, mode: *const c_char) -> *mut crate::FILE;