Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 62 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -268,23 +268,49 @@ install-hooks:
git config merge.sums.driver "scripts/git-merge-sums.sh %O %A %B %L %P"
@echo "Registered the 'lgb' + 'sums' merge drivers (core_compiled.lgb / generated.sums)."

# Non-mutating front gate: fail before any target has a chance to refresh
# pkg/rt/generated.sums. This catches the exact drift that a prior go generate
# or lgbgen invocation would otherwise mask in CI.
# Fast local staleness probe: compares the recorded source digest against the
# sources on disk. Used by scripts/pre-commit, which needs an answer in
# milliseconds rather than a full regeneration, and by the build job in CI.
#
# No longer a prerequisite of check-generated. As a prerequisite it aborted the
# content gate — the check that actually binds sources to artifacts — on a
# proxy's verdict, so a stale digest hid the answer you wanted. The two run
# independently now. This does not change whether a stale digest fails CI: it
# still does, here and in TestGeneratedArtifactsAreFresh.
check-generated-manifest: $(GO)
@go run ./cmd/check-generated

# Committed generated artifacts that the content gate compares. Keep in step
# with scripts/generate.lg. core_go_lowered/ is absent because it is gitignored
# (a build artifact) and gets the behavioral gate instead.
GENERATED-TRACKED := \
pkg/rt/core_compiled.lgb \
pkg/ir/op_generated.go \
pkg/rt/ir_bridge_generated.go \
pkg/rt/zz_primitives_generated.go \
pkg/rt/core/ir/data/generated.lg

# Committed, generated, and deliberately NOT compared. lgbgen's writeBundle
# refreshes this on every regeneration, so `make generate` — and therefore this
# gate — rewrites it as a side effect. The gate stashes and restores it so a
# check-* target does not mutate a tracked file. Its own staleness is covered by
# check-generated-manifest and by TestGeneratedArtifactsAreFresh.
GENERATED-DIGEST := pkg/rt/generated.sums

# Single gate for every generated artifact. One target to remember, and it
# treats the two artifacts by their actual nature. VCS-agnostic by design:
# it shells out to no `git` (this repo is used with jj, whose secondary
# workspaces have no .git, so a `git diff` gate breaks there).
#
# * core_compiled.lgb is byte-deterministic, so it gets a CONTENT gate:
# stash the committed bytes, regenerate, and `cmp`. A difference means the
# committed bundle was stale. Survives a fresh checkout (an mtime
# `find -newer` check silently passes after any VCS checkout — in fact the
# old check-lowered-fresh pointed at a path that no longer exists and had
# been a silent no-op).
# * Every artifact in GENERATED-TRACKED is byte-deterministic, so they get a
# CONTENT gate: stash the committed bytes, regenerate, and `cmp`. A
# difference means the committed artifact was stale. Survives a fresh
# checkout, unlike an mtime `find -newer` check.
#
# Regenerating through `make generate` rather than lgbgen alone is what
# widened this from the bundle to all five. lgbgen emits only the .lgb and
# the lowered tree, so op/ir_bridge/primitives/ir-data were never verified —
# and zz_primitives_generated.go had drifted from its generator.
#
# * core_go_lowered/ (+ the gogen_ir wireup files) is NOT committed — it is
# a build artifact, regenerated on demand and gitignored. Its self-lower
Expand All @@ -297,20 +323,34 @@ check-generated-manifest: $(GO)
#
# This is the gate CI runs. After a merge/rebase touching pkg/rt/core/**, run
# `make check-generated` (or `make generate` to refresh, then commit).
check-generated: check-generated-manifest $(GO)
@echo ">> regenerate bundle + lowered tree from a SINGLE core compile (--target=both)"
@cp pkg/rt/core_compiled.lgb pkg/rt/.core_compiled.lgb.committed
@go run -tags bootstrap ./cmd/lgbgen --target=both >/dev/null
@echo ">> bundle: verify lockstep (content-based, VCS-agnostic)"
@if cmp -s pkg/rt/core_compiled.lgb pkg/rt/.core_compiled.lgb.committed; then \
rm -f pkg/rt/.core_compiled.lgb.committed; \
echo "OK: core_compiled.lgb in lockstep with the .lg sources."; \
else \
rm -f pkg/rt/.core_compiled.lgb.committed; \
echo "ERROR: pkg/rt/core_compiled.lgb is stale — the regenerated bytes differ."; \
echo " Run 'make generate' and commit the regenerated bundle."; \
check-generated: $(GO)
@echo ">> stash committed artifacts, regenerate all of them, compare"
@stash=$$(mktemp -d) || exit 1; \
trap 'rm -rf "$$stash"' EXIT HUP INT TERM; \
for f in $(GENERATED-TRACKED) $(GENERATED-DIGEST); do \
cp "$$f" "$$stash/$$(echo $$f | tr / _)" || exit 1; \
done; \
if ! $(MAKE) --no-print-directory generate >"$$stash/generate.log" 2>&1; then \
cat "$$stash/generate.log"; \
exit 1; \
fi
fi; \
cp "$$stash/$$(echo $(GENERATED-DIGEST) | tr / _)" $(GENERATED-DIGEST); \
echo ">> verify lockstep (content-based, VCS-agnostic)"; \
fail=0; \
for f in $(GENERATED-TRACKED); do \
if cmp -s "$$f" "$$stash/$$(echo $$f | tr / _)"; then \
echo " OK $$f"; \
else \
echo " STALE $$f — the regenerated bytes differ."; \
fail=1; \
fi; \
done; \
if [ $$fail -ne 0 ]; then \
echo "ERROR: committed generated artifacts are stale."; \
echo " Run 'make generate' and commit the result."; \
exit 1; \
fi; \
echo "OK: all tracked generated artifacts in lockstep with their sources."
@echo ">> lowered tree: compile + dispatch natively under -tags gogen_ir"
@go build -tags gogen_ir ./pkg/rt/...
@out=$$(printf '(require (quote ir.passes.dce)) (println "DCE-TYPE:" (type ir.passes.dce/dce))' \
Expand Down
13 changes: 10 additions & 3 deletions cmd/lginterop/prims_emit.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,20 @@ package rt

import (
"fmt"

"github.com/nooga/let-go/pkg/vm"
`)

// The blank line above separates stdlib from external imports. It has to be
// emitted here: main.go runs the file through gofmt, and gofmt PRESERVES
// existing grouping rather than introducing it — only goimports inserts that
// separator, and it is a linter here, not part of generation. Without the
// blank line every regeneration produced a file the goimports linter would
// rewrite, so the committed copy (hand-grouped) and generator output drifted.
//
// Add imports for scanned packages (excluding same-package). Emission order
// here is irrelevant: the final file is run through gofmt (main.go), which
// canonicalizes the import block. Registration STATEMENT order below is NOT
// touched by gofmt, so that path sorts its keys explicitly.
// within the group is irrelevant — gofmt sorts it. Registration STATEMENT
// order below is NOT touched by gofmt, so that path sorts its keys explicitly.
for goPkg := range pkgAliasMap {
if !isSamePackage(goPkg) {
fmt.Fprintf(&b, "\t%q\n", goPkg)
Expand Down
2 changes: 1 addition & 1 deletion pkg/rt/generated.sums
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
# Content digest of all .lg + lgbgen sources that feed the .lgb
# bundle and the lowered Go tree. The genmanifest staleness test
# fails if this no longer matches the sources on disk.
0a3d203cefa6782f9266fe6aa86a36d1f34dc65170af0eb37708162ced4f4d6d
b6f7838debc9b8c2b9d4822c02e341c4153cdd9cbb0d302d9f5a434b4671d202
Loading