go.uber.org/cadence@v1.2.9/Makefile (about) 1 # get rid of default behaviors, they're just noise 2 MAKEFLAGS += --no-builtin-rules 3 .SUFFIXES: 4 5 default: help 6 7 # ########################################### 8 # TL;DR DOCS: 9 # ########################################### 10 # - Targets should never, EVER be *actual source files*. 11 # Always use book-keeping files in $(BUILD). 12 # Otherwise e.g. changing git branches could confuse Make about what it needs to do. 13 # - Similarly, prerequisites should be those book-keeping files, 14 # not source files that are prerequisites for book-keeping. 15 # e.g. depend on .build/fmt, not $(ALL_SRC), and not both. 16 # - Be strict and explicit about prerequisites / order of execution / etc. 17 # - Test your changes with `-j 27 --output-sync` or something! 18 # - Test your changes with `make -d ...`! It should be reasonable! 19 20 # temporary build products and book-keeping targets that are always good to / safe to clean. 21 # 22 # the go version is embedded in the path, so changing Go's version or arch triggers rebuilds. 23 # other things can be added if necessary, but hopefully only go's formatting behavior matters? 24 # converts: "go version go1.19.5 darwin/arm64" -> "go1.19.5_darwin_arm64" 25 BUILD := .build/$(shell go version | cut -d' ' -f3- | sed 's/[^a-zA-Z0-9.]/_/g') 26 # tools that can be easily re-built on demand, and may be sensitive to dependency or go versions. 27 # currently this covers all needs. if not, consider STABLE_BIN like github.com/uber/cadence has. 28 BIN := $(BUILD)/bin 29 30 # current (when committed) version of Go used in CI, and ideally also our docker images. 31 # this generally does not matter, but can impact goimports or formatting output. 32 # for maximum stability, make sure you use the same version as CI uses. 33 # 34 # this can _likely_ remain a major version, as fmt output does not tend to change in minor versions, 35 # which will allow findstring to match any minor version. 36 EXPECTED_GO_VERSION := go1.19 37 CURRENT_GO_VERSION := $(shell go version) 38 ifeq (,$(findstring $(EXPECTED_GO_VERSION),$(CURRENT_GO_VERSION))) 39 # if you are seeing this warning: consider using https://github.com/travis-ci/gimme to pin your version 40 $(warning Caution: you are not using CI's go version. Expected: $(EXPECTED_GO_VERSION), current: $(CURRENT_GO_VERSION)) 41 endif 42 43 # ==================================== 44 # book-keeping files that are used to control sequencing. 45 # 46 # you should use these as prerequisites in almost all cases, not the source files themselves. 47 # these are defined in roughly the reverse order that they are executed, for easier reading. 48 # 49 # recipes and any other prerequisites are defined only once, further below. 50 # ==================================== 51 52 # note that vars that do not yet exist are empty, so stick to BUILD/BIN and probably nothing else. 53 $(BUILD)/lint: $(BUILD)/fmt $(BUILD)/dummy # lint will fail if fmt or dummy fails, so run them first 54 $(BUILD)/dummy: $(BUILD)/fmt # do a build after fmt-ing 55 $(BUILD)/fmt: $(BUILD)/copyright # formatting must occur only after all other go-file-modifications are done 56 $(BUILD)/copyright: $(BUILD)/codegen # must add copyright to generated code 57 $(BUILD)/codegen: $(BUILD)/thrift # thrift is currently the only codegen, but this way it's easier to extend 58 $(BUILD)/thrift: $(BUILD)/go_mod_check 59 $(BUILD)/go_mod_check: | $(BUILD) $(BIN) 60 61 # ==================================== 62 # helper vars 63 # ==================================== 64 65 PROJECT_ROOT = go.uber.org/cadence 66 67 # helper for executing bins that need other bins, just `$(BIN_PATH) the_command ...` 68 # I'd recommend not exporting this in general, to reduce the chance of accidentally using non-versioned tools. 69 BIN_PATH := PATH="$(abspath $(BIN)):$$PATH" 70 71 # default test args, easy to override 72 TEST_ARG ?= -v -race 73 74 # set a V=1 env var for verbose output. V=0 (or unset) disables. 75 # this is used to make two verbose flags: 76 # - $Q, to replace ALL @ use, so CI can be reliably verbose 77 # - $(verbose), to forward verbosity flags to commands via `$(if $(verbose),-v)` or similar 78 # 79 # SHELL='bash -x' is useful too, but can be more confusing to understand. 80 V ?= 0 81 ifneq (0,$(V)) 82 verbose := 1 83 Q := 84 else 85 verbose := 86 Q := @ 87 endif 88 89 # and enforce ^ that rule: grep the makefile for line-starting @ use, error if any exist. 90 # limit to one match because multiple look too weird. 91 _BAD_AT_USE=$(shell grep -n -m1 '^\s*@' $(MAKEFILE_LIST)) 92 ifneq (,$(_BAD_AT_USE)) 93 $(warning Makefile cannot use @ to silence commands, use $$Q instead:) 94 $(warning found on line $(_BAD_AT_USE)) 95 $(error fix that line and try again) 96 endif 97 98 # automatically gather all source files that currently exist. 99 # works by ignoring everything in the parens (and does not descend into matching folders) due to `-prune`, 100 # and everything else goes to the other side of the `-o` branch, which is `-print`ed. 101 # this is dramatically faster than a `find . | grep -v vendor` pipeline, and scales far better. 102 FRESH_ALL_SRC = $(shell \ 103 find . \ 104 \( \ 105 -path './vendor/*' \ 106 -o -path './idls/*' \ 107 -o -path './$(BUILD)/*' \ 108 -o -path './$(BIN)/*' \ 109 \) \ 110 -prune \ 111 -o -name '*.go' -print \ 112 ) 113 # most things can use a cached copy, e.g. all dependencies. 114 # this will not include any files that are created during a `make` run, e.g. via protoc, 115 # but that generally should not matter (e.g. dependencies are computed at parse time, so it 116 # won't affect behavior either way - choose the fast option). 117 # 118 # if you require a fully up-to-date list, e.g. for shell commands, use FRESH_ALL_SRC instead. 119 ALL_SRC := $(FRESH_ALL_SRC) 120 # as lint ignores generated code, it can use the cached copy in all cases. 121 LINT_SRC := $(filter-out %_test.go ./.gen/% ./mock% ./tools.go ./internal/compatibility/%, $(ALL_SRC)) 122 123 # ==================================== 124 # $(BIN) targets 125 # ==================================== 126 127 # builds a go-gettable tool, versioned by internal/tools/go.mod, and installs it into 128 # the build folder, named the same as the last portion of the URL or the second arg. 129 define go_build_tool 130 $Q echo "building $(or $(2), $(notdir $(1))) from internal/tools/go.mod..." 131 $Q go build -mod=readonly -modfile=internal/tools/go.mod -o $(BIN)/$(or $(2), $(notdir $(1))) $(1) 132 endef 133 134 # utility target. 135 # use as an order-only prerequisite for targets that do not implicitly create these folders. 136 $(BIN) $(BUILD): 137 $Q mkdir -p $@ 138 139 $(BIN)/thriftrw: internal/tools/go.mod 140 $(call go_build_tool,go.uber.org/thriftrw) 141 142 $(BIN)/thriftrw-plugin-yarpc: internal/tools/go.mod 143 $(call go_build_tool,go.uber.org/yarpc/encoding/thrift/thriftrw-plugin-yarpc) 144 145 $(BIN)/goimports: internal/tools/go.mod 146 $(call go_build_tool,golang.org/x/tools/cmd/goimports) 147 148 $(BIN)/revive: internal/tools/go.mod 149 $(call go_build_tool,github.com/mgechev/revive) 150 151 $(BIN)/staticcheck: internal/tools/go.mod 152 $(call go_build_tool,honnef.co/go/tools/cmd/staticcheck) 153 154 $(BIN)/errcheck: internal/tools/go.mod 155 $(call go_build_tool,github.com/kisielk/errcheck) 156 157 $(BIN)/goveralls: internal/tools/go.mod 158 $(call go_build_tool,github.com/mattn/goveralls) 159 160 # copyright header checker/writer. only requires stdlib, so no other dependencies are needed. 161 $(BIN)/copyright: internal/cmd/tools/copyright/licensegen.go 162 go build -mod=readonly -o $@ ./internal/cmd/tools/copyright/licensegen.go 163 164 # dummy binary that ensures most/all packages build, without needing to wait for tests. 165 $(BUILD)/dummy: $(ALL_SRC) $(BUILD)/go_mod_check 166 go build -mod=readonly -o $@ internal/cmd/dummy/dummy.go 167 168 # ensures mod files are in sync for critical packages 169 $(BUILD)/go_mod_check: go.mod internal/tools/go.mod 170 $Q # ensure both have the same apache/thrift replacement 171 $Q ./scripts/check-gomod-version.sh go.uber.org/thriftrw $(if $(verbose),-v) 172 $Q touch $@ 173 174 # ==================================== 175 # Codegen targets 176 # ==================================== 177 178 # IDL submodule must be populated, or files will not exist -> prerequisites will be wrong -> build will fail. 179 # Because it must exist before the makefile is parsed, this cannot be done automatically as part of a build. 180 # Instead: call this func in targets that require the submodule to exist, so that target will not be built. 181 # 182 # THRIFT_FILES is just an easy identifier for "the submodule has files", others would work fine as well. 183 define ensure_idl_submodule 184 $(if $(wildcard THRIFT_FILES),,$(error idls/ submodule must exist, or build will fail. Run `git submodule update --init` and try again)) 185 endef 186 187 # codegen is done when thrift is done (it's just a naming-convenience, $(BUILD)/thrift would be fine too) 188 $(BUILD)/codegen: $(BUILD)/thrift | $(BUILD) 189 $Q touch $@ 190 191 THRIFT_FILES := idls/thrift/cadence.thrift idls/thrift/shadower.thrift 192 # book-keeping targets to build. one per thrift file. 193 # idls/thrift/thing.thrift -> .build/go_version/thing.thrift 194 # the reverse is done in the recipe. 195 THRIFT_GEN := $(subst idls/thrift,$(BUILD),$(THRIFT_FILES)) 196 197 # dummy targets to detect when the idls submodule does not exist, to provide a better error message 198 $(THRIFT_FILES): 199 $(call ensure_idl_submodule) 200 201 # thrift is done when all sub-thrifts are done. 202 $(BUILD)/thrift: $(THRIFT_GEN) 203 $Q touch $@ 204 205 # how to generate each thrift book-keeping file. 206 # 207 # note that each generated file depends on ALL thrift files - this is necessary because they can import each other. 208 # ideally this would --no-recurse like the server does, but currently that produces a new output file, and parallel 209 # compiling appears to work fine. seems likely it only risks rare flaky builds. 210 $(THRIFT_GEN): $(THRIFT_FILES) $(BIN)/thriftrw $(BIN)/thriftrw-plugin-yarpc 211 $Q echo 'thriftrw for $(subst $(BUILD),idls/thrift,$@)...' 212 $Q $(BIN_PATH) $(BIN)/thriftrw \ 213 --plugin=yarpc \ 214 --pkg-prefix=$(PROJECT_ROOT)/.gen/go \ 215 --out=.gen/go \ 216 $(subst $(BUILD),idls/thrift,$@) 217 $Q touch $@ 218 219 # ==================================== 220 # other intermediates 221 # ==================================== 222 223 # note that LINT_SRC is fairly fake as a prerequisite. 224 # it's a coarse "you probably don't need to re-lint" filter, nothing more. 225 $(BUILD)/lint: $(LINT_SRC) $(BIN)/revive | $(BUILD) 226 $Q $(BIN)/revive -config revive.toml -exclude './vendor/...' -exclude './.gen/...' -formatter stylish ./... 227 $Q touch $@ 228 229 # fmt and copyright are mutually cyclic with their inputs, so if a copyright header is modified: 230 # - copyright -> makes changes 231 # - fmt sees changes -> makes changes 232 # - now copyright thinks it needs to run again (but does nothing) 233 # - which means fmt needs to run again (but does nothing) 234 # and now after two passes it's finally stable, because they stopped making changes. 235 # 236 # this is not fatal, we can just run 2x. 237 # to be fancier though, we can detect when *both* are run, and re-touch the book-keeping files to prevent the second run. 238 # this STRICTLY REQUIRES that `copyright` and `fmt` are mutually stable, and that copyright runs before fmt. 239 # if either changes, this will need to change. 240 MAYBE_TOUCH_COPYRIGHT= 241 242 # TODO: switch to goimports, so we can pin the version 243 $(BUILD)/fmt: $(ALL_SRC) $(BIN)/goimports 244 $Q echo "goimports..." 245 $Q # use FRESH_ALL_SRC so it won't miss any generated files produced earlier 246 $Q $(BIN)/goimports -local "go.uber.org/cadence" -w $(FRESH_ALL_SRC) 247 $Q touch $@ 248 $Q $(MAYBE_TOUCH_COPYRIGHT) 249 250 $(BUILD)/copyright: $(ALL_SRC) $(BIN)/copyright 251 $(BIN)/copyright --verifyOnly 252 $Q $(eval MAYBE_TOUCH_COPYRIGHT=touch $@) 253 $Q touch $@ 254 255 # ==================================== 256 # developer-oriented targets 257 # 258 # many of these share logic with other intermediates, but are useful to make .PHONY for output on demand. 259 # as the Makefile is fast, it's reasonable to just delete the book-keeping file recursively make. 260 # this way the effort is shared with future `make` runs. 261 # ==================================== 262 263 # "re-make" a target by deleting and re-building book-keeping target(s). 264 # the + is necessary for parallelism flags to be propagated 265 define remake 266 $Q rm -f $(addprefix $(BUILD)/,$(1)) 267 $Q +$(MAKE) --no-print-directory $(addprefix $(BUILD)/,$(1)) 268 endef 269 270 .PHONY: build 271 build: $(BUILD)/dummy ## ensure all packages build 272 273 .PHONY: lint 274 # useful to actually re-run to get output again. 275 # reuse the intermediates for simplicity and consistency. 276 lint: ## (re)run the linter 277 $(call remake,lint) 278 279 .PHONY: fmt 280 # intentionally not re-making, it's clear when it's unnecessary 281 fmt: $(BUILD)/fmt ## run goimports 282 283 .PHONY: copyright 284 # not identical to the intermediate target, but does provide the same codegen (or more). 285 copyright: $(BIN)/copyright ## update copyright headers 286 $(BIN)/copyright 287 $Q touch $(BUILD)/copyright 288 289 .PHONY: staticcheck 290 staticcheck: $(BIN)/staticcheck $(BUILD)/fmt ## (re)run staticcheck 291 $(BIN)/staticcheck ./... 292 293 .PHONY: errcheck 294 errcheck: $(BIN)/errcheck $(BUILD)/fmt ## (re)run errcheck 295 $(BIN)/errcheck ./... 296 297 .PHONY: all 298 all: $(BUILD)/lint ## refresh codegen, lint, and ensure the dummy binary builds, if necessary 299 300 .PHONY: clean 301 clean: 302 $Q # intentionally not using $(BUILD) as that covers only a single version 303 rm -Rf .build .gen 304 $Q # remove old things (no longer in use). this can be removed "eventually", when we feel like they're unlikely to exist. 305 rm -Rf .bin 306 307 # broken up into multiple += so I can interleave comments. 308 # this all becomes a single line of output. 309 # you must not use single-quotes within the string in this var. 310 JQ_DEPS_AGE = jq ' 311 # only deal with things with updates 312 JQ_DEPS_AGE += select(.Update) 313 # allow additional filtering, e.g. DEPS_FILTER='$(JQ_DEPS_ONLY_DIRECT)' 314 JQ_DEPS_AGE += $(DEPS_FILTER) 315 # add "days between current version and latest version" 316 JQ_DEPS_AGE += | . + {Age:(((.Update.Time | fromdate) - (.Time | fromdate))/60/60/24 | floor)} 317 # add "days between latest version and now" 318 JQ_DEPS_AGE += | . + {Available:((now - (.Update.Time | fromdate))/60/60/24 | floor)} 319 # 123 days: library old_version -> new_version 320 JQ_DEPS_AGE += | ([.Age, .Available] | max | tostring) + " days: " + .Path + " \t" + .Version + " -> " + .Update.Version 321 JQ_DEPS_AGE += ' 322 # remove surrounding quotes from output 323 JQ_DEPS_AGE += --raw-output 324 325 # exclude `"Indirect": true` dependencies. direct ones have no "Indirect" key at all. 326 JQ_DEPS_ONLY_DIRECT = | select(has("Indirect") | not) 327 328 .PHONY: deps 329 deps: ## Check for dependency updates, for things that are directly imported 330 $Q make --no-print-directory DEPS_FILTER='$(JQ_DEPS_ONLY_DIRECT)' deps-all 331 332 .PHONY: deps-all 333 deps-all: ## Check for all dependency updates 334 $Q go list -u -m -json all \ 335 | $(JQ_DEPS_AGE) \ 336 | sort -n 337 338 .PHONY: help 339 help: 340 $Q # print help first, so it's visible 341 $Q printf "\033[36m%-20s\033[0m %s\n" 'help' 'Prints a help message showing any specially-commented targets' 342 $Q # then everything matching "target: ## magic comments" 343 $Q cat $(MAKEFILE_LIST) | grep -e "^[a-zA-Z_\-]*:.* ## .*" | awk 'BEGIN {FS = ":.*? ## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}' | sort 344 345 # v==================== not yet cleaned up =======================v 346 347 INTEG_TEST_ROOT := ./test 348 COVER_ROOT := $(BUILD)/coverage 349 UT_COVER_FILE := $(COVER_ROOT)/unit_test_cover.out 350 INTEG_STICKY_OFF_COVER_FILE := $(COVER_ROOT)/integ_test_sticky_off_cover.out 351 INTEG_STICKY_ON_COVER_FILE := $(COVER_ROOT)/integ_test_sticky_on_cover.out 352 INTEG_GRPC_COVER_FILE := $(COVER_ROOT)/integ_test_grpc_cover.out 353 354 UT_DIRS := $(filter-out $(INTEG_TEST_ROOT)%, $(sort $(dir $(filter %_test.go,$(ALL_SRC))))) 355 356 .PHONY: unit_test integ_test_sticky_off integ_test_sticky_on integ_test_grpc cover cover_ci 357 test: unit_test integ_test_sticky_off integ_test_sticky_on ## run all tests (requires a running cadence instance) 358 359 unit_test: $(ALL_SRC) ## run all unit tests 360 $Q mkdir -p $(COVER_ROOT) 361 $Q echo "mode: atomic" > $(UT_COVER_FILE) 362 $Q failed=0; \ 363 for dir in $(UT_DIRS); do \ 364 mkdir -p $(COVER_ROOT)/"$$dir"; \ 365 go test "$$dir" $(TEST_ARG) -coverprofile=$(COVER_ROOT)/"$$dir"/cover.out || failed=1; \ 366 cat $(COVER_ROOT)/"$$dir"/cover.out | grep -v "mode: atomic" >> $(UT_COVER_FILE); \ 367 done; \ 368 cat $(UT_COVER_FILE) > .build/cover.out; 369 exit $$failed 370 371 integ_test_sticky_off: $(ALL_SRC) 372 $Q mkdir -p $(COVER_ROOT) 373 STICKY_OFF=true go test $(TEST_ARG) ./test -coverprofile=$(INTEG_STICKY_OFF_COVER_FILE) -coverpkg=./... 374 375 integ_test_sticky_on: $(ALL_SRC) 376 $Q mkdir -p $(COVER_ROOT) 377 STICKY_OFF=false go test $(TEST_ARG) ./test -coverprofile=$(INTEG_STICKY_ON_COVER_FILE) -coverpkg=./... 378 379 integ_test_grpc: $(ALL_SRC) 380 $Q mkdir -p $(COVER_ROOT) 381 STICKY_OFF=false go test $(TEST_ARG) ./test -coverprofile=$(INTEG_GRPC_COVER_FILE) -coverpkg=./... 382 383 # intermediate product, ci needs a stable output, so use coverage_report. 384 # running this target requires coverage files to have already been created, e.g. run ^ the above by hand, which happens in ci. 385 $(COVER_ROOT)/cover.out: $(UT_COVER_FILE) $(INTEG_STICKY_OFF_COVER_FILE) $(INTEG_STICKY_ON_COVER_FILE) $(INTEG_GRPC_COVER_FILE) 386 $Q echo "mode: atomic" > $(COVER_ROOT)/cover.out 387 cat $(UT_COVER_FILE) | grep -v "mode: atomic" | grep -v ".gen" >> $(COVER_ROOT)/cover.out 388 cat $(INTEG_STICKY_OFF_COVER_FILE) | grep -v "mode: atomic" | grep -v ".gen" >> $(COVER_ROOT)/cover.out 389 cat $(INTEG_STICKY_ON_COVER_FILE) | grep -v "mode: atomic" | grep -v ".gen" >> $(COVER_ROOT)/cover.out 390 cat $(INTEG_GRPC_COVER_FILE) | grep -v "mode: atomic" | grep -v ".gen" >> $(COVER_ROOT)/cover.out 391 392 coverage_report: $(COVER_ROOT)/cover.out 393 cp $< $@ 394 395 cover: $(COVER_ROOT)/cover.out 396 go tool cover -html=$(COVER_ROOT)/cover.out; 397 398 cover_ci: $(COVER_ROOT)/cover.out $(BIN)/goveralls 399 $(BIN)/goveralls -coverprofile=$(COVER_ROOT)/cover.out -service=buildkite || echo -e "\x1b[31mCoveralls failed\x1b[m";