Skip to content

Commit c5231f1

Browse files
committed
with-watch: fall back for opaque codegen args
1 parent 6af3284 commit c5231f1

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

crates/with-watch/src/analysis.rs

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1917,7 +1917,8 @@ fn analyze_fd(
19171917
matches!(
19181918
token.as_str(),
19191919
"-x" | "-X" | "--exec" | "--exec-batch" | "--list-details"
1920-
)
1920+
) || token.starts_with("--exec=")
1921+
|| token.starts_with("--exec-batch=")
19211922
}) {
19221923
return analyze_fallback(argv, redirects, cwd);
19231924
}
@@ -2794,9 +2795,23 @@ fn analyze_protoc(
27942795
}
27952796

27962797
if token.starts_with('@') {
2797-
push_inferred_input(&mut inputs, &token[1..], cwd)?;
2798-
index += 1;
2799-
continue;
2798+
let mut analysis = analyze_fallback(argv, redirects, cwd)?;
2799+
analysis.status = CommandAnalysisStatus::AmbiguousFallback;
2800+
for argfile_token in argv.iter().skip(1).filter(|value| value.starts_with('@')) {
2801+
let literal_argfile_path = absolutize(argfile_token, cwd);
2802+
analysis.inputs.retain(|input| {
2803+
!matches!(
2804+
input,
2805+
WatchInput::Path { path, .. } if path == &literal_argfile_path
2806+
)
2807+
});
2808+
if let Some(argfile_path) = argfile_token.strip_prefix('@') {
2809+
if !argfile_path.is_empty() {
2810+
push_inferred_input(&mut analysis.inputs, argfile_path, cwd)?;
2811+
}
2812+
}
2813+
}
2814+
return Ok(analysis);
28002815
}
28012816

28022817
if !positional_only {
@@ -4025,6 +4040,21 @@ mod tests {
40254040
.expect("analyze");
40264041
assert_eq!(fallback.adapter_ids, vec![CommandAdapterId::Fallback]);
40274042
assert_path_inputs(&fallback, &[cwd.path().join("src")]);
4043+
4044+
let inline_exec_fallback = analyze_argv(
4045+
&[
4046+
OsString::from("fd"),
4047+
OsString::from("--exec-batch=echo"),
4048+
OsString::from("src"),
4049+
],
4050+
cwd.path(),
4051+
)
4052+
.expect("analyze");
4053+
assert_eq!(
4054+
inline_exec_fallback.adapter_ids,
4055+
vec![CommandAdapterId::Fallback]
4056+
);
4057+
assert_path_inputs(&inline_exec_fallback, &[cwd.path().join("src")]);
40284058
}
40294059

40304060
#[test]
@@ -4218,6 +4248,28 @@ mod tests {
42184248
assert_eq!(protoc_plugin.adapter_ids, vec![CommandAdapterId::Fallback]);
42194249
assert_path_inputs(&protoc_plugin, &[cwd.path().join("src/service.proto")]);
42204250

4251+
fs::write(
4252+
cwd.path().join("args.txt"),
4253+
"--proto_path=src src/service.proto",
4254+
)
4255+
.expect("write args file");
4256+
let protoc_argfile = analyze_argv(
4257+
&[
4258+
OsString::from("protoc"),
4259+
OsString::from("@args.txt"),
4260+
OsString::from("--go_out"),
4261+
OsString::from("gen"),
4262+
],
4263+
cwd.path(),
4264+
)
4265+
.expect("analyze");
4266+
assert_eq!(protoc_argfile.adapter_ids, vec![CommandAdapterId::Fallback]);
4267+
assert_eq!(
4268+
protoc_argfile.status,
4269+
CommandAnalysisStatus::AmbiguousFallback
4270+
);
4271+
assert_path_inputs(&protoc_argfile, &[cwd.path().join("args.txt")]);
4272+
42214273
let capnp_non_compile = analyze_argv(
42224274
&[
42234275
OsString::from("capnp"),

docs/crates-with-watch-foundation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
- Plain `ls`/`dir`/`vdir` directory operands must watch only the named directory plus immediate children, `-R` must switch them to recursive metadata tree snapshots, and `-d`/`--directory` must watch only the named path entry.
4848
- Built-in inference must exclude known outputs, scripts, inline patterns, and opaque fallback operands from the watch set.
4949
- First-class search adapters must cover `grep`, `rg`, `ag`, and `fd`; inline search patterns, globs, and type filters must be treated as control data while explicit search roots and file-valued pattern/ignore inputs stay watchable.
50-
- First-class schema/codegen adapters must cover `protoc`, `flatc`, `thrift`, and `capnp compile`; explicit schema inputs, include/import roots, descriptor/arg files, and conform-reference inputs stay watchable while generated output paths remain filtered. When `protoc` runs without `--proto_path`, the current working directory stays watchable as the implicit import root.
50+
- First-class schema/codegen adapters must cover `protoc`, `flatc`, `thrift`, and `capnp compile`; explicit schema inputs, include/import roots, descriptor inputs, and conform-reference inputs stay watchable while generated output paths remain filtered. `protoc` response files (`@argfile`) are too opaque for v1 inference and must degrade to `exec --input` guidance rather than remaining partially watchable. When `protoc` runs without `--proto_path`, the current working directory stays watchable as the implicit import root.
5151
- Wrapper commands (`env`, `nice`, `nohup`, `stdbuf`, and `timeout`) must unwrap to the delegated command before adapter selection.
5252
- `exec --input` remains the canonical explicit input contract when inference is insufficient, but command-side side-effect metadata may still be inferred for rerun suppression and logging.
5353
- Commands marked as `WritesWatchedInputs` must refresh the baseline snapshot after each run and suppress reruns caused only by their own writes while they were executing.

docs/project-with-watch.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Provide a Rust-based CLI wrapper that reruns delegated shell utilities and arbit
2727
- Commands without safe inferred filesystem inputs must fail clearly and direct operators to `with-watch exec --input ...`.
2828
- Passthrough and shell modes must use adapter-driven input inference that excludes known outputs, scripts, and pattern operands from the watch set.
2929
- First-class search adapters must include `grep`, `rg`, `ag`, and `fd`; search patterns, globs, and type filters must stay out of the watch set while explicit search roots and file-valued pattern/ignore inputs remain watchable.
30-
- First-class schema/codegen adapters must include `protoc`, `flatc`, `thrift`, and `capnp compile`; explicit source files, include/import roots, descriptor/arg files, and conform-reference inputs must remain watchable while generated output paths stay filtered out of the watch set. When `protoc` omits `--proto_path`, the current working directory must be treated as an implicit import root and remain watchable.
30+
- First-class schema/codegen adapters must include `protoc`, `flatc`, `thrift`, and `capnp compile`; explicit source files, include/import roots, descriptor inputs, and conform-reference inputs must remain watchable while generated output paths stay filtered out of the watch set. `protoc` response files (`@argfile`) are too opaque for v1 inference and must fall back to `exec --input` guidance instead of being partially watched. When `protoc` omits `--proto_path`, the current working directory must be treated as an implicit import root and remain watchable.
3131
- `ls`, `dir`, and `vdir` must use metadata listing snapshots instead of recursive file-content hashing: the default watch scope is immediate children, `-R` stays recursive, and `-d` watches only the named path.
3232
- Shell redirects must treat `<` and `<>` targets as watched inputs and `>`, `>>`, `&>`, `&>>`, and `>|` targets as filtered outputs.
3333
- Shell parsing support is limited to command-line expressions plus `&&`, `||`, and `|`; broader shell control-flow stays out of scope until documented otherwise.

0 commit comments

Comments
 (0)