go-cli-build-multi-package
Root cause: When go build receives multiple main packages on the command line, it acts in compile-and-check mode — it validates every package builds (exit 0 on success) but discards all produced executables. Binaries are only written to the working directory when exactly one main package is named (or when -o is given). The same applies to go build ./... — it type-checks and links everything but writes nothing, which is why the README's dev command produced no runnable bunker/bunkerd (GAP-018).
Fix — one explicit -o per binary:
go build -o bunker ./cmd/bunker
go build -o bunkerd ./cmd/bunkerd
Recommended: a Makefile target with ldflags (adds version stamping, strips debug symbols):
VERSION ?= v0.1.0
LDFLAGS := -s -w -X example.com/bunker/internal/version.Version=$(VERSION)
.PHONY: build clean
build:
go build -ldflags "$(LDFLAGS)" -o bunker ./cmd/bunker
go build -ldflags "$(LDFLAGS)" -o bunkerd ./cmd/bunkerd
clean:
rm -f bunker bunkerd
Alternative valid forms (for completeness):
- go install ./cmd/bunker ./cmd/bunkerd — writes both binaries to $GOPATH/bin/$GOBIN, not cwd.
- go build -o ./bin ./cmd/bunker ./cmd/bunkerd — -o pointing at a directory keeps binaries from multiple packages (named bunker, bunkerd inside bin/).
What does NOT work: go build ./cmd/bunker ./cmd/bunkerd (discards binaries), go build ./... (discards binaries), and go build -o combined ./cmd/bunker ./cmd/bunkerd (fails: go: cannot write multiple packages to non-directory combined).
Verification ritual: after any build, ls -la and confirm -rwxr-xr-x executables exist, then smoke-test ./bunker and ./bunkerd.
Built a throwaway repo `/tmp/bunker-test` (`go.mod` module `example.com/bunker`, `cmd/bunker`, `cmd/bunkerd`, `internal/version`). Reproduced on go1.26.0 linux/amd64: | Test | Command | Result | |---|---|---| | Bug repro | `go build ./cmd/bunker ./cmd/bunkerd` | exit 0, **0 binaries in cwd** | | README dev cmd | `go build ./...` | exit 0, `ls bunker` → *No such file* | | Fix | `go build -o bunker ./cmd/bunker && go build -o bunkerd ./cmd/bunkerd` | exit 0, both `-rwxr-xr-x`; `./bunker` prints `bunker dev`, `./bunkerd` prints `bunkerd daemon` | | Makefile + ldflags | `make clean && make build` | both binaries written; `./bunker` prints `bunker v0.1.0` (version stamping works, sizes drop 2.4MB→1.6MB with `-s -w`) | | Control (single pkg) | `go build ./cmd/bunker` | binary written to cwd — confirms single-package builds are unaffected | | Invalid `-o` file | `go build -o combined ./cmd/bunker ./cmd/bunkerd` | fails: `go: cannot write multiple packages to non-directory combined` | | `-o` directory form | `go build -o ./bin ./cmd/bunker ./cmd/bunkerd` | both binaries in `bin/` | | `go install` | `GOPATH=$(mktemp -d) go install ./cmd/bunker ./cmd/bunkerd` | both binaries in `$GOPATH/bin`, none in cwd | Edge cases covered: multi-package without `-o` (the bug), `./...` (the README trap), single-package (control), `-o` to a file vs. directory, ldflags version injection, and `go install` semantics. The fix is deterministic — `go build -o NAME ./cmd/PKG` always emits an executable regardless of how many other packages are also listed in the build (each runs in its own invocation, so package count never matters).
{"model": "pi", "problem_class": "go-cli-build-multi-package", "result": "passed", "tests": 9}