Skip to content

Commit d1320a2

Browse files
branchseerclaude
andcommitted
fix: remove fstat interception that caused duplicate path tracking
fstat(fd) uses F_GETPATH on macOS which returns canonical paths, while open(path) preserves raw relative paths. This caused the same file to be recorded under two different paths, leading to non-deterministic cache miss messages via rayon's find_map_any. Also simplify glob_inputs negation handling (always use .not()) and add e2e test for glob meta characters in package paths (wax::escape). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c11aa0f commit d1320a2

File tree

9 files changed

+57
-31
lines changed

9 files changed

+57
-31
lines changed

crates/fspy_preload_unix/src/interceptions/stat.rs

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@ use fspy_shared::ipc::AccessMode;
22
use libc::{c_char, c_int, stat as stat_struct};
33

44
use crate::{
5-
client::{
6-
convert::{Fd, PathAt},
7-
handle_open,
8-
},
5+
client::{convert::PathAt, handle_open},
96
macros::intercept,
107
};
118

@@ -30,16 +27,6 @@ unsafe extern "C" fn lstat(path: *const c_char, buf: *mut stat_struct) -> c_int
3027
unsafe { lstat::original()(path, buf) }
3128
}
3229

33-
intercept!(fstat(64): unsafe extern "C" fn(fd: c_int, buf: *mut stat_struct) -> c_int);
34-
unsafe extern "C" fn fstat(fd: c_int, buf: *mut stat_struct) -> c_int {
35-
// SAFETY: fd is a valid file descriptor provided by the caller of the interposed function
36-
unsafe {
37-
handle_open(Fd(fd), AccessMode::READ);
38-
}
39-
// SAFETY: calling the original libc fstat() with the same arguments forwarded from the interposed function
40-
unsafe { fstat::original()(fd, buf) }
41-
}
42-
4330
intercept!(fstatat(64): unsafe extern "C" fn(dirfd: c_int, pathname: *const c_char, buf: *mut stat_struct, flags: c_int) -> c_int);
4431
unsafe extern "C" fn fstatat(
4532
dirfd: c_int,

crates/vite_task/src/session/execute/glob_inputs.rs

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -94,29 +94,18 @@ pub fn compute_globbed_inputs(
9494
return Ok(BTreeMap::new());
9595
}
9696

97-
let negation = if negative_globs.is_empty() {
98-
None
99-
} else {
100-
let negatives: Vec<Glob<'static>> = negative_globs
101-
.iter()
102-
.map(|p| Ok(Glob::new(p.as_str())?.into_owned()))
103-
.collect::<anyhow::Result<_>>()?;
104-
Some(wax::any(negatives)?)
105-
};
97+
let negatives: Vec<Glob<'static>> = negative_globs
98+
.iter()
99+
.map(|p| Ok(Glob::new(p.as_str())?.into_owned()))
100+
.collect::<anyhow::Result<_>>()?;
101+
let negation = wax::any(negatives)?;
106102

107103
let mut result = BTreeMap::new();
108104

109105
for pattern in positive_globs {
110106
let glob = Glob::new(pattern.as_str())?.into_owned();
111107
let walk = glob.walk(workspace_root.as_path());
112-
match &negation {
113-
Some(negation) => {
114-
collect_walk_entries(walk.not(negation.clone())?, workspace_root, &mut result)?;
115-
}
116-
None => {
117-
collect_walk_entries(walk, workspace_root, &mut result)?;
118-
}
119-
}
108+
collect_walk_entries(walk.not(negation.clone())?, workspace_root, &mut result)?;
120109
}
121110

122111
Ok(result)
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "inputs-glob-meta-in-path",
3+
"private": true
4+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "[lib]",
3+
"private": true
4+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export const lib = 'initial';
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"tasks": {
3+
"build": {
4+
"command": "print-file src/main.ts",
5+
"inputs": ["src/**/*.ts"],
6+
"cache": true
7+
}
8+
}
9+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
packages:
2+
- 'packages/*'
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Test that glob meta characters in package paths are correctly escaped by wax::escape.
2+
# Without escaping, "packages/[lib]/src/**/*.ts" would interpret [lib] as a character
3+
# class matching 'l', 'i', or 'b' instead of the literal directory name.
4+
5+
[[e2e]]
6+
name = "cache hit then miss on file change"
7+
steps = [
8+
"vp run [lib]#build",
9+
"vp run [lib]#build",
10+
"replace-file-content packages/[lib]/src/main.ts initial modified",
11+
"vp run [lib]#build",
12+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
---
2+
source: crates/vite_task_bin/tests/e2e_snapshots/main.rs
3+
expression: e2e_outputs
4+
---
5+
> vp run [lib]#build
6+
~/packages/[lib]$ print-file src/main.ts
7+
export const lib = 'initial';
8+
> vp run [lib]#build
9+
~/packages/[lib]$ print-file src/main.tscache hit, replaying
10+
export const lib = 'initial';
11+
12+
---
13+
[vp run] cache hit, <duration> saved.
14+
> replace-file-content packages/[lib]/src/main.ts initial modified
15+
16+
> vp run [lib]#build
17+
~/packages/[lib]$ print-file src/main.ts ✗ cache miss: content of input 'packages/[lib]/src/main.ts' changed, executing
18+
export const lib = 'modified';

0 commit comments

Comments
 (0)