#!/usr/bin/env bash
set -e -o pipefail -u -x

tags="netgo"
packages="./..."

nonfmt="$(go fmt $packages)"
if [ -n "${nonfmt}" ]; then
	echo "${nonfmt}" | sed -n -e 's/^./non-gofmt code: &/p'
	echo "Please run gofmt on code before committing:"
	echo "	go fmt ./..."
	false
fi
go vet $packages

# Remove all compiled packages - this is required to make the netgo build tag
# work with go1.4's definition of go build's -a flag.
find "$(go env GOROOT)/pkg/$(go env GOOS)_$(go env GOARCH)" -name '*.a' -type f -delete
go install -a -tags "$tags" std

# Find the path where common/golibs/bininfo will be linked - this changes when
# the vendor experiment is active.
bininfo="code.justin.tv/common/golibs/bininfo"
bininfo="$(go list "./vendor/$bininfo" || echo "$bininfo")"

# The format for -ldflags -X changed with go1.5 (when the linker command name
# changed from 5l/6l/8l/etc to link). go1.4 and earlier use a space between
# symbol name and value, go1.5 and later use an equals sign.
xsep="$(go tool -n link &>/dev/null && echo '=' || echo ' ')"

# We need to remove a prefix from the paths stored in the DWARF info to allow
# annotating source code with tools like pprof even when it's not at the same
# absolute path we used during the build.
#
# Find which GOPATH entry applies to the root of this directory, trim it (and
# the /src segment that follows) from all filenames.
rootpkg="$(go list -e .)"
trimpath="$(echo $PWD | head -c $((${#PWD}-${#rootpkg})) | sed -e 's,/*$,,')"

# Compile all commands.
for cmd in $(go list -f '{{if eq .Name "main"}}{{.ImportPath}}{{end}}' $packages); do

	# Build IDs are standard practice in large software distributions; see
	# Roland McGrath's description here:
	# https://fedoraproject.org/wiki/Releases/FeatureBuildId.
	#
	# Some linkers are able to generate good build IDs automatically based on
	# some parts of the linked executable. Go's linker doesn't do that for us,
	# but it allows us to specify our own. This is one of the first known uses
	# of build IDs in Go programs at Twitch (2016-03-07), so let's come up
	# with something good enough for now.
	#
	# The build IDs should be unique for each executable shipped to production
	# hardware. We can get that by hashing the current git commit and the name
	# of the program, since these next few lines are the only way we build
	# production binaries for this repo and we only link each program once.
	#
	# We'd need something more if we started building and shipping race-
	# enabled versions of these programs, since the commit and program name
	# would match.
	#
	# This doesn't allow calculating the program name and commit if only the
	# build ID is known. We can tackle that later.

	buildid="$( (echo $tags ; echo $GIT_COMMIT ; echo $cmd) | sha256sum | cut -c -64 )"
	racebuildid="$( (echo $tags,race ; echo $GIT_COMMIT ; echo $cmd) | sha256sum | cut -c -64 )"
	if [ "${GIT_COMMIT}" != "${GIT_COMMIT%-dirty}" ]; then
		# Don't write a real-looking Build ID if the repo has local
		# modifications
		buildid="deadbeefdeadbeef6755232880654942deadbeefdeadbeef"
		racebuildid="deadbeefdeadbeef6755232880654942deadbeefdeadbeef"
	fi

	go install -v -tags "$tags" \
		-asmflags "-trimpath ${trimpath}" \
		-gcflags "-trimpath ${trimpath}" \
		-ldflags "-X ${bininfo}.revision${xsep}${GIT_COMMIT} -B 0x$buildid" \
		-- "$cmd"
	if [ -n "${RACEBIN:-}" ]; then
		GOBIN="${RACEBIN}" go install -v -tags "$tags" -race \
			-asmflags "-trimpath ${trimpath}" \
			-gcflags "-trimpath ${trimpath}" \
			-ldflags "-X ${bininfo}.revision${xsep}${GIT_COMMIT} -B 0x$racebuildid" \
			-- "$cmd"
	fi
done

nobininfo=""
nochitin=""
for exe in $(go list -f '{{if eq .Name "main"}}{{.Target}}{{end}}' $packages); do
	if [ -z "$(go tool nm -- "$exe" 2>/dev/null | \
		grep -E "( | vendor/|/vendor/)code\.justin\.tv/common/golibs/bininfo\.revision$")" ]; then
		nobininfo="${nobininfo}${exe}"$'\n'
	fi

	if [ -z "$(go tool nm -- "$exe" 2>/dev/null | \
		grep -E "( | vendor/|/vendor/)code\.justin\.tv/common/chitin\.ExperimentalTraceProcessOptIn$")" ]; then
		nochitin="${nochitin}${exe}"$'\n'
	fi
done
if [ -n "$nobininfo" ]; then
	echo "$nobininfo" | sed -n -e 's/^./missing bininfo: &/p'
	echo 'Please import "code.justin.tv/common/golibs/bininfo" in your main package.'
	false
fi
if [ -n "$nochitin" ]; then
	echo "$nochitin" | sed -n -e 's/^./missing chitin opt in: &/p'
	echo 'Please call "code.justin.tv/common/chitin.ExperimentalTraceProcessOptIn"'
	echo "in your main package. A good place for this call is near the beginning"
	echo "of main.main, possibly after calling flag.Parse."
	false
fi

# Fork and run kinesalite.
script_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
${script_dir}/test/run_kinesalite.sh &

go test -tags "$tags" $packages
go test -tags "$tags" -race $packages
