先安装好go环境 并且已经安装好protobuf环境
将下面代码保存为 proto_gen.go
package main
import (
"fmt"
"io/fs"
"os"
"os/exec"
"path/filepath"
"strings"
)
func main() {
if len(os.Args) < 2 {
fmt.Println("Usage: proto_gen <proto_root_dir>")
os.Exit(1)
}
root := filepath.Clean(os.Args[1])
if _, err := exec.LookPath("protoc"); err != nil {
fmt.Println("❌ protoc not found in PATH")
os.Exit(1)
}
cppOut := filepath.Join(root, "cpp")
luaOut := filepath.Join(root, "lua")
_ = os.MkdirAll(cppOut, 0755)
_ = os.MkdirAll(luaOut, 0755)
fmt.Println("📂 scan:", root)
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
if strings.Contains(path, string(filepath.Separator)+"cpp") ||
strings.Contains(path, string(filepath.Separator)+"lua") {
return filepath.SkipDir
}
return nil
}
if !strings.HasSuffix(path, ".proto") {
return nil
}
relDir, _ := filepath.Rel(root, filepath.Dir(path))
cppDir := filepath.Join(cppOut, relDir)
luaDir := filepath.Join(luaOut, relDir)
_ = os.MkdirAll(cppDir, 0755)
_ = os.MkdirAll(luaDir, 0755)
base := strings.TrimSuffix(filepath.Base(path), ".proto")
luaPb := filepath.Join(luaDir, base+".pb")
fmt.Println("🚀 compiling:", path)
// 1️⃣ 生成 C++
cmdCpp := exec.Command("protoc",
"-I", root,
"--cpp_out="+cppOut,
path,
)
cmdCpp.Stdout = os.Stdout
cmdCpp.Stderr = os.Stderr
if err := cmdCpp.Run(); err != nil {
return err
}
// 2️⃣ 生成 Lua descriptor (.pb)
cmdLua := exec.Command("protoc",
"-I", root,
"--include_imports",
"--descriptor_set_out="+luaPb,
path,
)
cmdLua.Stdout = os.Stdout
cmdLua.Stderr = os.Stderr
if err := cmdLua.Run(); err != nil {
return err
}
return nil
})
if err != nil {
fmt.Println("❌ Error:", err)
os.Exit(1)
}
fmt.Println("🎉 All proto generated (C++ + Lua pb)!")
}
编译
Win11
go build -o proto_gen.exe proto_gen.go
Ubuntu24.04
go build -o proto_gen proto_gen.go
示例:
protos
├─common
│ base.proto
│
└─msg
user.proto
文件 base.proto
syntax = "proto3";
package common;
// 可选:给 C++ 指定命名空间前缀
option csharp_namespace = "Proto.Common";
option java_package = "proto.common";
// 通用错误码
enum ErrorCode {
ERROR_OK = 0;
ERROR_UNKNOWN = 1;
ERROR_INVALID_PARAM = 2;
ERROR_TIMEOUT = 3;
ERROR_NOT_FOUND = 4;
}
// 所有消息通用头
message PacketHeader {
uint32 msg_id = 1; // 消息ID
uint64 timestamp = 2; // 时间戳(ms)
uint32 version = 3; // 协议版本
}
// 通用响应结构
message Result {
ErrorCode code = 1;
string message = 2;
}
// 通用分页
message PageInfo {
uint32 page = 1;
uint32 page_size = 2;
uint32 total = 3;
}
文件 user.proto
syntax = "proto3";
package msg;
import "common/base.proto";
// 可选:给 C++ 指定命名空间前缀
option csharp_namespace = "Proto.Msg";
option java_package = "proto.msg";
enum UserStatus {
USER_STATUS_UNKNOWN = 0;
USER_STATUS_OFFLINE = 1;
USER_STATUS_ONLINE = 2;
USER_STATUS_BANNED = 3;
}
message User {
uint64 uid = 1;
string name = 2;
UserStatus status = 3;
repeated string tags = 4;
map<string, string> extra = 5;
}
message LoginReq {
common.PacketHeader header = 1;
string username = 2;
string password = 3;
}
message LoginRsp {
common.PacketHeader header = 1;
common.Result result = 2;
User user = 3;
}
开始生成
Win11
.\proto_gen.exe protos
Ubuntu24.04
./proto_gen protos
生成后
protos
├─common
│ base.proto
│
├─cpp
│ ├─common
│ │ base.pb.cc
│ │ base.pb.h
│ │
│ └─msg
│ user.pb.cc
│ user.pb.h
│
├─lua
│ ├─common
│ │ base.pb
│ │
│ └─msg
│ user.pb
│
└─msg
user.proto
Enjoy~。~