Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Les espaces de travail Cargo

Dans le chapitre 12, nous avons construit un paquet qui incluait un crate binaire et un crate de bibliothèque. Au fur et a mesure que votre projet se developpe, vous pourriez constater que le crate de bibliothèque continue de grossir et que vous souhaitez diviser votre paquet en plusieurs crates de bibliothèque. Cargo offre une fonctionnalité appelée workspaces (espaces de travail) qui peut aider a gérer plusieurs paquets lies developpes en tandem.

Créer un espace de travail

Un workspace est un ensemble de paquets qui partagent le même Cargo.lock et le même repertoire de sortie. Créons un projet utilisant un workspace – nous utiliserons du code trivial pour pouvoir nous concentrer sur la structure du workspace. Il y à plusieurs façons de structurer un workspace, donc nous n’en montrerons qu’une courante. Nous aurons un workspace contenant un binaire et deux bibliothèques. Le binaire, qui fournira la fonctionnalité principale, dependra des deux bibliothèques. Une bibliothèque fournira une fonction add_one et l’autre bibliothèque une fonction add_two. Ces trois crates feront partie du même workspace. Nous commencerons par créer un nouveau repertoire pour le workspace :

$ mkdir add
$ cd add

Ensuite, dans le repertoire add, nous créons le fichier Cargo.toml qui configurera l’ensemble du workspace. Ce fichier n’aura pas de section [package]. A la place, il commencera par une section [workspace] qui nous permettra d’ajouter des membres au workspace. Nous prenons également soin d’utiliser la version la plus récente de l’algorithme de resolution de Cargo dans notre workspace en definissant la valeur resolver a "3" :

Fichier : Cargo.toml

[workspace]
resolver = "3"

Ensuite, nous allons créer le crate binaire adder en exécutant cargo new dans le repertoire add :

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

