5.7 KiB
Development Guide
Repository: sourcedock.dev/petrbalvin/gasm-devkit
Prerequisites
- Go 1.27.1, the exact version the
godirective ingo.moddeclares - just, the command runner; every task below is a just recipe
- A Linux host on amd64, arm64, riscv64 or loong64:
gasm debugneeds ptrace and the JIT checks ofgasm verifyneed executable memory - No external dependencies beyond the Go toolchain
Setup
git clone https://sourcedock.dev/petrbalvin/gasm-devkit.git
cd gasm-devkit
just build # compile bin/gasm, zero errors and zero warnings
just gates # build, fmt-check, vet, test, race: the definition of done
Recipes
Every recipe in the justfile, and what it does.
| Recipe | What it does |
|---|---|
just build |
compiles bin/gasm with CGO_ENABLED=0 and stripped symbols; zero errors and zero warnings |
just test |
the test gate: the suite with -count=1, the coverage profile and the 80 % floor |
just race |
the same suite under the race detector; the expensive one, so it runs once, inside gates |
just unit [packages] [run] |
fast, cached, scoped run for iterating: no race and no coverage, so an unchanged package reports instantly |
just fuzz <target> <pkg> [fuzztime] |
time-boxed fuzz of one target; the package is required, because go test -fuzz refuses more than one |
just bench [packages] |
benchmarks (-benchmem -count=5); on an idle machine only |
just fmt |
formats the tree in place with gofmt |
just fmt-check |
zero diff; prints nothing when everything is formatted, which is the shape the CI step wants |
just vet |
both static gates: go vet and go fix -diff |
just gates |
build, fmt-check, vet, test and race, in that order: the definition of done |
just clean |
removes the build artefacts, bin/ and coverage.out |
just install |
builds, then copies the binary into bindir (~/.local/bin); gasm debug needs an installed binary, because it spawns the debuggee from $PATH |
just uninstall |
removes the installed binary from bindir |
just run |
runs the CLI with go run -buildvcs=true; the recipe takes no arguments, so flags go through the package instead |
just dev |
the same as run; the project has no watcher to add |
just gen |
regenerates the arch instruction tables from the Go toolchain source; not a gate |
just test
go test -count=1 -timeout 10m -coverprofile=coverage.out \
./arch/... ./asm/... ./ast/... ./disasm/... ./format/... ./lexer/... \
./lint/... ./lsp/... ./parser/... ./token/... ./verify/...
The suite runs over the logic packages (-count=1, so no cached pass
counts): arch, asm, ast, disasm, format, lexer, lint, lsp, parser,
token, verify. debug traces a live process and cmd/gasm is thin CLI
glue, so both sit outside the sweep, and a thin cmd/ in it would drag
the coverage total under the floor. The floor fails if the total is
below 80 %. CI runs the same command with the same ten-minute bound, so
the number is the same everywhere.
just run
just run
go run -buildvcs=true ./cmd/gasm lint kernel_amd64.s
go run -buildvcs=true ./cmd/gasm verify --ground-truth kernel_amd64.s
The flag on go run is there because it does not stamp the build otherwise,
which --version would then report as (devel).
just gen
Regenerates the architecture instruction tables in arch/ by parsing the Go
toolchain's own assembler source
($GOROOT/src/cmd/internal/obj/<arch>/anames.go). Requires a Go
installation. Output is committed, with no runtime dependency on the
toolchain.
Running a single test
go test -run TestVexGroundTruth ./asm/
go test -run TestGroundTruthBasic ./verify/
go test -run TestGOObjectLinkAndRun ./asm/
go test -run TestFuzzWideCopy ./verify/
Add -v for the sub-test names, and -race when the change touches
concurrency. -count=1 defeats the test cache when a result looks stale.
Coverage
just test
go tool cover -func=coverage.out
The total: line is the number that matters, and it stays at 80 percent or
more.
Debugging the build
go build -gcflags='-m' ./... # inlining decisions
go build -gcflags='-S' ./... # what the compiler generated
go tool asm -S kernel_amd64.s # how the toolchain's assembler encodes a kernel
gasm dis kernel_amd64.s # what gasm makes of the same kernel
gasm tokens kernel_amd64.s # the token stream
gasm profile kernel_amd64.s # the basic blocks of each function
gasm verify --ground-truth is the differential check that ties the two
together: it compares gasm's bytes with go tool asm's, with the relocation
sites masked, so an encoding drift shows up as a byte difference rather than a
crash later.
Continuous integration
Workflows live in .gitea/workflows/ and run on the project's own runners:
Test on a push or pull request to development, race dispatched by hand, and
the release on a v* tag. They are written by hand rather than through
just, but they enforce the same set of gates minus the race detector, which
the shared runner cannot afford on a push; a green just gates locally is
therefore the fastest way to a green pipeline.
Releases
Releases are cut by merging development into main and tagging vX.Y.Z,
which triggers the release workflow: it builds the portable Linux targets,
takes the notes from the matching CHANGELOG.md section and uploads the
assets.
The version is never injected. gasm --version prints what the
toolchain recorded in the build information: the tag on a tagged
checkout, a pseudo-version naming the commit below one, +dirty on a
dirty tree, and (devel) outside version control. There is no
-ldflags "-X" anywhere and no version constant in the source.