# gasm-devkit. # # Replace gasm-devkit here and the values in the variable block. Everything below the block # is the standard recipe set: the names and their meanings are fixed, identical in every # repository, and `gates` is the definition of done. binary := "gasm" package := "./cmd/gasm" # What the test and bench recipes sweep. Scope this to the logic packages when a thin # cmd/ drags the coverage floor down, for example "./internal/... ./pkg/...". Never name # a directory the project does not have: a pattern that matches nothing is a setup # failure, not an empty run. packages := "./arch/... ./asm/... ./ast/... ./disasm/... ./format/... ./lexer/... ./lint/... ./lsp/... ./parser/... ./token/... ./verify/..." bindir := env_var_or_default("BINDIR", env_var("HOME") / ".local" / "bin") # Where `install-man` puts the gzip-compressed pages (man1 below it). Exported because # the Perl recipes read it from the environment. export MANDIR := env_var_or_default("MANDIR", env_var("HOME") / ".local" / "share" / "man") default: @just --list # Compile. Zero errors, zero warnings. build: CGO_ENABLED=0 go build -ldflags "-s -w" -o bin/{{binary}} {{package}} # The test gate: the suite, no cache, the coverage floor. test: #!/usr/bin/env perl system(q{go}, q{test}, q{-count=1}, q{-timeout}, q{10m}, q{-coverprofile}, q{coverage.out}, qw({{packages}})) == 0 or die qq{the test suite failed\n}; # The packages outside the coverage set carry tests of their own: the CLI's # exit codes and manual-page guard, and the debugger's architecture-neutral # units. They run without a profile, because a thin main and a ptrace-bound # package would drag the floor down rather than measure the product. system(q{go}, q{test}, q{-count=1}, q{-timeout}, q{10m}, q{./cmd/...}, q{./debug/...}) == 0 or die qq{the tests outside the coverage set failed\n}; open(my $c, q{-|}, q{go}, q{tool}, q{cover}, q{-func=coverage.out}) or die qq{cover: $!}; my $total; while (my $l = <$c>) { $total = $1 if $l =~ m{^total:\s+\S+\s+([0-9.]+)%} } close($c); die qq{no total line in coverage.out\n} unless defined $total; printf qq{Total coverage: %s%%\n}, $total; exit($total < 80 ? 1 : 0); # The same suite under the race detector. The expensive one. race: go test -race -count=1 -timeout 10m {{packages}} # Fast scoped run for iterating. This is the one that runs after every edit. unit pkgs=packages run=".*": go test {{pkgs}} -run '{{run}}' # Time-boxed fuzz of one target in one package. The package is required; never a gate. fuzz target pkg fuzztime="60s": go test -run '^$' -fuzz '{{target}}' -fuzztime={{fuzztime}} {{pkg}} # Benchmarks. On an idle machine only. bench pkgs=packages: go test -run '^$' -bench=. -benchmem -count=5 {{pkgs}} # Format in place. fmt: gofmt -w . # Zero diff. Prints nothing when everything is formatted. fmt-check: #!/usr/bin/env perl open(my $g, q{-|}, q{gofmt}, q{-l}, q{.}) or die qq{gofmt: $!}; my @bad = <$g>; close($g); print @bad; exit(@bad ? 1 : 0); # Both static gates: go vet and go fix -diff. vet: go vet ./... go fix -diff ./... # The definition of done, in one command. Once per task, never per edit. gates: build fmt-check vet test race # Build artefacts only, not the installed binary. clean: rm -rf bin/ coverage.out # Build, then copy the binary into bindir. install: build install -d "{{bindir}}" install -m 755 bin/{{binary}} "{{bindir}}/{{binary}}" # Remove the installed binary. uninstall: rm -f "{{bindir}}/{{binary}}" # Install the man pages under docs/man into mandir/man1, gzip-compressed; not a gate. install-man: #!/usr/bin/env perl my $out = $ENV{MANDIR} . q{/man1}; system(q{mkdir}, q{--parents}, $out) == 0 or die qq{mkdir $out: $!\n}; for my $p (glob q{docs/man/*.1}) { open(my $g, q{-|}, q{gzip}, q{--stdout}, $p) or die qq{gzip $p: $!\n}; my $content = do { local $/; <$g> }; close($g); my $base = $p; $base =~ s{docs/man/}{}; open(my $o, q{>}, qq{$out/$base.gz}) or die qq{write $out/$base.gz: $!\n}; print {$o} $content; close($o); print qq{$out/$base.gz\n}; } # Remove the installed man pages. uninstall-man: #!/usr/bin/env perl for my $p (glob q{docs/man/*.1}) { my $base = $p; $base =~ s{docs/man/}{}; my $f = $ENV{MANDIR} . q{/man1/} . $base . q{.gz}; if (-f $f) { unlink($f) or die qq{unlink $f: $!\n}; print qq{removed $f\n}; } } # Run the program. The flag is there because `go run` does not stamp the build otherwise. run: go run -buildvcs=true {{package}} # Run with watch or hot reload, where the project has one. dev: go run -buildvcs=true {{package}} # Regenerate the architecture instruction tables from the Go toolchain source. Not a gate. gen: go run _gen/gen.go gofmt -w arch/