build.rs does not track native/metal headers; stale object file can be linked after a header change #15
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Symptom
build.rsdeclares a rebuild dependency onnative/metal/ds4_metal.monly. Editing any of the headers it compiles against does not trigger a rebuild, so cargo happily links a stale object file against changed declarations. The result is a silently mismatched ABI — the worst class of FFI bug, because it produces wrong numbers or a crash with no compiler diagnostic pointing at the cause.This matters more than usual here because the native sources are a vendored mirror of
../ds4, re-synced by hand.Evidence
build.rs:8-15:native/metal/contains three headers that are on the include path and none of them are tracked:ds4.h,ds4_gpu.h,ds4_ssd.h.ds4_gpu.his the one that matters — it declares every function in theunsafe extern "C"block atsrc/engine/metal/gpu.rs:8-773. Change a parameter there without a rebuild and the Rust and ObjC sides disagree about the call frame.The
.metalkernel sources undermetal/are not affected: they are loaded at runtime via the env vars set insrc/engine/metal.rs:51-73, not compiled bybuild.rs.How ds4 handles this
../ds4/Makefilelists header prerequisites explicitly for every object, including the Metal one:Makefile:243—ds4_metal.o: ds4_metal.m ds4_gpu.h $(METAL_SRCS)Makefile:168ds4.o: ds4.c ds4.h ds4_ssd.h ds4_distributed.h ds4_gpu.h,Makefile:189ds4_server.o: ds4_server.c ds4.h ds4_ssd.h ds4_distributed.h ds4_help.h ds4_kvstore.h rax.hNote ds4 tracks
$(METAL_SRCS)too, because its build embeds the kernels; DS4Server ships them as bundle resources instead, so only the headers are missing here.Vendoring context that raises the stakes — the native layer is a near-verbatim copy of ds4's:
native/metal/ds4_gpu.his byte-identical to../ds4/ds4_gpu.h.native/metal/ds4_metal.mdiffers from../ds4/ds4_metal.min exactly 3 lines, all comments ("Objective-C Metal glue for the Rust engine" vs "the C engine").metal/*.metalkernels are byte-identical to../ds4/metal/*.metal.So the expected workflow is "pull upstream ds4 changes into
native/metal/" — which is precisely the workflow that will drop a newds4_gpu.hinto the tree and get a stalelibds4_metal.alinked against it.Suggested fix
One line, plus optional tightening:
Directory-level
rerun-if-changedcovers the.mand all three headers, and survives new files being added. (Cargo recurses into directories for this directive.) Keep or drop the existing per-file line as you prefer — the directory one subsumes it.While in here, consider adding a short comment recording that
native/metal/is a vendored mirror of../ds4and which files are expected to stay byte-identical upstream. That is the missing half of the sync workflow.Acceptance criteria
touch native/metal/ds4_gpu.h && cargo buildrecompilesds4_metal.m. (Verify withcargo build -vor by checking the object file's mtime.)ds4.handds4_ssd.h.make bundlestill succeeds.Fixed in
7686c22. build.rs now tracks the entire native/metal directory, covering ds4_metal.m, all three current headers, and future vendored additions. Verified by touching ds4_gpu.h, ds4.h, and ds4_ssd.h individually; each caused Cargo to recompile the native target. Also passed cargo fmt, Clippy (-D warnings), make bundle, and cargo test --all-features.