Reputation: 873
How can you have multiple nested workspace with Cargo?
I have the following project structure:
myworkspace
├── project_a
│ └── Cargo.toml
├── project_b
│ └── Cargo.toml
│ └── project_b_dependency
| └── Cargo.toml
└── Cargo.toml
Where project_b_dependency
is a big library which is a git submodule which has a workspace by itself.
I get an error when I run cargo build
because there is a workspace within a workspace.
$ cargo build
error: multiple workspace roots found in the same workspace:
/myworkspace
/myworkspace/project_b/project_b_dependency
Is there a simple work-around? I want to keep project_b_dependency
in source control as a submodule.
This is not a duplicate of Refactoring to workspace structure causes extern crate imports to not work because I want to know how I can deal with nested workspaces.
Upvotes: 16
Views: 17043
Reputation: 1058
I've figured out a way to do this one, but it requires cooperation for the submodules you are bringing in to follow a similar pattern.
My issue here was that I had a lib bigger_lib
which is in the repo as a workspace, and then a common_lib
which is a dependency for both my_app
and bigger_lib
(not specifically what the question was asking, but what I'm trying to solve). These are representative our current state of many little workspace repos, all on git.
With a layout like this
workspace root
├── Cargo.toml (workspace)
├── my_app (executable)
| └── Cargo.toml
├── common_lib (lib, via submodule)
| └── Cargo.toml
└── bigger_lib_repo (workspace, via submodule)
├── Cargo.toml (workspace)
├── common_lib (lib, via submodule)
| └── Cargo.toml
└── bigger_lib (lib, not a submodule)
With content looking like this
# ./Cargo.toml
[workspace]
members = ["app"]
# Important to avoid the multiple workspaces error
exclude = ["bigger_lib_repo"]
# Unsure if this is necessary
resolver = "2"
[patch.crates-io]
# The common dep gets overridden for everybody here
common_lib = { path = "./common_lib" }
# ./my_app/Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
[dependencies]
# Note this isn't specced using a path
common_lib = { version = "0.1.0" }
bigger_lib = { version = "0.1.0", path = "../bigger_lib_repo/bigger_lib" }
# ./bigger_lib_repo/Cargo.toml
[workspace]
members = [ "bigger_lib" ]
resolver = "2"
[patch.crates-io]
# Same as the root workspace
common_lib = { path = "./common_lib" }
# ./bigger_lib_repo/bigger_lib/Cargo.toml
[package]
name = "bigger_lib"
version = "0.1.0"
edition = "2021"
[dependencies]
# Note this isn't specced using a path
common_lib = { version = "0.1.0" }
All other Cargo.toml
files are not particularly different from what you'd expect.
With this pattern I can
bigger_lib::common_lib::MyStruct
)Upvotes: 1
Reputation: 1333
I find a method to keep git submodule.
Upvotes: 1
Reputation: 18993
It seems that nested workspaces are quite difficult to manage, so one possibility is to change the layout of you project:
.
├── myworkspace
│ ├── Cargo.lock
│ ├── Cargo.toml
│ ├── project_a
│ │ ├── Cargo.toml
│ │ └── src
│ │ └── lib.rs
│ ├── project_b
│ │ ├── Cargo.toml
│ │ └── src
│ │ └── lib.rs
│ └── src
│ └── main.rs
└── project_b_dependency
├── Cargo.toml
└── src
└── lib.rs
in myworkspace/Cargo.toml
:
[workspace]
members= ["project_a", "project_b"]
In myworkspace/project_b/Cargo.toml
[dependencies]
project_b_dependency = {path = "../../project_b_dependency"}
I've tried to use workspace.exclude
property with your layout but without success.
Upvotes: 7
Reputation: 22283
Workspaces can't be nested; as the docs state:
A crate may either specify package.workspace or specify
[workspace]
. That is, a crate cannot both be a root crate in a workspace (contain[workspace]
) and also be a member crate of another workspace (containpackage.workspace
).
The Cargo workspace RFC also specified this:
A workspace is valid if these two properties hold:
- A workspace has only one root crate (that with
[workspace]
inCargo.toml
).- All workspace crates defined in
workspace.members
point back to the workspace root withpackage.workspace
.
Upvotes: 10