rust 有条件地一次只编译一个模块

uz75evzq  于 2023-05-07  发布在  其他
关注(0)|答案(3)|浏览(217)

bounty还有2天到期。回答此问题可获得+500声望奖励。Hubro正在寻找此问题的最新答案:由于这个问题已经有8年的历史了,我希望已经有一些发展来解决这个问题,无论是在语言本身还是通过第三方工具。

长话短说:如果你想对crate中的一个核心模块做一个实验性的更改,你可能想在完全提交并将更改传播到crate的其余部分之前,对该模块运行测试(可能还要编写一些新的测试)。
能够独立地构建和测试模块将允许您重构代码库或从头开始迭代地实现功能。如果变更有任何根本性问题,现在几乎可以立即发现,而不是在工作数小时后。
目前有没有什么方法可以用Rust实现这一点,除了复制crate和删除每个无法构建的模块?
有时,在重构时,其中一个模块中的更改会影响其他模块(或许多其他模块),因为例如类型已更改或函数参数已更改等。在这种情况下,而不是改变一切,然后立即编译,有没有一种方法,我可以编译和运行单元测试,只有特定的模块,调整它的心脏内容,当一切都是正确的改变其他模块等,并运行完整的/正常的cargo build/test?当然,其中一种方法可以是在引入它们的相应根模块中手动注解/取消注解模块列表,但是是否有其他方法直接从cargo test --someflag -only_this_module或其他东西开始?

详细说明:

假设我有以下模块:a, b, c, d, e, f, g, h,其中f仅依赖于gh,但a, b, c, d, e的其余部分依赖于f(可能还有gh,但这不重要)。gh当然不依赖于上面的任何东西。因此,如果我对f进行更改,我希望只编译f并测试它,而不更改所有这些。由于f只依赖于gh,我假设如果这三个都很好,那么我应该能够测试f,即使a, b ... e被破坏,如果我做正常的cargo build/test,crate将无法编译。这是可能的。

vawmfj5a

vawmfj5a1#

通过将某个模块作为参数提供给测试二进制文件,可以专门针对该模块运行测试。如果在--之后指定参数,Cargo会将参数传递给测试二进制文件,所以应该可以这样做:

cargo test -- module::you::want::to::test

但是,你不能只编译crate的一部分。Rust中的crate是编译单元,就像C中的.c文件一样,你不能编译一半而忽略另一半。

ttvkxqim

ttvkxqim2#

在Rust中,编译的基本单元是一个crate,所以你不能编译crate的子集。如何克服它:

  • 将项目切换到cargo workspace。您将能够build和测试只有特定的subcrate。
  • 自顶向下。在根文件中,注解掉所有“下游”模块(// mod utils)。编译并测试您正在修改的核心模块。一对一,取消注解下游模块,修复它,然后继续。
  • 自下而上。用todo!()宏替换核心模块定义以满足编译器的要求,#[ignore]失败测试,在附近编写新的实现,通过新的实现重新实现旧的定义,迁移其余的代码。
  • 复制核心模块及其测试,如mod utils2。在实验之后,迁移代码库。
  • 遵循座右铭“Loose coupling, tight cohesion”,排除项目中存在stars的可能性,并彻底消除原始问题。
  • 使用rust-script执行特定文件:
rust-script --test utils.rs --dep serde
13z8s7eq

13z8s7eq3#

要做到这一点并不容易。
我将列出3个解决方案的顺序从建议。只有第一个是惯用的。
1.将模块ghf移至单独的板条箱中。由于Rust单独编译crate,因此您可以轻松选择编译哪个crate。
1.制作了用于测试的虚拟目标板条箱,其中不包括您想要测试的模块以外的其他模块。不幸的是,这意味着Cargo.toml必须只包含bin目标:
Cargo.toml

[package]
name = "test_compile_separate"
version = "0.1.0"
edition = "2021"

# This is your main executable, contains all modules.
[[bin]]
path = "src/main.rs"
name = "main"

# This is dummy build which used only to test module g.
[[bin]]
path = "src/limited_g.rs"
name = "limited_g"

# This is dummy build which used only to test module h.
[[bin]]
path = "src/limited_h.rs"
name = "limited_h"

limited_x.rs中,你可以这样写:

mod g;
fn main() {}

如果你运行cargo test --verbose --bin limited_h命令,你会看到,它只构建二进制limited_h,所以它不会编译没有包含在其中的模块。
主要的缺点是,如果你的主crate是一个库,它将无法工作,因为它将在这里与其他目标一起构建。
1.使用crate特性和条件编译来排除你不喜欢的模块。Cargo.toml

[features]
default = ["include_a", "include_b", ...]
include_a = ["include_g", "include_h"]
...
include_g = []
include_h = []

lib.rs中,你可以这样写:

#[cfg(include_a)]
mod a;
#[cfg(include_b)]
mod b;
...
#[cfg(include_g)]
mod g;
#[cfg(include_h)]
mod h;

当你用cargo test --no-default-features --features include_g编译代码时,它只会编译和运行模块g,而不会编译和运行其他模块,因为它们会被条件编译排除在外。

相关问题