topfans/backend/scripts/compile-proto.sh
2026-06-22 17:19:48 +08:00

293 lines
11 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/bin/bash
# Proto 文件编译脚本(自动扫描模式)
# ----------------------------------------------------------------------------
# 扫描 proto/*.proto 自动编译为 Go 代码:
# - 含 service 的 proto生成 .pb.go + .triple.go
# - 不含 service 的 proto如 common.proto / event.proto只生成 .pb.go
#
# protoc-gen-gopb.go按 --go_out + 源文件名输出;
# protoc-gen-go-triple v3.0.0triple.go忽略 --go-triple_out
# 严格按 go_package 第一段生成目录、按源文件名生成文件。
# 因此同一个 proto 的两个产物可能落在不同目录典型castlove_config.proto
# → pb.go 在 pkg/proto/castlove_config/triple.go 在 pkg/proto/castlove/)。
#
# 兼容 bash 3.2macOS 自带 bash不使用 declare -A 关联数组。
#
# 新增 proto 文件时无需修改本脚本,放到 proto/ 目录下即可。
# ----------------------------------------------------------------------------
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR/.."
export PATH="$PATH:$(go env GOPATH)/bin"
echo "======================================"
echo "开始编译 Proto 文件(自动扫描模式)"
echo "======================================"
# --- 检查 protoc ---
if ! command -v protoc &> /dev/null; then
echo "❌ protoc 未安装!"
echo " macOS: brew install protobuf"
echo " Ubuntu: apt-get install protobuf-compiler"
exit 1
fi
echo "✅ protoc 版本: $(protoc --version)"
# --- 检查 Go 插件 ---
MISSING_PLUGINS=false
if ! command -v protoc-gen-go &> /dev/null; then
echo "❌ protoc-gen-go 未安装"
MISSING_PLUGINS=true
fi
if ! command -v protoc-gen-go-triple &> /dev/null; then
echo "❌ protoc-gen-go-triple 未安装"
MISSING_PLUGINS=true
fi
if [ "$MISSING_PLUGINS" = true ]; then
echo ""
echo "请安装缺失的插件:"
echo " go install google.golang.org/protobuf/cmd/protoc-gen-go@latest"
echo " go install github.com/dubbogo/protoc-gen-go-triple/v3@latest"
exit 1
fi
echo "✅ 所有必需插件已安装"
echo ""
# --- 工具函数:从 proto 文件解析 go_package 第一段(生成路径段),用于定位 triple.go 输出目录 ---
# protoc-gen-go-triple v3.0.0 忽略 --go-triple_out严格按 go_package 第一段生成路径,
# 但文件名仍用源文件名(如 castlove_config.proto → .../castlove/castlove_config.triple.go
# 退化:解析失败时按源文件名推断。
resolve_triple_out_dir() {
local name="$1"
local proto_file="proto/${name}.proto"
local go_pkg_path
# 用 sed -E 替代 grep -oPmacOS BSD grep 不支持 -Psed -E 是 POSIX ERE
# 匹配 option go_package = "PATH;NAME";,提取 PATH 段
go_pkg_path=$(sed -nE 's/.*option go_package = "([^";]+);.*/\1/p' "$proto_file" | head -1)
if [ -n "$go_pkg_path" ]; then
echo "pkg/proto/$(basename "$go_pkg_path")"
else
echo "pkg/proto/$name"
fi
}
# --- 工具函数:从 protoc-gen-go-triple v3 默认输出位置把 triple.go 搬到目标目录 ---
move_triple_files() {
local name="$1" # proto 文件名(不含扩展名)
local target_dir="$2" # triple.go 的目标目录pkg/proto/<go_pkg_basename>
local triple_file="${name}.triple.go"
# v3 生成位置github.com/<go_pkg>/<name>.triple.gogo_pkg 第一段 + 源文件名)
local src_file="github.com/topfans/backend/pkg/proto/$(basename "$target_dir")/${triple_file}"
if [ -f "$src_file" ]; then
mv "$src_file" "$target_dir/"
# 清掉残留的空目录v3 在 github.com/ 下生成的路径骨架)
local pkg_dir_name=$(basename "$target_dir")
rm -rf "github.com/topfans/backend/pkg/proto/${pkg_dir_name}"
echo "${triple_file} 已移动到 ${target_dir}/"
else
# 兜底:尝试源文件名目录(兼容不同版本行为)
local fallback="github.com/topfans/backend/pkg/proto/${name}/${triple_file}"
if [ -f "$fallback" ]; then
mv "$fallback" "$target_dir/"
rm -rf "github.com/topfans/backend/pkg/proto/${name}"
echo "${triple_file} 已在兜底路径找到并移动"
else
echo " ⚠️ 未找到 ${src_file}v3 插件输出位置可能已变,需检查)"
fi
fi
}
# --- 跨包类型引用后处理protoc-gen-go-triple v3.0.0 在跨包类型作 RPC 参数时丢包前缀 ---
# 已知触发statistic.proto 引用 event.protoevent.Event / event.BatchEventRequest 作 RPC 参数)。
# 如果以后其他 proto 也出现同样症状,把 name 加到下方 case 分支即可。
patch_cross_package_refs() {
local name="$1"
local triple_dir
triple_dir=$(resolve_triple_out_dir "$name")
case "$name" in
statistic)
local TRIPLE_FILE="${triple_dir}/${name}.triple.go"
if [ ! -f "$TRIPLE_FILE" ]; then
return
fi
if grep -q 'github.com/topfans/backend/pkg/proto/event"' "$TRIPLE_FILE"; then
return # 已修复过
fi
python3 -c "
import re
with open('$TRIPLE_FILE', 'r') as f:
src = f.read()
pattern = re.compile(r'(import\s*\([^)]*\))', re.MULTILINE)
blocks = list(pattern.finditer(src))
if len(blocks) >= 2:
second = blocks[1]
block_text = second.group(1)
if 'pkg/proto/event' not in block_text:
new_block = block_text[:-1].rstrip() + '\n\tevent \"github.com/topfans/backend/pkg/proto/event\"\n)'
src = src[:second.start()] + new_block + src[second.end():]
with open('$TRIPLE_FILE', 'w') as f:
f.write(src)
"
sed -i '' \
-e 's/\*Event\([,)]\)/\*event.Event\1/g' \
-e 's/\*Event$/&XXX/g' \
-e 's/\*Event[^a-zA-Z._)/]/\*event.Event\&/g' \
-e 's/new(Event)/new(event.Event)/g' \
-e 's/\*BatchEventRequest\([,)]\)/\*event.BatchEventRequest\1/g' \
-e 's/new(BatchEventRequest)/new(event.BatchEventRequest)/g' \
"$TRIPLE_FILE"
sed -i '' \
-e 's/(\*Event)/(\*event.Event)/g' \
-e 's/(\*BatchEventRequest)/(\*event.BatchEventRequest)/g' \
"$TRIPLE_FILE"
echo "${TRIPLE_FILE} 已修复跨包类型引用protoc-gen-go-triple v3.0.0 bug"
;;
esac
}
# --- 扫描 proto/*.proto ---
shopt -s nullglob
PROTO_FILES=(proto/*.proto)
shopt -u nullglob
if [ ${#PROTO_FILES[@]} -eq 0 ]; then
echo "❌ proto/ 目录下未发现 .proto 文件"
exit 1
fi
# 按文件名排序,保证输出稳定、可复现
IFS=$'\n' PROTO_FILES=($(printf '%s\n' "${PROTO_FILES[@]}" | sort))
unset IFS
echo "🔍 发现 ${#PROTO_FILES[@]} 个 proto 文件:"
for f in "${PROTO_FILES[@]}"; do
echo " - $(basename "$f")"
done
echo ""
# --- 创建 pkg/proto 子目录 ---
echo "📁 创建目标目录..."
for f in "${PROTO_FILES[@]}"; do
name=$(basename "$f" .proto)
pb_dir="pkg/proto/$name"
triple_dir=$(resolve_triple_out_dir "$name")
mkdir -p "$pb_dir"
[ "$triple_dir" != "$pb_dir" ] && mkdir -p "$triple_dir"
done
echo ""
# --- 逐个编译 ---
echo "⚙️ 开始编译..."
for f in "${PROTO_FILES[@]}"; do
name=$(basename "$f" .proto)
pb_dir="pkg/proto/$name"
triple_dir=$(resolve_triple_out_dir "$name")
has_service=false
if grep -q '^service ' "$f"; then
has_service=true
fi
if [ "$has_service" = true ]; then
if [ "$pb_dir" = "$triple_dir" ]; then
echo "📦 编译 ${name}.proto (含 service → pb + triple 同目录)"
else
echo "📦 编译 ${name}.proto (含 service → pb@${pb_dir}/, triple@${triple_dir}/)"
fi
protoc \
--proto_path=proto \
--proto_path=. \
--go_out="$pb_dir" \
--go_opt=paths=source_relative \
--go-triple_out="$triple_dir" \
--go-triple_opt=paths=source_relative \
"$name.proto"
move_triple_files "$name" "$triple_dir"
else
# 不含 service不生成 triple.go避免未使用 import 编译失败)
echo "📦 编译 ${name}.proto (无 service → 仅生成 pb@${pb_dir}/)"
protoc \
--proto_path=proto \
--proto_path=. \
--go_out="$pb_dir" \
--go_opt=paths=source_relative \
"$name.proto"
fi
patch_cross_package_refs "$name"
done
echo ""
echo "✅ 编译阶段完成"
echo ""
# --- 清理 protoc 临时产物 ---
echo "🔄 清理冗余文件..."
if [ -d "github.com" ]; then
rm -rf github.com
echo " ✅ github.com/ 目录已清理"
fi
for f in "${PROTO_FILES[@]}"; do
name=$(basename "$f" .proto)
pb_dir="pkg/proto/$name"
triple_dir=$(resolve_triple_out_dir "$name")
if [ -f "proto/${name}.pb.go" ]; then
rm "proto/${name}.pb.go"
echo " ✅ proto/${name}.pb.go 已清理"
fi
if [ -f "proto/${name}.triple.go" ]; then
rm "proto/${name}.triple.go"
echo " ✅ proto/${name}.triple.go 已清理"
fi
for d in "$pb_dir" "$triple_dir"; do
[ "$d" = "$pb_dir" ] && [ "$d" = "$triple_dir" ] && continue
if [ -d "$d/proto" ]; then
rm -rf "$d/proto"
echo "${d}/proto/ 子目录已清理"
fi
done
done
echo ""
# --- 汇总输出 ---
echo "📂 生成的文件结构:"
echo " pkg/proto/"
# 收集所有出现过的目录pb_dir + triple_dir 去重),按目录名排序
TMP_DIRS=$(mktemp)
trap 'rm -f "$TMP_DIRS"' EXIT
for f in "${PROTO_FILES[@]}"; do
name=$(basename "$f" .proto)
pb_dir="pkg/proto/$name"
triple_dir=$(resolve_triple_out_dir "$name")
echo "$pb_dir" >> "$TMP_DIRS"
echo "$triple_dir" >> "$TMP_DIRS"
done
# 按目录排序后去重,逐个打印
sort -u "$TMP_DIRS" | while read -r d; do
dir_name=$(basename "$d")
pb_files=$(find "$d" -maxdepth 1 -name "*.pb.go" 2>/dev/null | sort)
triple_files=$(find "$d" -maxdepth 1 -name "*.triple.go" 2>/dev/null | sort)
echo " ├── ${dir_name}/"
for pf in $pb_files; do
echo " │ ├── $(basename "$pf")"
done
for tf in $triple_files; do
echo " │ └── $(basename "$tf")"
done
done
GENERATED_FILES=$(find pkg/proto -name "*.pb.go" -o -name "*.triple.go" 2>/dev/null | wc -l | tr -d ' ')
echo ""
echo "======================================"
echo "✅ 所有 Proto 文件编译完成!"
echo "======================================"
echo "生成文件总数: ${GENERATED_FILES}"
echo ""
echo "提示:以后新增 proto 只需放到 proto/ 目录,无需修改本脚本。"