Les espaces de travail de cargo
Dans le chapitre 12, nous avons construit un paquet qui comprenait une crate binaire et une crate de bibliothèque. Au fur et à mesure que votre projet se développe, vous pourrez constater que la crate de bibliothèque continue de s'agrandir et vous voudriez alors peut-être diviser votre paquet en plusieurs crates de bibliothèque. Pour cette situation, cargo a une fonctionnalité qui s'appelle les espaces de travail qui peuvent aider à gérer plusieurs paquets liés qui sont développés en tandem.
Créer un espace de travail
Un espace de travail est un jeu de paquets qui partagent tous le même
Cargo.lock et le même dossier de sortie. Créons donc un projet en utilisant un
espace de travail — nous allons utiliser du code trivial afin de nous concentrer
sur la structure de l'espace de travail. Il existe plusieurs façons de
structurer un espace de travail ; nous allons vous montrer une manière commune
d'organisation. Nous allons avoir un espace de travail contenant un binaire et
deux bibliothèques. Le binaire, qui devrait fournir les fonctionnalités
principales, va dépendre des deux bibliothèques. Une bibliothèque va fournir une
fonction ajouter_un
, et la seconde bibliothèque, une fonction ajouter_deux
.
Ces trois crates feront partie du même espace de travail. Nous allons commencer
par créer un nouveau dossier pour cet espace de travail :
$ mkdir ajout
$ cd ajout
Ensuite, dans le dossier ajout, nous créons le fichier Cargo.toml qui va
configurer l'intégralité de l'espace de travail. Ce fichier n'aura pas de
section [package]
ou les métadonnées que nous avons vues dans les autres
fichiers Cargo.toml. A la place, il commencera par une section [workspace]
qui va nous permettre d'ajouter des membres à l'espace de travail en
renseignant le chemin vers le paquet qui contient notre crate binaire ; dans ce
cas, ce chemin est additioneur :
Fichier : Cargo.toml
[workspace]
members = [
"additioneur",
]
Ensuite, nous allons créer la crate binaire additioneur
en lançant cargo new
dans le dossier ajout :
$ cargo new additioneur
Created binary (application) `additioneur` package
A partir de ce moment, nous pouvons compiler l'espace de travail en lançant
cargo build
. Les fichiers dans votre dossier ajout devraient ressembler à
ceci :
├── Cargo.lock
├── Cargo.toml
├── additioneur
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
L'espace de travail a un dossier target au niveau le plus haut pour y placer
les artefacts compilés ; le paquet additioneur
n'a pas son propre dossier
target. Même si nous lancions cargo build
à l'intérieur du dossier
additioneur, les artefacts compilés finirons toujours dans ajout/target
plutôt que dans ajout/additioneur/target. Cargo organise ainsi le dossier
target car les crates d'un espace de travail sont censés dépendre l'une de
l'autre. Si chaque crate avait son propre dossier target, chaque crate
devrait recompiler chacune des autres crates présentes dans l'espace de
travail pour avoir les artefacts dans son propre dossier target. En
partageant un seul dossier target, les crates peuvent éviter des
re-compilations inutiles.
Créer le second paquet dans l'espace de travail
Ensuite, créons un autre paquet, membre de l'espace de travail et appelons-le
ajouter_un
. Changeons le Cargo.toml du niveau le plus haut pour renseigner
le chemin vers ajouter_un dans la liste members
:
Fichier : Cargo.toml
[workspace]
members = [
"additioneur",
"ajouter_un",
]
Ensuite, générons une nouvelle crate de bibliothèque ajouter_un
:
$ cargo new ajouter_un --lib
Created library `ajouter_un` package
Votre dossier ajout devrait maintenant avoir ces dossiers et fichiers :
├── Cargo.lock
├── Cargo.toml
├── ajouter_un
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── additioneur
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── target
Dans le fichier ajouter_un/src/lib.rs, ajoutons une fonction ajouter_un
:
Fichier : ajouter_un/src/lib.rs
pub fn ajouter_un(x: i32) -> i32 {
x + 1
}
Maintenant que nous avons un autre paquet dans l'espace de travail, nous pouvons
faire en sorte que le paquet additioneur
qui contient notre binaire dépende du
paquet ajouter_un
, qui contient notre bibliothèque. D'abord, nous devons
ajouter un chemin de dépendance à ajouter_un
dans additioneur/Cargo.toml.
Fichier : additioneur/Cargo.toml
[dependencies]
ajouter_un = { path = "../ajouter_un" }
Cargo ne fait pas la supposition que les crates d'un espace de travail dépendent l'une de l'autre, donc vous devez être explicites sur les relations de dépendance entre les crates.
Ensuite, utilisons la fonction ajouter_un
de la crate ajouter_un
dans la
crate additioneur
. Ouvrez le fichier additioneur/src/main.rs et ajoutez une
ligne use
tout en haut pour importer la bibliothèque ajouter_un
dans la
portée. Changez ensuite la fonction main
pour appeler la fonction
ajouter_un
, comme dans l'encart 14-7.
Fichier : additioneur/src/main.rs
use ajouter_un;
fn main() {
let nombre = 10;
println!(
"Hello, world ! {} plus un vaut {} !",
nombre,
ajouter_un::ajouter_un(nombre)
);
}
Compilons l'espace de travail en lançant cargo build
dans le niveau le plus
haut du dossier ajout !
$ cargo build
Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
Compiling additioneur v0.1.0 (file:///projects/ajout/additioneur)
Finished dev [unoptimized + debuginfo] target(s) in 0.68s
Pour lancer la crate binaire à partir du dossier ajout, nous pouvons
préciser quel paquet nous souhaitons exécuter dans l'espace de travail en
utilisant l'argument -p
suivi du nom du paquet avec cargo run
:
$ cargo run -p additioneur
Finished dev [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/additioneur`
Hello, world ! 10 plus un vaut 11 !
Cela exécute le code de additioneur/src/main.rs, qui dépend de la crate
ajouter_un
.
Dépendre d'un paquet externe dans un espace de travail
Notez que l'espace de travail a un seul fichier Cargo.lock dans le niveau le
plus haut de l'espace de travail plutôt que d'avoir un Cargo.lock dans chaque
dossier de chaque crate. Cela garantit que toutes les crates utilisent la même
version de toutes les dépendances. Si nous ajoutons le paquet rand
aux
fichiers additioneur/Cargo.toml et ajouter_un/Cargo.toml, cargo va réunir
les deux en une seule version de rand
et enregistrer cela dans un seul
Cargo.lock. Faire en sorte que toutes les crates de l'espace de travail
utilisent la même dépendance signifie que les crates dans l'espace de travail
seront toujours compatibles l'une avec l'autre. Ajoutons la crate rand
à la
section [dependencies]
du fichier ajouter_un/Cargo.toml pour pouvoir
utiliser la crate rand
dans la crate ajouter_un
:
Fichier : ajouter_un/Cargo.toml
[dependencies]
rand = "0.8.3"
Nous pouvons maintenant ajouter use rand;
au fichier ajouter_un/src/lib.rs
et compiler l'ensemble de l'espace de travail en lançant cargo build
dans le
dossier ajout, ce qui va importer et compiler la crate rand
. Nous devrions
avoir un avertissement car nous n'avons pas utilisé le rand
que nous avons
introduit dans la portée :
$ cargo build
Updating crates.io index
Downloaded rand v0.8.3
-- partie masquée ici --
Compiling rand v0.8.3
Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
warning: unused import: `rand`
--> ajouter_un/src/lib.rs:1:5
|
1 | use rand;
| ^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: 1 warning emitted
Compiling additioneur v0.1.0 (file:///projects/ajout/additioneur)
Finished dev [unoptimized + debuginfo] target(s) in 10.18s
Le Cargo.lock du niveau le plus haut contient maintenant les informations
de dépendance à rand
pour ajouter_un
. Cependant, même si rand
est
utilisé quelque part dans l'espace de travail, nous ne pouvons pas l'utiliser
dans d'autres crates de l'espace de travail tant que nous n'ajoutons pas
rand
dans leurs fichiers Cargo.toml. Par exemple, si nous ajoutons
use rand;
dans le fichier additioneur/src/main.rs pour le paquet
additioneur
, nous allons avoir une erreur :
$ cargo build
-- partie masquée ici --
Compiling additioneur v0.1.0 (file:///projects/ajout/additioneur)
error[E0432]: unresolved import `rand`
--> additioneur/src/main.rs:2:5
|
2 | use rand;
| ^^^^ no external crate `rand`
Pour corriger cela, modifiez le fichier Cargo.toml pour le paquet
additioneur
et indiquez que rand
est une dépendance de cette crate aussi.
La compilation du paquet additioneur
va rajouter rand
à la liste des
dépendances pour additioneur
dans Cargo.lock, mais aucune copie
supplémentaire de rand
ne sera téléchargée. Cargo s'est assuré que toutes les
crates de chaque paquet de l'espace de travail qui utilise le paquet rand
seraient de la même version. Utiliser la même version de rand
dans les
espaces de travail économise de l'espace car nous n'avons pas à multiplier les
copies, ni à nous assurer que les crates dans l'espace de travail sont
compatibles les unes avec les autres.
Ajouter un test à l'espace de travail
Afin de procéder à une autre amélioration, ajoutons un test de la fonction
ajouter_un::ajouter_un
dans la crate ajouter_un
:
Fichier : add_one/src/lib.rs
pub fn ajouter_un(x: i32) -> i32 {
x + 1
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn cela_fonctionne() {
assert_eq!(3, ajouter_un(2));
}
}
Lancez maintenant cargo test
dans le niveau le plus haut du
dossier ajout :
$ cargo test
Compiling ajouter_un v0.1.0 (file:///projects/ajout/ajouter_un)
Compiling additioneur v0.1.0 (file:///projects/ajout/additioneur)
Finished test [unoptimized + debuginfo] target(s) in 0.27s
Running target/debug/deps/ajouter_un-f0253159197f7841
running 1 test
test tests::cela_fonctionne ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Running target/debug/deps/additioneur-49979ff40686fa8e
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests ajouter_un
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 indique que le test cela_fonctionne
de la
crate ajouter_un
a réussi. La section suivante indique qu'aucun test n'a été
trouvé dans la crate additioneur
, puis la dernière section indique elle
aussi qu'aucun test de documentation n'a été trouvé dans la crate ajouter_un
.
Lancer cargo test
dans un espace de travail structuré comme celui-ci va
exécuter les tests pour toutes les crates de cet espace de travail.
Nous pouvons aussi lancer des tests pour une crate en particulier dans un
espace de travail à partir du dossier du plus haut niveau en utilisant le
drapeau -p
et en renseignant le nom de la crate que nous voulons tester :
$ cargo test -p ajouter_un
Finished test [unoptimized + debuginfo] target(s) in 0.00s
Running target/debug/deps/ajouter_un-b3235fea9a156f74
running 1 test
test tests::cela_fonctionne ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
Doc-tests ajouter_un
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
a lancé les tests uniquement pour la
crate ajouter_un
et n'a pas lancé les tests de la crate additioneur
.
Si vous publiez les crates présentes dans l'espace de travail sur
crates.io, chaque crate de l'espace de travail va avoir
besoin d'être publiée de manière séparée. La commande cargo publish
n'a pas
de drapeau --all
ou -p
, donc vous devrez vous rendre dans chaque dossier de
chaque crate et lancer cargo publish
sur chaque crate présente dans l'espace
de travail pour publier les crates.
En guise d'entrainement supplémentaire, ajoutez une crate ajouter_deux
dans
cet espace de travail de la même manière que nous l'avons fait pour la crate
ajouter_un
!
Au fur et à mesure que votre projet se développe, pensez à utiliser un espace de travail : il est plus facile de comprendre des composants individuels, plus petits, plutôt qu'un gros tas de code. De plus, garder les crates dans un espace de travail peut améliorer la coordination entre elles si elles sont souvent modifiées ensemble.