blob: 3210bcc7a5120b1b9d4d2fb20dc138cb60448e43 [file] [log] [blame]
#!/bin/bash
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
set -euo pipefail
PROTOBUF_BASELINE_VERSION="${PROTOBUF_BASELINE_VERSION:-3.25.5}"
if [[ -n "${XDG_CACHE_HOME:-}" ]]; then
_PROTOC_DEFAULT_CACHE_BASE="${XDG_CACHE_HOME}"
elif [[ -n "${HOME:-}" ]]; then
_PROTOC_DEFAULT_CACHE_BASE="${HOME}/.cache"
else
_PROTOC_DEFAULT_CACHE_BASE="/tmp"
fi
_PROTOC_UNAME_S="$(uname -s | tr '[:upper:]' '[:lower:]')"
case "${_PROTOC_UNAME_S}" in
linux*)
_PROTOC_DEFAULT_OS="linux"
;;
darwin*)
_PROTOC_DEFAULT_OS="osx"
;;
*)
echo "ERROR: unsupported host OS '${_PROTOC_UNAME_S}'. Please set PROTOC_OS explicitly." >&2
exit 1
;;
esac
_PROTOC_UNAME_M="$(uname -m)"
case "${_PROTOC_UNAME_M}" in
x86_64|amd64)
_PROTOC_DEFAULT_ARCH="x86_64"
;;
aarch64|arm64)
_PROTOC_DEFAULT_ARCH="aarch_64"
;;
*)
echo "ERROR: unsupported host arch '${_PROTOC_UNAME_M}'. Please set PROTOC_ARCH explicitly." >&2
exit 1
;;
esac
PROTOC_INSTALL_ROOT="${PROTOC_INSTALL_ROOT:-${_PROTOC_DEFAULT_CACHE_BASE}/fluss-cpp-tools}"
PROTOC_OS="${PROTOC_OS:-${_PROTOC_DEFAULT_OS}}"
PROTOC_ARCH="${PROTOC_ARCH:-${_PROTOC_DEFAULT_ARCH}}"
PROTOC_FORCE_INSTALL="${PROTOC_FORCE_INSTALL:-0}"
PROTOC_PRINT_PATH_ONLY="${PROTOC_PRINT_PATH_ONLY:-0}"
PROTOC_ALLOW_INSECURE_DOWNLOAD="${PROTOC_ALLOW_INSECURE_DOWNLOAD:-0}"
PROTOC_SKIP_CHECKSUM_VERIFY="${PROTOC_SKIP_CHECKSUM_VERIFY:-0}"
usage() {
cat <<'EOF'
Usage: bindings/cpp/scripts/ensure_protoc.sh [--print-path]
Ensures a protoc binary matching the configured protobuf baseline is available.
Installs into a local cache directory (default: \$XDG_CACHE_HOME/fluss-cpp-tools or
\$HOME/.cache/fluss-cpp-tools) and prints
the protoc path on stdout.
Env vars:
PROTOBUF_BASELINE_VERSION Baseline protobuf version (default: 3.25.5)
PROTOC_INSTALL_ROOT Local cache root (default: XDG/HOME cache dir)
PROTOC_OS protoc package OS (default: auto-detect host: linux/osx)
PROTOC_ARCH protoc package arch (default: auto-detect host: x86_64/aarch_64)
PROTOC_FORCE_INSTALL 1 to force re-download
PROTOC_ALLOW_INSECURE_DOWNLOAD
1 to disable TLS verification (not recommended)
PROTOC_SKIP_CHECKSUM_VERIFY
1 to skip pinned archive checksum verification
BAZEL_PROXY_URL Optional proxy (sets curl/wget proxy envs if present)
EOF
}
for arg in "$@"; do
case "$arg" in
--print-path)
PROTOC_PRINT_PATH_ONLY=1
;;
-h|--help)
usage
exit 0
;;
*)
echo "Unknown argument: $arg" >&2
usage >&2
exit 1
;;
esac
done
setup_proxy_env() {
if [[ -n "${BAZEL_PROXY_URL:-}" ]]; then
export http_proxy="${http_proxy:-$BAZEL_PROXY_URL}"
export https_proxy="${https_proxy:-$BAZEL_PROXY_URL}"
export HTTP_PROXY="${HTTP_PROXY:-$http_proxy}"
export HTTPS_PROXY="${HTTPS_PROXY:-$https_proxy}"
fi
}
normalize_version_for_protoc_release() {
local v="$1"
# Protobuf release packaging switched from v3.x.y to vX.Y for newer versions.
# For our current agreed baseline (3.25.5), the protoc archive/tag is 25.5.
if [[ "$v" =~ ^3\.([0-9]+\.[0-9]+)$ ]]; then
local stripped="${BASH_REMATCH[1]}"
local major="${stripped%%.*}"
if [[ "$major" -ge 21 ]]; then
echo "$stripped"
return 0
fi
fi
echo "$v"
}
version_matches_baseline() {
local actual="$1"
local baseline="$2"
local actual_norm baseline_norm
actual_norm="$(normalize_version_for_protoc_release "$actual")"
baseline_norm="$(normalize_version_for_protoc_release "$baseline")"
[[ "$actual" == "$baseline" || "$actual_norm" == "$baseline_norm" ]]
}
lookup_protoc_archive_sha256() {
local release_version="$1"
local os="$2"
local arch="$3"
case "${release_version}:${os}:${arch}" in
25.5:linux:aarch_64)
echo "dc715bb5aab2ebf9653d7d3efbe55e01a035e45c26f391ff6d9b7923e22914b7"
;;
25.5:linux:x86_64)
echo "e1ed237a17b2e851cf9662cb5ad02b46e70ff8e060e05984725bc4b4228c6b28"
;;
25.5:osx:aarch_64)
echo "781a6fc4c265034872cadc65e63dd3c0fc49245b70917821b60e2d457a6876ab"
;;
25.5:osx:x86_64)
echo "c5447e4f0d5caffb18d9ff21eae7bc7faf2bb2000083d6f49e5b6000b30fceae"
;;
*)
return 1
;;
esac
}
verify_download_sha256() {
local file="$1"
local expected="$2"
local actual=""
if command -v sha256sum >/dev/null 2>&1; then
actual="$(sha256sum "$file" | awk '{print $1}')"
elif command -v shasum >/dev/null 2>&1; then
actual="$(shasum -a 256 "$file" | awk '{print $1}')"
else
echo "ERROR: neither sha256sum nor shasum is available for checksum verification." >&2
return 1
fi
if [[ "$actual" != "$expected" ]]; then
echo "ERROR: protoc archive checksum mismatch." >&2
echo " expected: $expected" >&2
echo " actual: $actual" >&2
return 1
fi
}
download_file() {
local url="$1"
local out="$2"
if command -v curl >/dev/null 2>&1; then
local curl_args=(-fL)
if [[ "${PROTOC_ALLOW_INSECURE_DOWNLOAD}" == "1" ]]; then
curl_args+=(-k)
fi
curl "${curl_args[@]}" "$url" -o "$out"
return 0
fi
if command -v wget >/dev/null 2>&1; then
local wget_args=()
if [[ -n "${https_proxy:-}" || -n "${http_proxy:-}" ]]; then
wget_args+=(-e use_proxy=yes)
if [[ -n "${https_proxy:-}" ]]; then
wget_args+=(-e "https_proxy=${https_proxy}")
fi
if [[ -n "${http_proxy:-}" ]]; then
wget_args+=(-e "http_proxy=${http_proxy}")
fi
fi
if [[ "${PROTOC_ALLOW_INSECURE_DOWNLOAD}" == "1" ]]; then
wget_args+=(--no-check-certificate)
fi
wget "${wget_args[@]}" -O "$out" "$url"
return 0
fi
echo "ERROR: neither curl nor wget is available for downloading protoc." >&2
return 1
}
ensure_zip_tools() {
command -v unzip >/dev/null 2>&1 || {
echo "ERROR: unzip not found." >&2
exit 1
}
}
setup_proxy_env
ensure_zip_tools
if command -v protoc >/dev/null 2>&1; then
existing_out="$(protoc --version 2>/dev/null || true)"
if [[ "$existing_out" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then
existing_ver="${BASH_REMATCH[1]}"
if version_matches_baseline "$existing_ver" "$PROTOBUF_BASELINE_VERSION"; then
command -v protoc
exit 0
fi
fi
fi
PROTOC_RELEASE_VERSION="$(normalize_version_for_protoc_release "$PROTOBUF_BASELINE_VERSION")"
PROTOC_ARCHIVE="protoc-${PROTOC_RELEASE_VERSION}-${PROTOC_OS}-${PROTOC_ARCH}.zip"
PROTOC_URL="https://github.com/protocolbuffers/protobuf/releases/download/v${PROTOC_RELEASE_VERSION}/${PROTOC_ARCHIVE}"
PROTOC_PREFIX="${PROTOC_INSTALL_ROOT}/protoc-${PROTOC_RELEASE_VERSION}-${PROTOC_OS}-${PROTOC_ARCH}"
PROTOC_BIN="${PROTOC_PREFIX}/bin/protoc"
if [[ "${PROTOC_FORCE_INSTALL}" != "1" && -x "${PROTOC_BIN}" ]]; then
if [[ "${PROTOC_PRINT_PATH_ONLY}" == "1" ]]; then
echo "${PROTOC_BIN}"
else
echo "${PROTOC_BIN}"
fi
exit 0
fi
mkdir -p "${PROTOC_INSTALL_ROOT}"
tmpdir="$(mktemp -d "${PROTOC_INSTALL_ROOT}/.protoc-download.XXXXXX")"
trap 'rm -rf "${tmpdir}"' EXIT
archive_path="${tmpdir}/${PROTOC_ARCHIVE}"
download_file "${PROTOC_URL}" "${archive_path}"
if [[ "${PROTOC_SKIP_CHECKSUM_VERIFY}" != "1" ]]; then
if expected_sha256="$(lookup_protoc_archive_sha256 "${PROTOC_RELEASE_VERSION}" "${PROTOC_OS}" "${PROTOC_ARCH}")"; then
verify_download_sha256 "${archive_path}" "${expected_sha256}"
else
echo "ERROR: no pinned checksum for protoc archive ${PROTOC_ARCHIVE}. Set PROTOC_SKIP_CHECKSUM_VERIFY=1 to bypass." >&2
exit 1
fi
fi
extract_dir="${tmpdir}/extract"
mkdir -p "${extract_dir}"
unzip -q "${archive_path}" -d "${extract_dir}"
rm -rf "${PROTOC_PREFIX}"
mkdir -p "${PROTOC_PREFIX}"
cp -a "${extract_dir}/." "${PROTOC_PREFIX}/"
chmod +x "${PROTOC_BIN}"
echo "${PROTOC_BIN}"