# 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) )