Skip to content
Clone
2308dd0
72 lines 2.0 KB Rust
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
fn main() -> Result<(), Box<dyn std::error::Error>> {
let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?);
let proto_root = manifest_dir.join("proto");
println!("cargo:rerun-if-changed=build.rs");
if !proto_root.is_dir() {
println!("cargo:warning=proto 目录不存在,跳过协议生成");
return Ok(());
}
println!("cargo:rerun-if-changed={}", proto_root.display());
let sources = collect_protos(&proto_root)?;
if sources.is_empty() {
println!("cargo:warning=proto 下没有 .proto 文件,跳过协议生成");
return Ok(());
}
let cwd = env::current_dir()?;
let include_root = relative_path(&cwd, &proto_root);
let protos: Vec<PathBuf> = sources
.iter()
.map(|path| relative_path(&cwd, path))
.collect();
tonic_prost_build::configure()
.build_server(false)
.build_client(true)
.compile_protos(&protos, &[include_root])?;
Ok(())
}
fn collect_protos(dir: &Path) -> std::io::Result<Vec<PathBuf>> {
let mut found = Vec::new();
let mut stack = vec![dir.to_path_buf()];
while let Some(dir) = stack.pop() {
for entry in fs::read_dir(&dir)? {
let path = entry?.path();
if path.is_dir() {
stack.push(path);
} else if path.extension().is_some_and(|ext| ext == "proto") {
found.push(path);
}
}
}
found.sort();
Ok(found)
}
fn relative_path(base: &Path, target: &Path) -> PathBuf {
let base: Vec<_> = base.components().collect();
let target: Vec<_> = target.components().collect();
let common = base
.iter()
.zip(target.iter())
.take_while(|(a, b)| a == b)
.count();
if common == 0 {
return target.iter().collect();
}
let mut rel = PathBuf::new();
for _ in common..base.len() {
rel.push("..");
}
rel.extend(target[common..].iter().map(|c| c.as_os_str()));
rel
}