A single Rust package can contain one library and multiple binaries.
Rust dependencies can only be specified at the package level, not for each
binary in a package.
If multiple binaries require different dependencies, using a cargo workspace
is the appropriate way to organize a project.
There are multiple ways to organize a workspace.
This post will cover workspace organization for a no_std library that is
called from both std and no_std binaries.
The library will accept an FFI-safe pointer to a logging function.
The binaries will call the library, and log command line arguments using the
same function without passing it to the library.
Software Versions
Instructions
Workspace
First, create workspace directory and add all of the member packages for
the project.
Next, add a top level Cargo.toml file that lists project level metadata
and all of member packages.
Cargo.toml
Core Library
Move into the core_library package.
Modify Cargo.toml.
Note that version and edition are inherited from the workspace.
core_library/Cargo.toml
Add the no_std library code.
It accepts an FFI-safe logging function and uses it to print a message.
core_library/src/lib.rs
It should now be possible to build but not run the library.
std Rust Binary
Move into the pc package.
Modify Cargo.toml.
Note the core_library sibling dependency,
in addition to inherited metadata.
pc/Cargo.toml
The code for the binary follows.
Note that the core_library sibling dependency can be used like any
other dependency.
The code defines an FFI-safe logging function for the library, and an
adapter to the logging function that takes a String for use in local code.
It then logs a message, calls the library, and echoes the command line
arguments.
pc/src/main.rs
Run the binary with and without arguments to make sure everything works.
no_std Rust Binary
Finally, move into the embedded package.
Modify Cargo.toml.
This binary depends on libc_alloc because it is
a core+alloc no_std binary.
embedded/Cargo.toml
The code for the binary does exactly the same thing as the std Rust code.
Lacking std results in more verbose code that is a little harder to follow.
embedded/src/main.rs
It should run just like the std version, with slightly different output.
Specifying Targets in a Workspace
Commands like cargo build will operate on the curent package, or on all
package if in the top level of the workspace.
The –workspace command-line flag can be used to build all targets from
anywhere in the workspace, and the -p or –package flag can be used
to specify a particular package.
More Information
The Cargo
and Rust Programming Language
books have sections on workspaces.
The documentation for std::ffi
can be useful for FFI work involving strings.
Note that the types are reexported to std, but they need to be pulled
in from core or alloc in no_std Rust.
The Embedded Rust Book is a good place to get started with
no_std Rust.