You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止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时重新编译库,但不执行其中的代码生成逻辑。

现提出以下疑问:

  1. 为何依赖的build脚本在GitHub Actions中运行,本地却不运行?(仅为满足好奇心)
  2. 如何阻止依赖包中的代码生成逻辑运行?
    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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 04:05:04