Skip to content

Commit 9615547

Browse files
committed
Rewrote motiviation
1 parent cb65e59 commit 9615547

File tree

1 file changed

+81
-36
lines changed

1 file changed

+81
-36
lines changed

text/3529-cargo-path-bases.md

Lines changed: 81 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -12,51 +12,96 @@ turn enable base-relative `path` dependencies.
1212
# Motivation
1313
[motivation]: #motivation
1414

15-
While developing locally, users may wish to specify many `path`
16-
dependencies that all live in the same local directory. If that local
17-
directory is not a short distance from the `Cargo.toml`, this can get
18-
unwieldy. They may end up with a `Cargo.toml` that contains
15+
As a project grows in size, it becomes necessary to split it into smaller
16+
sub-projects, architected into layers with well-defined boundaries.
17+
18+
One way to enforce these boundaries is to use different Git repos (aka
19+
"multi-repo"). Cargo has good support for multi-repo projects using either `git`
20+
dependencies, or developers can use private registries if they want to
21+
explicitly publish code or need to preprocess their sub-projects (e.g.,
22+
generating code) before they can be consumed.
23+
24+
If all of the code is kept in a single Git repo (aka "mono-repo"), then these
25+
boundaries must be enforced a different way: either leveraging tooling during
26+
the build to check layering, or requiring that sub-projects explicitly publish
27+
and consume from some intermediate directory. Cargo has poor support for
28+
mono-repos: the only viable mechanism is `path` dependencies, but these require
29+
relative paths (which makes refactoring and moving sub-projects very difficult)
30+
and don't work at all if the mono-repo requires publishing and consuming from an
31+
intermediate directory (as this may very per host, or per target being built).
32+
33+
This RFC proposes a mechanism to specify `base` directories in `Config.toml`
34+
files which can be used to prepend `path` dependencies. This allows mono-repos
35+
to specify dependencies relative to their root directory, which allows the
36+
consuming project to be moved freely (no relative paths to update) and a simple
37+
find-and-replace to handle a producing project being moved. Additionally, a
38+
host-specific or target-specific intermediate directory may be specified as a
39+
`base`, allowing code to be consumed from there using `path` dependencies.
40+
41+
### Example
42+
43+
If we had a sub-project that depends on three others:
44+
45+
* `foo` which is in a different layer of the mono-repo.
46+
* `bar_with_generated` that must be consumed from an intermediate directory
47+
because it contains target-specific generated code.
48+
* `baz` which is in the current layer.
49+
50+
We may have a `Cargo.toml` snippet that looks like this:
1951

2052
```toml
21-
foo = { path = "/home/jon/dev/rust/foo" }
22-
bar = { path = "/home/jon/dev/rust/bar" }
23-
baz = { path = "/home/jon/dev/rust/ws/baz" }
53+
[dependencies]
54+
foo = { path = "../../../other_layer/foo" }
55+
bar_with_generated = { path = "../../../../intermediates/x86_64/Debug/third_layer/bar_with_generated" }
56+
baz = { path = "../baz" }
2457
```
2558

26-
This is not only frustrating to type out, but also requires many changes
27-
should any component of the path change. For example, if `foo`, `bar`,
28-
and `ws/baz` were to move under a sub-directory of `libs`, all the paths
29-
would have to be updated. If they are used in more than one local
30-
project, each project would have to be updated.
59+
This has many issues:
60+
61+
* Moving the current sub-project may require changing all of these relative
62+
paths.
63+
* `bar_with_generated` will only work if we're building x86_64 Debug.
64+
* `bar_with_generated` assumes that the `intermediates` directory is a sibling
65+
to our source directory, and not somewhere else completely (e.g., a different
66+
drive for performance reasons).
67+
* Moving `foo` or `baz` requires searching the code for each possible relative
68+
path (e.g., `../../../other_layer/foo` and `../foo`) and may be error prone if
69+
there is some other sub-project in directory with the same name.
3170

32-
As related issue arises in contexts where an external build system may
33-
make certain dependencies available through vendoring. Such a build
34-
system might place vendored packages under some complex path under a
35-
build-root, like
71+
Instead, if we could specify these `base` directories in a `Config.toml` (which
72+
may be generated by an external build system which in turn invokes Cargo):
3673

74+
```toml
75+
[base_path]
76+
sources = "/home/user/dev/src"
77+
intermediates = "/home/user/dev/intermediates/x86_64/Debug"
3778
```
38-
/home/user/workplace/feature-1/build/first-party-package/first-party-package-1.0/x86_64/dev/build/private/rust-vendored/
79+
80+
Then the `Cargo.toml` can use those `base` directories and avoid relative paths:
81+
82+
```toml
83+
[dependencies]
84+
foo = { path = "other_layer/foo", base = "sources" }
85+
bar_with_generated = { path = "third_layer/bar_with_generated", base = "intermediates" }
86+
baz = { path = "this_layer/baz", base = "sources" }
3987
```
4088

41-
If a developer wishes to use such an auto-vendored dependency, a
42-
contract must be established with the build system about exactly where
43-
vendred dependencies will end up. And since that path may not be near
44-
the project's `Cargo.toml`, the user's `Cargo.toml` may end up with
45-
either an absolute path or a long relative path, both of which may not
46-
work on other hosts, and thus cannot be checked in (or must be
47-
overwritten in-place by the build system).
48-
49-
The proposed mechanism aims to simplify both of these use-cases by
50-
introducing named "base" paths in the Cargo configuration
51-
(`.cargo/config.toml`). Path dependencies can then be given relative to
52-
those base path names, which can be set either by a local developer in
53-
their user-wide configuration (`~/.cargo/config.toml`), or by an
54-
external build system in a project-wide configuration file.
55-
56-
This effectively makes a "group" of path dependencies available at some
57-
undisclosed location to `Cargo.toml`, which then only has to know the
58-
layout to path dependencies _within_ that directory, and not the path
59-
_to_ that directory.
89+
Which resolves the issues we previously had:
90+
91+
* The current project can be moved without modifying the `Cargo.toml` at all.
92+
* `bar_with_generated` works for all targets (assuming the `Config.toml` is
93+
generated).
94+
* The `intermediates` directory can be placed anywhere.
95+
* Moving `foo` or `baz` only requires searching for the canonical form relative
96+
to the `base` directory.
97+
98+
## Other uses
99+
100+
The ability to use `base` directories for `path` dependencies is convenient for
101+
developers who are using a large number of `path` dependencies within the same
102+
root directory. Instead of repeating the same path fragment many times in their
103+
`Cargo.toml`, they can instead specify it once in a `Config.toml` as a `base`
104+
directory, then use that `base` directory in each of their `path` dependencies.
60105

61106
# Guide-level explanation
62107
[guide-level-explanation]: #guide-level-explanation

0 commit comments

Comments
 (0)