tools: fix stale archive members surviving a Kconfig-driven CSRCS change

During the Toybox port to NuttX, Claude noticed that changes in the
menuconfig weren't taking affect. This issue exists for a long time on
NuttX, in fact BayLibre's presentation from 2017 make jokes about our
building system not been reliable:
https://www.youtube.com/watch?v=XUJK2htXxKw&t=320s

Stale archive members from $(AR)'s additive-only behavior can linger
after Kconfig toggles change which files provide a symbol, causing dead
weight or "multiple definition" link errors on incremental builds.
Fixed by splitting ARCHIVE into two macros: ARCHIVE keeps the original
additive behavior for apps/libapps.a, which many independent
subdirectories contribute to across a build, while the new
ARCHIVE_REBUILD deletes then archives for the far more common case
of a single Makefile building its own self-contained $(OBJS)
- all 39 such call sites now use it.

Assisted-By: Claude Sonnet 5
Signed-off-by: Alan C. Assis <acassis@gmail.com>
This commit is contained in:
Alan Carvalho de Assis 2026-07-28 05:52:38 -03:00 committed by Alan C. Assis
parent 69249fc9e6
commit c027e7c3e4
40 changed files with 106 additions and 56 deletions

View file

@ -183,14 +183,14 @@ $(STARTUP_OBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(STARTUP_OBJS) $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(STARTUP_OBJS) $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board$(DELIM)libboard$(LIBEXT): FORCE
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -147,14 +147,14 @@ $(STARTUP_OBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(OBJS) $(STARTUP_OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(UOBJS) $(STARTUP_OBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board$(DELIM)libboard$(LIBEXT): FORCE
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -86,7 +86,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -125,10 +125,10 @@ $(sort $(COBJS) $(UCOBJS)): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN) $(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
$(UBIN): $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
board$(DELIM)libboard$(LIBEXT):
ifeq ($(CONFIG_ARCH_XC5),y)

View file

@ -98,7 +98,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -84,7 +84,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -88,7 +88,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -115,14 +115,14 @@ $(COBJS) $(UCOBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board$(DELIM)libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -92,7 +92,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -148,14 +148,14 @@ $(STARTUP_OBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(STARTUP_OBJS) $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(STARTUP_OBJS) $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT): FORCE
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -417,7 +417,7 @@ $(HOSTMOBJS): %$(OBJEXT): %.m
# The architecture-specific library
libarch$(LIBEXT): $(NUTTXOBJS)
$(call ARCHIVE, $@, $(NUTTXOBJS))
$(call ARCHIVE_REBUILD, $@, $(NUTTXOBJS))
# The "board"-specific library. Of course, there really are no boards in
# the simulation. However, this is a good place to keep parts of the simulation

View file

@ -101,7 +101,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board TOPDIR="$(TOPDIR)" libboard$(LIBEXT) EXTRADEFINES=$(EXTRADEFINES)

View file

@ -147,14 +147,14 @@ $(COBJS) $(UCOBJS) $(HEAD_COBJ): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board$(DELIM)libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -110,7 +110,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -120,14 +120,14 @@ $(STARTUP_OBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(OBJS) $(STARTUP_OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(UOBJS) $(STARTUP_OBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT): FORCE
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -150,14 +150,14 @@ $(STARTUP_ELF_OBJS): %$(OBJEXT): %.c
ifeq ($(CONFIG_BUILD_FLAT),y)
$(BIN): $(STARTUP_ELF_OBJS) $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
else
$(BIN): $(STARTUP_ELF_OBJS) $(UOBJS)
$(call ARCHIVE, $@, $(UOBJS))
$(call ARCHIVE_REBUILD, $@, $(UOBJS))
endif
$(KBIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT): FORCE
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -78,7 +78,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
libarch$(LIBEXT): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
board/libboard$(LIBEXT):
$(Q) $(MAKE) -C board libboard$(LIBEXT) EXTRAFLAGS="$(EXTRAFLAGS)"

View file

@ -54,7 +54,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -82,7 +82,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -64,7 +64,7 @@ $(CXXOBJS): %$(OBJEXT): %.cxx
$(call COMPILEXX, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds) $(CXXSRCS:.cxx=.ddx)
$(call CATFILE, Make.dep, $^)

View file

@ -102,7 +102,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -108,7 +108,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
context::

View file

@ -89,7 +89,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
context::

View file

@ -105,7 +105,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
mklibgraphics: $(BIN)

View file

@ -66,7 +66,7 @@ context:: bin kbin
depend:: .depend
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
# C library for the kernel phase of the two-pass kernel build

View file

@ -161,7 +161,7 @@ $(COBJS): $(BINDIR)$(DELIMS)%$(OBJEXT): %.c
# the user phase of the two-pass kernel build
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
ifeq ($(CONFIG_LIBC_ZONEINFO_ROMFS),y)
$(Q) $(MAKE) -C zoneinfo all BIN=$(BIN)
endif

View file

@ -48,7 +48,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
.built: .tzbuilt romfs $(OBJS)
$(call ARCHIVE, ..$(DELIM)$(BIN), $(OBJS))
$(call ARCHIVE_REBUILD, ..$(DELIM)$(BIN), $(OBJS))
$(Q) touch .built
# ROMFS file system containing the TZ database

View file

@ -62,7 +62,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -56,7 +56,7 @@ $(COBJS): $(BINDIR)$(DELIM)$(DELIM)%$(OBJEXT): %.c
# the user phase of the two-pass kernel build
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
# math library for the kernel phase of the two-pass kernel build

View file

@ -216,7 +216,7 @@ $(COBJS): $(BINDIR)$(DELIM)%$(OBJEXT): %.c
# the user phase of the two-pass kernel build
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
# NX library for the kernel phase of the two-pass kernel build

View file

@ -77,7 +77,7 @@ $(CPPOBJS): %$(OBJEXT): %.cpp
$(call COMPILEXX, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
context::

View file

@ -80,7 +80,7 @@ $(COBJS): $(BINDIR)$(DELIMS)%$(OBJEXT): %.c
# the user phase of the two-pass kernel build
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
# Memory manager for the kernel phase of the two-pass kernel build

View file

@ -86,7 +86,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -46,7 +46,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
context::

View file

@ -43,7 +43,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -65,7 +65,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -72,13 +72,13 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN1): $(PROXY_OBJS)
$(call ARCHIVE, $@, $(PROXY_OBJS))
$(call ARCHIVE_REBUILD, $@, $(PROXY_OBJS))
$(BIN2): $(STUB_OBJS)
$(call ARCHIVE, $@, $(STUB_OBJS))
$(call ARCHIVE_REBUILD, $@, $(STUB_OBJS))
$(BIN3): $(WRAP_OBJS)
$(call ARCHIVE, $@, $(WRAP_OBJS))
$(call ARCHIVE_REBUILD, $@, $(WRAP_OBJS))
$(SYSCALLWRAPS): .context

View file

@ -443,6 +443,21 @@ endef
# Note: The fileN strings may not contain spaces or characters that may be
# interpreted strangely by the shell
#
# Purely additive: $(AR) only adds or replaces the members it's given, it
# never removes anything already in the archive. That's exactly right for
# a target that many independent callers each contribute a partial object
# list to over the life of a build (apps/libapps.a, built up one
# subdirectory's objects at a time via apps/Make.defs' ARLOCK, which wraps
# this same macro as `flock $1.lock $(call ARCHIVE, $1, $2)` to serialize
# concurrent contributions under -j) -- rebuilding from scratch on every
# call would only ever leave the last contributor's objects behind. Use
# ARCHIVE_REBUILD instead for the other shape: a single Makefile archiving
# its own complete, self-contained object list in one call.
#
# This has to stay a single command, not two separate recipe lines: flock
# execs its argument directly rather than handing it to a shell, so a
# second recipe line here would run outside the lock entirely.
#
# Depends on these settings defined in board-specific Make.defs file
# installed at $(TOPDIR)/Make.defs:
#
@ -457,6 +472,41 @@ define ARCHIVE
$(AR) $1 $2
endef
# ARCHIVE_REBUILD - Replace an archive's entire contents with a list of files
# Example: $(call ARCHIVE_REBUILD, archive-file, "file1 file2 file3 ...")
#
# For the common case ARCHIVE itself doesn't cover: a single Makefile
# archiving its own complete object list ($(OBJS), derived from CSRCS/
# ASRCS) in one call, as nearly every "$(BIN): $(OBJS)" rule in the tree
# does (libs/libc, libs/libm, sched, drivers, fs, mm, net, graphics,
# video, binfmt, crypto, boards, arch/*/src, ...). There, $2 is meant to
# be the archive's entire, authoritative contents, not one contribution
# among several -- so remove any pre-existing archive-file first. Without
# that, a Kconfig change that alters which files CSRCS puts in $2 (e.g.
# one implementation of a function replacing another under a different
# filename) leaves whatever was archived under the old configuration
# lingering in the .a indefinitely across an incremental build: best case
# dead weight, worst case a stale, outdated definition the linker picks up
# instead of the current one, silently or as a "multiple definition" error
# if the two coexist. Rebuilding the archive from scratch every time this
# rule fires costs a few milliseconds even for a large (700+ member)
# library, which is worth paying to not carry that risk.
#
# Depends on these settings defined in board-specific Make.defs file
# installed at $(TOPDIR)/Make.defs:
#
# AR - The command to invoke the archiver (includes any options)
#
# Depends on this settings defined in board-specific defconfig file installed
# at $(TOPDIR)/.config:
#
# CONFIG_WINDOWS_NATIVE - Defined for a Windows native build
define ARCHIVE_REBUILD
$(Q) rm -f $1
$(AR) $1 $2
endef
# PRELINK - Prelink a list of files
# This is useful when files were compiled with fvisibility=hidden.
# Any symbol which was not explicitly made global is invisible outside the

View file

@ -48,7 +48,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)

View file

@ -46,7 +46,7 @@ $(COBJS): %$(OBJEXT): %.c
$(call COMPILE, $<, $@)
$(BIN): $(OBJS)
$(call ARCHIVE, $@, $(OBJS))
$(call ARCHIVE_REBUILD, $@, $(OBJS))
makedepfile: $(CSRCS:.c=.ddc) $(ASRCS:.S=.dds)
$(call CATFILE, Make.dep, $^)