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";