rules.mk 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. # +--------------------------------------------------------------+
  2. # | * * * moul.io/rules.mk |
  3. # +--------------------------------------------------------------+
  4. # | |
  5. # | ++ ______________________________________ |
  6. # | ++++ / \ |
  7. # | ++++ | | |
  8. # | ++++++++++ | https://moul.io/rules.mk is a set | |
  9. # | +++ | | of common Makefile rules that can | |
  10. # | ++ | | be configured from the Makefile | |
  11. # | + -== ==| | or with environment variables. | |
  12. # | ( <*> <*> | | |
  13. # | | | /| Manfred Touron | |
  14. # | | _) / | manfred.life | |
  15. # | | +++ / \______________________________________/ |
  16. # | \ =+ / |
  17. # | \ + |
  18. # | |\++++++ |
  19. # | | ++++ ||// |
  20. # | ___| |___ _||/__ __|
  21. # | / --- \ \| ||| __ _ ___ __ __/ /|
  22. # |/ | | \ \ / / ' \/ _ \/ // / / |
  23. # || | | | | | /_/_/_/\___/\_,_/_/ |
  24. # +--------------------------------------------------------------+
  25. .PHONY: _default_entrypoint
  26. _default_entrypoint: help
  27. ##
  28. ## Common helpers
  29. ##
  30. rwildcard = $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2) $(filter $(subst *,%,$2),$d))
  31. check-program = $(foreach exec,$(1),$(if $(shell PATH="$(PATH)" which $(exec)),,$(error "No $(exec) in PATH")))
  32. my-filter-out = $(foreach v,$(2),$(if $(findstring $(1),$(v)),,$(v)))
  33. novendor = $(call my-filter-out,vendor/,$(1))
  34. ##
  35. ## rules.mk
  36. ##
  37. ifneq ($(wildcard rules.mk),)
  38. .PHONY: rulesmk.bumpdeps
  39. rulesmk.bumpdeps:
  40. wget -O rules.mk https://raw.githubusercontent.com/moul/rules.mk/master/rules.mk
  41. BUMPDEPS_STEPS += rulesmk.bumpdeps
  42. endif
  43. ##
  44. ## Maintainer
  45. ##
  46. ifneq ($(wildcard .git/HEAD),)
  47. .PHONY: generate.authors
  48. generate.authors: AUTHORS
  49. AUTHORS: .git/
  50. echo "# This file lists all individuals having contributed content to the repository." > AUTHORS
  51. echo "# For how it is generated, see 'https://github.com/moul/rules.mk'" >> AUTHORS
  52. echo >> AUTHORS
  53. git log --format='%aN <%aE>' | LC_ALL=C.UTF-8 sort -uf >> AUTHORS
  54. GENERATE_STEPS += generate.authors
  55. endif
  56. ##
  57. ## Golang
  58. ##
  59. ifndef GOPKG
  60. ifneq ($(wildcard go.mod),)
  61. GOPKG = $(shell sed '/module/!d;s/^omdule\ //' go.mod)
  62. endif
  63. endif
  64. ifdef GOPKG
  65. GO ?= go
  66. GOPATH ?= $(HOME)/go
  67. GO_INSTALL_OPTS ?=
  68. GO_TEST_OPTS ?= -test.timeout=30s
  69. GOMOD_DIRS ?= $(sort $(call novendor,$(dir $(call rwildcard,*,*/go.mod go.mod))))
  70. GOCOVERAGE_FILE ?= ./coverage.txt
  71. GOTESTJSON_FILE ?= ./go-test.json
  72. GOBUILDLOG_FILE ?= ./go-build.log
  73. GOINSTALLLOG_FILE ?= ./go-install.log
  74. ifdef GOBINS
  75. .PHONY: go.install
  76. go.install:
  77. ifeq ($(CI),true)
  78. @rm -f /tmp/goinstall.log
  79. @set -e; for dir in $(GOBINS); do ( set -xe; \
  80. cd $$dir; \
  81. $(GO) install -v $(GO_INSTALL_OPTS) .; \
  82. ); done 2>&1 | tee $(GOINSTALLLOG_FILE)
  83. else
  84. @set -e; for dir in $(GOBINS); do ( set -xe; \
  85. cd $$dir; \
  86. $(GO) install $(GO_INSTALL_OPTS) .; \
  87. ); done
  88. endif
  89. INSTALL_STEPS += go.install
  90. .PHONY: go.release
  91. go.release:
  92. $(call check-program, goreleaser)
  93. goreleaser --snapshot --skip-publish --rm-dist
  94. @echo -n "Do you want to release? [y/N] " && read ans && \
  95. if [ $${ans:-N} = y ]; then set -xe; goreleaser --rm-dist; fi
  96. RELEASE_STEPS += go.release
  97. endif
  98. .PHONY: go.unittest
  99. go.unittest:
  100. ifeq ($(CI),true)
  101. @echo "mode: atomic" > /tmp/gocoverage
  102. @rm -f $(GOTESTJSON_FILE)
  103. @set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -euf pipefail; \
  104. cd $$dir; \
  105. (($(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); \
  106. ); \
  107. rm $@.ok 2>/dev/null || exit 1; \
  108. if [ -f /tmp/profile.out ]; then \
  109. cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
  110. rm -f /tmp/profile.out; \
  111. fi)); done
  112. @mv /tmp/gocoverage $(GOCOVERAGE_FILE)
  113. else
  114. @echo "mode: atomic" > /tmp/gocoverage
  115. @set -e; for dir in $(GOMOD_DIRS); do (set -e; (set -xe; \
  116. cd $$dir; \
  117. $(GO) test ./... $(GO_TEST_OPTS) -cover -coverprofile=/tmp/profile.out -covermode=atomic -race); \
  118. if [ -f /tmp/profile.out ]; then \
  119. cat /tmp/profile.out | sed "/mode: atomic/d" >> /tmp/gocoverage; \
  120. rm -f /tmp/profile.out; \
  121. fi); done
  122. @mv /tmp/gocoverage $(GOCOVERAGE_FILE)
  123. endif
  124. .PHONY: go.checkdoc
  125. go.checkdoc:
  126. go doc $(first $(GOMOD_DIRS))
  127. .PHONY: go.coverfunc
  128. go.coverfunc: go.unittest
  129. go tool cover -func=$(GOCOVERAGE_FILE) | grep -v .pb.go: | grep -v .pb.gw.go:
  130. .PHONY: go.lint
  131. go.lint:
  132. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  133. cd $$dir; \
  134. golangci-lint run --verbose ./...; \
  135. ); done
  136. .PHONY: go.tidy
  137. go.tidy:
  138. @# tidy dirs with go.mod files
  139. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  140. cd $$dir; \
  141. $(GO) mod tidy; \
  142. ); done
  143. .PHONY: go.depaware-update
  144. go.depaware-update: go.tidy
  145. @# gen depaware for bins
  146. @set -e; for dir in $(GOBINS); do ( set -xe; \
  147. cd $$dir; \
  148. $(GO) run github.com/tailscale/depaware --update .; \
  149. ); done
  150. @# tidy unused depaware deps if not in a tools_test.go file
  151. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  152. cd $$dir; \
  153. $(GO) mod tidy; \
  154. ); done
  155. .PHONY: go.depaware-check
  156. go.depaware-check: go.tidy
  157. @# gen depaware for bins
  158. @set -e; for dir in $(GOBINS); do ( set -xe; \
  159. cd $$dir; \
  160. $(GO) run github.com/tailscale/depaware --check .; \
  161. ); done
  162. .PHONY: go.build
  163. go.build:
  164. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  165. cd $$dir; \
  166. $(GO) build ./...; \
  167. ); done
  168. .PHONY: go.bump-deps
  169. go.bumpdeps:
  170. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  171. cd $$dir; \
  172. $(GO) get -u ./...; \
  173. ); done
  174. .PHONY: go.bump-deps
  175. go.fmt:
  176. @set -e; for dir in $(GOMOD_DIRS); do ( set -xe; \
  177. cd $$dir; \
  178. $(GO) run golang.org/x/tools/cmd/goimports -w `go list -f '{{.Dir}}' ./...` \
  179. ); done
  180. VERIFY_STEPS += go.depaware-check
  181. BUILD_STEPS += go.build
  182. BUMPDEPS_STEPS += go.bumpdeps go.depaware-update
  183. TIDY_STEPS += go.tidy
  184. LINT_STEPS += go.lint
  185. UNITTEST_STEPS += go.unittest
  186. FMT_STEPS += go.fmt
  187. # FIXME: disabled, because currently slow
  188. # new rule that is manually run sometimes, i.e. `make pre-release` or `make maintenance`.
  189. # alternative: run it each time the go.mod is changed
  190. #GENERATE_STEPS += go.depaware-update
  191. endif
  192. ##
  193. ## Gitattributes
  194. ##
  195. ifneq ($(wildcard .gitattributes),)
  196. .PHONY: _linguist-ignored
  197. _linguist-kept:
  198. @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
  199. .PHONY: _linguist-kept
  200. _linguist-ignored:
  201. @git check-attr linguist-vendored linguist-ignored `find . -not -path './.git/*' -type f` | grep '\ set$$' | cut -d: -f1 | sort -u
  202. endif
  203. ##
  204. ## Node
  205. ##
  206. ifndef NPM_PACKAGES
  207. ifneq ($(wildcard package.json),)
  208. NPM_PACKAGES = .
  209. endif
  210. endif
  211. ifdef NPM_PACKAGES
  212. .PHONY: npm.publish
  213. npm.publish:
  214. @echo -n "Do you want to npm publish? [y/N] " && read ans && \
  215. @if [ $${ans:-N} = y ]; then \
  216. set -e; for dir in $(NPM_PACKAGES); do ( set -xe; \
  217. cd $$dir; \
  218. npm publish --access=public; \
  219. ); done; \
  220. fi
  221. RELEASE_STEPS += npm.publish
  222. endif
  223. ##
  224. ## Docker
  225. ##
  226. docker_build = docker build \
  227. --build-arg VCS_REF=`git rev-parse --short HEAD` \
  228. --build-arg BUILD_DATE=`date -u +"%Y-%m-%dT%H:%M:%SZ"` \
  229. --build-arg VERSION=`git describe --tags --always` \
  230. -t "$2" -f "$1" "$(dir $1)"
  231. ifndef DOCKERFILE_PATH
  232. DOCKERFILE_PATH = ./Dockerfile
  233. endif
  234. ifndef DOCKER_IMAGE
  235. ifneq ($(wildcard Dockerfile),)
  236. DOCKER_IMAGE = $(notdir $(PWD))
  237. endif
  238. endif
  239. ifdef DOCKER_IMAGE
  240. ifneq ($(DOCKER_IMAGE),none)
  241. .PHONY: docker.build
  242. docker.build:
  243. $(call check-program, docker)
  244. $(call docker_build,$(DOCKERFILE_PATH),$(DOCKER_IMAGE))
  245. BUILD_STEPS += docker.build
  246. endif
  247. endif
  248. ##
  249. ## Common
  250. ##
  251. TEST_STEPS += $(UNITTEST_STEPS)
  252. TEST_STEPS += $(LINT_STEPS)
  253. TEST_STEPS += $(TIDY_STEPS)
  254. ifneq ($(strip $(TEST_STEPS)),)
  255. .PHONY: test
  256. test: $(PRE_TEST_STEPS) $(TEST_STEPS)
  257. endif
  258. ifdef INSTALL_STEPS
  259. .PHONY: install
  260. install: $(PRE_INSTALL_STEPS) $(INSTALL_STEPS)
  261. endif
  262. ifdef UNITTEST_STEPS
  263. .PHONY: unittest
  264. unittest: $(PRE_UNITTEST_STEPS) $(UNITTEST_STEPS)
  265. endif
  266. ifdef LINT_STEPS
  267. .PHONY: lint
  268. lint: $(PRE_LINT_STEPS) $(FMT_STEPS) $(LINT_STEPS)
  269. endif
  270. ifdef TIDY_STEPS
  271. .PHONY: tidy
  272. tidy: $(PRE_TIDY_STEPS) $(TIDY_STEPS)
  273. endif
  274. ifdef BUILD_STEPS
  275. .PHONY: build
  276. build: $(PRE_BUILD_STEPS) $(BUILD_STEPS)
  277. endif
  278. ifdef VERIFY_STEPS
  279. .PHONY: verify
  280. verify: $(PRE_VERIFY_STEPS) $(VERIFY_STEPS)
  281. endif
  282. ifdef RELEASE_STEPS
  283. .PHONY: release
  284. release: $(PRE_RELEASE_STEPS) $(RELEASE_STEPS)
  285. endif
  286. ifdef BUMPDEPS_STEPS
  287. .PHONY: bumpdeps
  288. bumpdeps: $(PRE_BUMDEPS_STEPS) $(BUMPDEPS_STEPS)
  289. endif
  290. ifdef FMT_STEPS
  291. .PHONY: fmt
  292. fmt: $(PRE_FMT_STEPS) $(FMT_STEPS)
  293. endif
  294. ifdef GENERATE_STEPS
  295. .PHONY: generate
  296. generate: $(PRE_GENERATE_STEPS) $(GENERATE_STEPS)
  297. endif
  298. .PHONY: help
  299. help::
  300. @echo "General commands:"
  301. @[ "$(BUILD_STEPS)" != "" ] && echo " build" || true
  302. @[ "$(BUMPDEPS_STEPS)" != "" ] && echo " bumpdeps" || true
  303. @[ "$(FMT_STEPS)" != "" ] && echo " fmt" || true
  304. @[ "$(GENERATE_STEPS)" != "" ] && echo " generate" || true
  305. @[ "$(INSTALL_STEPS)" != "" ] && echo " install" || true
  306. @[ "$(LINT_STEPS)" != "" ] && echo " lint" || true
  307. @[ "$(RELEASE_STEPS)" != "" ] && echo " release" || true
  308. @[ "$(TEST_STEPS)" != "" ] && echo " test" || true
  309. @[ "$(TIDY_STEPS)" != "" ] && echo " tidy" || true
  310. @[ "$(UNITTEST_STEPS)" != "" ] && echo " unittest" || true
  311. @[ "$(VERIFY_STEPS)" != "" ] && echo " verify" || true
  312. @# FIXME: list other commands
  313. print-% : ; $(info $* is a $(flavor $*) variable set to [$($*)]) @true