2020-04-27 19:09:07 +08:00
# +--------------------------------------------------------------+
# | * * * moul.io/rules.mk |
# +--------------------------------------------------------------+
# | |
# | ++ ______________________________________ |
# | ++++ / \ |
# | ++++ | | |
# | ++++++++++ | https://moul.io/rules.mk is a set | |
# | +++ | | of common Makefile rules that can | |
# | ++ | | be configured from the Makefile | |
# | + -== ==| | or with environment variables. | |
# | ( <*> <*> | | |
# | | | /| Manfred Touron | |
# | | _) / | manfred.life | |
# | | +++ / \______________________________________/ |
# | \ =+ / |
# | \ + |
# | |\++++++ |
# | | ++++ ||// |
# | ___| |___ _||/__ __|
# | / --- \ \| ||| __ _ ___ __ __/ /|
# |/ | | \ \ / / ' \/ _ \/ // / / |
# || | | | | | /_/_/_/\___/\_,_/_/ |
# +--------------------------------------------------------------+
2020-11-10 05:52:48 +08:00
.PHONY : _default_entrypoint
_default_entrypoint : help
2020-04-27 19:09:07 +08:00
##
## Common helpers
##
rwildcard = $( foreach d,$( wildcard $1 *) ,$( call rwildcard,$d /,$2 ) $( filter $( subst *,%,$2 ) ,$d ) )
2020-11-10 05:52:48 +08:00
check-program = $( foreach exec,$( 1) ,$( if $( shell PATH = " $( PATH) " which $( exec ) ) ,,$( error " No $( exec ) in PATH " ) ) )
my-filter-out = $( foreach v,$( 2) ,$( if $( findstring $( 1) ,$( v) ) ,,$( v) ) )
novendor = $( call my-filter-out,vendor/,$( 1) )
2020-04-27 19:09:07 +08:00
##
## rules.mk
##
i f n e q ( $( wildcard rules .mk ) , )
.PHONY : rulesmk .bumpdeps
rulesmk.bumpdeps :
wget -O rules.mk https://raw.githubusercontent.com/moul/rules.mk/master/rules.mk
BUMPDEPS_STEPS += rulesmk.bumpdeps
e n d i f
##
## Maintainer
##
i f n e q ( $( wildcard .git /HEAD ) , )
.PHONY : generate .authors
generate.authors : AUTHORS
AUTHORS : .git /
echo "# This file lists all individuals having contributed content to the repository." > AUTHORS
echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS
echo >> AUTHORS
git log --format= '%aN <%aE>' | LC_ALL = C.UTF-8 sort -uf >> AUTHORS
GENERATE_STEPS += generate.authors
e n d i f
##
## Golang
##
i f n d e f G O P K G
i f n e q ( $( wildcard go .mod ) , )
GOPKG = $( shell sed '/module/!d;s/^omdule\ //' go.mod)
e n d i f
e n d i f
i f d e f G O P K G
GO ?= go
GOPATH ?= $( HOME) /go
GO_INSTALL_OPTS ?=
GO_TEST_OPTS ?= -test.timeout= 30s
2020-11-10 05:52:48 +08:00
GOMOD_DIRS ?= $( sort $( call novendor,$( dir $( call rwildcard,*,*/go.mod go.mod) ) ) )
2020-04-27 19:09:07 +08:00
GOCOVERAGE_FILE ?= ./coverage.txt
2020-07-02 06:47:28 +08:00
GOTESTJSON_FILE ?= ./go-test.json
GOBUILDLOG_FILE ?= ./go-build.log
GOINSTALLLOG_FILE ?= ./go-install.log
2020-04-27 19:09:07 +08:00
i f d e f G O B I N S
.PHONY : go .install
go.install :
2020-07-02 06:47:28 +08:00
i f e q ( $( CI ) , t r u e )
@rm -f /tmp/goinstall.log
@set -e; for dir in $( GOBINS) ; do ( set -xe; \
cd $$ dir; \
$( GO) install -v $( GO_INSTALL_OPTS) .; \
) ; done 2>& 1 | tee $( GOINSTALLLOG_FILE)
e l s e
2020-04-27 19:09:07 +08:00
@set -e; for dir in $( GOBINS) ; do ( set -xe; \
cd $$ dir; \
$( GO) install $( GO_INSTALL_OPTS) .; \
) ; done
2020-07-02 06:47:28 +08:00
e n d i f
2020-04-27 19:09:07 +08:00
INSTALL_STEPS += go.install
.PHONY : go .release
go.release :
2020-11-10 05:52:48 +08:00
$( call check-program, goreleaser)
2020-04-27 19:09:07 +08:00
goreleaser --snapshot --skip-publish --rm-dist
@echo -n "Do you want to release? [y/N] " && read ans && \
if [ $$ { ans:-N} = y ] ; then set -xe; goreleaser --rm-dist; fi
RELEASE_STEPS += go.release
e n d i f
.PHONY : go .unittest
go.unittest :
2020-07-02 06:47:28 +08:00
i f e q ( $( CI ) , t r u e )
@echo "mode: atomic" > /tmp/gocoverage
@rm -f $( GOTESTJSON_FILE)
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -e; ( set -euf pipefail; \
2020-07-02 06:47:28 +08:00
cd $$ dir; \
2020-11-10 05:52:48 +08:00
( ( $( GO) test ./... $( GO_TEST_OPTS) -cover -coverprofile= /tmp/profile.out -covermode= atomic -race -json && touch $@ .ok) | tee -a $( GOTESTJSON_FILE) 3>& 1 1>& 2 2>& 3 | tee -a $( GOBUILDLOG_FILE) ; \
2020-07-02 06:47:28 +08:00
) ; \
2020-11-10 05:52:48 +08:00
rm $@ .ok 2>/dev/null || exit 1; \
2020-07-02 06:47:28 +08:00
if [ -f /tmp/profile.out ] ; then \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi ) ) ; done
@mv /tmp/gocoverage $( GOCOVERAGE_FILE)
e l s e
2020-04-27 19:09:07 +08:00
@echo "mode: atomic" > /tmp/gocoverage
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -e; ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
2020-07-02 06:47:28 +08:00
$( GO) test ./... $( GO_TEST_OPTS) -cover -coverprofile= /tmp/profile.out -covermode= atomic -race) ; \
2020-04-27 19:09:07 +08:00
if [ -f /tmp/profile.out ] ; then \
cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
rm -f /tmp/profile.out; \
fi ) ; done
@mv /tmp/gocoverage $( GOCOVERAGE_FILE)
2020-07-02 06:47:28 +08:00
e n d i f
2020-04-27 19:09:07 +08:00
2020-06-09 21:24:59 +08:00
.PHONY : go .checkdoc
go.checkdoc :
2020-11-10 05:52:48 +08:00
go doc $( first $( GOMOD_DIRS) )
2020-06-09 21:24:59 +08:00
2020-04-27 19:09:07 +08:00
.PHONY : go .coverfunc
go.coverfunc : go .unittest
go tool cover -func= $( GOCOVERAGE_FILE) | grep -v .pb.go: | grep -v .pb.gw.go:
.PHONY : go .lint
go.lint :
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
golangci-lint run --verbose ./...; \
) ; done
.PHONY : go .tidy
go.tidy :
2020-11-10 05:52:48 +08:00
@# tidy dirs with go.mod files
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
$( GO) mod tidy; \
) ; done
2020-11-10 05:52:48 +08:00
.PHONY : go .depaware -update
go.depaware-update : go .tidy
@# gen depaware for bins
@set -e; for dir in $( GOBINS) ; do ( set -xe; \
cd $$ dir; \
$( GO) run github.com/tailscale/depaware --update .; \
) ; done
@# tidy unused depaware deps if not in a tools_test.go file
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
cd $$ dir; \
$( GO) mod tidy; \
) ; done
.PHONY : go .depaware -check
go.depaware-check : go .tidy
@# gen depaware for bins
@set -e; for dir in $( GOBINS) ; do ( set -xe; \
cd $$ dir; \
$( GO) run github.com/tailscale/depaware --check .; \
) ; done
2020-04-27 19:09:07 +08:00
.PHONY : go .build
go.build :
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
$( GO) build ./...; \
) ; done
.PHONY : go .bump -deps
go.bumpdeps :
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
$( GO) get -u ./...; \
) ; done
.PHONY : go .bump -deps
go.fmt :
2020-11-10 05:52:48 +08:00
@set -e; for dir in $( GOMOD_DIRS) ; do ( set -xe; \
2020-04-27 19:09:07 +08:00
cd $$ dir; \
2020-11-10 05:52:48 +08:00
$( GO) run golang.org/x/tools/cmd/goimports -w ` go list -f '{{.Dir}}' ./...` \
2020-04-27 19:09:07 +08:00
) ; done
2020-11-10 05:52:48 +08:00
VERIFY_STEPS += go.depaware-check
2020-04-27 19:09:07 +08:00
BUILD_STEPS += go.build
2020-11-10 05:52:48 +08:00
BUMPDEPS_STEPS += go.bumpdeps go.depaware-update
2020-04-27 19:09:07 +08:00
TIDY_STEPS += go.tidy
LINT_STEPS += go.lint
UNITTEST_STEPS += go.unittest
FMT_STEPS += go.fmt
2020-11-10 05:52:48 +08:00
# FIXME: disabled, because currently slow
# new rule that is manually run sometimes, i.e. `make pre-release` or `make maintenance`.
# alternative: run it each time the go.mod is changed
#GENERATE_STEPS += go.depaware-update
2020-04-27 19:09:07 +08:00
e n d i f
2020-07-02 06:47:28 +08:00
##
## Gitattributes
##
i f n e q ( $( wildcard .gitattributes ) , )
.PHONY : _linguist -ignored
_linguist-kept :
@git check-attr linguist-vendored $( shell git check-attr linguist-generated $( shell find . -type f | grep -v .git/) | grep unspecified | cut -d: -f1) | grep unspecified | cut -d: -f1 | sort
.PHONY : _linguist -kept
_linguist-ignored :
@git check-attr linguist-vendored linguist-ignored ` find . -not -path './.git/*' -type f` | grep '\ set$$' | cut -d: -f1 | sort -u
e n d i f
2020-04-27 19:09:07 +08:00
##
## Node
##
i f n d e f N P M _ P A C K A G E S
i f n e q ( $( wildcard package .json ) , )
NPM_PACKAGES = .
e n d i f
e n d i f
i f d e f N P M _ P A C K A G E S
.PHONY : npm .publish
npm.publish :
@echo -n "Do you want to npm publish? [y/N] " && read ans && \
@if [ $$ { ans:-N} = y ] ; then \
set -e; for dir in $( NPM_PACKAGES) ; do ( set -xe; \
cd $$ dir; \
npm publish --access= public; \
) ; done ; \
fi
RELEASE_STEPS += npm.publish
e n d i f
##
## Docker
##
2020-06-09 21:24:59 +08:00
docker_build = docker build \
--build-arg VCS_REF = ` git rev-parse --short HEAD` \
--build-arg BUILD_DATE = ` date -u +"%Y-%m-%dT%H:%M:%SZ" ` \
--build-arg VERSION = ` git describe --tags --always` \
-t " $2 " -f " $1 " " $( dir $1 ) "
2020-04-27 19:09:07 +08:00
i f n d e f D O C K E R F I L E _ P A T H
DOCKERFILE_PATH = ./Dockerfile
e n d i f
i f n d e f D O C K E R _ I M A G E
i f n e q ( $( wildcard Dockerfile ) , )
DOCKER_IMAGE = $( notdir $( PWD) )
e n d i f
e n d i f
i f d e f D O C K E R _ I M A G E
i f n e q ( $( DOCKER_IMAGE ) , n o n e )
.PHONY : docker .build
docker.build :
2020-11-10 05:52:48 +08:00
$( call check-program, docker)
2020-06-09 21:24:59 +08:00
$( call docker_build,$( DOCKERFILE_PATH) ,$( DOCKER_IMAGE) )
2020-04-27 19:09:07 +08:00
BUILD_STEPS += docker.build
e n d i f
e n d i f
##
## Common
##
TEST_STEPS += $( UNITTEST_STEPS)
TEST_STEPS += $( LINT_STEPS)
TEST_STEPS += $( TIDY_STEPS)
i f n e q ( $( strip $ ( TEST_STEPS ) ) , )
.PHONY : test
test : $( PRE_TEST_STEPS ) $( TEST_STEPS )
e n d i f
i f d e f I N S T A L L _ S T E P S
.PHONY : install
install : $( PRE_INSTALL_STEPS ) $( INSTALL_STEPS )
e n d i f
i f d e f U N I T T E S T _ S T E P S
.PHONY : unittest
unittest : $( PRE_UNITTEST_STEPS ) $( UNITTEST_STEPS )
e n d i f
i f d e f L I N T _ S T E P S
.PHONY : lint
lint : $( PRE_LINT_STEPS ) $( FMT_STEPS ) $( LINT_STEPS )
e n d i f
i f d e f T I D Y _ S T E P S
.PHONY : tidy
tidy : $( PRE_TIDY_STEPS ) $( TIDY_STEPS )
e n d i f
i f d e f B U I L D _ S T E P S
.PHONY : build
build : $( PRE_BUILD_STEPS ) $( BUILD_STEPS )
e n d i f
2020-11-10 05:52:48 +08:00
i f d e f V E R I F Y _ S T E P S
.PHONY : verify
verify : $( PRE_VERIFY_STEPS ) $( VERIFY_STEPS )
e n d i f
2020-04-27 19:09:07 +08:00
i f d e f R E L E A S E _ S T E P S
.PHONY : release
release : $( PRE_RELEASE_STEPS ) $( RELEASE_STEPS )
e n d i f
i f d e f B U M P D E P S _ S T E P S
.PHONY : bumpdeps
bumpdeps : $( PRE_BUMDEPS_STEPS ) $( BUMPDEPS_STEPS )
e n d i f
i f d e f F M T _ S T E P S
.PHONY : fmt
fmt : $( PRE_FMT_STEPS ) $( FMT_STEPS )
e n d i f
i f d e f G E N E R A T E _ S T E P S
.PHONY : generate
generate : $( PRE_GENERATE_STEPS ) $( GENERATE_STEPS )
e n d i f
.PHONY : help
2020-06-09 21:24:59 +08:00
help ::
2020-04-27 19:09:07 +08:00
@echo "General commands:"
@[ " $( BUILD_STEPS) " != "" ] && echo " build" || true
@[ " $( BUMPDEPS_STEPS) " != "" ] && echo " bumpdeps" || true
@[ " $( FMT_STEPS) " != "" ] && echo " fmt" || true
@[ " $( GENERATE_STEPS) " != "" ] && echo " generate" || true
@[ " $( INSTALL_STEPS) " != "" ] && echo " install" || true
@[ " $( LINT_STEPS) " != "" ] && echo " lint" || true
@[ " $( RELEASE_STEPS) " != "" ] && echo " release" || true
@[ " $( TEST_STEPS) " != "" ] && echo " test" || true
@[ " $( TIDY_STEPS) " != "" ] && echo " tidy" || true
@[ " $( UNITTEST_STEPS) " != "" ] && echo " unittest" || true
2020-11-10 05:52:48 +08:00
@[ " $( VERIFY_STEPS) " != "" ] && echo " verify" || true
2020-04-27 19:09:07 +08:00
@# FIXME: list other commands
2020-11-10 05:52:48 +08:00
print-% : ; $( info $ * is a $ ( flavor $ *) variable set to [$ ( $ *) ]) @true