URL: <https://savannah.gnu.org/bugs/?68774>
Summary: Existing shared implicit prerequisite is
reclassified as intermediate, causing unnecessary rebuilds
Group: make
Submitter: chuckweiss
Submitted: Tue 13 Oct 2026 11:17:22 PM UTC
Severity: 3 - Normal
Priority: 5 - Normal
Item Group: Bug
Status: None
Privacy: Public
Assigned to: None
Open/Closed: Open
Discussion Lock: Unlocked
Component Version: 4.4.1
Operating System: POSIX-Based
Fixed Release: None
Triage Status: None
_______________________________________________________
Follow-up Comments:
-------------------------------------------------------
Date: Tue 13 Oct 2026 11:17:22 PM UTC By: chuckweiss <chuckweiss>
*Issue summary*
GNU Make incorrectly reclassifies an existing shared implicit prerequisite as
intermediate, causing unnecessary rebuilds even when its recipe preserves its
timestamp. The issue also affects `.NOTINTERMEDIATE` declarations and can
result in deletion of a pre-existing prerequisite.
*Affected versions:*
- GNU Make 4.4.1
- Official HEAD b3802782de3eff2c0f1eda9e7c0befd8cd142162, reporting 4.4.90
*Test platforms:*
- macOS 15.5, arm64
- Linux, x86_64 (GNU Make 4.4.1)
GNU Make 3.81 does not reproduce the problem.
*Reproducer*
The attached repro.mk is standalone and uses only ordinary shell commands. It
defines two implicit rules (%.a and %.b) sharing a prerequisite (%.flags). The
shared prerequisite has a phony force dependency, but its recipe is a no-op.
The .PRECIOUS declaration prevents intermediate-file cleanup so the
unnecessary rebuild can be observed independently.
Contents of repro.mk:
.PHONY: all force
.PRECIOUS: %.flags
all: cached.a cached.b
%.flags: force
@:
%.a: %.flags
@echo REBUILD $@
%.b: %.flags
@echo REBUILD $@
Run these commands in an empty directory containing repro.mk. Set MAKE to the
unpatched GNU Make executable being tested:
MAKE=gmake
touch cached.flags cached.a cached.b
touch -t 200001010000 cached.flags
for pass in 1 2; do
printf 'RUN %s\n' "$pass"
"$MAKE" --no-print-directory -f repro.mk -rR -j1
done
The loop invokes GNU Make twice as two separate processes against the same
files. Both the prerequisite and target recipes leave their files unchanged:
the former is a no-op, and the latter only prints a diagnostic message.
Consequently, all file timestamps remain identical between the two
invocations.
The second invocation demonstrates that the erroneous rebuild is reproducible
without any intervening file changes. It is not dependent on changes made by
the first invocation.
Actual output with both 4.4.1 and unpatched HEAD:
RUN 1
REBUILD cached.b
RUN 2
REBUILD cached.b
Expected output:
RUN 1
RUN 2
Both output files already exist and are newer than cached.flags. The forced
cached.flags recipe is a no-op, so it changes neither the file nor its
timestamp. Neither target should rebuild on either invocation.
*Root cause*
During the second implicit-rule search, pattern_search() reclassifies the
already-processed cached.flags prerequisite as intermediate. Dependency
checking then follows its phony force prerequisite rather than relying on the
unchanged timestamp, causing cached.b to rebuild.
*Additional findings*
The behavior also reproduces with .NOTINTERMEDIATE: %.flags and with global
.NOTINTERMEDIATE:, despite both declarations explicitly preventing
intermediate-file classification.
Additionally, removing .PRECIOUS: %.flags from the reproducer causes GNU Make
4.4.1 to delete the pre-existing cached.flags file after rebuilding cached.b:
REBUILD cached.b
rm cached.flags
This deletion was independently reproduced on Linux. The file existed before
Make was invoked, and its recipe did not modify it.
*Proposed fix and validation*
Attached fix.patch is a candidate fix against the HEAD revision above. It
prevents this reclassification while preserving newly generated
intermediate-file behavior.
All six added regression cases fail on unpatched HEAD and pass with the patch.
All 204 tests in the focused implicit-search, pattern-rule, INTERMEDIATE,
NOTINTERMEDIATE, and SECONDARY categories pass on patched HEAD.
*Attachments*
- repro.mk — Minimal standalone reproducer
- fix.patch — Proposed fix and regression tests
_______________________________________________________
File Attachments:
Name: fix.patch Size: 2.0KiB
<https://file.savannah.gnu.org/file/fix.patch?file_id=59094>
Name: repro.mk Size: 145B
<https://file.savannah.gnu.org/file/repro.mk?file_id=59095>
AGPL NOTICE
These attachments are served by Savane. You can download the corresponding
source code of Savane at
https://savannah.gnu.org/source/savane-4936aed2f86e11f70ec9b06b0570639b784662e4.tar.gz
_______________________________________________________
Reply to this item at:
<https://savannah.gnu.org/bugs/?68774>
_______________________________________________
Message sent via Savannah
https://savannah.gnu.org/
signature.asc
Description: PGP signature
