Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Proposal: Improve Makefile #735

Merged
merged 5 commits into from
Dec 20, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/commit.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: "build"

on:
push: # We run tests on non-tagged pushes to main.
branches:
- main
paths-ignore:
- '**/*.md'

pull_request: # We also run tests on pull requests targeted at the main branch.
branches:
- main
paths-ignore:
- '**/*.md'

defaults:
run: # use bash as the default shell.
shell: bash

jobs:
test:
name: "Run tests"
runs-on: ubuntu-20.04
steps:
- name: Cancel when duplicated
uses: styfle/cancel-workflow-action@0.9.1
with:
access_token: ${{ secrets.GITHUB_TOKEN }}

- name: "Checkout"
uses: actions/checkout@v3

- uses: actions/setup-go@v3
with:
go-version: "1.19.3"
cache: true

- uses: actions/setup-python@v4
with:
python-version: "3.10"
cache: "pip"

- name: Download cache for bazel
uses: actions/cache@v3
with:
path: "~/.cache/bazel"
key: test-${{ runner.os }}-bazel-cache

- name: "Install python requirements"
run: "pip3 install -r requirements.txt --no-deps"

- name: "Generate *.pb.go and build the plugin binary"
run: make -f Next.mk clean build # we do "clean build" here, hence validate.pb.go is regenerated.

- name: "Verify clean check-in"
run: make -f Next.mk check # we do verification after validate.pb.go is regenerated.

- name: "Run tests"
run: make -f Next.mk test

- name: "Run sanity checks"
run: make -f Next.mk sanity
13 changes: 13 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,3 +47,16 @@ __pycache__/
.settings
.idea/
.ijwb/

# Local cache directory.
.cache

# Local build directory.
/build

# Generated test cases Go files.
/tests/harness/**/*.pb.go
/tests/harness/**/*.pb.validate.go

# Harness test binary.
/tests/harness/**/*-harness
File renamed without changes.
111 changes: 111 additions & 0 deletions Next.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Licensed under the Apache License, Version 2.0 (the "License")

# Plugin name.
name := protoc-gen-validate

# Root dir returns absolute path of current directory. It has a trailing "/".
root_dir := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))

# Local cache directory.
cache_dir := $(root_dir).cache

# Directory of Go tools.
go_tools_dir := $(cache_dir)/tools/go

# Directory of prepackaged tools (e.g. protoc).
prepackaged_tools_dir := $(cache_dir)/tools/prepackaged

# Currently we resolve Go using `which`. But more sophisticated approach is to use infer GOROOT.
go := $(shell which go)
goarch := $(shell $(go) env GOARCH)
goexe := $(shell $(go) env GOEXE)
goos := $(shell $(go) env GOOS)

# The current binary location for the current runtime (goos, goarch). We install our plugin here.
current_binary_path := build/$(name)_$(goos)_$(goarch)
current_binary := $(current_binary_path)/$(name)$(goexe)

# This makes sure protoc can access the installed plugins.
export PATH := $(root_dir)$(current_binary_path):$(go_tools_dir)/bin:$(prepackaged_tools_dir)/bin:$(PATH)

# The main generated file.
validate_pb_go := validate/validate.pb.go

# List of harness test cases.
tests_harness_cases := \
/harness \
/harness/cases \
/harness/cases/other_package \
/harness/cases/yet_another_package

# Include versions of tools we build on-demand
include Tools.mk
# This provides the "help" target.
include tools/build/Help.mk
# This sets some required environment variables.
include tools/build/Env.mk

# Path to the installed protocol buffer compiler.
protoc := $(prepackaged_tools_dir)/bin/protoc

# Go based tools.
bazel := $(go_tools_dir)/bin/bazelisk
protoc-gen-go := $(go_tools_dir)/bin/protoc-gen-go

test: $(bazel) $(tests_harness_cases) ## Run tests
@$(bazel) test //tests/... --test_output=errors

build: $(current_binary) ## Build the plugin

clean: ## Clean all build and test artifacts
@rm -f $(validate_pb_go)
@rm -f $(current_binary)

check: ## Verify contents of last commit
@# Make sure the check-in is clean
@if [ ! -z "`git status -s`" ]; then \
echo "The following differences will fail CI until committed:"; \
git diff --exit-code; \
fi

# This provides shortcut to various bazel related targets.
sanity: bazel-build bazel-build-tests-generation bazel-test-example-workspace

bazel-build: $(bazel) ## Build the plugin using bazel
@$(bazel) build //:$(name)
@mkdir -p $(current_binary_path)
@cp -f bazel-bin/$(name)_/$(name)$(goexe) $(current_binary)

bazel-build-tests-generation: $(bazel) ## Build tests generation using bazel
@$(bazel) build //tests/generation/...

bazel-test-example-workspace: $(bazel) ## Test example workspace using bazel
@cd example-workspace && bazel test //... --test_output=errors

# Generate validate/validate.pb.go from validate/validate.proto.
$(validate_pb_go): $(protoc) $(protoc-gen-go) validate/validate.proto
@$(protoc) -I . --go_opt=paths=source_relative --go_out=. $(filter %.proto,$^)

# Build target for current binary.
build/$(name)_%/$(name)$(goexe): $(validate_pb_go)
@GOBIN=$(root_dir)$(current_binary_path) $(go) install .

# Generate all required files for harness tests in Go.
$(tests_harness_cases): $(current_binary)
$(call generate-test-cases-go,tests$@)

# Generates a test-case for Go.
define generate-test-cases-go
@cd $1 && \
mkdir -p go && \
$(protoc) \
-I . \
-I $(root_dir) \
--go_opt=paths=source_relative \
--go_out=go \
--validate_opt=paths=source_relative \
--validate_out=lang=go:go \
*.proto
endef

include tools/build/Installer.mk
5 changes: 5 additions & 0 deletions Tools.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Licensed under the Apache License, Version 2.0 (the "License")

bazelisk@v := github.com/bazelbuild/bazelisk@v1.15.0
protoc@v := github.com/protocolbuffers/protobuf@v21.9
protoc-gen-go@v := google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
7 changes: 7 additions & 0 deletions tools/build/Env.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Licensed under the Apache License, Version 2.0 (the "License")

# Disable cgo.
export CGOENABLED := 0

# Reference: https://developers.google.com/protocol-buffers/docs/reference/go/faq#namespace-conflict.
export GOLANG_PROTOBUF_REGISTRATION_CONFLICT := warn
21 changes: 21 additions & 0 deletions tools/build/Help.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Licensed under the Apache License, Version 2.0 (the "License")

# This is adopted from https://github.com/tetratelabs/func-e/blob/3df66c9593e827d67b330b7355d577f91cdcb722/Makefile#L60-L76.
# ANSI escape codes. f_ means foreground, b_ background.
# See https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters.
f_black := $(shell printf "\33[30m")
b_black := $(shell printf "\33[40m")
f_white := $(shell printf "\33[97m")
f_gray := $(shell printf "\33[37m")
f_dark_gray := $(shell printf "\33[90m")
f_bright_cyan := $(shell printf "\33[96m")
b_bright_cyan := $(shell printf "\33[106m")
ansi_reset := $(shell printf "\33[0m")
ansi_$(name) := $(b_black)$(f_black)$(b_bright_cyan)$(name)$(ansi_reset)
ansi_format_dark := $(f_gray)$(f_bright_cyan)%-10s$(ansi_reset) $(f_dark_gray)%s$(ansi_reset)\n
ansi_format_bright := $(f_white)$(f_bright_cyan)%-10s$(ansi_reset) $(f_black)$(b_bright_cyan)%s$(ansi_reset)\n

# This formats help statements in ANSI colors. To hide a target from help, don't comment it with a trailing '##'.
help: ## Describe how to use each target
@printf "$(ansi_$(name))$(f_white)\n"
@awk 'BEGIN {FS = ":.*?## "} /^[0-9a-zA-Z_-]+:.*?## / {sub("\\\\n",sprintf("\n%22c"," "), $$2);printf "$(ansi_format_dark)", $$1, $$2}' $(MAKEFILE_LIST)
23 changes: 23 additions & 0 deletions tools/build/Installer.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Licensed under the Apache License, Version 2.0 (the "License")

# Catch all rules for Go-based tools.
$(go_tools_dir)/bin/%:
@GOBIN=$(go_tools_dir)/bin $(go) install $($(notdir $@)@v)

# Install protoc from github.com/protocolbuffers/protobuf.
protoc-os := $(if $(findstring $(goos),darwin),osx,$(goos))
protoc-arch := $(if $(findstring $(goarch),arm64),aarch_64,x86_64)
protoc-version = $(subst github.com/protocolbuffers/protobuf@v,$(empty),$($(notdir $1)@v))
protoc-archive = protoc-$(call protoc-version,$1)-$(protoc-os)-$(protoc-arch).zip
protoc-url = https://$(subst @,/releases/download/,$($(notdir $1)@v))/$(call protoc-archive,$1)
protoc-zip = $(prepackaged_tools_dir)/$(call protoc-archive,$1)
$(protoc):
@mkdir -p $(prepackaged_tools_dir)
ifeq ($(goos),linux)
@curl -sSL $(call protoc-url,$@) -o $(call protoc-zip,$@)
@unzip -oqq $(call protoc-zip,$@) -d $(prepackaged_tools_dir)
@rm -f $(call protoc-zip,$@)
else
@curl -sSL $(call protoc-url,$@) | tar xf - -C $(prepackaged_tools_dir)
@chmod +x $@
endif