পাঠ ১৪.৩

Cargo Workspace

Cargo Workspaces

Project বড় হলে এক library crate-কে কয়েকটা ছোট crate-এ ভেঙে আলাদা আলাদা manage করা সুবিধাজনক। Workspace হলো একদল package যারা একই Cargo.lock আর target directory share করে। এতে related crate একসাথে develop করা সহজ হয়।

Workspace বানানো

প্রথমে directory আর root Cargo.toml:

terminalbash
$ mkdir add
$ cd add
Cargo.tomltoml
[workspace]
resolver = "3"

Workspace root-এ [package] section নেই — শুধু [workspace]resolver = "3" latest dependency resolver algorithm use করে।

প্রথম package — binary crate

terminalbash
$ cargo new adder
     Created binary (application) `adder` package
      Adding `adder` as member of workspace at `file:///projects/add`

cargo new automatically members-এ যোগ করে দেয়:

Cargo.tomltoml
[workspace]
resolver = "3"
members = ["adder"]

cargo build-এর পরে directory structure:

├── Cargo.lock
├── Cargo.toml
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

লক্ষ করো — target directory একটাই, top-level-এ। adder-এর ভেতর থেকে build করলেও artifact add/target-এ যাবে, add/adder/target-এ না। এতে workspace-এর crate-গুলো একে অপরের ওপর depend করলে অপ্রয়োজনীয় recompilation এড়ানো যায়।

দ্বিতীয় package — library crate

terminalbash
$ cargo new add_one --lib
     Created library `add_one` package
      Adding `add_one` as member of workspace at `file:///projects/add`
Cargo.tomltoml
[workspace]
resolver = "3"
members = ["adder", "add_one"]
├── Cargo.lock
├── Cargo.toml
├── add_one
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── adder
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── target

Library function

add_one/src/lib.rsrust
pub fn add_one(x: i32) -> i32 {
    x + 1
}

Workspace crate-এর মধ্যে dependency

Cargo automatically assume করে না যে workspace-এর crate-গুলো একে অপরের ওপর depend করবে। তোমাকে explicitly declare করতে হবে — path dependency দিয়ে:

adder/Cargo.tomltoml
[dependencies]
add_one = { path = "../add_one" }
adder/src/main.rsrust
fn main() {
    let num = 10;
    println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
}

Build এবং run

terminalbash
$ cargo build
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.22s

নির্দিষ্ট package run করতে -p flag:

terminalbash
$ cargo run -p adder
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/adder`
Hello, world! 10 plus one is 11!

External dependency

Workspace-এ একটাই Cargo.lock — top level-এ। এতে সব crate একই version-এর external dependency use করে, compatibility নিশ্চিত হয়।

add_one/Cargo.tomltoml
[dependencies]
rand = "0.8.5"
add_one/src/lib.rsrust
use rand;

pub fn add_one(x: i32) -> i32 {
    x + 1
}
terminalbash
$ cargo build
    Updating crates.io index
  Downloaded rand v0.8.5
   --snip--
   Compiling rand v0.8.5
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
warning: unused import: `rand`
 --> add_one/src/lib.rs:1:5
  |
1 | use rand;
  |     ^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: `add_one` (lib) generated 1 warning (run `cargo fix --lib -p add_one` to apply 1 suggestion)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.95s

Dependency automatic share হয় না

এক crate-এ rand add করলে অন্য crate সেটা automatically পাবে না। অন্য crate use করতে চাইলে তারও Cargo.toml-এ declare করতে হবে।

cargo build errortext
$ cargo build
  --snip--
   Compiling adder v0.1.0 (file:///projects/add/adder)
error[E0432]: unresolved import `rand`
 --> adder/src/main.rs:2:5
  |
2 | use rand;
  |     ^^^^ no external crate `rand`

adder/Cargo.toml-এ rand add করে দাও — Cargo Cargo.lock-এ থাকা একই version reuse করবে, আলাদা copy compile করবে না। দুই crate incompatible version চাইলে Cargo দু'টোই resolve করে, কিন্তু version সংখ্যা minimize-এর চেষ্টা করে।

Workspace-এ test

add_one/src/lib.rsrust
pub fn add_one(x: i32) -> i32 {
    x + 1
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn it_works() {
        assert_eq!(3, add_one(2));
    }
}

cargo test top-level-এ run করলে workspace-এর সব crate-এর test চলবে:

terminalbash
$ cargo test
   Compiling add_one v0.1.0 (file:///projects/add/add_one)
   Compiling adder v0.1.0 (file:///projects/add/adder)
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.20s
     Running unittests src/lib.rs (target/debug/deps/add_one-93c49ee75dc46543)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

     Running unittests src/main.rs (target/debug/deps/adder-3a47283c568d2b6a)

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_one

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

নির্দিষ্ট crate-এর test:

terminalbash
$ cargo test -p add_one
    Finished `test` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running unittests src/lib.rs (target/debug/deps/add_one-93c49ee75dc46543)

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

   Doc-tests add_one

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Workspace crate publish

Workspace-এর প্রতিটা crate-কে আলাদা ভাবে crates.io-তে publish করতে হয়:

terminalbash
$ cargo publish -p add_one

এই পাঠ থেকে যা শিখলে

  • Workspace = একদল crate, share করে একটা Cargo.lock এবং target
  • Root Cargo.toml-এ [workspace] + members
  • Workspace crate-গুলোর মধ্যে dependency explicit path দিয়ে declare করতে হয়।
  • External dependency প্রতিটা crate-এ আলাদা declare; Cargo single version reuse করে।
  • cargo build/test/run -p <name> দিয়ে specific crate target করা যায়।
  • Publish আলাদা ভাবে — cargo publish -p <name>