diff options
author | Adin Scannell <ascannell@google.com> | 2020-12-09 09:31:44 -0800 |
---|---|---|
committer | Shentubot <shentubot@google.com> | 2020-12-09 15:53:23 -0800 |
commit | a855a814d601a4c30f26743ef1bf016df956e042 (patch) | |
tree | b017a831f6eff9c9734663114bbdf39fc4b6ffc7 /tools/bazel.mk | |
parent | f6cb96bd57dec4e3baa8c57ccdeb0f1d8706b682 (diff) |
Refactor the Makefile to avoid recursive Make.
Recursive make is difficult to follow and debug. Drop this by using
internal functions, which, while difficult, are easier than trying to
following recursive invokations.
Further simplify the Makefile by collapsing the image bits and removing
the tools/vm directory, which is effectively unused.
Fixes #4952
PiperOrigin-RevId: 346569133
Diffstat (limited to 'tools/bazel.mk')
-rw-r--r-- | tools/bazel.mk | 243 |
1 files changed, 120 insertions, 123 deletions
diff --git a/tools/bazel.mk b/tools/bazel.mk index ca5621a9c..ae99a2c76 100644 --- a/tools/bazel.mk +++ b/tools/bazel.mk @@ -14,22 +14,36 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Make hacks. -EMPTY := -SPACE := $(EMPTY) $(EMPTY) +## +## Docker options. +## +## This file supports targets that wrap bazel in a running Docker +## container to simplify development. Some options are available to +## control the behavior of this container: +## +## USER - The in-container user. +## DOCKER_RUN_OPTIONS - Options for the container (default: --privileged, required for tests). +## DOCKER_NAME - The container name (default: gvisor-bazel-HASH). +## DOCKER_PRIVILEGED - Docker privileged flags (default: --privileged). +## BAZEL_CACHE - The bazel cache directory (default: detected). +## GCLOUD_CONFIG - The gcloud config directory (detect: detected). +## DOCKER_SOCKET - The Docker socket (default: detected). +## +## To opt out of these wrappers, set DOCKER_BUILD=false. +DOCKER_BUILD := true +ifeq ($(DOCKER_BUILD),true) +-include bazel-server +endif # See base Makefile. -SHELL=/bin/bash -o pipefail BRANCH_NAME := $(shell (git branch --show-current 2>/dev/null || \ - git rev-parse --abbrev-ref HEAD 2>/dev/null) | \ - xargs -n 1 basename 2>/dev/null) + git rev-parse --abbrev-ref HEAD 2>/dev/null) | \ + xargs -n 1 basename 2>/dev/null) BUILD_ROOTS := bazel-bin/ bazel-out/ # Bazel container configuration (see below). USER := $(shell whoami) HASH := $(shell readlink -m $(CURDIR) | md5sum | cut -c1-8) -BUILDER_BASE := gvisor.dev/images/default -BUILDER_IMAGE := gvisor.dev/images/builder BUILDER_NAME := gvisor-builder-$(HASH) DOCKER_NAME := gvisor-bazel-$(HASH) DOCKER_PRIVILEGED := --privileged @@ -38,28 +52,43 @@ GCLOUD_CONFIG := $(shell readlink -m ~/.config/gcloud/) DOCKER_SOCKET := /var/run/docker.sock DOCKER_CONFIG := /etc/docker -# Bazel flags. -BAZEL := bazel $(STARTUP_OPTIONS) -BASE_OPTIONS := --color=no --curses=no +## +## Bazel helpers. +## +## Bazel will be run with standard flags. You can specify the following flags +## to control which flags are passed: +## +## STARTUP_OPTIONS - Startup options passed to Bazel. +## BAZEL_CONFIG - A bazel config file. +## +STARTUP_OPTIONS := +BAZEL_CONFIG := +BAZEL := bazel $(STARTUP_OPTIONS) +BASE_OPTIONS := --color=no --curses=no ifneq (,$(BAZEL_CONFIG)) BASE_OPTIONS += --config=$(BAZEL_CONFIG) endif +TEST_OPTIONS := $(BASE_OPTIONS) \ + --test_output=errors \ + --keep_going \ + --verbose_failures=true \ + --build_event_json_file=.build_events.json # Basic options. UID := $(shell id -u ${USER}) GID := $(shell id -g ${USER}) USERADD_OPTIONS := -FULL_DOCKER_RUN_OPTIONS := $(DOCKER_RUN_OPTIONS) -FULL_DOCKER_RUN_OPTIONS += --user $(UID):$(GID) -FULL_DOCKER_RUN_OPTIONS += --entrypoint "" -FULL_DOCKER_RUN_OPTIONS += --init -FULL_DOCKER_RUN_OPTIONS += -v "$(BAZEL_CACHE):$(BAZEL_CACHE)" -FULL_DOCKER_RUN_OPTIONS += -v "$(GCLOUD_CONFIG):$(GCLOUD_CONFIG)" -FULL_DOCKER_RUN_OPTIONS += -v "/tmp:/tmp" -FULL_DOCKER_EXEC_OPTIONS := --user $(UID):$(GID) -FULL_DOCKER_EXEC_OPTIONS += --interactive -ifeq (true,$(shell [[ -t 0 ]] && echo true)) -FULL_DOCKER_EXEC_OPTIONS += --tty +DOCKER_RUN_OPTIONS := +DOCKER_RUN_OPTIONS += --user $(UID):$(GID) +DOCKER_RUN_OPTIONS += --entrypoint "" +DOCKER_RUN_OPTIONS += --init +DOCKER_RUN_OPTIONS += -v "$(BAZEL_CACHE):$(BAZEL_CACHE)" +DOCKER_RUN_OPTIONS += -v "$(GCLOUD_CONFIG):$(GCLOUD_CONFIG)" +DOCKER_RUN_OPTIONS += -v "/tmp:/tmp" +DOCKER_EXEC_OPTIONS := --user $(UID):$(GID) +DOCKER_EXEC_OPTIONS += --interactive +ifeq (true,$(shell test -t 0 && echo true)) +DOCKER_EXEC_OPTIONS += --tty endif # Add basic UID/GID options. @@ -83,86 +112,75 @@ endif # Add docker passthrough options. ifneq ($(DOCKER_PRIVILEGED),) -FULL_DOCKER_RUN_OPTIONS += -v "$(DOCKER_SOCKET):$(DOCKER_SOCKET)" -FULL_DOCKER_RUN_OPTIONS += -v "$(DOCKER_CONFIG):$(DOCKER_CONFIG)" -FULL_DOCKER_RUN_OPTIONS += $(DOCKER_PRIVILEGED) -FULL_DOCKER_EXEC_OPTIONS += $(DOCKER_PRIVILEGED) +DOCKER_RUN_OPTIONS += -v "$(DOCKER_SOCKET):$(DOCKER_SOCKET)" +DOCKER_RUN_OPTIONS += -v "$(DOCKER_CONFIG):$(DOCKER_CONFIG)" +DOCKER_RUN_OPTIONS += $(DOCKER_PRIVILEGED) +DOCKER_EXEC_OPTIONS += $(DOCKER_PRIVILEGED) DOCKER_GROUP := $(shell stat -c '%g' $(DOCKER_SOCKET)) ifneq ($(GID),$(DOCKER_GROUP)) USERADD_OPTIONS += --groups $(DOCKER_GROUP) GROUPADD_DOCKER += groupadd --gid $(DOCKER_GROUP) --non-unique docker-$(HASH) && -FULL_DOCKER_RUN_OPTIONS += --group-add $(DOCKER_GROUP) +DOCKER_RUN_OPTIONS += --group-add $(DOCKER_GROUP) endif endif # Add KVM passthrough options. ifneq (,$(wildcard /dev/kvm)) -FULL_DOCKER_RUN_OPTIONS += --device=/dev/kvm +DOCKER_RUN_OPTIONS += --device=/dev/kvm KVM_GROUP := $(shell stat -c '%g' /dev/kvm) ifneq ($(GID),$(KVM_GROUP)) USERADD_OPTIONS += --groups $(KVM_GROUP) GROUPADD_DOCKER += groupadd --gid $(KVM_GROUP) --non-unique kvm-$(HASH) && -FULL_DOCKER_RUN_OPTIONS += --group-add $(KVM_GROUP) +DOCKER_RUN_OPTIONS += --group-add $(KVM_GROUP) endif endif -bazel-image: load-default - @if docker ps --all | grep $(BUILDER_NAME); then docker rm -f $(BUILDER_NAME); fi - docker run --user 0:0 --entrypoint "" --name $(BUILDER_NAME) \ - $(BUILDER_BASE) \ - sh -c "$(GROUPADD_DOCKER) \ - $(USERADD_DOCKER) \ - if [[ -e /dev/kvm ]]; then chmod a+rw /dev/kvm; fi" - docker commit $(BUILDER_NAME) $(BUILDER_IMAGE) - @docker rm -f $(BUILDER_NAME) -.PHONY: bazel-image - -## -## Bazel helpers. -## -## This file supports targets that wrap bazel in a running Docker -## container to simplify development. Some options are available to -## control the behavior of this container: -## USER - The in-container user. -## DOCKER_RUN_OPTIONS - Options for the container (default: --privileged, required for tests). -## DOCKER_NAME - The container name (default: gvisor-bazel-HASH). -## BAZEL_CACHE - The bazel cache directory (default: detected). -## GCLOUD_CONFIG - The gcloud config directory (detect: detected). -## DOCKER_SOCKET - The Docker socket (default: detected). -## -bazel-server-start: bazel-image ## Starts the bazel server. - @mkdir -p $(BAZEL_CACHE) - @mkdir -p $(GCLOUD_CONFIG) - @if docker ps --all | grep $(DOCKER_NAME); then docker rm -f $(DOCKER_NAME); fi - # This command runs a bazel server, and the container sticks around - # until the bazel server exits. This should ensure that it does not - # exit in the middle of running a build, but also it won't stick around - # forever. The build commands wrap around an appropriate exec into the - # container in order to perform work via the bazel client. - docker run -d --rm --name $(DOCKER_NAME) \ - -v "$(CURDIR):$(CURDIR)" \ - --workdir "$(CURDIR)" \ - $(FULL_DOCKER_RUN_OPTIONS) \ - $(BUILDER_IMAGE) \ - sh -c "tail -f --pid=\$$($(BAZEL) info server_pid) /dev/null" -.PHONY: bazel-server-start +# Top-level functions. +# +# This command runs a bazel server, and the container sticks around +# until the bazel server exits. This should ensure that it does not +# exit in the middle of running a build, but also it won't stick around +# forever. The build commands wrap around an appropriate exec into the +# container in order to perform work via the bazel client. +ifeq ($(DOCKER_BUILD),true) +wrapper = docker exec $(DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) $(1) +else +wrapper = $(1) +endif bazel-shutdown: ## Shuts down a running bazel server. - @docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) $(BAZEL) shutdown; \ - rc=$$?; docker kill $(DOCKER_NAME) || [[ $$rc -ne 0 ]] + @$(call wrapper,$(BAZEL) shutdown) .PHONY: bazel-shutdown bazel-alias: ## Emits an alias that can be used within the shell. - @echo "alias bazel='docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) bazel'" + @echo "alias bazel='$(call wrapper,$(BAZEL))'" .PHONY: bazel-alias -bazel-server: ## Ensures that the server exists. Used as an internal target. - @docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) true >&2 || $(MAKE) bazel-server-start >&2 -.PHONY: bazel-server +bazel-image: load-default ## Ensures that the local builder exists. + @$(call header,DOCKER BUILD) + @docker rm -f $(BUILDER_NAME) 2>/dev/null || true + @docker run --user 0:0 --entrypoint "" --name $(BUILDER_NAME) gvisor.dev/images/default \ + sh -c "$(GROUPADD_DOCKER) $(USERADD_DOCKER) if test -e /dev/kvm; then chmod a+rw /dev/kvm; fi" + @docker commit $(BUILDER_NAME) gvisor.dev/images/builder +.PHONY: bazel-image -# build_cmd builds the given targets in the bazel-server container. -build_cmd = docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) sh -o pipefail -c \ - '$(BAZEL) build $(BASE_OPTIONS) $(OPTIONS) "$(TARGETS)"' +ifeq (,$(findstring $(DOCKER_NAME),$(shell docker ps 2>/dev/null))) +bazel-server: bazel-image ## Ensures that the server exists. + @$(call header,DOCKER RUN) + @docker rm -f $(DOCKER_NAME) 2>/dev/null || true + @mkdir -p $(GCLOUD_CONFIG) + @mkdir -p $(BAZEL_CACHE) + @docker run -d --rm --name $(DOCKER_NAME) \ + -v "$(CURDIR):$(CURDIR)" \ + --workdir "$(CURDIR)" \ + $(DOCKER_RUN_OPTIONS) \ + gvisor.dev/images/builder \ + sh -c "set -x; tail -f --pid=\$$($(BAZEL) info server_pid) /dev/null" +else +bazel-server: + @ +endif +.PHONY: bazel-server # build_paths extracts the built binary from the bazel stderr output. # @@ -172,49 +190,28 @@ build_cmd = docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) sh -o pipefai # command here? Yikes, let's just stick with the ugly shell pipeline. # # The last line is used to prevent terminal shenanigans. -build_paths = command_line=$$( $(build_cmd) 2>&1 \ - | grep -A1 -E '^Target' \ - | grep -E '^ ($(subst $(SPACE),|,$(BUILD_ROOTS)))' \ - | sed "s/ /\n/g" \ - | strings -n 10 \ - | awk '{$$1=$$1};1' \ - | xargs -n 1 -I {} readlink -f "{}" \ - | xargs -n 1 -I {} echo "$(1)" ) && \ - (set -xeuo pipefail; eval $${command_line}) - -build: bazel-server - @$(call build_cmd) -.PHONY: build - -copy: bazel-server -ifeq (,$(DESTINATION)) - $(error Destination not provided.) -endif - @$(call build_paths,cp -fa {} $(DESTINATION)) - -run: bazel-server - @$(call build_paths,{} $(ARGS)) -.PHONY: run - -sudo: bazel-server - @$(call build_paths,sudo -E {} $(ARGS)) -.PHONY: sudo - -test: bazel-server - @docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) \ - $(BAZEL) test $(BASE_OPTIONS) \ - --test_output=errors --keep_going --verbose_failures=true \ - --build_event_json_file=.build_events.json \ - $(OPTIONS) $(TARGETS) -.PHONY: test - -testlogs: - @cat .build_events.json | jq -r \ - 'select(.testSummary?.overallStatus? | tostring | test("(FAILED|FLAKY|TIMEOUT)")) | .testSummary.failed | .[] | .uri' | \ - awk -Ffile:// '{print $$2;}' +build_paths = \ + $(call wrapper,$(BAZEL) build $(BASE_OPTIONS) $(1)) 2>&1 \ + | tee /proc/self/fd/2 \ + | grep -A1 -E '^Target' \ + | grep -E '^ ($(subst $(SPACE),|,$(BUILD_ROOTS)))' \ + | sed "s/ /\n/g" \ + | strings -n 10 \ + | awk '{$$1=$$1};1' \ + | xargs -n 1 -I {} readlink -f "{}" \ + | xargs -n 1 -I {} bash -c 'set -xeuo pipefail; $(2)' + +clean = $(call header,CLEAN) && $(call wrapper,$(BAZEL) clean) +build = $(call header,BUILD $(1)) && $(call build_paths,$(1),echo {}) +copy = $(call header,COPY $(1) $(2)) && $(call build_paths,$(1),cp -fa {} $(2)) +run = $(call header,RUN $(1) $(2)) && $(call build_paths,$(1),{} $(2)) +sudo = $(call header,SUDO $(1) $(2)) && $(call build_paths,$(1),sudo -E {} $(2)) +test = $(call header,TEST $(1)) && $(call wrapper,$(BAZEL) test $(TEST_OPTIONS) $(1)) + +testlogs: ## Returns the most recent set of test logs. + @if test -f .build_events.json; then \ + cat .build_events.json | jq -r \ + 'select(.testSummary?.overallStatus? | tostring | test("(FAILED|FLAKY|TIMEOUT)")) | .testSummary.failed | .[] | .uri' | \ + awk -Ffile:// '{print $$2;}'; \ + fi .PHONY: testlogs - -query: bazel-server - @docker exec $(FULL_DOCKER_EXEC_OPTIONS) $(DOCKER_NAME) sh -o pipefail -c \ - '$(BAZEL) query $(BASE_OPTIONS) $(OPTIONS) "$(TARGETS)" 2>/dev/null' -.PHONY: query |