]> git.proxmox.com Git - rustc.git/blame - src/doc/book/src/ch14-03-cargo-workspaces.md
New upstream version 1.52.0~beta.3+dfsg1
[rustc.git] / src / doc / book / src / ch14-03-cargo-workspaces.md
CommitLineData
13cf67c4
XL
1## Cargo Workspaces
2
3In Chapter 12, we built a package that included a binary crate and a library
4crate. As your project develops, you might find that the library crate
5continues to get bigger and you want to split up your package further into
6multiple library crates. In this situation, Cargo offers a feature called
7*workspaces* that can help manage multiple related packages that are developed
8in tandem.
9
10### Creating a Workspace
11
12A *workspace* is a set of packages that share the same *Cargo.lock* and output
13directory. Let’s make a project using a workspace—we’ll use trivial code so we
14can concentrate on the structure of the workspace. There are multiple ways to
15structure a workspace; we’re going to show one common way. We’ll have a
16workspace containing a binary and two libraries. The binary, which will provide
17the main functionality, will depend on the two libraries. One library will
18provide an `add_one` function, and a second library an `add_two` function.
19These three crates will be part of the same workspace. We’ll start by creating
20a new directory for the workspace:
21
f035d41b 22```console
13cf67c4
XL
23$ mkdir add
24$ cd add
25```
26
27Next, in the *add* directory, we create the *Cargo.toml* file that will
28configure the entire workspace. This file won’t have a `[package]` section or
29the metadata we’ve seen in other *Cargo.toml* files. Instead, it will start
30with a `[workspace]` section that will allow us to add members to the workspace
f035d41b
XL
31by specifying the path to the package with our binary crate; in this case,
32that path is *adder*:
13cf67c4
XL
33
34<span class="filename">Filename: Cargo.toml</span>
35
36```toml
74b04a01 37{{#include ../listings/ch14-more-about-cargo/no-listing-01-workspace-with-adder-crate/add/Cargo.toml}}
13cf67c4
XL
38```
39
40Next, we’ll create the `adder` binary crate by running `cargo new` within the
41*add* directory:
42
74b04a01
XL
43<!-- manual-regeneration
44cd listings/ch14-more-about-cargo/output-only-01-adder-crate/add
45rm -rf adder
46cargo new adder
47copy output below
48-->
49
f035d41b 50```console
13cf67c4 51$ cargo new adder
74b04a01 52 Created binary (application) `adder` package
13cf67c4
XL
53```
54
55At this point, we can build the workspace by running `cargo build`. The files
56in your *add* directory should look like this:
57
58```text
59├── Cargo.lock
60├── Cargo.toml
61├── adder
62│ ├── Cargo.toml
63│ └── src
64│ └── main.rs
65└── target
66```
67
68The workspace has one *target* directory at the top level for the compiled
f035d41b 69artifacts to be placed into; the `adder` package doesn’t have its own *target*
13cf67c4
XL
70directory. Even if we were to run `cargo build` from inside the *adder*
71directory, the compiled artifacts would still end up in *add/target* rather
72than *add/adder/target*. Cargo structures the *target* directory in a workspace
73like this because the crates in a workspace are meant to depend on each other.
74If each crate had its own *target* directory, each crate would have to
75recompile each of the other crates in the workspace to have the artifacts in
76its own *target* directory. By sharing one *target* directory, the crates can
77avoid unnecessary rebuilding.
78
f035d41b 79### Creating the Second Package in the Workspace
13cf67c4 80
f035d41b 81Next, let’s create another member package in the workspace and call it `add-one`.
13cf67c4
XL
82Change the top-level *Cargo.toml* to specify the *add-one* path in the
83`members` list:
84
85<span class="filename">Filename: Cargo.toml</span>
86
87```toml
74b04a01 88{{#include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/Cargo.toml}}
13cf67c4
XL
89```
90
91Then generate a new library crate named `add-one`:
92
74b04a01
XL
93<!-- manual-regeneration
94cd listings/ch14-more-about-cargo/output-only-02-add-one/add
95rm -rf add-one
96cargo new add-one --lib
97copy output below
98-->
99
f035d41b 100```console
13cf67c4 101$ cargo new add-one --lib
74b04a01 102 Created library `add-one` package
13cf67c4
XL
103```
104
105Your *add* directory should now have these directories and files:
106
107```text
108├── Cargo.lock
109├── Cargo.toml
110├── add-one
111│ ├── Cargo.toml
112│ └── src
113│ └── lib.rs
114├── adder
115│ ├── Cargo.toml
116│ └── src
117│ └── main.rs
118└── target
119```
120
121In the *add-one/src/lib.rs* file, let’s add an `add_one` function:
122
123<span class="filename">Filename: add-one/src/lib.rs</span>
124
5869c6ff 125```rust,noplayground
74b04a01 126{{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/add-one/src/lib.rs}}
13cf67c4
XL
127```
128
f035d41b
XL
129Now that we have another package in the workspace, we can have the `adder`
130package with our binary depend on the `add-one` package, that has our
131library. First, we’ll need to add a path dependency on `add-one` to
132*adder/Cargo.toml*.
13cf67c4
XL
133
134<span class="filename">Filename: adder/Cargo.toml</span>
135
136```toml
74b04a01 137{{#include ../listings/ch14-more-about-cargo/no-listing-02-workspace-with-two-crates/add/adder/Cargo.toml:7:9}}
13cf67c4
XL
138```
139
140Cargo doesn’t assume that crates in a workspace will depend on each other, so
141we need to be explicit about the dependency relationships between the crates.
142
143Next, let’s use the `add_one` function from the `add-one` crate in the `adder`
9fa01778 144crate. Open the *adder/src/main.rs* file and add a `use` line at the top to
13cf67c4 145bring the new `add-one` library crate into scope. Then change the `main`
9fa01778 146function to call the `add_one` function, as in Listing 14-7.
13cf67c4
XL
147
148<span class="filename">Filename: adder/src/main.rs</span>
149
150```rust,ignore
74b04a01 151{{#rustdoc_include ../listings/ch14-more-about-cargo/listing-14-07/add/adder/src/main.rs}}
13cf67c4
XL
152```
153
fc512014 154<span class="caption">Listing 14-7: Using the `add-one` library crate from the
f035d41b 155 `adder` crate</span>
13cf67c4
XL
156
157Let’s build the workspace by running `cargo build` in the top-level *add*
158directory!
159
74b04a01
XL
160<!-- manual-regeneration
161cd listings/ch14-more-about-cargo/listing-14-07/add
162cargo build
163copy output below; the output updating script doesn't handle subdirectories in paths properly
164-->
165
f035d41b 166```console
13cf67c4
XL
167$ cargo build
168 Compiling add-one v0.1.0 (file:///projects/add/add-one)
169 Compiling adder v0.1.0 (file:///projects/add/adder)
74b04a01 170 Finished dev [unoptimized + debuginfo] target(s) in 0.68s
13cf67c4
XL
171```
172
f035d41b
XL
173To run the binary crate from the *add* directory, we can specify which
174package in the workspace we want to run by using the `-p` argument and the
13cf67c4
XL
175package name with `cargo run`:
176
74b04a01
XL
177<!-- manual-regeneration
178cd listings/ch14-more-about-cargo/listing-14-07/add
179cargo run -p adder
180copy output below; the output updating script doesn't handle subdirectories in paths properly
181-->
182
f035d41b 183```console
13cf67c4 184$ cargo run -p adder
74b04a01 185 Finished dev [unoptimized + debuginfo] target(s) in 0.0s
13cf67c4
XL
186 Running `target/debug/adder`
187Hello, world! 10 plus one is 11!
188```
189
190This runs the code in *adder/src/main.rs*, which depends on the `add-one` crate.
191
f035d41b 192#### Depending on an External Package in a Workspace
13cf67c4
XL
193
194Notice that the workspace has only one *Cargo.lock* file at the top level of
195the workspace rather than having a *Cargo.lock* in each crate’s directory. This
196ensures that all crates are using the same version of all dependencies. If we
f035d41b 197add the `rand` package to the *adder/Cargo.toml* and *add-one/Cargo.toml*
13cf67c4
XL
198files, Cargo will resolve both of those to one version of `rand` and record
199that in the one *Cargo.lock*. Making all crates in the workspace use the same
200dependencies means the crates in the workspace will always be compatible with
201each other. Let’s add the `rand` crate to the `[dependencies]` section in the
202*add-one/Cargo.toml* file to be able to use the `rand` crate in the `add-one`
203crate:
204
e74abb32
XL
205<!-- When updating the version of `rand` used, also update the version of
206`rand` used in these files so they all match:
207* ch02-00-guessing-game-tutorial.md
208* ch07-04-bringing-paths-into-scope-with-the-use-keyword.md
209-->
210
13cf67c4
XL
211<span class="filename">Filename: add-one/Cargo.toml</span>
212
213```toml
74b04a01 214{{#include ../listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add/add-one/Cargo.toml:7:8}}
13cf67c4
XL
215```
216
217We can now add `use rand;` to the *add-one/src/lib.rs* file, and building the
218whole workspace by running `cargo build` in the *add* directory will bring in
fc512014
XL
219and compile the `rand` crate. We will get one warning because we aren’t
220referring to the `rand` we brought into scope:
13cf67c4 221
74b04a01
XL
222<!-- manual-regeneration
223cd listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add
224cargo build
225copy output below; the output updating script doesn't handle subdirectories in paths properly
226-->
227
f035d41b 228```console
13cf67c4 229$ cargo build
e74abb32 230 Updating crates.io index
6a06907d 231 Downloaded rand v0.8.3
13cf67c4 232 --snip--
6a06907d 233 Compiling rand v0.8.3
13cf67c4 234 Compiling add-one v0.1.0 (file:///projects/add/add-one)
fc512014
XL
235warning: unused import: `rand`
236 --> add-one/src/lib.rs:1:5
237 |
2381 | use rand;
239 | ^^^^
240 |
241 = note: `#[warn(unused_imports)]` on by default
242
243warning: 1 warning emitted
244
13cf67c4 245 Compiling adder v0.1.0 (file:///projects/add/adder)
74b04a01 246 Finished dev [unoptimized + debuginfo] target(s) in 10.18s
13cf67c4
XL
247```
248
249The top-level *Cargo.lock* now contains information about the dependency of
250`add-one` on `rand`. However, even though `rand` is used somewhere in the
251workspace, we can’t use it in other crates in the workspace unless we add
532ac7d7 252`rand` to their *Cargo.toml* files as well. For example, if we add `use rand;`
f035d41b 253to the *adder/src/main.rs* file for the `adder` package, we’ll get an error:
13cf67c4 254
74b04a01
XL
255<!-- manual-regeneration
256cd listings/ch14-more-about-cargo/output-only-03-use-rand/add
257cargo build
258copy output below; the output updating script doesn't handle subdirectories in paths properly
259-->
260
f035d41b 261```console
13cf67c4 262$ cargo build
74b04a01 263 --snip--
13cf67c4 264 Compiling adder v0.1.0 (file:///projects/add/adder)
74b04a01
XL
265error[E0432]: unresolved import `rand`
266 --> adder/src/main.rs:2:5
13cf67c4 267 |
74b04a01 2682 | use rand;
fc512014 269 | ^^^^ no external crate `rand`
13cf67c4
XL
270```
271
f035d41b
XL
272To fix this, edit the *Cargo.toml* file for the `adder` package and indicate
273that `rand` is a dependency for it as well. Building the `adder` package will
13cf67c4
XL
274add `rand` to the list of dependencies for `adder` in *Cargo.lock*, but no
275additional copies of `rand` will be downloaded. Cargo has ensured that every
f035d41b
XL
276crate in every package in the workspace using the `rand` package will be
277using the same version. Using the same version of `rand` across the workspace
278saves space because we won’t have multiple copies and ensures that the crates
279in the workspace will be compatible with each other.
13cf67c4
XL
280
281#### Adding a Test to a Workspace
282
283For another enhancement, let’s add a test of the `add_one::add_one` function
284within the `add_one` crate:
285
286<span class="filename">Filename: add-one/src/lib.rs</span>
287
5869c6ff 288```rust,noplayground
74b04a01 289{{#rustdoc_include ../listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add/add-one/src/lib.rs}}
13cf67c4
XL
290```
291
292Now run `cargo test` in the top-level *add* directory:
293
74b04a01
XL
294<!-- manual-regeneration
295cd listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add
296cargo test
297copy output below; the output updating script doesn't handle subdirectories in paths properly
298-->
299
f035d41b 300```console
13cf67c4
XL
301$ cargo test
302 Compiling add-one v0.1.0 (file:///projects/add/add-one)
303 Compiling adder v0.1.0 (file:///projects/add/adder)
74b04a01 304 Finished test [unoptimized + debuginfo] target(s) in 0.27s
13cf67c4
XL
305 Running target/debug/deps/add_one-f0253159197f7841
306
307running 1 test
308test tests::it_works ... ok
309
6a06907d 310test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
13cf67c4 311
74b04a01 312 Running target/debug/deps/adder-49979ff40686fa8e
13cf67c4
XL
313
314running 0 tests
315
6a06907d 316test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
13cf67c4
XL
317
318 Doc-tests add-one
319
320running 0 tests
321
6a06907d 322test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
13cf67c4
XL
323```
324
325The first section of the output shows that the `it_works` test in the `add-one`
326crate passed. The next section shows that zero tests were found in the `adder`
327crate, and then the last section shows zero documentation tests were found in
328the `add-one` crate. Running `cargo test` in a workspace structured like this
329one will run the tests for all the crates in the workspace.
330
331We can also run tests for one particular crate in a workspace from the
332top-level directory by using the `-p` flag and specifying the name of the crate
333we want to test:
334
74b04a01
XL
335<!-- manual-regeneration
336cd listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add
337cargo test -p add-one
338copy output below; the output updating script doesn't handle subdirectories in paths properly
339-->
340
f035d41b 341```console
13cf67c4 342$ cargo test -p add-one
74b04a01 343 Finished test [unoptimized + debuginfo] target(s) in 0.00s
13cf67c4
XL
344 Running target/debug/deps/add_one-b3235fea9a156f74
345
346running 1 test
347test tests::it_works ... ok
348
6a06907d 349test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
13cf67c4
XL
350
351 Doc-tests add-one
352
353running 0 tests
354
6a06907d 355test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
13cf67c4
XL
356```
357
358This output shows `cargo test` only ran the tests for the `add-one` crate and
359didn’t run the `adder` crate tests.
360
dc9dc135
XL
361If you publish the crates in the workspace to [crates.io](https://crates.io/),
362each crate in the workspace will need to be published separately. The `cargo
363publish` command does not have an `--all` flag or a `-p` flag, so you must
364change to each crate’s directory and run `cargo publish` on each crate in the
365workspace to publish the crates.
13cf67c4
XL
366
367For additional practice, add an `add-two` crate to this workspace in a similar
368way as the `add-one` crate!
369
370As your project grows, consider using a workspace: it’s easier to understand
371smaller, individual components than one big blob of code. Furthermore, keeping
372the crates in a workspace can make coordination between them easier if they are
373often changed at the same time.