|
5 | 5 |
|
6 | 6 | # Summary
|
7 | 7 |
|
8 |
| -Currently, all packages implicitly depend on libstd. This makes Cargo unsuitable for packages that |
9 |
| -need a custom-built libstd, or otherwise depend on crates with the same names as libstd and the |
10 |
| -crates behind the facade. The proposed fixes also open the door to a future where libstd can be |
11 |
| -Cargoized. |
| 8 | +Currently, Cargo doesn't know whether packages depend on libstd. This makes Cargo unsuitable for |
| 9 | +packages that need a cross-compiled or custom libstd, or otherwise depend on crates with the same |
| 10 | +names as libstd and the crates behind the facade. The proposed fixes also open the door to a future |
| 11 | +where libstd can be Cargoized. |
| 12 | + |
12 | 13 |
|
13 | 14 | # Motivation
|
14 | 15 |
|
15 |
| -Bare-metal work cannot use a standard build of libstd. But since any crate built with Cargo can link |
16 |
| -with a system-installed libstd if the target matches, using Cargo for such projects can be irksome |
17 |
| -or impossible. |
| 16 | +First some background. The current situation seems to be more of an accident of `rustc`'s pre-Cargo |
| 17 | +history than an explicit design decision. Cargo passes the location and name of all depended-on |
| 18 | +crates to `rustc`. This method is good for a number of reasons stemming from its fine granularity, |
| 19 | +such as: |
18 | 20 |
|
19 |
| -Cargoizing libstd also generally simplifies the infrastructure, and makes cross compiling much |
20 |
| -slicker, but that is a separate discussion. |
| 21 | + - No undeclared dependencies can be used |
21 | 22 |
|
22 |
| -Finally, I first raised this issue here: https://github.com/rust-lang/Cargo/issues/1096 Also, there |
23 |
| -are some (heavily bit-rotted) projects at https://github.com/RustOS-Fork-Holding-Ground that depend |
24 |
| -on each other in the way this RFC would make much more feasible. |
| 23 | + - Conversely, `rustc` can warn against *unused* declared dependencies |
25 | 24 |
|
26 |
| -# Detailed design |
| 25 | + - Crate/symbol names are frobbed so that packages with the overlapping names don't conflict |
| 26 | + |
| 27 | + |
| 28 | +However rather than passing in libstd and its deps, Cargo lets the compiler look for them as need in |
| 29 | +the compiler's sysroot [specifically `<sysroot>/lib/<target>`]. This is quite coarse in comparison, |
| 30 | +and we loose all the advantages of the previous method: |
| 31 | + |
| 32 | + - Packages may link or not link against libs in that directory as they please, with Cargo being |
| 33 | + none the wiser. |
| 34 | + |
| 35 | + - Cargo-built crates with the same name as those in there will collide, as the sysroot libs don't |
| 36 | + have their names frobbed. |
27 | 37 |
|
28 |
| -The current situation seems to be more of an accident of `rustc`'s pre-Cargo history than an |
29 |
| -explicit design decision. Cargo passes the location and name of all depended on crates to `rustc`. |
30 |
| -This is good because it means that that no undeclared dependencies on other Cargo packages can leak |
31 |
| -through. However, it also passes in `--sysroot /path/to/some/libdir`, the directory being were |
32 |
| -libstd is. This means packages are free to use libstd, the crates behind the facade, or none of the |
33 |
| -above, with Cargo being none the wiser. |
| 38 | + - Cross compiling may fail at build-time (as opposed to the much shorter |
| 39 | + "gather-dependencies-time") because of missing packages |
34 | 40 |
|
35 |
| -The only new interface proposed is a boolean field to the package meta telling Cargo that the |
36 |
| -package does not depend on libstd by default. This need not imply Rust's `no_std`, as one might want |
37 |
| -to `use` their own build of libstd by default. To disambiguate, this field is called |
38 |
| -`implicit-deps`; please, go ahead and bikeshead the name. `implicit-deps` is true by default to |
39 |
| -maintain compatibility with existing packages. |
40 | 41 |
|
41 |
| -The meaning of this flag is defined in 3 phases, where each phase extends the last. The idea being |
42 |
| -is that while earlier phases are easier to implement, later phases yield a more elegant system. |
| 42 | +Cargo doesn't look inside the sysroot to see what is or isn't there, but it would hardly help if it |
| 43 | +did, because it doesn't know what any package needs. Assuming all packages need libstd, for example, |
| 44 | +means Cargo just flat-out won't build freestanding packages that just use libcore on a platform that |
| 45 | +doesn't support libstd. |
43 | 46 |
|
44 |
| -## Phase 1 |
| 47 | +For an anecdote: in https://github.com/RustOS-Fork-Holding-Ground I tried to rig up Cargo to cross |
| 48 | +compile libstd for me. Since I needed to use an unstable compiler anyways, it was possible in |
| 49 | +principle to build absolutely everything I needed with the same `rustc` version. Because of some |
| 50 | +trouble with Cargo and target JSONs, I didn't use a custom target specification, and just used |
| 51 | +`x86_64-gnu-linux`, meaning that depending on platform I was compiling on, I may or may have been |
| 52 | +cross-compiling. In the case where I wasn't, I couldn't complete the build because `rustc` |
| 53 | +complained about the libstd I was building overlapping with the libstd in the sysroot. |
45 | 54 |
|
46 |
| -Add a `--use-sysroot=<true|false>` flag to `rustc`, where true is the default. Make Cargo pass |
47 |
| -`--use-sysroot=false` to `rustc` is the case that `implicit-deps` is false. |
| 55 | +For these reasons, most freestanding projects I know of avoid Cargo altogether, and just include |
| 56 | +submodule rust and run make in that. Cargo can still be used if one manages to get the requisite |
| 57 | +libraries in the sysroot. But this is a tedious operation that individual projects shouldn't need to |
| 58 | +reimplement, and one that has serious security implications if the normal libstd is modified. |
48 | 59 |
|
49 |
| -This hotfix is enough to allow us bare-metal devs to use Cargo for our own projects, but doesn't |
50 |
| -suffice for creating an ecosystem of packages that depend on crates behind the facade but not libstd |
51 |
| -itself. This is because the choices are all or nothing: Either one implicitly depends on libstd or |
52 |
| -the crates behind the facade, or they don't depend on them at all. |
| 60 | +The fundamental plan proposed in this RFC is to make sure that anything Cargo builds never blindly |
| 61 | +links against libraries in the sysroot. This is achieved by making Cargo aware of all dependencies, |
| 62 | +including those libstd or its backing crates. That way, these problems are avoided. |
53 | 63 |
|
54 |
| -## Phase 2 |
| 64 | +For the record, I first raised this issue [here](https://github.com/rust-lang/Cargo/issues/1096). |
55 | 65 |
|
56 |
| -Since, passing in a directory of crates is inherently more fragile than passing in a crate itself, |
57 |
| -make Cargo use `--use-sysroot=false` in all cases. |
58 | 66 |
|
59 |
| -Cargo would special case package names corresponding to the crates behind the facade, such that if |
60 |
| -the package don't exist, it would simply pass the corresponding system crate to `rustc`. I assume |
61 |
| -the names are blacklisted on crates.io already, so by default the packages won't exist. But users |
62 |
| -can use config files to extend the namespace so their own modded libstds can be used instead. Even |
63 |
| -if they don't want to change libstd but just cross-compile it, this is frankly the easiest way as |
64 |
| -Cargo will seemliest cross compile both their project and it's transitive dependencies. |
| 67 | +# Detailed design |
| 68 | + |
| 69 | +The only new interface proposed is a boolean field in `Cargo.toml` specifying that the package does |
| 70 | +not depend on libstd by default. Note that this is technically orthogonal to Rust's `no_std`, as one |
| 71 | +might want to `use` their own build of libstd by default, or implicitly depend on it but not |
| 72 | +glob-import the prelude. To disambiguate, this field is called `implicit-deps`; please, go ahead and |
| 73 | +bikeshead the name. `implicit-deps` is true by default to maintain compatibility with existing |
| 74 | +packages. When true, "std" will be implicitly appended to the list of dependencies. |
| 75 | + |
| 76 | +When Cargo sees a package name it cannot resolve, it will query `rustc` for the default sysroot, and |
| 77 | +look inside to see if it can find a matching rlib. [It is necessary to query `rustc` because the |
| 78 | +`rustc` directory layout is not stabilized and `rustc` and Cargo are versioned independently. The |
| 79 | +same version issues make giving a Cargo a whitelist of potential standard library crate-names |
| 80 | +risky.] If a matching rlib is successful found, Cargo will copy it (or simlink it) into the |
| 81 | +project's build directly as if it built the rlib. Each rlib in the sysroot must be paired with some |
| 82 | +sort of manifest listing its dependencies, so Cargo can copy those too. |
65 | 83 |
|
66 |
| -In this way we can put packages on crates.io that depend on the crates behind the facade. Some |
67 |
| -packages that already exist, like liblog and libbitflags, should be given features that optionally |
68 |
| -allow them to avoid libstd and just depend directly on the crates behind the facade they really |
69 |
| -need. |
| 84 | +`rustc` will have a new `--use-sysroot=<true|false>` flag. When Cargo builds a package, it will |
| 85 | +always pass `--use-sysroot=false` to `rustc`, as any rlibs it needs will have been copied to the |
| 86 | +build directory. Cargo can and will then pass those rlibs directly just as it does with normal Cargo |
| 87 | +deps. |
70 | 88 |
|
71 |
| -## Phase 3 |
| 89 | +If Cargo cannot find the libraries it needs in the sysroot, or a library's dependency manifest is |
| 90 | +missing, it will complain that the standard libraries needed for the current job are missing and |
| 91 | +give up. |
72 | 92 |
|
73 |
| -If/when the standard library is built with Cargo and put on crates.io, all the specially-cased |
74 |
| -package names can be treated normally, |
| 93 | +## Future Compatibility |
75 | 94 |
|
76 |
| -The standard library is downloaded and built from crates.io. Or equivalently, Cargo comes with a |
77 |
| -cache of that build, as Cargo should be able cache builds between projects at this point. Just as in |
78 |
| -phase 2, `implicit-deps = false` just prevents libstd from implicitly being appended to the list of |
79 |
| -dependencies. |
| 95 | +In the future, rather than giving up if libraries are missing Cargo could attempt to download them |
| 96 | +from some build cache. In the farther future, the stdlib libraries may be Cargoized, and Cargo able |
| 97 | +to query pre-built binaries for any arbitrary package. In that scenario, we can remove all code |
| 98 | +relating to falling back on the sysroot to look for rlibs. |
| 99 | + |
| 100 | +In the meantime, developers living dangerously with an unstable compiler can package the standard |
| 101 | +library themselves, and use their Cargo config file to get Cargo to cross compiler libstd for them. |
80 | 102 |
|
81 |
| -Again, to make this as least controversial as possible, this RFC does not propose outright that the |
82 |
| -standard library should be Cargoized. This 3rd phases just describes how this feature would work |
83 |
| -were that to happen. |
84 | 103 |
|
85 | 104 | # Drawbacks
|
86 | 105 |
|
87 |
| -I really don't know of any. Development for hosted environments would hardly be very affected. |
| 106 | +Cargo does more work than is strictly necessary for rlibs installed in sysroot; some more metadata |
| 107 | +must be maintained by `rustc` or its installation. |
| 108 | + |
| 109 | + - But in a future where Cargo can build stdlib like any other, all this cruft goes away. |
| 110 | + |
88 | 111 |
|
89 | 112 | # Alternatives
|
90 | 113 |
|
91 |
| -Make it so all dependencies, even libstd, must be explicit. C.f. Cabal and base. |
| 114 | + - Simply have `implicit-deps = false` make Cargo pass `--use-sysroot=false` to `rustc`. |
| 115 | + |
| 116 | + - This doesn't by-itself make a way for package to depend on only some of the crates behind the |
| 117 | + facade. That, in turn, means Cargo is little better at cross compiling those than before. |
| 118 | + |
| 119 | + - While unstable compiler users can just package the standard library and depend on it as a |
| 120 | + normal crate, it would be weird to have freestanding projects coalesce around some bootleg |
| 121 | + libcore on crates.io. |
| 122 | + |
| 123 | + - Make it so all dependencies, even libstd, must be explicit. C.f. Cabal and base. Slightly |
| 124 | + simpler, but breaks nearly all existing packages. |
| 125 | + |
| 126 | + - Don't track stdlib depencies. Then, in the future when Cargo tries to obtain libs for cross |
| 127 | + compiling, stick them in the sysroot instead. Cargo either assumes package needs all of stdlib, |
| 128 | + or examines target to see what crates behind the facade are buildable and just goes for those. |
| 129 | + |
| 130 | + - Cargo does extra work if you need less of the stdlib |
| 131 | + |
| 132 | + - No nice migration into a world where Cargo can build stdlib without hacks. |
| 133 | + |
92 | 134 |
|
93 | 135 | # Unresolved questions
|
94 | 136 |
|
95 |
| -There are multiple lists of dependencies for different things (e.g. tests), Should libstd be append |
96 |
| -to all of them in phases 2 and 3? |
| 137 | + - There are multiple lists of dependencies for different things (e.g. tests), Should libstd be |
| 138 | + append to all of them in phases 2 and 3? |
| 139 | + |
| 140 | + - Should rlibs in the sysroot respect Cargo name-frobbing conventions? If they don't, should Cargo |
| 141 | + frob the name when it copies it (e.g. with `ld -i`)? |
| 142 | + |
| 143 | + - Just as make libstd a real dependency, we can make `rustc` a real dev dependency. The standard |
| 144 | + library can thus be built with Cargo by depending on the associated unstable compiler. There are |
| 145 | + some challenges to be overcome, including: |
| 146 | + |
| 147 | + - Teaching Cargo and its frobber an "x can build for y" relation for stable/unstable compiler |
| 148 | + compatibility, rather than simply assuming all distinct compilers are mutually incompatible. |
| 149 | + |
| 150 | + - Coalescing a "virtual package" out of many different packages with disjoint dependencies. This |
| 151 | + is needed because different `rustc` version has a different library implementation that |
| 152 | + present the same interface. |
| 153 | + |
| 154 | + This almost certainly is better addressed in a later RFC. |
0 commit comments