如何阻止Rust依赖包的build-scripts运行?及相关技术疑问
我的技术栈包含SQL数据库、用于访问该数据库的Rust库(lorecore)、以及展示数据的Rust GUI(loregui)。该Rust库的build.rs脚本通过diesel_cli生成SQL数据库的schema.rs文件,因此构建该库需预先安装diesel_cli。近期我将GUI与库拆分为独立包,GUI通过Git URL依赖该库。
本地构建一切正常,但GitHub Actions中GUI的构建流水线因库的build脚本执行时缺少diesel_cli而panic。我确信本地构建GUI时并未运行该库的build脚本。
我曾尝试通过判断CARGO_PKG_NAME环境变量,仅在构建库时执行build脚本内容,但未成功。查阅Cargo手册得知含links键的清单可覆盖构建脚本,但这并非我所需——我希望构建GUI时重新编译库,但不执行其中的代码生成逻辑。
现提出以下疑问:
- 为何依赖的build脚本在GitHub Actions中运行,本地却不运行?(仅为满足好奇心)
- 如何阻止依赖包中的代码生成逻辑运行?
3.(额外问题)是否有比在build.rs中调用diesel_cli更优的schema.rs生成方式?
相关代码
Rust库lorecore的build.rs:
extern crate cbindgen; use std::{env, process::Command}; fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/api/c_api.rs"); println!("cargo:rerun-if-changed=migrations/"); if env!("CARGO_PKG_NAME") != "lorecore" { return; } let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); // Generate C header file cbindgen::Builder::new() .with_crate(crate_dir) .with_no_includes() .with_language(cbindgen::Language::C) .generate() .expect("Unable to generate C header file.") .write_to_file("lorecore_api.h"); // Create dummy database let mut cmd = Command::new("diesel"); cmd.arg("setup").arg("--database-url=dummy.db"); cmd.output().expect("Failed to execute diesel migrations"); // Create/Update schema.rs let mut cmd = Command::new("diesel"); cmd.arg("print-schema").arg("--database-url=dummy.db"); let schema = cmd.output().expect("Failed to execute diesel migrations"); std::fs::write("src/sql/schema.rs", schema.stdout).expect("Failed to write schema.rs"); // Delete dummy.db std::fs::remove_file("dummy.db").expect("Failed to delete dummy.db"); // Call cargo fmt let mut cmd = Command::new("cargo"); cmd.arg("fmt"); cmd.output().expect("Failed to execute cargo fmt"); }
GUI的Cargo.toml:
[package] name = "loregui" version = "0.1.0" edition = "2021" [dependencies] iced = { version = "0.10.0", features=["lazy"] } iced_aw = { version = "0.7.0", default-features = false, features = ["card", "modal", "selection_list"] } lorecore = { git = "https://github.com/TheComamba/LoreCore.git" } preferences = {version = "1.1.0"} rfd = "0.11.3" # file dialogs
GitHub Actions流水线片段:
steps: - uses: actions/checkout@v3 - name: Install dependencies for Linux if: matrix.os == 'ubuntu-latest' run: | apt-get update apt-get install -y sudo curl build-essential cmake pkg-config libfontconfig1-dev cargo ./scripts/install_build_dependencies_linux.sh - uses: dtolnay/rust-toolchain@stable - uses: clechasseur/rs-cargo@v1 if: ${{ !inputs.isRelease }} with: command: build
1. 本地与GitHub Actions中build脚本执行差异的原因
本地构建GUI时,Cargo大概率复用了lorecore的缓存构建产物——比如你之前单独构建过lorecore,或者曾经用本地路径依赖而非Git URL,Cargo直接跳过了重新构建步骤,自然不会执行build脚本。而GitHub Actions是全新的干净环境,没有任何缓存,Cargo必须从头编译lorecore,所以会触发build脚本执行。
另外你用env!("CARGO_PKG_NAME")的判断逻辑无效,是因为env!是编译期宏,当构建lorecore作为依赖时,这个值始终是lorecore,判断条件永远不成立,build脚本还是会完整执行。
2. 阻止依赖包执行代码生成逻辑的方法
推荐两种可靠方案:
特性标志控制:
在lorecore的Cargo.toml中添加一个默认启用的codegen特性:# lorecore/Cargo.toml [features] default = ["codegen"] codegen = []然后修改
build.rs的判断逻辑,仅在启用该特性时执行代码生成:fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/api/c_api.rs"); println!("cargo:rerun-if-changed=migrations/"); // 未启用codegen特性则直接返回 if !cfg!(feature = "codegen") { return; } // 后续cbindgen、diesel相关逻辑... }最后在GUI的
Cargo.toml中依赖lorecore时禁用默认特性:lorecore = { git = "https://github.com/TheComamba/LoreCore.git", default-features = false }这种方式既能保证GUI构建时重新编译
lorecore的业务代码,又能跳过代码生成逻辑。环境变量控制:
在build.rs中判断是否存在SKIP_CODEGEN环境变量,存在则跳过代码生成:fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=src/api/c_api.rs"); println!("cargo:rerun-if-changed=migrations/"); if env::var_os("SKIP_CODEGEN").is_some() { return; } // 后续逻辑... }然后在GitHub Actions的构建步骤中设置该环境变量:
- uses: clechasseur/rs-cargo@v1 if: ${{ !inputs.isRelease }} env: SKIP_CODEGEN: 1 with: command: build这种方式无需修改依赖声明,适合临时场景化控制。
3. 更优的schema.rs生成方式
推荐直接将schema.rs提交到Git仓库,替代build脚本动态生成:
- 每次修改数据库迁移后,手动执行
diesel print-schema生成/更新schema.rs,然后提交到仓库。这样依赖该库的项目无需安装diesel_cli,构建速度更快,也避免了构建时的数据库操作风险。 - 可以在仓库的CI流水线中添加检查步骤:当
migrations/目录变化时,自动验证schema.rs是否已更新,若未更新则触发提醒或自动提交,保证schema与迁移同步。
另外Diesel也支持手动编写schema.rs的静态方式,但这种方式维护成本较高,仅适合对数据库结构有极致控制的场景。
内容的提问来源于stack exchange,提问作者The Comamba

