make build exits 0 without building anything #110
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?
make buildreports success and produces no binary.Makefile:1listsbuildin.PHONY, but nobuild:rule exists —the real target is
vaultik:at line 70. Declaring it phony is whatconverts the error you would otherwise get ("No rule to make target") into
a silent success: make considers a phony target with no prerequisites and
no recipe already satisfied.
Found during the PR #109
review as an out-of-scope observation, then reproduced. Pre-existing —
Makefileis byte-identical tomainacross that PR.Why this matters more than a missing alias
make buildis the documented convention for building in this org, sothis is the command a person or agent reaches for first. It exits 0. A
caller that checks the exit code — CI, a script, an agent verifying its
own work — concludes the build succeeded. Nothing is produced and nothing
says so.
That is the same failure shape this repo has now closed in five other
places: #78 (linter version skew), #85 (Docker layer cache), #93 (Go test
cache), #88/#99 (shared lint cache), each a gate reporting a green it did
not earn. This one is in the build target itself.
Definition of done
make buildbuilds the binary. Simplest correct fix isbuild: vaultik, keepingvaultik:as the file rule.rm -f vaultik && make buildproduces the binary and exits 0; thenegative control is that a deliberately broken build makes it exit
non-zero. Verify both — a target that cannot fail is no better than one
that cannot build.
.PHONYfor the same defect: any name listed therewithout a corresponding rule silently succeeds. Fix or remove each.
script/cibuildexits 0.Note
.PHONYon this line also listsall,local,install,docker,hooks,depsand others. I have not checked whether each has a rule —item 3 covers it, and any that do not have the identical silent-success
behaviour.
Plan
Implementing this together with
issue #108 as one PR
against
main.build: vaultikas the description suggests, keepingvaultik:as the file rule so incremental builds still work.
rm -f vaultik && make buildproduces the binary and exits 0, and adeliberately broken source tree makes the same command exit non-zero.
.PHONYaudit, already done against the currentMakefile: the 19names listed are
all,bootstrap,setup,check,test,lint,lint-fix,fmt,fmt-check,build,clean,deps,test-coverage,local,install,release,release-snapshot,docker,hooks. Every one of them has a rule exceptbuild, sobuildis the only instance of this defect.vaultikis correctlyabsent from
.PHONY, being a real file target.a test that parses the
Makefile, extracts the.PHONYnames andthe set of declared rules, and fails on any phony name with no rule.
It fails on the current tree (
build) and passes once the rule isadded, which is also how the audit stays true for names added later.
The guard is a parse rather than an invocation of
makeon purpose:make testis what runs it, so shelling back intomake buildfrominside it would nest the build in the test and drop a binary into the
tree as a side effect of testing. The one thing a parse cannot prove —
that the recipe can still fail — is the manual negative control in
item 2.
Implemented in
PR #111, on branch
fix-prune-json-and-build.What was built
build: vaultikadded, withvaultik:kept as the file rule..PHONYaudit (item 3)All 19 names on that line were checked against the rules in the file:
all,bootstrap,setup,check,test,lint,lint-fix,fmt,fmt-check,build,clean,deps,test-coverage,local,install,release,release-snapshot,docker,hooks.buildwas the only one without a rule. Every other name has one,so nothing else needed fixing or removing.
vaultikis correctly absentfrom
.PHONY, being a real file target — putting it there would havemade the binary rebuild unconditionally.
A regression guard keeps that true without a re-audit by hand:
TestPhonyTargetsAllHaveRulesparses theMakefile, extracts the.PHONYnames and the set of declared rules, and fails on any phonyname with no rule. A companion test asserts
buildreaches the rulethat produces the binary, so giving
build:an empty recipe of its ownwould not satisfy it. Both fail on the tree before this change.
The guard parses rather than invoking
make:make testis what runsit, so shelling back into
make buildwould nest a build inside thetest run and drop a binary into the tree as a side effect of testing.
Both directions (item 2)
Negative control, with a deliberate syntax error added to a file in
cmd/vaultik:So the target both builds and can fail. The broken file was removed
before committing and is not in the branch.
Item 4
script/cibuildexits 0. The three checkRUNlayers each echoed thefresh
CHECK_EPOCH, somake fmt-check,make lintandmake testexecuted rather than replaying from cache; the only
CACHEDlayers arebelow the
ARG CHECK_EPOCHline.One reporting note for whoever reads the gate next:
make testnowprints 16
oklines rather than 15.cmd/vaultikhad no test fileuntil this change and was reported as
? no test files; the Makefileguard lives there, beside the Makefile it guards, so the package now
compiles and runs tests.