Skip to content

fix(build): stamp-file dependency for version.o REVISION staleness - #1386

Open
nerdCopter wants to merge 7 commits into
emuflight:masterfrom
nerdCopter:fix/version-o-stale-revision
Open

fix(build): stamp-file dependency for version.o REVISION staleness#1386
nerdCopter wants to merge 7 commits into
emuflight:masterfrom
nerdCopter:fix/version-o-stale-revision

Conversation

@nerdCopter

@nerdCopter nerdCopter commented Aug 14, 2026

Copy link
Copy Markdown
Member

AI Generated pull-request

Summary

version.o's build rule only depends on $(SRC), not on the REVISION value itself. A
dirty-tree build (REVISION=uncommitted_<timestamp>) followed by a clean-tree build with no
source-file changes leaves that stale object untouched by Make's own dependency graph, so the
firmware keeps reporting uncommitted in CLI version even though HEAD is clean and
REVISION now resolves to the real 7-char commit hash.

This is a regression: #1125 (e8d439d26) originally fixed this exact bug by making version.o
depend on a FORCE phony target. #1238 (ae9a736d8) reverted that back to $(SRC), explicitly
to restore no-op incremental-build behavior for parallel builds — a legitimate goal, but it
reintroduced the staleness bug because $(SRC) cannot express "REVISION's value changed."

Fix: add REVISION_STAMP, a per-target file containing the current REVISION string. version.o
now depends on both $(SRC) and REVISION_STAMP, so:

  • same REVISION, no source change → stamp untouched → no rebuild (preserves fix(make): eliminate mkdir races in parallel builds (-jN) #1238's no-op
    incremental-build behavior)
  • REVISION changes (dirty→clean transition, or a new commit landing with no other source diff)
    → stamp rewritten → version.o rebuilds with the correct value

REVISION_STAMP's mkdir/compare logic is an explicit recipe rule (check_revision_stamp phony
trigger), not a parse-time $(shell ...) call, so it only runs when actually pulled into a build
goal — non-build goals like make help never touch it.

Test plan

  • Reproduced the exact regression: clean build → SHA embedded; dirty a tracked non-$(SRC)
    file → rebuild → uncommitted_<timestamp> embedded; revert to clean, zero further changes →
    rebuild → version.o correctly recompiles with the real SHA (this transition was previously
    silently skipped)
  • Confirmed no-op case unaffected: rebuild again with zero changes → version.o mtime
    unchanged, no unnecessary recompile
  • Confirmed non-build goals don't touch the stamp: make help leaves .revision_stamp absent
  • Hardware bench check: flashed HELIOSPRING, TUNERCF405, and PYRODRONEF7, all report (7da81fa) — the
    real commit hash, not uncommitted — in CLI version

Summary by CodeRabbit

  • Bug Fixes
    • Improved build accuracy by detecting revision changes and recompiling affected components when needed.
    • Preserved incremental build performance when the revision remains unchanged.

PR#1238 (ae9a736) reverted the FORCE dependency on version.o back to
$(SRC) for BF-parity no-op incremental builds, reintroducing the exact
bug PR#1125 (e8d439d) had fixed: a dirty-tree build embeds
REVISION=uncommitted_<timestamp> into version.o, and a subsequent
clean-tree build with no source-file changes leaves that stale object
untouched, so the firmware keeps reporting "uncommitted" even though
HEAD is clean and REVISION now resolves to the real commit hash.

