Merge pull request #2123 from brazilofmux/fix/2118-orphan-object-deps

Drop objects that lack sibling .d files so header edits cannot under-rebuild (#2118).
This commit is contained in:
Stephen Dennis 2026-08-05 17:45:58 -06:00 committed by GitHub
commit b01294b1f0
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 57 additions and 2 deletions

View file

@ -20,6 +20,12 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co
- **Always `make clean` after changing configure flags.** A partially-built
tree (stale `muxscript`, fresh `engine.so`) produces failures that describe
nothing real — ASan's "Interceptors are not working" is the usual tell.
- **Header edits can under-rebuild the same way (#2118).** Engine/libmux use
`-MMD` side-car `.d` files for header deps. An object without its `.d` is
invisible to make: a layout or struct-size header change then rebuilds only
some TUs and the binary lies coherently (that was the whole of #2107). The
Makefiles drop orphan objects automatically; if another host is green on the
same commit, treat that as a build-hygiene signal first.
Platform prerequisites (Debian/Ubuntu and Homebrew package lists), autotools
version constraints for regenerating `configure`, and a symptom→cause table

View file

@ -129,12 +129,17 @@ that look like bugs and are not:
| `#error Need EVP_MD_CTX_new() or EVP_MD_CTX_create()` | configure's OpenSSL function probes answered "no". They probe by linking, so this means OpenSSL was not in scope *at configure time* — re-run configure and check `checking for EVP_MD_CTX_new... yes`. |
| `--enable-nls requires libintl.h` | macOS without the Homebrew prefix passed in. See above. |
| ASan reports "Interceptors are not working" | A partially-built tree: some objects instrumented, some not. `make clean` and rebuild the whole thing. Do not debug the report itself. |
| Coherent wrong behaviour after a header edit; same commit green on another box | Objects present without sibling `.d` files — make has no header edges for them and under-rebuilds. `#2118`. Engine and libmux drop such orphans automatically; if the smell remains, `make clean`. |
That last one deserves emphasis, because it wastes an afternoon every time.
That first partial-tree row deserves emphasis, because it wastes an afternoon every time.
**A partially-built tree lies.** After changing configure flags — sanitizers,
`--enable-nls`, JIT — run `make clean` before `make install`. A stale
`muxscript` against a fresh `engine.so` produces failures that describe nothing
real.
real. The same class of lie is a **header edit that rebuilds only some of its
dependents** when `.d` files are missing (#2118, closed the false #2107 chase):
the binary is internally inconsistent but fails in a way that looks like a
real defect. Prefer treating same-commit green on another host as a build-hygiene
signal before inventing a platform bug.
## Verifying a build

View file

@ -35,6 +35,17 @@ all-local: libmux.so
%.lo: %.c
$(CC) $(AM_CFLAGS) -I$(top_srcdir)/include $(DYNAMICLIB_CXXFLAGS) -fvisibility=hidden -MMD -MF $(@:.lo=.d) -c -o $@ $<
# An object without a sibling .d is invisible to header tracking (#2118).
# Drop it so the next compile regenerates both; otherwise a header edit
# under-rebuilds and the binary lies coherently.
# strip: foreach leaves spaces for non-orphans; unstripped whitespace
# would make ifneq always true and spam the warning on every make.
ORPHAN_LOS := $(strip $(foreach o,$(LIBMUX_OBJS),$(if $(wildcard $(o)),$(if $(wildcard $(o:.lo=.d)),,$(o)))))
ifneq ($(ORPHAN_LOS),)
$(warning libmux: dropping objects with no .d (header deps unknown, #2118): $(ORPHAN_LOS))
$(shell rm -f $(ORPHAN_LOS))
endif
-include $(LIBMUX_CXX_OBJS:.lo=.d)
-include $(LIBMUX_C_OBJS:.lo=.d)

View file

@ -491,6 +491,17 @@ all-local: libmux.so
%.lo: %.c
$(CC) $(AM_CFLAGS) -I$(top_srcdir)/include $(DYNAMICLIB_CXXFLAGS) -fvisibility=hidden -MMD -MF $(@:.lo=.d) -c -o $@ $<
# An object without a sibling .d is invisible to header tracking (#2118).
# Drop it so the next compile regenerates both; otherwise a header edit
# under-rebuilds and the binary lies coherently.
# strip: foreach leaves spaces for non-orphans; unstripped whitespace
# would make ifneq always true and spam the warning on every make.
ORPHAN_LOS := $(strip $(foreach o,$(LIBMUX_OBJS),$(if $(wildcard $(o)),$(if $(wildcard $(o:.lo=.d)),,$(o)))))
ifneq ($(ORPHAN_LOS),)
$(warning libmux: dropping objects with no .d (header deps unknown, #2118): $(ORPHAN_LOS))
$(shell rm -f $(ORPHAN_LOS))
endif
-include $(LIBMUX_CXX_OBJS:.lo=.d)
-include $(LIBMUX_C_OBJS:.lo=.d)

View file

@ -41,6 +41,17 @@ all-local: engine.so
%.eo: %.cpp
$(CXX) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(DYNAMICLIB_CXXFLAGS) -fvisibility=hidden -MMD -MF $(@:.eo=.d) -c -o $@ $<
# An object without a sibling .d is invisible to header tracking (#2118).
# Drop it so the next compile regenerates both; otherwise a header edit
# under-rebuilds and the binary lies coherently (see #2107, closed invalid).
# strip: foreach leaves spaces for non-orphans; unstripped whitespace
# would make ifneq always true and spam the warning on every make.
ORPHAN_EOS := $(strip $(foreach o,$(ENGINE_CXX_OBJS),$(if $(wildcard $(o)),$(if $(wildcard $(o:.eo=.d)),,$(o)))))
ifneq ($(ORPHAN_EOS),)
$(warning engine: dropping objects with no .d (header deps unknown, #2118): $(ORPHAN_EOS))
$(shell rm -f $(ORPHAN_EOS))
endif
-include $(ENGINE_CXX_OBJS:.eo=.d)
engine.so: $(ENGINE_CXX_OBJS) $(top_builddir)/sqlite/libsqlite3.a $(top_builddir)/lua54/liblua54.a $(top_builddir)/lib/libmux.so

View file

@ -498,6 +498,17 @@ all-local: engine.so
%.eo: %.cpp
$(CXX) $(CXXFLAGS) $(AM_CPPFLAGS) $(CPPFLAGS) $(DYNAMICLIB_CXXFLAGS) -fvisibility=hidden -MMD -MF $(@:.eo=.d) -c -o $@ $<
# An object without a sibling .d is invisible to header tracking (#2118).
# Drop it so the next compile regenerates both; otherwise a header edit
# under-rebuilds and the binary lies coherently (see #2107, closed invalid).
# strip: foreach leaves spaces for non-orphans; unstripped whitespace
# would make ifneq always true and spam the warning on every make.
ORPHAN_EOS := $(strip $(foreach o,$(ENGINE_CXX_OBJS),$(if $(wildcard $(o)),$(if $(wildcard $(o:.eo=.d)),,$(o)))))
ifneq ($(ORPHAN_EOS),)
$(warning engine: dropping objects with no .d (header deps unknown, #2118): $(ORPHAN_EOS))
$(shell rm -f $(ORPHAN_EOS))
endif
-include $(ENGINE_CXX_OBJS:.eo=.d)
engine.so: $(ENGINE_CXX_OBJS) $(top_builddir)/sqlite/libsqlite3.a $(top_builddir)/lua54/liblua54.a $(top_builddir)/lib/libmux.so