相对路径在cargo build生效但cargo publish失效的问题求助
问题场景
项目结构如下:
root/ - proto/ - my.proto - python/ - rust/ - Cargo.toml - build.rs - ...
使用prost生成Rust代码的build.rs代码如下:
use std::io::Result; fn main() -> Result<()> { prost_build::compile_protos( // 待编译文件 &["my.proto"], // protoc的包含目录 &["../proto/"])?; Ok(()) }
cargo build执行正常,但cargo publish时失败,protoc报错:
ignoring ../proto/ since it does not exist
失败原因
cargo build是在当前rust目录下执行的,相对路径../proto/能正确指向root目录下的proto文件夹。但cargo publish执行时,Cargo会先把当前Rust包的代码复制到target目录下的临时子文件夹中做打包验证,此时临时目录的上级目录不是root,而是target下的其他目录,所以../proto/这个相对路径就找不到对应的目录了。
更关键的是,默认情况下cargo publish只会打包当前Rust包目录(rust/)下的文件,root/proto/里的内容不会被包含到发布的包中——就算路径问题解决了,发布后的包也会缺失proto文件,导致下游依赖编译失败。
解决方案
方案1:将proto文件纳入Rust包的源码范围(推荐用于发布)
- 在rust目录下创建proto子目录,把root/proto/下的my.proto复制过来(不推荐软链,避免跨目录依赖问题),调整后的项目结构:
root/ - proto/ - my.proto - python/ - rust/ - proto/ - my.proto - Cargo.toml - build.rs - ...
- 修改
build.rs中的路径配置:
use std::io::Result; fn main() -> Result<()> { prost_build::compile_protos( &["proto/my.proto"], &["."] )?; Ok(()) }
- 在
Cargo.toml中添加include配置,确保publish时能把proto文件打包进去:
[package] # 保留原有配置... include = ["src/**/*", "proto/**/*", "build.rs", "Cargo.toml"]
方案2:使用绝对路径临时解决本地publish验证(不推荐正式发布)
如果不想移动proto文件,可以在build.rs中通过env!("CARGO_MANIFEST_DIR")获取当前包的根目录,拼接出proto文件的绝对路径:
use std::io::Result; use std::path::Path; fn main() -> Result<()> { let manifest_dir = env!("CARGO_MANIFEST_DIR"); let proto_path = Path::new(manifest_dir).join("../proto/my.proto"); let include_dir = Path::new(manifest_dir).join("../proto/"); prost_build::compile_protos( &[proto_path.to_str().unwrap()], &[include_dir.to_str().unwrap()] )?; Ok(()) }
注意:这个方案只能解决本地publish时的路径验证问题,发布到crates.io后的包仍然会缺失proto文件,下游依赖编译时会失败,仅适合本地调试使用。
方案3:将proto文件作为独立模块共享(适合多语言场景)
如果需要Python、Rust等多语言共享proto定义,可以把proto文件放到单独的git仓库,在rust目录中通过git子模块引入;或者将proto发布为单独的Crate,让Rust包通过依赖的方式获取proto文件,彻底避免路径问题。
关于相对路径的合理性
本地开发中使用跨目录的相对路径是可行的,但绝对不适合用于需要发布的Cargo包。因为Cargo发布的包只能包含当前包目录下的文件,跨目录的相对路径在打包验证和下游依赖编译时都会失效。正式发布的包应该尽量将依赖的资源文件放在包目录内部,或者通过依赖管理的方式引入。
内容的提问来源于stack exchange,提问作者Achim