Exécuter cargo new à l’intérieur d’un workspace ajouté également automatiquement le paquet nouvellement crée à la clé members dans la définition [workspace] du Cargo.toml du workspace, comme ceci : toml {{#include ../listings/ch14-more-about-cargo/output-only-01-adder-crate/add/Cargo.toml}}

[workspace]
resolver = "3"
members = ["adder"]

A ce stade, nous pouvons compiler le workspace en exécutant cargo build. Les fichiers dans votre repertoire add devraient ressembler a ceci :

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

Le workspace à un seul repertoire target au niveau supérieur dans lequel les artefacts compilés seront places ; le paquet adder n’a pas son propre repertoire target. Même si nous exécutons cargo build depuis l’intérieur du repertoire adder, les artefacts compilés finiraient quand même dans add/target plutot que dans add/adder/target. Cargo structure le repertoire target dans un workspace de cette façon parce que les crates dans un workspace sont destines a dependre les uns des autres. Si chaque crate avait son propre repertoire target, chaque crate devrait recompiler chacun des autres crates du workspace pour placer les artefacts dans son propre repertoire target. En partageant un seul repertoire target, les crates peuvent éviter les recompilations inutiles.

Créer le deuxième paquet dans le workspace

Ensuite, créons un autre paquet membre dans le workspace et appelons-le add_one. Generez un nouveau crate de bibliothèque nomme add_one :

$ cargo new add_one --lib
     Created library `add_one` package
      Adding `add_one` as member of workspace at `file:///projects/add`

Le Cargo.toml de niveau supérieur inclura maintenant le chemin add_one dans la liste members :

Fichier : Cargo.toml

[workspace]
resolver = "3"
members = ["adder", "add_one"]

Votre repertoire add devrait maintenant avoir ces repertoires et fichiers :

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

Dans le fichier add_one/src/lib.rs, ajoutons une fonction add_one :

Fichier : add_one/src/lib.rs rust,noplayground {{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs}}

pub fn add_one(x: i32) -> i32 {
    x + 1
}

Maintenant, nous pouvons faire dependre le paquet adder avec notre binaire du paquet add_one qui contient notre bibliothèque. D’abord, nous devrons ajouter une dépendance de chemin vers add_one dans adder/Cargo.toml.

Fichier : adder/Cargo.toml toml {{#include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml:6:7}}

[dependencies]
add_one = { path = "../add_one" }

Cargo ne suppose pas que les crates dans un workspace dependront les uns des autres, nous devons donc être explicites sur les relations de dépendance.

Ensuite, utilisons la fonction add_one (du crate add_one) dans le crate adder. Ouvrez le fichier adder/src/main.rs et modifiez la fonction main pour appeler la fonction add_one, comme dans l’encart 14-7.

Filename: adder/src/main.rs
fn main() {
    let num = 10;
    println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
}
Listing 14-7: Using the add_one library crate from the adder crate

Compilons le workspace en exécutant cargo build dans le repertoire add de niveau supérieur !

$ 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

Pour exécuter le crate binaire depuis le repertoire add, nous pouvons spécifier quel paquet dans le workspace nous voulons exécuter en utilisant l’argument -p et le nom du paquet avec cargo run :

$ 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!

Cela exécute le code dans adder/src/main.rs, qui depend du crate add_one.

Dependre d’un paquet externe

Remarquez que le workspace n’a qu’un seul fichier Cargo.lock au niveau supérieur, plutot qu’un Cargo.lock dans le repertoire de chaque crate. Cela garantit que tous les crates utilisent la même version de toutes les dépendances. Si nous ajoutons le paquet rand aux fichiers adder/Cargo.toml et add_one/Cargo.toml, Cargo resoudra les deux vers une seule version de rand et l’enregistrera dans l’unique Cargo.lock. Faire en sorte que tous les crates du workspace utilisent les mêmes dépendances signifie que les crates seront toujours compatibles entre eux. Ajoutons le crate rand à la section [dependencies] dans le fichier add_one/Cargo.toml afin de pouvoir utiliser le crate rand dans le crate add_one :

Fichier : add_one/Cargo.toml toml {{#include ../listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add_one/Cargo.toml:6:7}}

[dependencies]
rand = "0.8.5"

Nous pouvons maintenant ajouter use rand; au fichier add_one/src/lib.rs, et compiler l’ensemble du workspace en exécutant cargo build dans le repertoire add integrera et compilera le crate rand. Nous obtiendrons un avertissement car nous ne faisons pas référence au rand que nous avons amene dans la portée :

$ 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

Le Cargo.lock de niveau supérieur contient maintenant des informations sur la dépendance d’add_one envers rand. Cependant, même si rand est utilise quelque part dans le workspace, nous ne pouvons pas l’utiliser dans d’autres crates du workspace a moins d’ajouter également rand à leurs fichiers Cargo.toml. Par exemple, si nous ajoutons use rand; au fichier adder/src/main.rs pour le paquet adder, nous obtiendrons une erreur :

$ 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`

Pour corriger cela, editez le fichier Cargo.toml du paquet adder et indiquez que rand est également une dépendance pour celui-ci. Compiler le paquet adder ajoutera rand à la liste des dépendances pour adder dans Cargo.lock, mais aucune copie supplementaire de rand ne sera téléchargée. Cargo s’assurera que chaque crate dans chaque paquet du workspace utilisant le paquet rand utilisera la même version tant qu’ils specifient des versions compatibles de rand, nous faisant economiser de l’espace et garantissant que les crates du workspace seront compatibles entre eux.

Si les crates du workspace specifient des versions incompatibles de la même dépendance, Cargo resoudra chacune d’entre elles mais essaiera quand même de résoudre le moins de versions possible.

Ajouter un test à un workspace

Pour une autre amélioration, ajoutons un test de la fonction add_one::add_one dans le crate add_one :

Fichier : add_one/src/lib.rs rust,noplayground {{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add_one/src/lib.rs}}

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));
    }
}

Maintenant, exécutez cargo test dans le repertoire add de niveau supérieur. Exécuter cargo test dans un workspace structure comme celui-ci exécutera les tests pour tous les crates du workspace :

$ 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

La première section de la sortie montre que le test it_works dans le crate add_one a réussi. La section suivante montre que zero test a été trouve dans le crate adder, puis la derniere section montre que zero test de documentation a été trouve dans le crate add_one.

Nous pouvons également exécuter les tests pour un crate particulier dans un workspace depuis le repertoire de niveau supérieur en utilisant le drapeau -p et en specifiant le nom du crate que nous voulons tester :

$ 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

Cette sortie montre que cargo test n’a exécute que les tests du crate add_one et n’a pas exécute les tests du crate adder.

Si vous publiez les crates du workspace sur crates.io, chaque crate du workspace devra être publiée séparément. Comme pour cargo test, nous pouvons publier une crate particulière dans notre workspace en utilisant le drapeau -p et en spécifiant le nom de la crate que nous voulons publier.

Pour vous exercer davantage, ajoutez un crate add_two à ce workspace de la même maniere que le crate add_one !

Au fur et a mesure que votre projet grandit, envisagez d’utiliser un workspace : il vous permet de travailler avec des composants plus petits et plus faciles a comprendre plutot qu’un seul gros bloc de code. De plus, garder les crates dans un workspace peut faciliter la coordination entre les crates s’ils sont souvent modifiés en même temps.