Description
Describe the problem you are trying to solve
tl;dr: If you have a lot of crates in a workspace, maintaining the levels of parent-directory traversal to wire them up with path dependencies is a little tedious.
Both at work and at home I use cargo a bit like a rust-only buck or bazel -- code grows in a big tree with many (often small) crates broken apart to make it easy to reuse things across the repository, and no versions. There are other languages and build systems mixed in there too, and most of our rust can build with Bazel too, but I'd prefer to stick with Cargo where possible.
When working in a workspace with many interdependent crates like this it gets pretty tedious to match up the number of ../../../
sections in path dependencies to walk back out to the root, then back in to another portion of the tree. It also means that you can't blindly copy and paste a dependency entry from one crate's Cargo.toml to another without checking/modifying the number of leading "parent directory" ../
segments to match the location of the file you're copying in to.
For a concrete example, this is taken from one of my crates:
[dependencies]
# crates.io deps elided for brevity
cli = { path = "../../common/cli", features = ["native"]}
buildkite = { path = "../../meta/buildkite"}
log_config = { path = "../../common/log_config" }
panic_config = { path = "../../common/panic_config" }
workspace = { path = "../../meta/workspace" }
which exists in a directory tree like this:
meta
├── buildkite
├── cargo_manifest
└── workspace
common
├── audio
├── cli
├── ... a dozen or more, some nested
└── time
net
├── nsframe
└── slack
projects
├── demos
│ ├── .. many others
├── lit
├── lit-* .. many subcommands, some nested
└── turbobunny
with a single Cargo.toml
at the root that does nothing but anchor the workspace, configure some settings, set some excludes, etc...
I frequently need to copy a few common dependencies over to a new crate to start working on it, and if it happens to be at a different level of depth it'll require some fixing-up of the paths. The same is true for relocating a crate in the tree -- for example promoting a binary out of a bin
directory to its own crate in a different subdirectory.
Describe the solution you'd like
I'd like to propose a way to specify workspace-relative dependency paths. How is a pretty bikesheddable question, so I've whipped up three ideas to get the conversation started:
1. Workspace Relative as a kind of Simple dependency
I doubt this'll be a popular idea since I suspect the way I use cargo is pretty different from most folks, but my personal favorite approach would be a new TomlDependency
enum variant, or an extension to the existing Simple parser, for workspace relative paths denoted by a punctuation character such that a dependency block like this would work:
[dependencies]
buildkite = "@/meta/buildkite"
cli = "@/common/cli"
workspace = "@/meta/workspace"
I've chosen @
for illustration since it doesn't intersect with punctuation used in the version parser, but it could be anything, or even multiple characters like //
or something.
2. New detailed dependency key: workspace_path
For an alternative that doesn't complicate the common case, we could add a new DetailedTomlDependency
key for workspace_path
, which would function similarly to the existing path key but operate relative to the workspace root.
[dependencies]
buildkite = { workspace_path = "/meta/buildkite" }
cli = { workspace_path = "/common/cli" }
workspace = { workspace_path = "/meta/workspace" }
3. Special parse mode on the existing path
key
Somewhere in between the previous two ideas, we could extend the parser for the existing path
key on DetailedTomlDependency
to recognize a prefix as indicating a path is relative to the workspace root. Illustrated with @
again, for consistency's sake:
[dependencies]
buildkite = { path = "@/meta/buildkite" }
cli = { path = "@/common/cli" }
workspace = { path = "@/meta/workspace" }
Notes
It doesn't look like this is feasible to do with a cargo subcommand for trying-it-out, or I'd have done that first to prove the value.
I recognize this is an ease-of-use papercut at best, not a huge problem, but if I'm not the only one it bugs I'd love to smooth it over upstream. I don't have a great sense for what's a reasonable thing to do with just a feature request ticket and a PR versus writing up an RFC, so please don't hesitate to correct me if this is too large a change to try doing this way. Likewise if this is just something there's not much maintainer or community interest in: lmk. no hard feelings.
If, otoh, y'all like this idea I'm happy to do the work to make it happen.