341 lines
9.4 KiB
Bash
Executable File
341 lines
9.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
readonly SCRIPT_NAME="$(basename ${BASH_SOURCE[0]})"
|
|
readonly SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
|
|
readonly SOURCE_DIR="$(dirname "$(dirname "${SCRIPT_DIR}")")"
|
|
readonly FN_DIR="$(dirname "${SCRIPT_DIR}")/functions"
|
|
|
|
source "${SCRIPT_DIR}/functions.sh"
|
|
|
|
unset CDPATH
|
|
|
|
set -euo pipefail
|
|
|
|
usage() {
|
|
cat <<-EOF
|
|
Usage: ${SCRIPT_NAME} [<options ...>]
|
|
|
|
Description:
|
|
Installs protoc, various supporting Go tools, and then regenerates all Go
|
|
files from protobuf definitions. In addition to running the protoc
|
|
generator it will also fixup build tags in the generated code and
|
|
regenerate mog outputs and RPC stubs.
|
|
|
|
Options:
|
|
--protoc-version Version of protoc to install. It defaults to what is specified in the makefile.
|
|
--tools-only Install all required tools but do not generate outputs.
|
|
-h | --help Print this help text.
|
|
EOF
|
|
}
|
|
|
|
function err_usage {
|
|
err "$1"
|
|
err ""
|
|
err "$(usage)"
|
|
}
|
|
|
|
function main {
|
|
local protoc_version=
|
|
local tools_only=
|
|
|
|
while test $# -gt 0
|
|
do
|
|
case "$1" in
|
|
-h | --help )
|
|
usage
|
|
return 0
|
|
;;
|
|
--protoc-version )
|
|
protoc_version="$2"
|
|
shift 2
|
|
;;
|
|
--tools-only )
|
|
tools_only=1
|
|
shift
|
|
;;
|
|
esac
|
|
done
|
|
|
|
if test -z "${protoc_version}"
|
|
then
|
|
protoc_version="$(make --no-print-directory print-PROTOC_VERSION)"
|
|
if test -z "${protoc_version}"
|
|
then
|
|
err_usage "ERROR: no proto-version specified and version could not be discovered"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
# ensure the correct protoc compiler is installed
|
|
protoc_install "${protoc_version}"
|
|
if test -z "${protoc_bin}" ; then
|
|
exit 1
|
|
fi
|
|
|
|
# ensure these tools are installed
|
|
proto_tools_install
|
|
|
|
if [[ -n $tools_only ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Compute some data from dependencies in non-local variables.
|
|
go mod download
|
|
golang_proto_path="$(go list -f '{{ .Dir }}' -m github.com/golang/protobuf)"
|
|
# golang_proto_mod_path="$(sed -e 's,\(.*\)github.com.*,\1,' <<< "${golang_proto_path}")"
|
|
golang_proto_mod_path="$(go env GOMODCACHE)"
|
|
|
|
declare -a proto_files
|
|
while IFS= read -r pkg; do
|
|
pkg="${pkg#"./"}"
|
|
proto_files+=( "$pkg" )
|
|
done < <(find . -name '*.proto' | grep -v 'vendor/' | grep -v '.protobuf' | sort )
|
|
|
|
for proto_file in "${proto_files[@]}"; do
|
|
generate_protobuf_code "${proto_file}"
|
|
done
|
|
|
|
status "Generated all protobuf Go files"
|
|
|
|
generate_mog_code
|
|
|
|
status "Generated all mog Go files"
|
|
|
|
return 0
|
|
}
|
|
|
|
# Installs the version of protoc specified by the first argument.
|
|
#
|
|
# Will set 'protoc_bin'
|
|
function protoc_install {
|
|
local protoc_version="${1:-}"
|
|
local protoc_os
|
|
|
|
if test -z "${protoc_version}"
|
|
then
|
|
protoc_version="$(make --no-print-directory print-PROTOC_VERSION)"
|
|
if test -z "${protoc_version}"
|
|
then
|
|
err "ERROR: no protoc-version specified and version could not be discovered"
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
case "$(uname)" in
|
|
Darwin)
|
|
protoc_os="osx"
|
|
;;
|
|
Linux)
|
|
protoc_os="linux"
|
|
;;
|
|
*)
|
|
err "unexpected OS: $(uname)"
|
|
return 1
|
|
esac
|
|
|
|
local protoc_zip="protoc-${protoc_version}-${protoc_os}-x86_64.zip"
|
|
local protoc_url="https://github.com/protocolbuffers/protobuf/releases/download/v${protoc_version}/${protoc_zip}"
|
|
local protoc_root=".protobuf/protoc-${protoc_os}-${protoc_version}"
|
|
# This is updated for use outside of the function.
|
|
protoc_bin="${protoc_root}/bin/protoc"
|
|
|
|
if [[ -x "${protoc_bin}" ]]; then
|
|
status "protocol buffer compiler version already installed: ${protoc_version}"
|
|
return 0
|
|
fi
|
|
|
|
status_stage "installing protocol buffer compiler version: ${protoc_version}"
|
|
|
|
mkdir -p .protobuf/tmp
|
|
if [[ ! -f .protobuf/tmp/${protoc_zip} ]]; then \
|
|
( cd .protobuf/tmp && curl -sSL "${protoc_url}" -o "${protoc_zip}" )
|
|
fi
|
|
|
|
mkdir -p "${protoc_root}"
|
|
unzip -d "${protoc_root}" ".protobuf/tmp/${protoc_zip}"
|
|
chmod -R a+Xr "${protoc_root}"
|
|
chmod +x "${protoc_bin}"
|
|
|
|
return 0
|
|
}
|
|
|
|
function proto_tools_install {
|
|
local protoc_gen_go_version
|
|
local mog_version
|
|
local protoc_go_inject_tag_version
|
|
|
|
protoc_gen_go_version="$(grep github.com/golang/protobuf go.mod | awk '{print $2}')"
|
|
mog_version="$(make --no-print-directory print-MOG_VERSION)"
|
|
protoc_go_inject_tag_version="$(make --no-print-directory print-PROTOC_GO_INJECT_TAG_VERSION)"
|
|
|
|
# echo "go: ${protoc_gen_go_version}"
|
|
# echo "mog: ${mog_version}"
|
|
# echo "tag: ${protoc_go_inject_tag_version}"
|
|
|
|
install_versioned_tool \
|
|
'protoc-gen-go' \
|
|
'github.com/golang/protobuf' \
|
|
"${protoc_gen_go_version}" \
|
|
'github.com/golang/protobuf/protoc-gen-go'
|
|
|
|
install_unversioned_tool \
|
|
protoc-gen-go-binary \
|
|
'github.com/hashicorp/protoc-gen-go-binary@master'
|
|
|
|
install_versioned_tool \
|
|
'protoc-go-inject-tag' \
|
|
'github.com/favadi/protoc-go-inject-tag' \
|
|
"${protoc_go_inject_tag_version}" \
|
|
'github.com/favadi/protoc-go-inject-tag'
|
|
|
|
install_versioned_tool \
|
|
'mog' \
|
|
'github.com/hashicorp/mog' \
|
|
"${mog_version}" \
|
|
'github.com/hashicorp/mog'
|
|
|
|
return 0
|
|
}
|
|
|
|
function install_unversioned_tool {
|
|
local command="$1"
|
|
local install="$2"
|
|
|
|
if ! command -v "${command}" &>/dev/null ; then
|
|
status_stage "installing tool: ${install}"
|
|
go install "${install}"
|
|
else
|
|
debug "skipping tool: ${install} (installed)"
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
function install_versioned_tool {
|
|
local command="$1"
|
|
local module="$2"
|
|
local version="$3"
|
|
local installbase="$4"
|
|
|
|
local should_install=
|
|
local got
|
|
|
|
local expect="${module}@${version}"
|
|
local install="${installbase}@${version}"
|
|
|
|
if [[ -z "$version" ]]; then
|
|
err "cannot install '${command}' no version selected"
|
|
return 1
|
|
fi
|
|
|
|
if [[ "$version" = "@DEV" ]]; then
|
|
if ! command -v "${command}" &>/dev/null ; then
|
|
err "dev version of '${command}' requested but not installed"
|
|
return 1
|
|
fi
|
|
status "skipping tool: ${installbase} (using development version)"
|
|
return 0
|
|
fi
|
|
|
|
if command -v "${command}" &>/dev/null ; then
|
|
got="$(go version -m $(which "${command}") | grep '\bmod\b' | grep "${module}" |
|
|
awk '{print $2 "@" $3}')"
|
|
if [[ "$expect" != "$got" ]]; then
|
|
should_install=1
|
|
fi
|
|
else
|
|
should_install=1
|
|
fi
|
|
|
|
if [[ -n $should_install ]]; then
|
|
status_stage "installing tool: ${install}"
|
|
go install "${install}"
|
|
else
|
|
debug "skipping tool: ${install} (installed)"
|
|
fi
|
|
return 0
|
|
}
|
|
|
|
function generate_protobuf_code {
|
|
local proto_path="${1:-}"
|
|
if [[ -z "${proto_path}" ]]; then
|
|
err "missing protobuf path argument"
|
|
return 1
|
|
fi
|
|
|
|
if [[ -z "${golang_proto_path}" ]]; then
|
|
err "golang_proto_path was not set"
|
|
return 1
|
|
fi
|
|
if [[ -z "${golang_proto_mod_path}" ]]; then
|
|
err "golang_proto_mod_path was not set"
|
|
return 1
|
|
fi
|
|
|
|
local proto_go_path="${proto_path%%.proto}.pb.go"
|
|
local proto_go_bin_path="${proto_path%%.proto}.pb.binary.go"
|
|
local proto_go_rpcglue_path="${proto_path%%.proto}.rpcglue.pb.go"
|
|
|
|
local go_proto_out='paths=source_relative,plugins=grpc:'
|
|
|
|
status_stage "Generating ${proto_path} into ${proto_go_path} and ${proto_go_bin_path}"
|
|
|
|
rm -f "${proto_go_path}" ${proto_go_bin_path}" ${proto_go_rpcglue_path}"
|
|
|
|
print_run ${protoc_bin} \
|
|
-I="${golang_proto_path}" \
|
|
-I="${golang_proto_mod_path}" \
|
|
-I="${SOURCE_DIR}" \
|
|
--go_out="${go_proto_out}${SOURCE_DIR}" \
|
|
--go-binary_out="${SOURCE_DIR}" \
|
|
"${proto_path}" || {
|
|
|
|
err "Failed to run protoc for ${proto_path}"
|
|
return 1
|
|
}
|
|
|
|
print_run protoc-go-inject-tag -input="${proto_go_path}" || {
|
|
err "Failed to run protoc-go-inject-tag for ${proto_path}"
|
|
return 1
|
|
}
|
|
|
|
local build_tags
|
|
build_tags="$(head -n 2 "${proto_path}" | grep '^//go:build\|// +build' || true)"
|
|
if test -n "${build_tags}"; then
|
|
echo -e "${build_tags}\n" >> "${proto_go_bin_path}.new"
|
|
cat "${proto_go_bin_path}" >> "${proto_go_bin_path}.new"
|
|
mv "${proto_go_bin_path}.new" "${proto_go_bin_path}"
|
|
fi
|
|
|
|
# NOTE: this has to run after we fix up the build tags above
|
|
rm -f "${proto_go_rpcglue_path}"
|
|
print_run go run ./internal/tools/proto-gen-rpc-glue/main.go -path "${proto_go_path}" || {
|
|
err "Failed to generate consul rpc glue outputs from ${proto_path}"
|
|
return 1
|
|
}
|
|
|
|
return 0
|
|
}
|
|
|
|
function generate_mog_code {
|
|
local mog_order
|
|
|
|
mog_order="$(go list -tags "${GOTAGS}" -deps ./proto/pb... | grep "consul/proto")"
|
|
|
|
for FULL_PKG in ${mog_order}; do
|
|
PKG="${FULL_PKG/#github.com\/hashicorp\/consul\/}"
|
|
status_stage "Generating ${PKG}/*.pb.go into ${PKG}/*.gen.go with mog"
|
|
find "$PKG" -name '*.gen.go' -delete
|
|
if [[ -n "${GOTAGS}" ]]; then
|
|
print_run mog -tags "${GOTAGS}" -source "./${PKG}/*.pb.go"
|
|
else
|
|
print_run mog -source "./${PKG}/*.pb.go"
|
|
fi
|
|
done
|
|
|
|
return 0
|
|
}
|
|
|
|
main "$@"
|
|
exit $?
|