feat: added cleanscript
This commit is contained in:
330
ch14/ch14-03-cargo-workspaces.html
Normal file
330
ch14/ch14-03-cargo-workspaces.html
Normal file
@@ -0,0 +1,330 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Cargo Workspaces</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2 id="cargo-workspaces"><a class="header" href="#cargo-workspaces">Cargo Workspaces</a></h2>
|
||||
<p>In Chapter 12, we built a package that included a binary crate and a library
|
||||
crate. As your project develops, you might find that the library crate
|
||||
continues to get bigger and you want to split your package further into
|
||||
multiple library crates. Cargo offers a feature called <em>workspaces</em> that can
|
||||
help manage multiple related packages that are developed in tandem.</p>
|
||||
<h3 id="creating-a-workspace"><a class="header" href="#creating-a-workspace">Creating a Workspace</a></h3>
|
||||
<p>A <em>workspace</em> is a set of packages that share the same <em>Cargo.lock</em> and output
|
||||
directory. Let’s make a project using a workspace—we’ll use trivial code so
|
||||
that we can concentrate on the structure of the workspace. There are multiple
|
||||
ways to structure a workspace, so we’ll just show one common way. We’ll have a
|
||||
workspace containing a binary and two libraries. The binary, which will provide
|
||||
the main functionality, will depend on the two libraries. One library will
|
||||
provide an <code>add_one</code> function and the other library an <code>add_two</code> function.
|
||||
These three crates will be part of the same workspace. We’ll start by creating
|
||||
a new directory for the workspace:</p>
|
||||
<pre><code class="language-console">$ mkdir add
|
||||
$ cd add
|
||||
</code></pre>
|
||||
<p>Next, in the <em>add</em> directory, we create the <em>Cargo.toml</em> file that will
|
||||
configure the entire workspace. This file won’t have a <code>[package]</code> section.
|
||||
Instead, it will start with a <code>[workspace]</code> section that will allow us to add
|
||||
members to the workspace. We also make a point to use the latest and greatest
|
||||
version of Cargo’s resolver algorithm in our workspace by setting the
|
||||
<code>resolver</code> value to <code>"3"</code>:</p>
|
||||
<p><span class="filename">Filename: Cargo.toml</span></p>
|
||||
<pre><code class="language-toml">[workspace]
|
||||
resolver = "3"
|
||||
</code></pre>
|
||||
<p>Next, we’ll create the <code>adder</code> binary crate by running <code>cargo new</code> within the
|
||||
<em>add</em> directory:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/output-only-01-adder-crate/add
|
||||
remove `members = ["adder"]` from Cargo.toml
|
||||
rm -rf adder
|
||||
cargo new adder
|
||||
copy output below
|
||||
-->
|
||||
<pre><code class="language-console">$ cargo new adder
|
||||
Created binary (application) `adder` package
|
||||
Adding `adder` as member of workspace at `file:///projects/add`
|
||||
</code></pre>
|
||||
<p>Running <code>cargo new</code> inside a workspace also automatically adds the newly created
|
||||
package to the <code>members</code> key in the <code>[workspace]</code> definition in the workspace
|
||||
<em>Cargo.toml</em>, like this:</p>
|
||||
<pre><code class="language-toml">[workspace]
|
||||
resolver = "3"
|
||||
members = ["adder"]
|
||||
</code></pre>
|
||||
<p>At this point, we can build the workspace by running <code>cargo build</code>. The files
|
||||
in your <em>add</em> directory should look like this:</p>
|
||||
<pre><code class="language-text">├── Cargo.lock
|
||||
├── Cargo.toml
|
||||
├── adder
|
||||
│ ├── Cargo.toml
|
||||
│ └── src
|
||||
│ └── main.rs
|
||||
└── target
|
||||
</code></pre>
|
||||
<p>The workspace has one <em>target</em> directory at the top level that the compiled
|
||||
artifacts will be placed into; the <code>adder</code> package doesn’t have its own
|
||||
<em>target</em> directory. Even if we were to run <code>cargo build</code> from inside the
|
||||
<em>adder</em> directory, the compiled artifacts would still end up in <em>add/target</em>
|
||||
rather than <em>add/adder/target</em>. Cargo structures the <em>target</em> directory in a
|
||||
workspace like this because the crates in a workspace are meant to depend on
|
||||
each other. If each crate had its own <em>target</em> directory, each crate would have
|
||||
to recompile each of the other crates in the workspace to place the artifacts
|
||||
in its own <em>target</em> directory. By sharing one <em>target</em> directory, the crates
|
||||
can avoid unnecessary rebuilding.</p>
|
||||
<h3 id="creating-the-second-package-in-the-workspace"><a class="header" href="#creating-the-second-package-in-the-workspace">Creating the Second Package in the Workspace</a></h3>
|
||||
<p>Next, let’s create another member package in the workspace and call it
|
||||
<code>add_one</code>. Generate a new library crate named <code>add_one</code>:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/output-only-02-add-one/add
|
||||
remove `"add_one"` from `members` list in Cargo.toml
|
||||
rm -rf add_one
|
||||
cargo new add_one --lib
|
||||
copy output below
|
||||
-->
|
||||
<pre><code class="language-console">$ cargo new add_one --lib
|
||||
Created library `add_one` package
|
||||
Adding `add_one` as member of workspace at `file:///projects/add`
|
||||
</code></pre>
|
||||
<p>The top-level <em>Cargo.toml</em> will now include the <em>add_one</em> path in the <code>members</code>
|
||||
list:</p>
|
||||
<p><span class="filename">Filename: Cargo.toml</span></p>
|
||||
<pre><code class="language-toml">[workspace]
|
||||
resolver = "3"
|
||||
members = ["adder", "add_one"]
|
||||
</code></pre>
|
||||
<p>Your <em>add</em> directory should now have these directories and files:</p>
|
||||
<pre><code class="language-text">├── Cargo.lock
|
||||
├── Cargo.toml
|
||||
├── add_one
|
||||
│ ├── Cargo.toml
|
||||
│ └── src
|
||||
│ └── lib.rs
|
||||
├── adder
|
||||
│ ├── Cargo.toml
|
||||
│ └── src
|
||||
│ └── main.rs
|
||||
└── target
|
||||
</code></pre>
|
||||
<p>In the <em>add_one/src/lib.rs</em> file, let’s add an <code>add_one</code> function:</p>
|
||||
<p><span class="filename">Filename: add_one/src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">pub fn add_one(x: i32) -> i32 {
|
||||
x + 1
|
||||
}</code></pre>
|
||||
<p>Now we can have the <code>adder</code> package with our binary depend on the <code>add_one</code>
|
||||
package that has our library. First, we’ll need to add a path dependency on
|
||||
<code>add_one</code> to <em>adder/Cargo.toml</em>.</p>
|
||||
<p><span class="filename">Filename: adder/Cargo.toml</span></p>
|
||||
<pre><code class="language-toml">[dependencies]
|
||||
add_one = { path = "../add_one" }
|
||||
</code></pre>
|
||||
<p>Cargo doesn’t assume that crates in a workspace will depend on each other, so
|
||||
we need to be explicit about the dependency relationships.</p>
|
||||
<p>Next, let’s use the <code>add_one</code> function (from the <code>add_one</code> crate) in the
|
||||
<code>adder</code> crate. Open the <em>adder/src/main.rs</em> file and change the <code>main</code>
|
||||
function to call the <code>add_one</code> function, as in Listing 14-7.</p>
|
||||
<figure class="listing" id="listing-14-7">
|
||||
<span class="file-name">Filename: adder/src/main.rs</span>
|
||||
<pre><code class="language-rust ignore">fn main() {
|
||||
let num = 10;
|
||||
println!("Hello, world! {num} plus one is {}!", add_one::add_one(num));
|
||||
}</code></pre>
|
||||
<figcaption><a href="#listing-14-7">Listing 14-7</a>: Using the <code>add_one</code> library crate from the <code>adder</code> crate</figcaption>
|
||||
</figure>
|
||||
<p>Let’s build the workspace by running <code>cargo build</code> in the top-level <em>add</em>
|
||||
directory!</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/listing-14-07/add
|
||||
cargo build
|
||||
copy output below; the output updating script doesn't handle subdirectories in paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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
|
||||
</code></pre>
|
||||
<p>To run the binary crate from the <em>add</em> directory, we can specify which package
|
||||
in the workspace we want to run by using the <code>-p</code> argument and the package name
|
||||
with <code>cargo run</code>:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/listing-14-07/add
|
||||
cargo run -p adder
|
||||
copy output below; the output updating script doesn't handle subdirectories in paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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!
|
||||
</code></pre>
|
||||
<p>This runs the code in <em>adder/src/main.rs</em>, which depends on the <code>add_one</code> crate.</p>
|
||||
<!-- Old headings. Do not remove or links may break. -->
|
||||
<p><a id="depending-on-an-external-package-in-a-workspace"></a></p>
|
||||
<h3 id="depending-on-an-external-package"><a class="header" href="#depending-on-an-external-package">Depending on an External Package</a></h3>
|
||||
<p>Notice that the workspace has only one <em>Cargo.lock</em> file at the top level,
|
||||
rather than having a <em>Cargo.lock</em> in each crate’s directory. This ensures that
|
||||
all crates are using the same version of all dependencies. If we add the <code>rand</code>
|
||||
package to the <em>adder/Cargo.toml</em> and <em>add_one/Cargo.toml</em> files, Cargo will
|
||||
resolve both of those to one version of <code>rand</code> and record that in the one
|
||||
<em>Cargo.lock</em>. Making all crates in the workspace use the same dependencies
|
||||
means the crates will always be compatible with each other. Let’s add the
|
||||
<code>rand</code> crate to the <code>[dependencies]</code> section in the <em>add_one/Cargo.toml</em> file
|
||||
so that we can use the <code>rand</code> crate in the <code>add_one</code> crate:</p>
|
||||
<!-- When updating the version of `rand` used, also update the version of
|
||||
`rand` used in these files so they all match:
|
||||
* ch02-00-guessing-game-tutorial.md
|
||||
* ch07-04-bringing-paths-into-scope-with-the-use-keyword.md
|
||||
-->
|
||||
<p><span class="filename">Filename: add_one/Cargo.toml</span></p>
|
||||
<pre><code class="language-toml">[dependencies]
|
||||
rand = "0.8.5"
|
||||
</code></pre>
|
||||
<p>We can now add <code>use rand;</code> to the <em>add_one/src/lib.rs</em> file, and building the
|
||||
whole workspace by running <code>cargo build</code> in the <em>add</em> directory will bring in
|
||||
and compile the <code>rand</code> crate. We will get one warning because we aren’t
|
||||
referring to the <code>rand</code> we brought into scope:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/no-listing-03-workspace-with-external-dependency/add
|
||||
cargo build
|
||||
copy output below; the output updating script doesn't handle subdirectories in paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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
|
||||
</code></pre>
|
||||
<p>The top-level <em>Cargo.lock</em> now contains information about the dependency of
|
||||
<code>add_one</code> on <code>rand</code>. However, even though <code>rand</code> is used somewhere in the
|
||||
workspace, we can’t use it in other crates in the workspace unless we add
|
||||
<code>rand</code> to their <em>Cargo.toml</em> files as well. For example, if we add <code>use rand;</code>
|
||||
to the <em>adder/src/main.rs</em> file for the <code>adder</code> package, we’ll get an error:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/output-only-03-use-rand/add
|
||||
cargo build
|
||||
copy output below; the output updating script doesn't handle subdirectories in paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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`
|
||||
</code></pre>
|
||||
<p>To fix this, edit the <em>Cargo.toml</em> file for the <code>adder</code> package and indicate
|
||||
that <code>rand</code> is a dependency for it as well. Building the <code>adder</code> package will
|
||||
add <code>rand</code> to the list of dependencies for <code>adder</code> in <em>Cargo.lock</em>, but no
|
||||
additional copies of <code>rand</code> will be downloaded. Cargo will ensure that every
|
||||
crate in every package in the workspace using the <code>rand</code> package will use the
|
||||
same version as long as they specify compatible versions of <code>rand</code>, saving us
|
||||
space and ensuring that the crates in the workspace will be compatible with
|
||||
each other.</p>
|
||||
<p>If crates in the workspace specify incompatible versions of the same
|
||||
dependency, Cargo will resolve each of them but will still try to resolve as
|
||||
few versions as possible.</p>
|
||||
<h3 id="adding-a-test-to-a-workspace"><a class="header" href="#adding-a-test-to-a-workspace">Adding a Test to a Workspace</a></h3>
|
||||
<p>For another enhancement, let’s add a test of the <code>add_one::add_one</code> function
|
||||
within the <code>add_one</code> crate:</p>
|
||||
<p><span class="filename">Filename: add_one/src/lib.rs</span></p>
|
||||
<pre><code class="language-rust noplayground">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));
|
||||
}
|
||||
}</code></pre>
|
||||
<p>Now run <code>cargo test</code> in the top-level <em>add</em> directory. Running <code>cargo test</code> in
|
||||
a workspace structured like this one will run the tests for all the crates in
|
||||
the workspace:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add
|
||||
cargo test
|
||||
copy output below; the output updating script doesn't handle subdirectories in
|
||||
paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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
|
||||
</code></pre>
|
||||
<p>The first section of the output shows that the <code>it_works</code> test in the <code>add_one</code>
|
||||
crate passed. The next section shows that zero tests were found in the <code>adder</code>
|
||||
crate, and then the last section shows that zero documentation tests were found
|
||||
in the <code>add_one</code> crate.</p>
|
||||
<p>We can also run tests for one particular crate in a workspace from the
|
||||
top-level directory by using the <code>-p</code> flag and specifying the name of the crate
|
||||
we want to test:</p>
|
||||
<!-- manual-regeneration
|
||||
cd listings/ch14-more-about-cargo/no-listing-04-workspace-with-tests/add
|
||||
cargo test -p add_one
|
||||
copy output below; the output updating script doesn't handle subdirectories in paths properly
|
||||
-->
|
||||
<pre><code class="language-console">$ 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
|
||||
</code></pre>
|
||||
<p>This output shows <code>cargo test</code> only ran the tests for the <code>add_one</code> crate and
|
||||
didn’t run the <code>adder</code> crate tests.</p>
|
||||
<p>If you publish the crates in the workspace to
|
||||
<a href="https://crates.io/">crates.io</a><!-- ignore -->, each crate in the workspace
|
||||
will need to be published separately. Like <code>cargo test</code>, we can publish a
|
||||
particular crate in our workspace by using the <code>-p</code> flag and specifying the
|
||||
name of the crate we want to publish.</p>
|
||||
<p>For additional practice, add an <code>add_two</code> crate to this workspace in a similar
|
||||
way as the <code>add_one</code> crate!</p>
|
||||
<p>As your project grows, consider using a workspace: It enables you to work with
|
||||
smaller, easier-to-understand components than one big blob of code.
|
||||
Furthermore, keeping the crates in a workspace can make coordination between
|
||||
crates easier if they are often changed at the same time.</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user