# This file is intended as a starting point for a customized makefile for a Go project. # # Targets: # all: Format, check, build, and test the code # setup: Install build/test toolchain dependencies (e.g. gox) # lint: Run linters against source code # bump-{major,minor,patch}: create a new semver git tag # release-{major,minor,patch}: push a tagged release # format: Format the source files # build: Build the command(s) for target OS/arch combinations # install: Install the command(s) # clean: Clean the build/test artifacts # report: Generate build/test reports # check: Run tests # bench: Run benchmarks # dist: zip/tar binaries & documentation # debug: print parameters # # Parameters: # VERSION: release version in semver format # BUILD_TAGS: additional build tags to pass to go build # DISTDIR: path to save distribution files # RPTDIR: path to save build/test reports # # Assumptions: # - Your package contains a cmd/ package, containing a directory for each produced binary. # - You have cloc installed and accessible in the PATH. # - Your GOPATH and GOROOT are set correctly. # - Your makefile is in the root of your package and does not have a space in its file name. # - Your root package contains global string variables Version and Build, to receive the bild version number and commit ID, respectively. # # Features: # - report generates files that can be consumed by Jenkins, as well as a list of external dependencies. # - setup installs all the tools aside from cloc. # - Works on Windows and with paths containing spaces. # - Works when executing from outside the makefile directory using -f. # - Targets are useful both in CI and developer workstations. # - Handles cross-compiation for multiple OSes and architectures. # - Bundles binaries and documentation into compressed archives, using tar/gz for Linux and Darwin, and zip for Windows. # Parameters PKG = code.ndumas.com/ndumas/obsidian-markdown NAME = parse-wikilinks DOC = README.md LICENSE # Replace backslashes with forward slashes for use on Windows. # Make is !@#$ing weird. E := BSLASH := \$E FSLASH := / # Directories WD := $(subst $(BSLASH),$(FSLASH),$(shell pwd)) MD := $(subst $(BSLASH),$(FSLASH),$(shell dirname "$(realpath $(lastword $(MAKEFILE_LIST)))")) PKGDIR = $(MD) CMDDIR = $(PKGDIR)/cmd DISTDIR ?= $(WD)/dist RPTDIR ?= $(WD)/reports GP = $(subst $(BSLASH),$(FSLASH),$(GOPATH)) # Parameters VERSION ?= $(shell git -C "$(MD)" describe --tags --dirty=-dev) COMMIT_ID := $(shell git -C "$(MD)" rev-parse HEAD | head -c8) BUILD_TAGS ?= release CMDPKG = $(PKG)/cmd CMDS := $(shell find "$(CMDDIR)/" -mindepth 1 -maxdepth 1 -type d | sed 's/ /\\ /g' | xargs -n1 basename) BENCHCPUS ?= 1,2,4 # Commands GOCMD = go ARCHES ?= amd64 386 OSES ?= windows linux darwin OUTTPL = $(DISTDIR)/$(NAME)-$(VERSION)-{{.OS}}_{{.Arch}}/{{.Dir}} LDFLAGS = -X $(PKG).Version=$(VERSION) -X $(PKG).Build=$(COMMIT_ID) GOBUILD = gox -osarch="!darwin/386" -rebuild -gocmd="$(GOCMD)" -arch="$(ARCHES)" -os="$(OSES)" -output="$(OUTTPL)" -tags "$(BUILD_TAGS)" -ldflags "$(LDFLAGS)" GOCLEAN = $(GOCMD) clean GOINSTALL = $(GOCMD) install -a -tags "$(BUILD_TAGS)" -ldflags "$(LDFLAGS)" GOTEST = $(GOCMD) test -race -v -tags "$(BUILD_TAGS)" DISABLED_LINTERS = varnamelen,interfacer,ifshort,exhaustivestruct,maligned,varcheck,scopelint,structcheck,deadcode,nosnakecase,golint,depguard GOLINT = golangci-lint run --enable-all --disable "$(DISABLED_LINTERS)" --timeout=30s --tests GODEP = $(GOCMD) get -d -t GOFMT = goreturns -w GOBENCH = $(GOCMD) test -v -tags "$(BUILD_TAGS)" -cpu=$(BENCHCPUS) -run=NOTHING -bench=. -benchmem -outputdir "$(RPTDIR)" GZCMD = tar -czf ZIPCMD = zip SHACMD = sha256sum SLOCCMD = cloc --by-file --xml --exclude-dir="vendor" --include-lang="Go" XUCMD = go2xunit DOCKER_CMD=docker --config ~/.docker/ # Dynamic Targets INSTALL_TARGETS := $(addprefix install-,$(CMDS)) .PHONY: all all: debug setup dep format lint test bench build dist git-push: git push origin main --tags release-major: bump-major git-push release-minor: bump-minor git-push release-patch: bump-patch git-push setup: setup-dirs setup-build setup-format setup-lint setup-reports setup-bump setup-stringer setup-stringer: go install golang.org/x/tools/cmd/stringer@latest setup-bump: go install github.com/guilhem/bump@latest bump-major: setup-bump bump major bump-minor: setup-bump bump minor bump-patch: setup-bump bump patch setup-reports: setup-dirs go install github.com/tebeka/go2xunit@latest setup-build: setup-dirs go install github.com/mitchellh/gox@latest setup-format: setup-dirs go install github.com/sqs/goreturns@latest setup-lint: setup-dirs go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.53.1 setup-dirs: mkdir -p "$(RPTDIR)" mkdir -p "$(DISTDIR)" clean: $(GOCLEAN) $(PKG) rm -vrf "$(DISTDIR)"/* rm -vf "$(RPTDIR)"/* format: $(GOFMT) "$(PKGDIR)" dep: $(GODEP) $(PKG)/... gen: $(GOCMD) generate lint: setup-dirs dep $(GOLINT) "$(PKGDIR)" | tee "$(RPTDIR)/lint.out" check: setup-dirs clean dep setup-stringer gen $(GOTEST) $$(go list "$(PKG)/..." | grep -v /vendor/) | tee "$(RPTDIR)/test.out" bench: setup-dirs clean dep $(GOBENCH) $$(go list "$(PKG)/..." | grep -v /vendor/) | tee "$(RPTDIR)/bench.out" report: check cd "$(PKGDIR)";$(SLOCCMD) --out="$(RPTDIR)/cloc.xml" . | tee "$(RPTDIR)/cloc.out" cat "$(RPTDIR)/test.out" | $(XUCMD) -output "$(RPTDIR)/tests.xml" go list -f '{{join .Deps "\n"}}' "$(CMDPKG)/..." | sort | uniq | xargs -I {} sh -c "go list -f '{{if not .Standard}}{{.ImportPath}}{{end}}' {} | tee -a '$(RPTDIR)/deps.out'" build: $(CMDS) $(CMDS): setup-dirs dep $(GOBUILD) "$(CMDPKG)/$@" | tee "$(RPTDIR)/build-$@.out" install: $(INSTALL_TARGETS) $(INSTALL_TARGETS): $(GOINSTALL) "$(CMDPKG)/$(subst install-,,$@)" dist: clean build for docfile in $(DOC); do \ for dir in "$(DISTDIR)"/*; do \ cp "$(PKGDIR)/$$docfile" "$$dir/"; \ done; \ done cd "$(DISTDIR)"; for dir in ./*linux*; do $(GZCMD) "$(basename "$$dir").tar.gz" "$$dir"; done cd "$(DISTDIR)"; for dir in ./*windows*; do $(ZIPCMD) "$(basename "$$dir").zip" "$$dir"; done cd "$(DISTDIR)"; for dir in ./*darwin*; do $(GZCMD) "$(basename "$$dir").tar.gz" "$$dir"; done cd "$(DISTDIR)"; find . -maxdepth 1 -type f -printf "$(SHACMD) %P | tee \"./%P.sha\"\n" | sh $(info "Built v$(VERSION), build $(COMMIT_ID)") .PHONY: docker docker: docker-image docker-push .PHONY: docker-push docker-push: $(DOCKER_CMD) tag $(PKG):$(VERSION) $(PKG):latest $(DOCKER_CMD) push $(PKG):latest $(DOCKER_CMD) push $(PKG):$(VERSION) .PHONY: docker-image docker-image: $(DOCKER_CMD) build --build-arg VERSION=$(VERSION) -t $(PKG):$(VERSION) . .PHONY: build-alpine build-alpine: # this version breaks build variable injection # CGO_ENABLED=0 GOOS=linux go build -ldflags="buildmode=exe $(LDFLAGS) -linkmode external -w -extldflags '-static' " -o $(DISTDIR)/$(NAME)-$(VERSION)-alpine/obp cmd/obp/*.go CGO_ENABLED=0 GOOS=linux go build -ldflags="$(LDFLAGS)" -o $(DISTDIR)/$(NAME)-$(VERSION)-alpine/obp cmd/obp/*.go debug: $(info MD=$(MD)) $(info WD=$(WD)) $(info PKG=$(PKG)) $(info PKGDIR=$(PKGDIR)) $(info DISTDIR=$(DISTDIR)) $(info VERSION=$(VERSION)) $(info COMMIT_ID=$(COMMIT_ID)) $(info BUILD_TAGS=$(BUILD_TAGS)) $(info CMDS=$(CMDS)) $(info BUILD_TARGETS=$(BUILD_TARGETS)) $(info INSTALL_TARGETS=$(INSTALL_TARGETS))