How can I build multiple binaries with Cargo?
RustRust CargoRust Problem Overview
I'd like to make a project with a daemon
and a client
, connecting through a unix socket.
A client
and a daemon
requires two binaries, so how do I tell Cargo
to build two targets from two different sources?
To add a bit of fantasy, I'd like to have a library
for the main part of the daemon
, and just have a binary to wrap around it and communicate through sockets.
So, we have this kind of tree architecture:
├── Cargo.toml
├── target
| └── debug
| ├── daemon
│ └── client
└── src
├── daemon
│ ├── bin
│ │ └── main.rs
│ └── lib
│ └── lib.rs
└── client
└── bin
└── main.rs
I could make one executable which manages both concerns, but that's not what I want to do, unless it's very good practice.
Rust Solutions
Solution 1 - Rust
You can specify multiple binaries using [[bin]]
, as mentioned here:
[[bin]]
name = "daemon"
path = "src/daemon/bin/main.rs"
[[bin]]
name = "client"
path = "src/client/bin/main.rs"
Tip: If you instead put these files in src/bin/daemon.rs
and src/bin/client.rs
, you'll get two executables named daemon
and client
as Cargo compiles all files in src/bin
into executables with the same name automatically. You need to specify names and paths like in the snippet above only if you don't follow this convention.
Solution 2 - Rust
Another way is to use the workspace feature. This will provide more flexibility due to the fact that we can have more than one library. Example project structure:
.
├── Cargo.toml
├── cli
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── core
│ ├── Cargo.toml
│ └── src
│ └── lib.rs
├── daemon
│ ├── Cargo.toml
│ └── src
│ └── main.rs
├── gui
│ ├── Cargo.toml
│ └── src
│ └── main.rs
└── rpc
├── Cargo.toml
└── src
└── lib.rs
Contents of the root Cargo.toml
:
[workspace]
members = ["cli", "core", "daemon", "gui", "rpc"]
Solution 3 - Rust
Another format could be to replicate what the Crates.io source code has done, if you have a massive project, something like:
Main Library in src, with a Bin folder with your executables. Then make calls to your main library crate from your executables.
That way you library is centralized so easier to find things as it's cached.