[A while ago](https://fieldnotes.tech/2019/07/04/how-to-shell-for-compatible-makefiles/) I documented a robust-ish way to set up the `SHELL` in a Makefile. Time has moved on and I’ve learned how to: - Make effective use of parallelism (so I now include `MAKEFLAGS += --jobs=8` most of the time. - Write neater recipes using `.ONESHELL` - Implement some “standard” starter targets for Go projects: - `make compile` to [quickly check _everything_ compiles correctly](https://fieldnotes.tech/2023/02/11/compiling-all-test-binaries-without-running-them/) - `make test` to run the tests, with minimal output. - `make install` to install binaries `make install` In order to support all this, we now need to switch the shell setup based on the version of GNU Make we’re using. For some reason `.ONESHELL` confuses things in this regard otherwise. You can see the most up-to-date version of this template at [samsalisbury/home](#) Here’s how it looks at the moment: ```Makefile .ONESHELL: # Make on macOS is stuck at 3.81 ifeq ($(MAKE_VERSION),3.81) SHELL := /usr/bin/env bash -euo pipefail -c else SHELL := /usr/bin/env .SHELLFLAGS = -euo pipefail -c endif MAKEFLAGS += --silent --jobs=8 QUIET_UNLESS_FAIL = OUT="$($2 2>&1)" || { CODE="$?"; printf "$1 failed:\n%s" "$OUT"; exit $CODE; }; echo "$1: OK" default: test .PHONY: default # ----- # Section: Go General # This section should be useful for all go projects. # ----- ALL_PACKAGES := ./... compile: $(call QUIET_UNLESS_FAIL,Compilation, go build $(ALL_PACKAGES)) $(call QUIET_UNLESS_FAIL,Test compilation, go test -exec true $(ALL_PACKAGES)) .PHONY: compile test: compile $(call QUIET_UNLESS_FAIL,Running go tests, go test $(ALL_PACKAGES)) .PHONY: test # ----- # Section: Go Executable. # This section is useful for go projects creating an executable. # ----- NAME := hc-gh # BIN is the binary name. BIN := ./$(NAME) # DESTDIR is the absolute path to the install path if running 'make install' # Default to wherever it's currently installed using which, or /usr/local/bin # if it's not yet installed. DESTDIR ?= $(shell $(dirname $(which $(NAME)) 2>/dev/null || echo /usr/local/bin) build: $(BIN) echo lt; .PHONY: build $(BIN): test go build -o "$@" .PHONY: $(BIN) install: $(BIN) mkdir -p "$(DESTDIR)" install "$(BIN)" "$(DESTDIR)" .PHONY: install ``` #FieldNotes.Tech/posts #FieldNotes.Tech/collections/categories/how-to #FieldNotes.Tech/collections/tags/makefile