Skip to content

Instantly share code, notes, and snippets.

@jaytaylor
Last active March 15, 2017 15:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaytaylor/a7a53ffef81ab232ca482014e27e550e to your computer and use it in GitHub Desktop.
Save jaytaylor/a7a53ffef81ab232ca482014e27e550e to your computer and use it in GitHub Desktop.
Universal Golang Makefile.
################################################################################
# Golang automatic Makefile #
################################################################################
##
#
# @author Jay Taylor <jay@gigawatt.io>
#
# @date 2015-05-20
#
# @description Automatically builds go projects, including ones with nested
# packages.
#
##
MAKEFLAGS += --warn-undefined-variables
SHELL := bash
.SHELLFLAGS := -e -c
GOPATH ?= ~/go
GO := $(shell \
test -d Godeps \
&& echo 'godep go' \
|| echo 'go' \
)
RM := rm -rf
KILL := kill -9
# Automatically discover all buildable targets (binaries).
# NB: `printf %s' used instead of `cat' to guard against trailing newlines.
BUILDABLE_PACKAGES_INFO := $(shell \
test -r .Makefile.packages.cache && printf %s "$$(< .Makefile.packages.cache)" \
|| ( \
find . \
-not \( -path ./vendor -prune \) \
-not \( -path ./Godeps -prune \) \
-not \( -path ./node_modules -prune \) \
-type f \
-name "*.go" \
-exec bash -c 'test -n "`tr "\n" " " < "{}" | grep "^package main .* func main() { .* }"`" && echo {}' \; \
| xargs -n1 -IX bash -c '( cd "$$(dirname "X")" && go list && cd - 1>/dev/null ) | sed "s|^\(.*\/\)\{0,1\}\(.*\)|`dirname "X"`\/\2:X|"' \
| sed 's/:\(.*\)\(\/.*\.go\)/:\1:\1\2/' \
| uniq \
| tee .Makefile.packages.cache ) \
)
BINARY_ARTIFACTS := $(foreach PACKAGE_INFO,$(BUILDABLE_PACKAGES_INFO), \
$(eval ARTIFACT = $(word 1,$(subst :, ,$(PACKAGE_INFO)))) $(ARTIFACT) \
)
BUILDABLE_GO_FILES := $(foreach PACKAGE_INFO,$(BUILDABLE_PACKAGES_INFO), \
$(eval GO_FILENAME = $(word 3,$(subst :, ,$(PACKAGE_INFO)))) $(GO_FILENAME) \
)
BUILD_DATE := $(shell date -u '+%Y-%m-%dT%H:%M:%S%z')
BUILD_HASH := $(shell git rev-parse HEAD)
LDFLAGS := "-X gigawatt-logger/pkg/version.BuildTimeStr=$(BUILD_DATE) -X gigawatt-logger/pkg/version.BuildCommitHash=$(BUILD_HASH)"
AUTO_GENERATED_FILES = $(shell \
find . \
-not \( -path ./vendor -prune \) \
-not \( -path ./Godeps -prune \) \
-not \( -path ./node_modules -prune \) \
-type f \
-name "*.go" \
-exec bash -c 'grep --files-with-matches "^\/\/ generated by stringer .*; DO NOT EDIT" {}' \; \
)
EXIT_ON_ERROR = set -o errexit && set -o pipefail &&
# `flags=foo' may be passed to build and test, e.g. `make build flags=-race' or `make test flags="-run TestCancellation"'.
flags ?=
tags ?=
ifneq ($(tags),)
tagsFlag := -tags $(tags)
else
tagsFlag :=
endif
pkg ?= ./...
n ?= 0
all: clean generate build
generate:
$(EXIT_ON_ERROR) $(GO) generate $(pkg)
list-buildable:
@echo -e $(foreach PACKAGE_INFO,$(BUILDABLE_PACKAGES_INFO), \
$(eval ARTIFACT = $(word 1,$(subst :, ,$(PACKAGE_INFO)))) \
$(eval GO_FILENAME = $(word 3,$(subst :, ,$(PACKAGE_INFO)))) \
"$(GO_FILENAME) -> $(ARTIFACT) \n" \
) | tr -d '\t' | column -t
build:
$(foreach PACKAGE_INFO,$(BUILDABLE_PACKAGES_INFO), \
$(eval ARTIFACT = $(word 1,$(subst :, ,$(PACKAGE_INFO)))) \
$(eval PACKAGE_PATH = $(word 2,$(subst :, ,$(PACKAGE_INFO)))) \
$(EXIT_ON_ERROR) $(GO) build $(flags) -ldflags $(LDFLAGS) -o $(ARTIFACT) $(PACKAGE_PATH) ; \
)
## Dyanmic build targets. Usage: `make <binary>', e.g. `make logger/logger'.
#$(BINARY_ARTIFACTS):
# $(EXIT_ON_ERROR) $(GO) build $(flags) -ldflags $(LDFLAGS) -o $@ $@.go
install:
$(EXIT_ON_ERROR) cp $(BINARY_ARTIFACTS) $(GOPATH)/bin/
logfile ?= test.log
test:
ifeq ($(n),0)
$(EXIT_ON_ERROR) bash -c 'startedAt=$$(date +%s) ; $(GO) test $(flags) $(tagsFlag) $(pkg) 2>&1 ; rc=$$? ; echo exit status-code=$$rc ; echo duration: $$(($$(date +%s)-$$startedAt))s ; exit $$rc' 2>&1 | tee $(logfile)
else
$(EXIT_ON_ERROR) rm -f $(logfile) && bash -c 'startedAt=$$(date +%s) ; for i in $$(seq 1 $(n)) ; do echo starting iteration $$i/$(n); iterStartedAt=$$(date +%s) ; $(GO) test $(flags) $(tagsFlag) $(pkg) 2>&1 ; rc=$$? ; echo completed iteration $$i/$(n) ; echo exit status-code=$$rc ; echo iter-duration: $$(($$(date +%s)-$$iterStartedAt))s ; if [ $$rc -ne 0 ] ; then break ; fi ; done ; echo total-duration: $$(($$(date +%s)-$$startedAt))s ; exit $$rc' 2>&1 | tee $(logfile)
endif
clean:
$(EXIT_ON_ERROR) $(RM) $(AUTO_GENERATED_FILES) $(BINARY_ARTIFACTS)
reset:
$(EXIT_ON_ERROR) $(RM) .Makefile.packages.cache
distclean: clean reset
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment