@@ -12,51 +12,96 @@ turn enable base-relative `path` dependencies.
12
12
# Motivation
13
13
[ motivation ] : #motivation
14
14
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:
19
51
20
52
``` 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" }
24
57
```
25
58
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.
31
70
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):
36
73
74
+ ``` toml
75
+ [base_path ]
76
+ sources = " /home/user/dev/src"
77
+ intermediates = " /home/user/dev/intermediates/x86_64/Debug"
37
78
```
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" }
39
87
```
40
88
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.
60
105
61
106
# Guide-level explanation
62
107
[ guide-level-explanation ] : #guide-level-explanation
0 commit comments