]> git.proxmox.com Git - rustc.git/blame - src/doc/book/second-edition/src/ch14-03-cargo-workspaces.md
New upstream version 1.18.0+dfsg1
[rustc.git] / src / doc / book / second-edition / src / ch14-03-cargo-workspaces.md
CommitLineData
cc61c64b
XL
1## Cargo Workspaces
2
3In Chapter 12, we built a package that included both a binary crate and a
4library crate. But what if the library crate continues to get bigger and we
5want to split our package up further into multiple library crates? As packages
6grow, separating out major components can be quite useful. In this situation,
7Cargo has a feature called *workspaces* that can help us manage multiple
8related packages that are developed in tandem.
9
10A *workspace* is a set of packages that will all share the same *Cargo.lock*
11and output directory. Let's make a project using a workspace where the code
12will be trivial so that we can concentrate on the structure of a workspace.
13We'll have a binary that uses two libraries: one that will provide an `add_one`
14method and a second that will provide an `add_two` method. Let's start by
15creating a new crate for the binary:
16
17```text
18$ cargo new --bin adder
19 Created binary (application) `adder` project
20$ cd adder
21```
22
23We need to modify the binary package's *Cargo.toml* to tell Cargo the `adder`
24package is a workspace. Add this at the bottom of the file:
25
26```toml
27[workspace]
28```
29
30Like many Cargo features, workspaces support convention over configuration: we
31don't need to say anything more than this as long as we follow the convention.
32The convention is that any crates that we depend on as sub-directories will be
33part of the workspace. Let's add a path dependency to the `adder` crate by
34changing the `[dependencies]` section of *Cargo.toml* to look like this:
35
36```toml
37[dependencies]
38add-one = { path = "add-one" }
39```
40
41If we add dependencies that don't have a `path` specified, those will be normal
42dependencies that aren't in this workspace.
43
44Next, generate the `add-one` crate within the `adder` directory:
45
46```text
47$ cargo new add-one
48 Created library `add-one` project
49```
50
51Your `adder` directory should now have these directories and files:
52
53```text
54├── Cargo.toml
55├── add-one
56│   ├── Cargo.toml
57│   └── src
58│   └── lib.rs
59└── src
60 └── main.rs
61```
62
63In *add-one/src/lib.rs*, let's add an implementation of an `add_one` function:
64
65<span class="filename">Filename: add-one/src/lib.rs</span>
66
67```rust
68pub fn add_one(x: i32) -> i32 {
69 x + 1
70}
71```
72
73Open up *src/main.rs* for `adder` and add an `extern crate` line to bring the
74new `add-one` library crate into scope, and change the `main` function to use
75the `add_one` function:
76
77```rust,ignore
78extern crate add_one;
79
80fn main() {
81 let num = 10;
82 println!("Hello, world! {} plus one is {}!", num, add_one::add_one(num));
83}
84```
85
86Let's build it!
87
88```text
89$ cargo build
90 Compiling add-one v0.1.0 (file:///projects/adder/add-one)
91 Compiling adder v0.1.0 (file:///projects/adder)
92 Finished debug [unoptimized + debuginfo] target(s) in 0.68 secs
93```
94
95Note that running `cargo build` in the *adder* directory built both that crate
96and the `add-one` crate in *adder/add-one*, but created only one *Cargo.lock*
97and one *target* directory, both in the *adder* directory. See if you can add
98an `add-two` crate in the same way.
99
100Let's now say that we'd like to use the `rand` crate in our `add-one` crate.
101As usual, we'll add it to the `[dependencies]` section in the `Cargo.toml` for
102that crate:
103
104<span class="filename">Filename: add-one/Cargo.toml</span>
105
106```toml
107[dependencies]
108
109rand = "0.3.14"
110```
111
112And if we add `extern crate rand;` to *add-one/src/lib.rs* then run `cargo
113build`, it will succeed:
114
115```text
116$ cargo build
117 Updating registry `https://github.com/rust-lang/crates.io-index`
118 Downloading rand v0.3.14
119 ...snip...
120 Compiling rand v0.3.14
121 Compiling add-one v0.1.0 (file:///projects/adder/add-one)
122 Compiling adder v0.1.0 (file:///projects/adder)
123 Finished debug [unoptimized + debuginfo] target(s) in 10.18 secs
124```
125
126The top level *Cargo.lock* now reflects the fact that `add-one` depends
127on `rand`. However, even though `rand` is used somewhere in the
128workspace, we can't use it in other crates in the workspace unless we add
129`rand` to their *Cargo.toml* as well. If we add `extern crate rand;` to
130*src/main.rs* for the top level `adder` crate, for example, we'll get an error:
131
132```text
133$ cargo build
134 Compiling adder v0.1.0 (file:///projects/adder)
135error[E0463]: can't find crate for `rand`
136 --> src/main.rs:1:1
137 |
1381 | extern crate rand;
139 | ^^^^^^^^^^^^^^^^^^^ can't find crate
140```
141
142To fix this, edit *Cargo.toml* for the top level and indicate that `rand` is a
143dependency for the `adder` crate.
144
145For another enhancement, let's add a test of the `add_one::add_one` function
146within that crate:
147
148<span class="filename">Filename: add-one/src/lib.rs</span>
149
150```rust
151pub fn add_one(x: i32) -> i32 {
152 x + 1
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn it_works() {
161 assert_eq!(3, add_one(2));
162 }
163}
164```
165
166Now run `cargo test` in the top-level *adder* directory:
167
168```text
169$ cargo test
170 Compiling adder v0.1.0 (file:///projects/adder)
171 Finished debug [unoptimized + debuginfo] target(s) in 0.27 secs
172 Running target/debug/adder-f0253159197f7841
173
174running 0 tests
175
176test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
177```
178
179Wait a second, zero tests? We just added one! If we look at the output, we can
180see that `cargo test` in a workspace only runs the tests for the top level
181crate. To run tests for the other crates, we need to use the `-p` argument to
182indicate we want to run tests for a particular package:
183
184```text
185$ cargo test -p add-one
186 Finished debug [unoptimized + debuginfo] target(s) in 0.0 secs
187 Running target/debug/deps/add_one-abcabcabc
188
189running 1 test
190test tests::it_works ... ok
191
192test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
193
194 Doc-tests add-one
195
196running 0 tests
197
198test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured
199```
200
201Similarly, if you choose to publish the workspace to crates.io, each crate in
202the workspace will get published separately.
203
204As your project grows, consider a workspace: smaller components are easier to
205understand individually than one big blob of code. Keeping the crates in a
206workspace can make coordination among them easier if they work together and are
207often changed at the same time.