23

Right now I have a Cargo workspace with three members.

[workspace]
members = [
    "foo",
    "bar",
    "baz",
]

If I run cargo run in the root directory, I get this error:

error: manifest path /home/lukas/dev/mahboi/Cargo.toml is a virtual manifest, but this command requires running against an actual package in this workspace

That makes sense. I can run cargo run -p foo and it works. But the thing is: foo is the only crate that is executable and I will execute it very often, so it would be nice if I could just run cargo run and execute it.

I tried to use the default-members key, but this didn't help:

default-members = ["foo"]

Is there another way to tell Cargo that cargo run should execute the foo crate (equivalent to running cargo run in the foo/ subdirectory)? I would also accept answers that make the root crate non virtual (i.e. add a [package] key).

Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
  • I've also been looking for this. I know that you can at least do `cargo run -p foo` to use package `foo` every time you run `cargo run`. There's also the option to have your top-level folder be both a crate, and a workspace for nested crates, but I didn't go that route, as it adds more confusion. I'd also like to see me being able to set the default binary crate at the virtual manifest level. – JeanMertz Aug 10 '18 at 11:41

1 Answers1

22

Single Binary

This is available as of Rust 1.30. Here is the complete set of files I tested with:

Cargo.toml

[workspace]
members = [
    "foo",
    "bar",
    "baz",
]

foo/Cargo.toml

[package]
name = "foo"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[dependencies]

foo/src/main.rs

fn main() {
    println!("Hello, world!");
}

bar/Cargo.toml

[package]
name = "bar"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[dependencies]

bar/src/lib.rs

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}

baz/Cargo.toml

[package]
name = "baz"
version = "0.1.0"
authors = ["An Devloper <an.devloper@example.com>"]

[dependencies]

baz/src/lib.rs

#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        assert_eq!(2 + 2, 4);
    }
}
$ tree .
.
├── Cargo.lock
├── Cargo.toml
├── bar
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── baz
│   ├── Cargo.toml
│   └── src
│       └── lib.rs
├── foo
│   ├── Cargo.toml
│   └── src
│       └── main.rs
├── src
│   └── lib.rs
└── target
    └── ...
$ cargo run
   Compiling baz v0.1.0 (file:///private/tmp/example/baz)
   Compiling bar v0.1.0 (file:///private/tmp/example/bar)
   Compiling foo v0.1.0 (file:///private/tmp/example/foo)
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/foo`
Hello, world!

Multiple Binaries

As of Rust 1.37.0 you can use Cargo's "default-run" feature to specify which one to use.

foo/Cargo.toml

[package]
name = "foo"
version = "0.0.1"
authors = ["An Devloper <an.devloper@example.com>"]
default-run = "foo"
Lukas Kalbertodt
  • 79,749
  • 26
  • 255
  • 305
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 13
    To aid anyone who might be a bit confused like me. Adding default-run to the Cargo.toml for the binary you want to be default does work, even though it may seem surprising that the default-run for an inner Cargo.toml shouldn't affect what happens when you invoke `cargo run` at the workspace root. – Winston Ewert Sep 13 '19 at 00:44
  • 1
    How can I make cargo run multiple binaries? I have a client and a server I want to test together for example. – justin.m.chase Jul 19 '20 at 03:04
  • 2
    @justin.m.chase that's not something that Cargo supports natively. One thing you could do is create a third binary that uses [`std::process::Command`](https://doc.rust-lang.org/std/process/struct.Command.html) to spawn the server and client. – Shepmaster Jul 20 '20 at 13:41
  • 1
    @Shepmaster Great minds think alike. That is exactlly what I ended up doing, its a little hacky but works fine for now. – justin.m.chase Jul 20 '20 at 14:00
  • In the `workspace` case, the `default-run` belongs into the top level toml file. In Visual studios workspace view, you can simply right click one or more projects and set them to start if you press `F5` (the equivalent to `cargo run`). This is a workspace feature and the sub projects have no business with it, IMHO. One could even find reasons to argue, that it does not belong into any .toml file but some other file (which might or might not be subject to version conrol of projects). One guy generates some data with a tool, the other guy just runs the main app -> constantly changing commits. – BitTickler Sep 07 '22 at 13:46
  • The project I currently work on has (simplified) 1 lib and 2 exes (binaries, whatever you call it). The lib is the core - AI (a fancy idea I pursue), one binary is a process, allowing others to use a trained network. The other binary (I call it `coach`) deals with training and persisting networks. In no case, running both at the same time makes any sense. So this is not a good default behavior. – BitTickler Sep 07 '22 at 13:55