From c896aa5bf0b48109c4939fb9ecf1a8729da30c85 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Thu, 20 Mar 2025 15:53:57 -0400 Subject: [PATCH 01/13] Skip cache output to /dev/null Signed-off-by: Zhang Maiyun --- src/cache/cache.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 3e883db19..a13733f40 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -210,6 +210,10 @@ impl CacheRead { optional, } in objects { + if path == Path::new("/dev/null") { + debug!("Skipping output to /dev/null"); + continue; + } let dir = match path.parent() { Some(d) => d, None => bail!("Output file without a parent directory!"), From a805e84623b71bb9ed3266bc66865f690ff46138 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Thu, 20 Mar 2025 15:55:16 -0400 Subject: [PATCH 02/13] Fall back to direct write if tmp creation fails Fixes #2288 Signed-off-by: Zhang Maiyun --- src/cache/cache.rs | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index a13733f40..a6f877c59 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -50,6 +50,7 @@ use fs_err as fs; use serde::{Deserialize, Serialize}; use std::fmt; +use std::fs::File; use std::io::{self, Cursor, Read, Seek, Write}; use std::path::{Path, PathBuf}; use std::sync::Arc; @@ -221,15 +222,30 @@ impl CacheRead { // Write the cache entry to a tempfile and then atomically // move it to its final location so that other rustc invocations // happening in parallel don't see a partially-written file. - let mut tmp = NamedTempFile::new_in(dir)?; - match (self.get_object(&key, &mut tmp), optional) { - (Ok(mode), _) => { - tmp.persist(&path)?; + match (NamedTempFile::new_in(dir), optional) { + (Ok(mut tmp), _) => { + match (self.get_object(&key, &mut tmp), optional) { + (Ok(mode), _) => { + tmp.persist(&path)?; + if let Some(mode) = mode { + set_file_mode(&path, mode)?; + } + } + (Err(e), false) => return Err(e), + // skip if no object found and it's optional + (Err(_), true) => continue, + } + } + (Err(e), false) => { + // Fall back to writing directly to the final location + warn!("Failed to create temp file on the same file system: {e}"); + let mut f = File::create(&path)?; + // `optional`` is false in this branch, so do not ignore errors + let mode = self.get_object(&key, &mut f)?; if let Some(mode) = mode { set_file_mode(&path, mode)?; } } - (Err(e), false) => return Err(e), // skip if no object found and it's optional (Err(_), true) => continue, } From 8b61d288f3f8dd5300e6ef001fe384c31eebc732 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Thu, 10 Apr 2025 05:54:34 -0700 Subject: [PATCH 03/13] Fix typo Co-authored-by: Alex Overchenko --- src/cache/cache.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index a6f877c59..abf4bd770 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -240,7 +240,7 @@ impl CacheRead { // Fall back to writing directly to the final location warn!("Failed to create temp file on the same file system: {e}"); let mut f = File::create(&path)?; - // `optional`` is false in this branch, so do not ignore errors + // `optional` is false in this branch, so do not ignore errors let mode = self.get_object(&key, &mut f)?; if let Some(mode) = mode { set_file_mode(&path, mode)?; From 7ba07c4ccfe66bcb9705cd61c9b39a0112447aca Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 26 May 2025 07:55:35 -0700 Subject: [PATCH 04/13] Test for cache extraction to /dev/null --- src/cache/cache.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index abf4bd770..07ca396a0 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -858,4 +858,28 @@ mod test { }); } } + + #[cfg(unix)] + #[test] + fn test_extract_object_to_devnull_works() { + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .worker_threads(1) + .build() + .unwrap(); + + let pool = runtime.handle(); + + let cache_data = CacheWrite::new(); + let cache_read = CacheRead::from(io::Cursor::new(cache_data.finish().unwrap())).unwrap(); + + let objects = vec![FileObjectSource { + key: "test_key".to_string(), + path: PathBuf::from("/dev/null"), + optional: false, + }]; + + let result = runtime.block_on(cache_read.extract_objects(objects, pool)); + assert!(result.is_ok(), "Extracting to /dev/null should succeed"); + } } From 5a554299e4a2286bce808b8235b9a66b9b2b724e Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 26 May 2025 07:56:15 -0700 Subject: [PATCH 05/13] Test for cache extraction to /dev/fd/{n} --- src/cache/cache.rs | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 07ca396a0..046e841ac 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -882,4 +882,38 @@ mod test { let result = runtime.block_on(cache_read.extract_objects(objects, pool)); assert!(result.is_ok(), "Extracting to /dev/null should succeed"); } + + #[cfg(unix)] + #[test] + fn test_extract_object_to_dev_fd_something() { + // Open a pipe, write to `/dev/fd/{fd}` and check the other end that the correct data was written. + use std::os::fd::AsRawFd; + use tokio::io::AsyncReadExt; + let runtime = tokio::runtime::Builder::new_current_thread() + .enable_all() + .worker_threads(1) + .build() + .unwrap(); + let pool = runtime.handle(); + let mut cache_data = CacheWrite::new(); + let data = b"test data"; + cache_data.put_bytes("test_key", data).unwrap(); + let cache_read = CacheRead::from(io::Cursor::new(cache_data.finish().unwrap())).unwrap(); + runtime.block_on(async { + let (sender, mut receiver) = tokio::net::unix::pipe::pipe().unwrap(); + let sender_fd = sender.into_blocking_fd().unwrap(); + let raw_fd = sender_fd.as_raw_fd(); + let objects = vec![FileObjectSource { + key: "test_key".to_string(), + path: PathBuf::from(format!("/dev/fd/{}", raw_fd)), + optional: false, + }]; + let result = cache_read.extract_objects(objects, pool).await; + assert!(result.is_ok(), "Extracting to /dev/fd/{} should succeed", raw_fd); + let mut buf = vec![0; data.len()]; + let n = receiver.read_exact(&mut buf).await.unwrap(); + assert_eq!(n, data.len(), "Read the correct number of bytes"); + assert_eq!(buf, data, "Read the correct data from /dev/fd/{}", raw_fd); + }); + } } From 2a39ab244a42bac2f3ed341488edcc8e8298c7fb Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 26 May 2025 08:39:02 -0700 Subject: [PATCH 06/13] Fix rustfmt --- src/cache/cache.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 046e841ac..172dda1a5 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -909,7 +909,11 @@ mod test { optional: false, }]; let result = cache_read.extract_objects(objects, pool).await; - assert!(result.is_ok(), "Extracting to /dev/fd/{} should succeed", raw_fd); + assert!( + result.is_ok(), + "Extracting to /dev/fd/{} should succeed", + raw_fd + ); let mut buf = vec![0; data.len()]; let n = receiver.read_exact(&mut buf).await.unwrap(); assert_eq!(n, data.len(), "Read the correct number of bytes"); From 50ecb68a550a175794375c3fedb1fcdd2868cdcf Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Sun, 15 Jun 2025 20:06:27 -0700 Subject: [PATCH 07/13] High-level tests into /dev/(null|stdout) --- tests/system.rs | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/tests/system.rs b/tests/system.rs index a8bda4f21..92f9e0875 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -195,6 +195,8 @@ const INPUT_FOR_HIP_A: &str = "test_a.hip"; const INPUT_FOR_HIP_B: &str = "test_b.hip"; const INPUT_FOR_HIP_C: &str = "test_c.hip"; const OUTPUT: &str = "test.o"; +const DEV_NULL: &str = "/dev/null"; +const DEV_STDOUT: &str = "/dev/stdout"; // Copy the source files into the tempdir so we can compile with relative paths, since the commandline winds up in the hash key. fn copy_to_tempdir(inputs: &[&str], tempdir: &Path) { @@ -265,6 +267,120 @@ fn test_basic_compile(compiler: Compiler, tempdir: &Path) { }); } +#[cfg(unix)] +fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { + let Compiler { + name, + exe, + env_vars, + } = compiler; + println!("test_basic_compile: {}", name); + // Compile a source file. + copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); + + let out_file = tempdir.join(OUTPUT); + trace!("compile"); + sccache_command() + .args(compile_cmdline(name, &exe, INPUT, DEV_NULL, Vec::new())) + .current_dir(tempdir) + .envs(env_vars.clone()) + .assert() + .success(); + trace!("request stats"); + get_stats(|info| { + assert_eq!(1, info.stats.compile_requests); + assert_eq!(1, info.stats.requests_executed); + assert_eq!(0, info.stats.cache_hits.all()); + assert_eq!(1, info.stats.cache_misses.all()); + assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + let adv_key = adv_key_kind("c", compiler.name); + assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + }); + trace!("compile"); + fs::remove_file(&out_file).unwrap(); + sccache_command() + .args(compile_cmdline(name, &exe, INPUT, DEV_NULL, Vec::new())) + .current_dir(tempdir) + .envs(env_vars) + .assert() + .success(); + assert!(fs::metadata(&out_file).map(|m| m.len() > 0).unwrap()); + trace!("request stats"); + get_stats(|info| { + assert_eq!(2, info.stats.compile_requests); + assert_eq!(2, info.stats.requests_executed); + assert_eq!(1, info.stats.cache_hits.all()); + assert_eq!(1, info.stats.cache_misses.all()); + assert_eq!(&1, info.stats.cache_hits.get("C/C++").unwrap()); + assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + let adv_key = adv_key_kind("c", compiler.name); + assert_eq!(&1, info.stats.cache_hits.get_adv(&adv_key).unwrap()); + assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + }); +} + +#[cfg(not(unix))] +fn test_basic_compile_into_dev_null(_: Compiler, _: &Path) { + warn!("Not unix, skipping /dev tests"); +} + +#[cfg(unix)] +fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { + let Compiler { + name, + exe, + env_vars, + } = compiler; + println!("test_basic_compile: {}", name); + // Compile a source file. + copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); + + let out_file = tempdir.join(OUTPUT); + trace!("compile"); + sccache_command() + .args(compile_cmdline(name, &exe, INPUT, DEV_STDOUT, Vec::new())) + .current_dir(tempdir) + .envs(env_vars.clone()) + .assert() + .success(); + trace!("request stats"); + get_stats(|info| { + assert_eq!(1, info.stats.compile_requests); + assert_eq!(1, info.stats.requests_executed); + assert_eq!(0, info.stats.cache_hits.all()); + assert_eq!(1, info.stats.cache_misses.all()); + assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + let adv_key = adv_key_kind("c", compiler.name); + assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + }); + trace!("compile"); + fs::remove_file(&out_file).unwrap(); + sccache_command() + .args(compile_cmdline(name, &exe, INPUT, DEV_STDOUT, Vec::new())) + .current_dir(tempdir) + .envs(env_vars) + .assert() + .success(); + assert!(fs::metadata(&out_file).map(|m| m.len() > 0).unwrap()); + trace!("request stats"); + get_stats(|info| { + assert_eq!(2, info.stats.compile_requests); + assert_eq!(2, info.stats.requests_executed); + assert_eq!(1, info.stats.cache_hits.all()); + assert_eq!(1, info.stats.cache_misses.all()); + assert_eq!(&1, info.stats.cache_hits.get("C/C++").unwrap()); + assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + let adv_key = adv_key_kind("c", compiler.name); + assert_eq!(&1, info.stats.cache_hits.get_adv(&adv_key).unwrap()); + assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + }); +} + +#[cfg(not(unix))] +fn test_basic_compile_into_dev_stdout(_: Compiler, _: &Path) { + warn!("Not unix, skipping /dev tests"); +} + fn test_noncacheable_stats(compiler: Compiler, tempdir: &Path) { let Compiler { name, @@ -631,6 +747,8 @@ fn run_sccache_command_tests(compiler: Compiler, tempdir: &Path, preprocessor_ca test_basic_compile(compiler.clone(), tempdir); } test_compile_with_define(compiler.clone(), tempdir); + test_basic_compile_into_dev_null(compiler.clone(), tempdir); + test_basic_compile_into_dev_stdout(compiler.clone(), tempdir); if compiler.name == "cl.exe" { test_msvc_deps(compiler.clone(), tempdir); test_msvc_responsefile(compiler.clone(), tempdir); From 31b3ed6009966702af1b62391503eb7f908b2dc0 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Fri, 10 Oct 2025 16:39:26 -0400 Subject: [PATCH 08/13] Split hardcoded test into unix and windows --- src/cache/cache.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 172dda1a5..8744e7cbc 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -211,10 +211,16 @@ impl CacheRead { optional, } in objects { + #[cfg(unix)] if path == Path::new("/dev/null") { debug!("Skipping output to /dev/null"); continue; } + #[cfg(windows)] + if path == Path::new("NUL") { + debug!("Skipping output to NUL"); + continue; + } let dir = match path.parent() { Some(d) => d, None => bail!("Output file without a parent directory!"), From 9175be32de91069ea818de9b9a22b819d0a9ee82 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Sat, 11 Oct 2025 06:11:22 -0400 Subject: [PATCH 09/13] Check /dev/null output on windows too --- src/cache/cache.rs | 2 -- tests/system.rs | 8 +------- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 8744e7cbc..03ab327c7 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -211,7 +211,6 @@ impl CacheRead { optional, } in objects { - #[cfg(unix)] if path == Path::new("/dev/null") { debug!("Skipping output to /dev/null"); continue; @@ -865,7 +864,6 @@ mod test { } } - #[cfg(unix)] #[test] fn test_extract_object_to_devnull_works() { let runtime = tokio::runtime::Builder::new_current_thread() diff --git a/tests/system.rs b/tests/system.rs index 92f9e0875..1753253f1 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -267,7 +267,6 @@ fn test_basic_compile(compiler: Compiler, tempdir: &Path) { }); } -#[cfg(unix)] fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { let Compiler { name, @@ -319,11 +318,6 @@ fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { }); } -#[cfg(not(unix))] -fn test_basic_compile_into_dev_null(_: Compiler, _: &Path) { - warn!("Not unix, skipping /dev tests"); -} - #[cfg(unix)] fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { let Compiler { @@ -378,7 +372,7 @@ fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { #[cfg(not(unix))] fn test_basic_compile_into_dev_stdout(_: Compiler, _: &Path) { - warn!("Not unix, skipping /dev tests"); + warn!("Not unix, skipping tests with /dev/stdout"); } fn test_noncacheable_stats(compiler: Compiler, tempdir: &Path) { From afe939641f8149ccd113ad9976fd7770a2a6c2fc Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Sat, 11 Oct 2025 07:12:59 -0400 Subject: [PATCH 10/13] Fix test log --- tests/system.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/system.rs b/tests/system.rs index 1753253f1..5b45e5ffa 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -273,7 +273,8 @@ fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { exe, env_vars, } = compiler; - println!("test_basic_compile: {}", name); + println!("test_basic_compile_into_dev_null: {}", name); + zero_stats(); // Compile a source file. copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); @@ -325,7 +326,8 @@ fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { exe, env_vars, } = compiler; - println!("test_basic_compile: {}", name); + println!("test_basic_compile_into_dev_stdout: {}", name); + zero_stats(); // Compile a source file. copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); From 64b33e316cf9b5cda1701345be508e8e147bfcd3 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 13 Oct 2025 17:48:43 -0400 Subject: [PATCH 11/13] Adopt test counts Both `test_basic_compile_into_dev_null` and `test_basic_compile_into_dev_stdout` always result in two hits and zero misses because the source file is the same as the one for the basic test. --- tests/system.rs | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/tests/system.rs b/tests/system.rs index 5b45e5ffa..06e943153 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -290,32 +290,30 @@ fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { get_stats(|info| { assert_eq!(1, info.stats.compile_requests); assert_eq!(1, info.stats.requests_executed); - assert_eq!(0, info.stats.cache_hits.all()); - assert_eq!(1, info.stats.cache_misses.all()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + assert_eq!(1, info.stats.cache_hits.all()); + assert_eq!(0, info.stats.cache_misses.all()); + assert!(info.stats.cache_misses.get("C/C++").is_none()); let adv_key = adv_key_kind("c", compiler.name); - assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + assert!(info.stats.cache_misses.get_adv(&adv_key).is_none()); }); trace!("compile"); - fs::remove_file(&out_file).unwrap(); sccache_command() .args(compile_cmdline(name, &exe, INPUT, DEV_NULL, Vec::new())) .current_dir(tempdir) .envs(env_vars) .assert() .success(); - assert!(fs::metadata(&out_file).map(|m| m.len() > 0).unwrap()); trace!("request stats"); get_stats(|info| { assert_eq!(2, info.stats.compile_requests); assert_eq!(2, info.stats.requests_executed); - assert_eq!(1, info.stats.cache_hits.all()); - assert_eq!(1, info.stats.cache_misses.all()); - assert_eq!(&1, info.stats.cache_hits.get("C/C++").unwrap()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + assert_eq!(2, info.stats.cache_hits.all()); + assert_eq!(0, info.stats.cache_misses.all()); + assert_eq!(&2, info.stats.cache_hits.get("C/C++").unwrap()); + assert!(info.stats.cache_misses.get("C/C++").is_none()); let adv_key = adv_key_kind("c", compiler.name); - assert_eq!(&1, info.stats.cache_hits.get_adv(&adv_key).unwrap()); - assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + assert_eq!(&2, info.stats.cache_hits.get_adv(&adv_key).unwrap()); + assert!(info.stats.cache_misses.get_adv(&adv_key).is_none()); }); } @@ -343,32 +341,30 @@ fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { get_stats(|info| { assert_eq!(1, info.stats.compile_requests); assert_eq!(1, info.stats.requests_executed); - assert_eq!(0, info.stats.cache_hits.all()); - assert_eq!(1, info.stats.cache_misses.all()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + assert_eq!(1, info.stats.cache_hits.all()); + assert_eq!(0, info.stats.cache_misses.all()); + assert!(info.stats.cache_misses.get("C/C++").is_none()); let adv_key = adv_key_kind("c", compiler.name); - assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + assert!(info.stats.cache_misses.get_adv(&adv_key).is_none()); }); trace!("compile"); - fs::remove_file(&out_file).unwrap(); sccache_command() .args(compile_cmdline(name, &exe, INPUT, DEV_STDOUT, Vec::new())) .current_dir(tempdir) .envs(env_vars) .assert() .success(); - assert!(fs::metadata(&out_file).map(|m| m.len() > 0).unwrap()); trace!("request stats"); get_stats(|info| { assert_eq!(2, info.stats.compile_requests); assert_eq!(2, info.stats.requests_executed); - assert_eq!(1, info.stats.cache_hits.all()); - assert_eq!(1, info.stats.cache_misses.all()); - assert_eq!(&1, info.stats.cache_hits.get("C/C++").unwrap()); - assert_eq!(&1, info.stats.cache_misses.get("C/C++").unwrap()); + assert_eq!(2, info.stats.cache_hits.all()); + assert_eq!(0, info.stats.cache_misses.all()); + assert_eq!(&2, info.stats.cache_hits.get("C/C++").unwrap()); + assert!(info.stats.cache_misses.get("C/C++").is_none()); let adv_key = adv_key_kind("c", compiler.name); - assert_eq!(&1, info.stats.cache_hits.get_adv(&adv_key).unwrap()); - assert_eq!(&1, info.stats.cache_misses.get_adv(&adv_key).unwrap()); + assert_eq!(&2, info.stats.cache_hits.get_adv(&adv_key).unwrap()); + assert!(info.stats.cache_misses.get_adv(&adv_key).is_none()); }); } From f82d58d021884a5d1f8fa35e589806f773c86a5f Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 13 Oct 2025 17:54:59 -0400 Subject: [PATCH 12/13] Allow set_file_mode to fail too In the error case, we likely have very little permission on the output directory/file (e.g. `/dev/stdout`), so just trying to write is our best bet. --- src/cache/cache.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/cache/cache.rs b/src/cache/cache.rs index 03ab327c7..7e958937e 100644 --- a/src/cache/cache.rs +++ b/src/cache/cache.rs @@ -248,7 +248,12 @@ impl CacheRead { // `optional` is false in this branch, so do not ignore errors let mode = self.get_object(&key, &mut f)?; if let Some(mode) = mode { - set_file_mode(&path, mode)?; + if let Err(e) = set_file_mode(&path, mode) { + // Here we ignore errors from setting file mode because + // if we could not create a temp file in the same directory, + // we probably can't set the mode either (e.g. /dev/stuff) + warn!("Failed to reset file mode: {e}"); + } } } // skip if no object found and it's optional From 0d086643ee110cf7baed544e5c1f7f56d0aa2b20 Mon Sep 17 00:00:00 2001 From: Zhang Maiyun Date: Mon, 13 Oct 2025 17:58:15 -0400 Subject: [PATCH 13/13] Fix unused variable --- tests/system.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/system.rs b/tests/system.rs index 06e943153..9a864e10c 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -278,7 +278,6 @@ fn test_basic_compile_into_dev_null(compiler: Compiler, tempdir: &Path) { // Compile a source file. copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); - let out_file = tempdir.join(OUTPUT); trace!("compile"); sccache_command() .args(compile_cmdline(name, &exe, INPUT, DEV_NULL, Vec::new())) @@ -329,7 +328,6 @@ fn test_basic_compile_into_dev_stdout(compiler: Compiler, tempdir: &Path) { // Compile a source file. copy_to_tempdir(&[INPUT, INPUT_ERR], tempdir); - let out_file = tempdir.join(OUTPUT); trace!("compile"); sccache_command() .args(compile_cmdline(name, &exe, INPUT, DEV_STDOUT, Vec::new()))