Add REVISION_STAMP, a per-target file whose content is the current
REVISION value. Its mtime only advances when the value actually
changes. version.o depends on both $(SRC) and REVISION_STAMP, so:
- same REVISION, no source change -> stamp untouched -> no rebuild
  (preserves the no-op incremental-build behavior PR#1238 wanted)
- REVISION changes (dirty->clean transition, or a new commit landing
  with no other source diff) -> stamp rewritten -> version.o rebuilds
  with the correct value

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@nerdCopter

Copy link
Copy Markdown
Member Author

@coderabbitai

AI generated comment

Not requesting a full review — asking for analysis on three specific points before this goes further:

  1. Is the REVISION_STAMP approach (Makefile lines ~307-313) a correct and minimal resolution to the staleness bug described in the PR body? Specifically: does making version.o depend on both $(SRC) and a stamp file whose content is $(REVISION) correctly cover every case where REVISION's resolved value can change without any $(SRC) file's mtime changing?

  2. Does this adhere to this codebase's existing Makefile design/architecture and prior decisions? In particular, fix(make): eliminate mkdir races in parallel builds (-jN) #1238 (ae9a736d8) deliberately moved version.o off a FORCE dependency and onto $(SRC) for parallel-build no-op-incremental-build correctness. Does the stamp-file approach preserve that intent while still closing the gap fix(make): eliminate mkdir races in parallel builds (-jN) #1238 reopened (originally fixed by fix(build): always recompile version.o with explicit 7-char revision #1125 / e8d439d26)?

  3. Is the implementation OS-agnostic where appropriate? The stamp-file logic uses $(shell mkdir -p ...) and a [ ... ] || echo ... > ... POSIX-shell comparison inside a $(shell ...) call. Is this consistent with the rest of the Makefile's existing shell/OS assumptions (e.g. the pre-existing git diff --shortstat, date, grep/awk calls), or does it introduce a new portability constraint (e.g. Windows without MSYS2/WSL/Cygwin) beyond what already exists?

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 948d5622-5c81-49ed-b762-8aeecebd95a5

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: f4765300-deb0-410a-9da9-1b9293622c31

📥 Commits

Reviewing files that changed from the base of the PR and between 6d774e6 and 7da81fa.

📒 Files selected for processing (1)
  • Makefile
📜 Recent review details
⏰ Context from checks skipped due to timeout. (13)
  • GitHub Check: Codacy Static Code Analysis
  • GitHub Check: build (targets-group-3)
  • GitHub Check: build (targets-group-10)
  • GitHub Check: build (targets-group-9)
  • GitHub Check: build (targets-group-11)
  • GitHub Check: build (targets-group-4)
  • GitHub Check: build (targets-group-6)
  • GitHub Check: build (targets-group-8)
  • GitHub Check: build (targets-group-5)
  • GitHub Check: build (targets-group-rest)
  • GitHub Check: build (targets-group-2)
  • GitHub Check: build (targets-group-1)
  • GitHub Check: build (targets-group-7)
🧰 Additional context used
🧠 Learnings (1)
📓 Common learnings
Learnt from: nerdCopter
Repo: emuflight/EmuFlight PR: 0
File: :0-0
Timestamp: 2026-08-14T18:13:52.129Z
Learning: For the EmuFlight Makefile `REVISION_STAMP` mechanism, the project accepts the GNU Make coarse-filesystem-mtime limitation because Linux CI and supported build environments do not use affected timestamp resolution.
🪛 checkmake (0.3.2)
Makefile

[warning] 314-314: Target "$(OBJECT_DIR)/$(TARGET)/build/version.o" should be declared PHONY.

(phonydeclared)

🔇 Additional comments (1)
Makefile (1)

307-322: LGTM!


📝 Walkthrough

Walkthrough

The Makefile adds a revision stamp to the version.o prerequisites. A phony check updates the stamp only when REVISION changes, preserving incremental builds and triggering recompilation when required.

Changes

Revision stamp build tracking

Layer / File(s) Summary
Revision stamp dependency and update rule
Makefile
version.o now depends on all source files and the revision stamp. The stamp directory is created as needed, and the stamp is rewritten only when its stored REVISION differs. A phony check ensures the dependency is evaluated.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 7da81

The change keeps version.o synchronized with the current revision while preserving no-op incremental builds; no actionable merge-blocking risk remains beyond normal checks and review.

Possibly related PRs

  • emuflight/EmuFlight#1125: Both changes update Makefile dependency handling so revision changes trigger version.o recompilation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly identifies the Makefile fix for stale REVISION values in version.o.
Description check ✅ Passed The description explains the regression, implementation, objectives, and detailed test results; the template’s removable text was appropriately omitted.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

makefile=$(fd -t f -a '^Makefile$' . | head -n 1)
if [ -z "$makefile" ]; then
  echo "Makefile not found" >&2
  exit 1
fi

echo "=== Makefile outline: revision and version-related lines ==="
rg -n -C 5 'REVISION|version\.o|FORCE|mkdir -p|git diff|shortstat|date|grep|awk|\$\(\s*shell' "$makefile"

echo
echo "=== Current target section (lines 260-340) ==="
sed -n '260,340p' "$makefile"

echo
echo "=== Commit e8d439d26 ==="
git show --format=fuller --find-renames --stat e8d439d26
git show --format= --find-renames e8d439d26 -- "$makefile"

echo
echo "=== Commit ae9a736d8 ==="
git show --format=fuller --find-renames --stat ae9a736d8
git show --format= --find-renames ae9a736d8 -- "$makefile"

echo
echo "=== Pull request metadata for `#1125` and `#1238` ==="
gh pr view 1125 --repo emuflight/EmuFlight --json number,title,state,mergedAt,body,commits,files
gh pr view 1238 --repo emuflight/EmuFlight --json number,title,state,mergedAt,body,commits,files

Length of output: 31506


🏁 Script executed:

#!/bin/bash
set -euo pipefail

makefile=$(fd -t f -a '^Makefile$' . | head -n 1)

echo "=== Shell declarations and POSIX-shell recipe constructs ==="
rg -n -C 3 '^[[:space:]]*(SHELL|\.SHELLFLAGS)[[:space:]]*[:?+]?=|(^|[^$])\[\s|&&|;[[:space:]]*\\$|trap |mktemp |for [A-Za-z_].* in |if \[' "$makefile"

echo
echo "=== Build, dependency-include, and cleanup rules ==="
rg -n -C 5 'include .*TARGET_DEPS|TARGET_DEPS|CLEAN_ARTIFACTS|^clean:|^clean_all:|build/version\.o|/%.o:' "$makefile"

echo
echo "=== Revision stamp path relative to target directory prerequisites ==="
rg -n -C 4 'TARGET_DIRS|REVISION_STAMP|\.SECONDEXPANSION|dir \$\$@' "$makefile"

echo
echo "=== GNU Make equal-mtime prerequisite experiment (temporary directory only) ==="
tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT
cat > "$tmpdir/Makefile" <<'EOF'
out: stamp
	`@echo` rebuilt > out
EOF
printf 'old\n' > "$tmpdir/out"
printf 'new\n' > "$tmpdir/stamp"
touch -t 202601010101 "$tmpdir/out" "$tmpdir/stamp"
echo "mtimes:"
stat -c '%n %Y' "$tmpdir/out" "$tmpdir/stamp"
echo "make result:"
make -C "$tmpdir" --no-print-directory out
printf 'out content: '
cat "$tmpdir/out"

Length of output: 15010


🏁 Script executed:

#!/bin/bash
set -euo pipefail

tmpdir=$(mktemp -d)
trap 'rm -rf "$tmpdir"' EXIT

cat > "$tmpdir/Makefile" <<'EOF'
out: stamp
	`@printf` 'rebuilt\n' > out
EOF

printf 'old\n' > "$tmpdir/out"
printf 'new\n' > "$tmpdir/stamp"

# Deliberately assign the same coarse (one-second) mtime to both files.
touch -t 202601010101 "$tmpdir/out" "$tmpdir/stamp"

echo "=== GNU Make version ==="
make --version | head -n 1
echo "=== Build result with equal target/prerequisite mtimes ==="
make -C "$tmpdir" --no-print-directory out
printf 'out content: '
cat "$tmpdir/out"

Length of output: 929


@nerdCopter

  1. Correctness and minimality

The dependency model is correct for the reported staleness path.

Makefile Lines 311 through 314 write $(REVISION) to a target-specific stamp. When the resolved value changes, the stamp content changes and its mtime updates. GNU Make then rebuilds version.o because $(REVISION_STAMP) is newer.

This covers these value changes without a $(SRC) mtime change:

  • Dirty tree → clean tree.
  • Clean HEAD → another clean HEAD.
  • Clean tree → dirty tree.
  • Dirty tree → a different dirty-state date value.
  • A command-line or environment override that changes REVISION.

The existing $(SRC) prerequisite remains useful. It retains the previous behavior that rebuilds version.o when source changes.

The implementation is minimal in the dependency-graph sense. It adds one content-addressed freshness input. It does not restore the unconditional FORCE dependency.

There is one strict qualification to “every case.” GNU Make compares mtimes. On a filesystem with coarse timestamp resolution, a rewritten stamp can receive the same mtime as an existing version.o. In that case, Make can consider version.o current even though the stamp content changed. Normal Linux CI filesystems use sufficiently fine timestamp resolution, so this is unlikely in the intended environment. It is still not a universal guarantee across all filesystems.

I could not run the focused equal-mtime Make experiment because the sandbox does not provide make.

  1. Fit with #1125 and #1238

The approach fits the intent of both prior changes.

  • #1125 correctly identified that $(SRC) alone cannot represent a changed REVISION.
  • #1238 correctly removed FORCE because it recompiles version.o and relinks each target on every invocation.
  • The stamp restores the missing REVISION input without making unchanged builds rebuild.

When REVISION is unchanged, Line 313 does not modify the stamp. version.o remains current. This preserves the no-op incremental-build behavior that #1238 intended.

When REVISION changes, only version.o gains a newer prerequisite. The normal object-to-ELF dependency graph then performs the required relink and artifact regeneration.

One minor architectural concern exists. The parse-time $(shell mkdir -p ...) and stamp update run for any Make invocation that parses this section, including non-build goals such as make help. This is a small side effect. It does not affect the correctness of the build dependency, but a normal explicit stamp rule would better match Make’s target-based architecture.

  1. OS portability

The stamp logic does not introduce a new practical shell-environment requirement.

The Makefile already assumes a POSIX-compatible shell and userland tools:

  • date, git, cut, grep, and awk run during Makefile parsing.
  • mkdir -p, mktemp, trap, [ ... ], shell loops, and rm -rf run in recipes.
  • The Makefile does not set SHELL or .SHELLFLAGS.

Therefore, native Windows GNU Make with cmd.exe was already unsupported by the existing Makefile. Builds require MSYS2, WSL, Cygwin, or a similar POSIX-compatible environment. The new [ ... ], cat, mkdir -p, and shell redirection are consistent with that existing requirement.

Conclusion: The stamp is the right minimal fix for the stated regression and preserves #1238’s no-op behavior. The only technical qualification is the rare coarse-mtime filesystem case.

You are interacting with an AI system.

The parse-time $(shell mkdir -p ...) and stamp-write ran on every Make
invocation, including non-build goals like 'make help', not just when
version.o was actually being built. Move the mkdir/stamp-compare logic
into a proper recipe rule (version.o's REVISION_STAMP prerequisite,
via a phony check_revision_stamp trigger) so it only runs when
REVISION_STAMP is actually pulled into the build, matching this
Makefile's existing target-based rule architecture.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@nerdCopter

Copy link
Copy Markdown
Member Author

@coderabbitai, AI generated comment

Addressed the architectural nit from the analysis above in 7da81fa:

Moved the REVISION_STAMP mkdir/compare logic out of parse-time $(shell ...) calls and into an
explicit recipe rule ($(REVISION_STAMP): check_revision_stamp, phony trigger). It now only runs
when REVISION_STAMP is actually pulled in as a prerequisite of a real build goal — confirmed
make TARGET=SITL help no longer creates or touches the stamp file. Re-ran the full A/B/C/D
regression sequence (clean->real SHA, dirty->uncommitted, dirty->clean transition with zero
source diff->correct SHA rebuild, true no-op->mtime unchanged) against the updated rule; all pass.

The coarse-mtime-filesystem caveat noted in the analysis is accepted as-is — not applicable on the
project's Linux CI/build environment.

@coderabbitai

coderabbitai Bot commented Aug 14, 2026

Copy link
Copy Markdown

Tip

For best results, initiate chat on the files or code changes.

@nerdCopter The explicit $(REVISION_STAMP) rule removes the parse-time side effect. The reported help check confirms that non-build goals do not create or modify the stamp.

The A/B/C/D results cover the original stale-REVISION path and preserve the no-op incremental-build behavior. The accepted coarse-mtime limitation is appropriate for the project Linux CI and build environment.


✏️ Learnings added
Learnt from: nerdCopter
URL: https://github.com/emuflight/EmuFlight/pull/1386

Timestamp: 2026-08-14T18:13:52.129Z
Learning: For the EmuFlight Makefile `REVISION_STAMP` mechanism, the project accepts the GNU Make coarse-filesystem-mtime limitation because Linux CI and supported build environments do not use affected timestamp resolution.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


If you found this review helpful, would you consider giving us a shout-out on X?

Thank you for using CodeRabbit!

You are interacting with an AI system.

@nerdCopter
nerdCopter marked this pull request as ready for review August 14, 2026 18:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant