build: Replace shell scripts with GNUmakefile

This commit replaces the shell script-driven build process for Nomad
with one based around GNU Make (note we _do_ use GNU-specific
constructs), requiring no additional scripts for common cases of
development. The following targets are implemented:

Per-OS/arch combinations:

    Binaries (Host - Mac OS X):
	pkg/darwin_amd64/nomad

    Binaries (Host - Linux):
	pkg/linux_386/nomad
	pkg/linux_amd64/nomad
	pkg/linux_amd64-lxc/nomad
	pkg/linux_arm/nomad
	pkg/linux_arm64/nomad
	pkg/windows_386/nomad
	pkg/windows_amd64/nomad

    Packages (Host - Mac OS X):
	pkg/darwin_amd64.zip

    Packages (Host - Linux):
	pkg/linux_386.zip
	pkg/linux_amd64.zip
	pkg/linux_amd64-lxc.zip
	pkg/linux_arm.zip
	pkg/linux_arm64.zip
	pkg/windows_386.zip
	pkg/windows_amd64.zip

Phony targets:

	dev - Builds for the current host GOOS/GOARCH (unless overriden
	      in the environment)

	release - Builds all appropriate release packages for the
	          current host GOOS/GOARCH (i.e. Windows and Linux
		  packages on a Linux host, Darwin packages on an OSX
		  host)

	generate - Generate code for the current host architecture using
	           `go generate`.

	test - Runs the Nomad test suite

	clean - Removes build artifacts

	travis - Runs `make test` with the wrapper script to prevent
	         Travis CI from timing out.

	help - Displays usage information about commonly used targets.

Note that there are some semantic differences from the previous version.

1. `generate` is no longer a dependency of `dev` builds. This is because
   it causes a rebuild every time, even when no code has changed, since
   `go generate` does not appear to leave file timestamps alone.
   Regardless, it is insufficient to generate on one host OS - it needs
   to be run on each target to ensure everything is generated correctly.

2. `gofmt` is no longer checked. This should be enabled as a linter once
   the `gofmt -s` refactoring will pass on the whole code base, in order
   to avoid special cased checks versus using go-metalinter.

Example Usages:

Make a development build for the current GOOS/GOARCH:

	make dev

Make release build packages appropriate for the host OS:

	make release

Update generated code for the host OS:

	make generate

Run linting checks:

	make check

Build a specific alternative GOOS/GOARCH/tags combination:

	make pkg/linux_amd64-pkg/nomad
	make pkg/linux_amd64-pkg.zip
This commit is contained in:
James Nugent
2017-08-18 06:29:26 +01:00
parent 879eb315ea
commit dcdc91fb6b
4 changed files with 194 additions and 230 deletions

View File

@@ -1,77 +1,209 @@
PACKAGES = $(shell go list ./... | grep -v '/vendor/')
EXTERNAL_TOOLS=\
github.com/kardianos/govendor \
golang.org/x/tools/cmd/cover \
github.com/axw/gocov/gocov \
gopkg.in/matm/v1/gocov-html \
github.com/ugorji/go/codec/codecgen
PROJECT_ROOT := $(patsubst %/,%,$(dir $(abspath $(lastword $(MAKEFILE_LIST)))))
THIS_OS := $(shell uname)
TEST_TOOLS=\
github.com/hashicorp/vault
GIT_COMMIT := $(shell git rev-parse HEAD)
GIT_DIRTY := $(if $(shell git status --porcelain),+CHANGES)
all: test
GO_LDFLAGS := "-X main.GitCommit=$(GIT_COMMIT)$(GIT_DIRTY)"
GO_TAGS =
dev: format generate
@scripts/build-dev.sh
# Enable additional linters as the codebase evolves to pass them
CHECKS ?= --enable goimports
bin: generate
@sh -c "'$(PWD)/scripts/build.sh'"
default: help
release: generate
@sh -c "TARGETS=release '$(PWD)/scripts/build.sh'"
ifeq (,$(findstring $(THIS_OS),Darwin Linux))
$(error Building Nomad is currently only supported on Darwin and Linux.)
endif
cov:
gocov test ./... | gocov-html > /tmp/coverage.html
open /tmp/coverage.html
# On Linux we build for Linux, Windows, and potentially Linux+LXC
ifeq (Linux,$(THIS_OS))
HAS_LXC = $(shell pkg-config --exists lxc)
ALL_TARGETS += linux_386 \
linux_amd64 \
linux_arm \
linux_arm64 \
windows_386 \
windows_amd64
test: generate
@echo "--> Running go fmt" ;
@if [ -n "`go fmt ${PACKAGES}`" ]; then \
echo "[ERR] go fmt updated formatting. Please commit formatted code first."; \
exit 1; \
fi
@sh -c "'$(PWD)/scripts/test.sh'"
@$(MAKE) vet
ifeq (,$(HAS_LAX))
ALL_TARGETS += linux_amd64-lxc
endif
endif
cover:
go list ./... | xargs -n1 go test --cover
# On MacOS we only build for MacOS
ifeq (Darwin,$(THIS_OS))
ALL_TARGETS += darwin_amd64
endif
format:
@echo "--> Running go fmt"
@go fmt $(PACKAGES)
pkg/darwin_amd64/nomad: $(SOURCE_FILES) ## Build Nomad for darwin/amd64
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=darwin GOARCH=amd64 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
generate:
@echo "--> Running go generate"
@go generate $(PACKAGES)
@sed -i.old -e 's|github.com/hashicorp/nomad/vendor/github.com/ugorji/go/codec|github.com/ugorji/go/codec|' nomad/structs/structs.generated.go
pkg/linux_386/nomad: $(SOURCE_FILES) ## Build Nomad for linux/386
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=linux GOARCH=386 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
vet:
@echo "--> Running go vet $(VETARGS) ${PACKAGES}"
@go vet $(VETARGS) ${PACKAGES} ; if [ $$? -eq 1 ]; then \
echo ""; \
echo "[LINT] Vet found suspicious constructs. Please check the reported constructs"; \
echo "and fix them if necessary before submitting the code for review."; \
exit 1; \
fi
@git grep -n `echo "log"".Print"` | grep -v 'vendor/' ; if [ $$? -eq 0 ]; then \
echo "[LINT] Found "log"".Printf" calls. These should use Nomad's logger instead."; \
fi
pkg/linux_amd64/nomad: $(SOURCE_FILES) ## Build Nomad for linux/amd64
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
# bootstrap the build by downloading additional tools
bootstrap:
@for tool in $(EXTERNAL_TOOLS) ; do \
echo "Installing $$tool" ; \
go get $$tool; \
done
@for tool in $(TEST_TOOLS) ; do \
echo "Installing $$tool (test dependency)" ; \
go get $$tool; \
done
pkg/linux_arm/nomad: $(SOURCE_FILES) ## Build Nomad for linux/arm
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=linux GOARCH=arm CC=arm-linux-gnueabihf-gcc-5 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
install: bin/nomad
install -o root -g wheel -m 0755 ./bin/nomad /usr/local/bin/nomad
pkg/linux_arm64/nomad: $(SOURCE_FILES) ## Build Nomad for linux/arm64
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=linux GOARCH=arm64 CC=aarch64-linux-gnu-gcc-5 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
travis:
@sh -c "'$(PWD)/scripts/travis.sh'"
# If CGO support for Windows is ever required, set the following variables
# in the environment for `go build` for both the windows/amd64 and the
# windows/386 targets:
# CC=i686-w64-mingw32-gcc
# CXX=i686-w64-mingw32-g++
pkg/windows_386/nomad: $(SOURCE_FILES) ## Build Nomad for windows/386
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=windows GOARCH=386 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
.PHONY: all bin cov integ test vet test-nodep
pkg/windows_amd64/nomad: $(SOURCE_FILES) ## Build Nomad for windows/amd64
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=windows GOARCH=386 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS)" \
-o "$@"
pkg/linux_amd64-lxc/nomad: $(SOURCE_FILES) ## Build Nomad+LXC for linux/amd64
@echo "==> Building $@..."
@CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
go build \
-ldflags $(GO_LDFLAGS) \
-tags "$(GO_TAGS) lxc" \
-o "$@"
# Define package targets for each of the build targets we actually have on this system
define makePackageTarget
pkg/$(1).zip: pkg/$(1)/nomad
@echo "==> Packaging for $(1)..."
@zip pkg/$(1).zip pkg/$(1)/*
endef
# Reify the package targets
$(foreach t,$(ALL_TARGETS),$(eval $(call makePackageTarget,$(t))))
# Only for Travis CI compliance
.PHONY: bootstrap
bootstrap: deps
.PHONY: deps
deps: ## Install build and development dependencies
@echo "==> Updating build dependencies..."
go get -u github.com/alecthomas/gometalinter
gometalinter --install
go get -u github.com/kardianos/govendor
go get -u golang.org/x/tools/cmd/cover
go get -u github.com/axw/gocov/gocov
go get -u gopkg.in/matm/v1/gocov-html
go get -u github.com/ugorji/go/codec/codecgen
go get -u github.com/hashicorp/vault
go get -u github.com/a8m/tree/cmd/tree
.PHONY: check
check: ## Lint the source code
@echo "==> Linting source code..."
@gometalinter \
--deadline 10m \
--vendor \
--exclude '.*\.generated\.go:\d+:' \
--disable-all \
--sort severity \
$(CHECKS) \
./...
generate: LOCAL_PACKAGES = $(shell go list ./... | grep -v '/vendor/')
generate: ## Update generated code for the current OS/arch
@echo "==> Generating code for $(GOOS)..."
@go generate $(LOCAL_PACKAGES)
.PHONY: dev
dev: GOOS=$(shell go env GOOS)
dev: GOARCH=$(shell go env GOARCH)
dev: GOPATH=$(shell go env GOPATH)
dev: DEV_TARGET=pkg/$(GOOS)_$(GOARCH)$(if $(HAS_LXC),-lxc)/nomad
dev: check ## Build for the current development platform
@echo "==> Removing old development build..."
@rm -f $(PROJECT_ROOT)/$(DEV_TARGET)
@rm -f $(PROJECT_ROOT)/bin/nomad
@rm -f $(GOPATH)/bin/nomad
@$(MAKE) --no-print-directory \
$(DEV_TARGET) \
GO_TAGS=nomad_test
@mkdir -p $(PROJECT_ROOT)/bin
@mkdir -p $(GOPATH)/bin
@cp $(PROJECT_ROOT)/$(DEV_TARGET) $(PROJECT_ROOT)/bin/
@cp $(PROJECT_ROOT)/$(DEV_TARGET) $(GOPATH)/bin
.PHONY: release
release: clean check $(foreach t,$(ALL_TARGETS),pkg/$(t).zip) ## Build all release packages which can be built on this platform.
@echo "==> Results:"
@tree --dirsfirst $(PROJECT_ROOT)/pkg
.PHONY: test
test: LOCAL_PACKAGES = $(shell go list ./... | grep -v '/vendor/')
test: dev ## Run Nomad test suites
@echo "==> Running Nomad test suites:"
@NOMAD_TEST_RKT=1 \
go test \
-cover \
-timeout=900s \
-tags="nomad_test $(if $(HAS_LXC),lxc)" \
$(LOCAL_PACKAGES)
.PHONY: clean
clean: GOPATH=$(shell go env GOPATH)
clean: ## Remove build artifacts
@echo "==> Cleaning build artifacts..."
@rm -rf "$(PROJECT_ROOT)/bin/"
@rm -rf "$(PROJECT_ROOT)/pkg/"
@rm -f "$(GOPATH)/bin/nomad"
.PHONY: travis
travis: ## Run Nomad test suites with output to prevent timeouts under Travis CI
@sh -C "$(PROJECT_ROOT)/scripts/travis.sh"
HELP_FORMAT=" \033[36m%-25s\033[0m %s\n"
.PHONY: help
help: ## Display this usage information
@echo "Valid targets:"
@grep -E '^[^ ]+:.*?## .*$$' $(MAKEFILE_LIST) | \
sort | \
awk 'BEGIN {FS = ":.*?## "}; \
{printf $(HELP_FORMAT), $$1, $$2}'
@echo "\nThis host will build the following targets if 'make release' is invoked:"
@echo $(ALL_TARGETS) | sed 's/^/ /'

View File

@@ -1,22 +0,0 @@
#!/usr/bin/env bash
set -e
GIT_COMMIT="$(git rev-parse HEAD)"
GIT_DIRTY="$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)"
LDFLAG="github.com/hashicorp/nomad/version.GitCommit=${GIT_COMMIT}${GIT_DIRTY}"
TAGS="nomad_test"
if [[ $(uname) == "Linux" ]]; then
if pkg-config --exists lxc; then
TAGS="$TAGS lxc"
fi
fi
echo "--> Installing with tags: $TAGS"
go install -ldflags "-X $LDFLAG" -tags "${TAGS}"
echo "--> Ensuring bin directory exists..."
mkdir -p bin
echo "--> Copying to bin"
cp $GOPATH/bin/nomad bin/nomad

View File

@@ -1,121 +0,0 @@
#!/usr/bin/env bash
#
# This script builds the application from source for multiple platforms.
set -e
# Get the parent directory of where this script is.
SOURCE="${BASH_SOURCE[0]}"
while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )"
# Change into that directory
cd "$DIR"
# Get the git commit
GIT_COMMIT="$(git rev-parse HEAD)"
GIT_DIRTY="$(test -n "`git status --porcelain`" && echo "+CHANGES" || true)"
LDFLAG="main.GitCommit=${GIT_COMMIT}${GIT_DIRTY}"
# Delete the old dir
echo "==> Removing old directory..."
rm -f bin/*
rm -rf pkg/*
mkdir -p bin/
targets="$(go env GOOS)_$(go env GOARCH)"
if [[ "$TARGETS" == "release" ]]; then
if [[ $(uname) == "Linux" ]]; then
targets="linux_386 linux_amd64 linux_amd64-lxc linux_arm linux_arm64 windows_386 windows_amd64"
elif [[ $(uname) == "Darwin" ]]; then
targets="darwin_amd64"
else
echo "Unable to build on $(uname). Use Linux or Darwin."
exit 1
fi
elif [[ "$TARGETS" != "" ]]; then
targets="$TARGETS"
fi
# Don't exit if a single target fails
set +e
echo "TARGETS=\"$targets\""
for target in $targets; do
case $target in
"linux_386")
echo "==> Building linux 386..."
CGO_ENABLED=1 GOARCH="386" GOOS="linux" go build -ldflags "-X $LDFLAG" -o "pkg/linux_386/nomad"
;;
"linux_amd64")
echo "==> Building linux amd64..."
CGO_ENABLED=1 GOARCH="amd64" GOOS="linux" go build -ldflags "-X $LDFLAG" -o "pkg/linux_amd64/nomad"
;;
"linux_amd64-lxc")
echo "==> Building linux amd64 with lxc..."
CGO_ENABLED=1 GOARCH="amd64" GOOS="linux" go build -ldflags "-X $LDFLAG" -o "pkg/linux_amd64-lxc/nomad" -tags "lxc"
;;
"linux_arm")
echo "==> Building linux arm..."
CGO_ENABLED=1 CC="arm-linux-gnueabihf-gcc-5" GOOS=linux GOARCH="arm" go build -ldflags "-X $LDFLAG" -o "pkg/linux_arm/nomad"
;;
"linux_arm64")
echo "==> Building linux arm64..."
CGO_ENABLED=1 CC="aarch64-linux-gnu-gcc-5" GOOS=linux GOARCH="arm64" go build -ldflags "-X $LDFLAG" -o "pkg/linux_arm64/nomad"
;;
"windows_386")
echo "==> Building windows 386..."
CGO_ENABLED=0 GOARCH="386" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_386/nomad.exe"
# Use the following if CGO is required
#CGO_ENABLED=1 CXX=i686-w64-mingw32-g++ CC=i686-w64-mingw32-gcc GOARCH="386" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_386/nomad.exe"
;;
"windows_amd64")
echo "==> Building windows amd64..."
CGO_ENABLED=0 GOARCH="amd64" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_amd64/nomad.exe"
# Use the following if CGO is required
#CGO_ENABLED=1 CXX=x86_64-w64-mingw32-g++ CC=x86_64-w64-mingw32-gcc GOARCH="amd64" GOOS="windows" go build -ldflags "-X $LDFLAG" -o "pkg/windows_amd64/nomad.exe"
;;
"darwin_amd64")
echo "==> Building darwin amd64..."
CGO_ENABLED=1 GOARCH="amd64" GOOS="darwin" go build -ldflags "-X $LDFLAG" -o "pkg/darwin_amd64/nomad"
;;
*)
echo "--> Invalid target: $target"
;;
esac
done
set -e
# Move all the compiled things to $GOPATH/bin
GOPATH=${GOPATH:-$(go env GOPATH)}
case $(uname) in
CYGWIN*)
GOPATH="$(cygpath $GOPATH)"
;;
esac
OLDIFS=$IFS
IFS=: MAIN_GOPATH=($GOPATH)
IFS=$OLDIFS
# Copy our OS/Arch to the bin/ directory
DEV_PLATFORM="./pkg/$(go env GOOS)_$(go env GOARCH)"
for F in $(find ${DEV_PLATFORM} -mindepth 1 -maxdepth 1 -type f); do
cp ${F} bin/
cp ${F} ${MAIN_GOPATH}/bin/
done
# Zip and copy to the dist dir
echo "==> Packaging..."
for PLATFORM in $(find ./pkg -mindepth 1 -maxdepth 1 -type d); do
OSARCH=$(basename ${PLATFORM})
echo "--> ${OSARCH}"
pushd $PLATFORM >/dev/null 2>&1
zip ../${OSARCH}.zip ./*
popd >/dev/null 2>&1
done
# Done!
echo
echo "==> Results:"
tree pkg/

View File

@@ -1,25 +0,0 @@
#!/usr/bin/env bash
set -e
GOTEST_TAGS="nomad_test"
if [[ $(uname) == "Linux" ]]; then
if pkg-config --exists lxc; then
GOTEST_TAGS="$GOTEST_TAGS lxc"
fi
fi
# Create a temp dir and clean it up on exit
TEMPDIR=`mktemp -d -t nomad-test.XXX`
trap "rm -rf $TEMPDIR" EXIT HUP INT QUIT TERM
# Build the Nomad binary for the API tests
echo "--> Building nomad"
echo go build -i -tags \"$GOTEST_TAGS\" -o $TEMPDIR/nomad
go build -i -tags "$GOTEST_TAGS" -o $TEMPDIR/nomad || exit 1
# Run the tests
echo "--> Running tests"
GOBIN="`which go`"
sudo -E PATH=$TEMPDIR:$PATH -E GOPATH=$GOPATH -E NOMAD_TEST_RKT=1 \
$GOBIN test -tags "$GOTEST_TAGS" ${GOTEST_FLAGS:--cover -timeout=900s} $($GOBIN list ./... | grep -v /vendor/)