Skip to content

Commit

Permalink
Merge #297
Browse files Browse the repository at this point in the history
297: Add a cargo features doc. r=jrvanwhy a=jrvanwhy

[Rendered](https://github.com/jrvanwhy/libtock-rs/blob/features-doc/doc/CargoFeatures.md)

This doc describes the issue we've discovered with using cargo features from a workspace, and discusses an alternative that `libtock-rs` developers can use to avoid it.

It stems from this code review discussion from several weeks ago: #283 (comment)

Co-authored-by: Johnathan Van Why <jrvanwhy@google.com>
  • Loading branch information
bors[bot] and jrvanwhy authored May 15, 2021
2 parents d903bcb + ebc3d5f commit 55e498a
Showing 1 changed file with 56 additions and 0 deletions.
56 changes: 56 additions & 0 deletions doc/CargoFeatures.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
Cargo Features
==============

On the surface, `cargo`'s
[features](https://doc.rust-lang.org/cargo/reference/features.html) mechanism
looks great for `libtock-rs`. Process binary crates can depend on `libtock-rs`,
and use `cargo` features to specify which optional `libtock-rs` functionality
they want.

However, `cargo` assumes that it can compile a crate with features that a binary
does not need. This isn't necessarily true for embedded crates, where process
binary authors care about the app size.

## Process Binaries in a Workspace

When used with workspaces, `cargo` features can result in excess code being
built into a repository.

For example, suppose that `libtock` exposes a `malloc` feature, which adds a
dynamic memory allocator. Then if a `libtock` user creates a `cargo` workspace
with the following process binaries in it:

1. `no_malloc`, which depends on `libtock` and does not depend on the `malloc`
feature.
2. `malloc`, which depends on `libtock` and the `malloc` feature.

With this setup, `malloc` will always work correctly. Also, if you run `cargo
build -p no_malloc`, then `no_malloc` will correctly build without a memory
allocation.

Seems optimal, right?

It's not: if you build the entire workspace with `cargo build --workspace`,
`libtock` will only be built once, with the `malloc` feature! That will result
in a `no_malloc` process binary that contains a memory allocator, which is not
what the author desired.

## Alternative to `cargo` Features

In many cases, `cargo` features can be replaced by splitting a crate up into
smaller crates, and letting process binaries choose what to depend on. For
example, memory allocation can be in a separate crate that process binaries
depend on if and only if they need memory allocation.

Here are some questions to help guide the decision between using a `cargo`
feature and separate crates:

1. Do you forsee a single user writing multiple process binaries, some of which
use this feature? If yes, then maybe it should not be a `cargo` feature.
2. Will the compiler optimize the feature away entirely if it is included but
unused? If yes, then making it a `cargo` feature is probably fine.

In many cases, it will make sense to add a `cargo` feature to `libtock` but use
optional crates to implement the feature internally. This way, users with
multiple process binaries in a workspace can choose whether each process binary
depends on the optimal crate.

0 comments on commit 55e498a

Please sign in to comment.