1 //! Loads "sysroot" crate.
3 //! One confusing point here is that normally sysroot is a bunch of `.rlib`s,
4 //! but we can't process `.rlib` and need source code instead. The source code
5 //! is typically installed with `rustup component add rust-src` command.
7 use std
::{env, fs, iter, ops, path::PathBuf, process::Command}
;
9 use anyhow
::{format_err, Result}
;
10 use la_arena
::{Arena, Idx}
;
11 use paths
::{AbsPath, AbsPathBuf}
;
12 use rustc_hash
::FxHashMap
;
14 use crate::{utf8_stdout, ManifestPath}
;
16 #[derive(Debug, Clone, Eq, PartialEq)]
20 crates
: Arena
<SysrootCrateData
>,
23 pub(crate) type SysrootCrate
= Idx
<SysrootCrateData
>;
25 #[derive(Debug, Clone, Eq, PartialEq)]
26 pub struct SysrootCrateData
{
28 pub root
: ManifestPath
,
29 pub deps
: Vec
<SysrootCrate
>,
32 impl ops
::Index
<SysrootCrate
> for Sysroot
{
33 type Output
= SysrootCrateData
;
34 fn index(&self, index
: SysrootCrate
) -> &SysrootCrateData
{
40 /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
41 /// subfolder live, like:
42 /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu`
43 pub fn root(&self) -> &AbsPath
{
47 /// Returns the sysroot "source" directory, where stdlib sources are located, like:
48 /// `$HOME/.rustup/toolchains/nightly-2022-07-23-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library`
49 pub fn src_root(&self) -> &AbsPath
{
53 pub fn public_deps(&self) -> impl Iterator
<Item
= (&'
static str, SysrootCrate
, bool
)> + '_
{
54 // core is added as a dependency before std in order to
55 // mimic rustcs dependency order
56 ["core", "alloc", "std"]
58 .zip(iter
::repeat(true))
59 .chain(iter
::once(("test", false)))
60 .filter_map(move |(name
, prelude
)| Some((name
, self.by_name(name
)?
, prelude
)))
63 pub fn proc_macro(&self) -> Option
<SysrootCrate
> {
64 self.by_name("proc_macro")
67 pub fn crates(&self) -> impl Iterator
<Item
= SysrootCrate
> + ExactSizeIterator
+ '_
{
68 self.crates
.iter().map(|(id
, _data
)| id
)
73 /// Attempts to discover the toolchain's sysroot from the given `dir`.
74 pub fn discover(dir
: &AbsPath
, extra_env
: &FxHashMap
<String
, String
>) -> Result
<Sysroot
> {
75 tracing
::debug
!("discovering sysroot for {}", dir
.display());
76 let sysroot_dir
= discover_sysroot_dir(dir
, extra_env
)?
;
78 discover_sysroot_src_dir_or_add_component(&sysroot_dir
, dir
, extra_env
)?
;
79 let res
= Sysroot
::load(sysroot_dir
, sysroot_src_dir
)?
;
83 pub fn discover_rustc(
84 cargo_toml
: &ManifestPath
,
85 extra_env
: &FxHashMap
<String
, String
>,
86 ) -> Option
<ManifestPath
> {
87 tracing
::debug
!("discovering rustc source for {}", cargo_toml
.display());
88 let current_dir
= cargo_toml
.parent();
89 let sysroot_dir
= discover_sysroot_dir(current_dir
, extra_env
).ok()?
;
90 get_rustc_src(&sysroot_dir
)
93 pub fn with_sysroot_dir(sysroot_dir
: AbsPathBuf
) -> Result
<Sysroot
> {
94 let sysroot_src_dir
= discover_sysroot_src_dir(&sysroot_dir
).ok_or_else(|| {
95 format_err
!("can't load standard library from sysroot {}", sysroot_dir
.display())
97 let res
= Sysroot
::load(sysroot_dir
, sysroot_src_dir
)?
;
101 pub fn load(sysroot_dir
: AbsPathBuf
, sysroot_src_dir
: AbsPathBuf
) -> Result
<Sysroot
> {
103 Sysroot { root: sysroot_dir, src_root: sysroot_src_dir, crates: Arena::default() }
;
105 for path
in SYSROOT_CRATES
.trim().lines() {
106 let name
= path
.split('
/'
).last().unwrap();
107 let root
= [format
!("{}/src/lib.rs", path
), format
!("lib{}/lib.rs", path
)]
109 .map(|it
| sysroot
.src_root
.join(it
))
110 .filter_map(|it
| ManifestPath
::try_from(it
).ok())
111 .find(|it
| fs
::metadata(it
).is_ok());
113 if let Some(root
) = root
{
114 sysroot
.crates
.alloc(SysrootCrateData
{
122 if let Some(std
) = sysroot
.by_name("std") {
123 for dep
in STD_DEPS
.trim().lines() {
124 if let Some(dep
) = sysroot
.by_name(dep
) {
125 sysroot
.crates
[std
].deps
.push(dep
)
130 if let Some(alloc
) = sysroot
.by_name("alloc") {
131 for dep
in ALLOC_DEPS
.trim().lines() {
132 if let Some(dep
) = sysroot
.by_name(dep
) {
133 sysroot
.crates
[alloc
].deps
.push(dep
)
138 if let Some(proc_macro
) = sysroot
.by_name("proc_macro") {
139 for dep
in PROC_MACRO_DEPS
.trim().lines() {
140 if let Some(dep
) = sysroot
.by_name(dep
) {
141 sysroot
.crates
[proc_macro
].deps
.push(dep
)
146 if sysroot
.by_name("core").is_none() {
147 let var_note
= if env
::var_os("RUST_SRC_PATH").is_some() {
148 " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
153 "could not find libcore in sysroot path `{}`{}",
154 sysroot
.src_root
.as_path().display(),
162 fn by_name(&self, name
: &str) -> Option
<SysrootCrate
> {
163 let (id
, _data
) = self.crates
.iter().find(|(_id
, data
)| data
.name
== name
)?
;
168 fn discover_sysroot_dir(
169 current_dir
: &AbsPath
,
170 extra_env
: &FxHashMap
<String
, String
>,
171 ) -> Result
<AbsPathBuf
> {
172 let mut rustc
= Command
::new(toolchain
::rustc());
173 rustc
.envs(extra_env
);
174 rustc
.current_dir(current_dir
).args(&["--print", "sysroot"]);
175 tracing
::debug
!("Discovering sysroot by {:?}", rustc
);
176 let stdout
= utf8_stdout(rustc
)?
;
177 Ok(AbsPathBuf
::assert(PathBuf
::from(stdout
)))
180 fn discover_sysroot_src_dir(sysroot_path
: &AbsPathBuf
) -> Option
<AbsPathBuf
> {
181 if let Ok(path
) = env
::var("RUST_SRC_PATH") {
182 if let Ok(path
) = AbsPathBuf
::try_from(path
.as_str()) {
183 let core
= path
.join("core");
184 if fs
::metadata(&core
).is_ok() {
185 tracing
::debug
!("Discovered sysroot by RUST_SRC_PATH: {}", path
.display());
188 tracing
::debug
!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core
);
190 tracing
::debug
!("RUST_SRC_PATH is set, but is invalid, ignoring");
194 get_rust_src(sysroot_path
)
197 fn discover_sysroot_src_dir_or_add_component(
198 sysroot_path
: &AbsPathBuf
,
199 current_dir
: &AbsPath
,
200 extra_env
: &FxHashMap
<String
, String
>,
201 ) -> Result
<AbsPathBuf
> {
202 discover_sysroot_src_dir(sysroot_path
)
204 let mut rustup
= Command
::new(toolchain
::rustup());
205 rustup
.envs(extra_env
);
206 rustup
.current_dir(current_dir
).args(&["component", "add", "rust-src"]);
207 tracing
::info
!("adding rust-src component by {:?}", rustup
);
208 utf8_stdout(rustup
).ok()?
;
209 get_rust_src(sysroot_path
)
214 can't load standard library from sysroot
216 (discovered via `rustc --print sysroot`)
217 try installing the Rust source the same way you installed rustc",
218 sysroot_path
.display(),
223 fn get_rustc_src(sysroot_path
: &AbsPath
) -> Option
<ManifestPath
> {
224 let rustc_src
= sysroot_path
.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
225 let rustc_src
= ManifestPath
::try_from(rustc_src
).ok()?
;
226 tracing
::debug
!("checking for rustc source code: {}", rustc_src
.display());
227 if fs
::metadata(&rustc_src
).is_ok() {
234 fn get_rust_src(sysroot_path
: &AbsPath
) -> Option
<AbsPathBuf
> {
235 let rust_src
= sysroot_path
.join("lib/rustlib/src/rust/library");
236 tracing
::debug
!("checking sysroot library: {}", rust_src
.display());
237 if fs
::metadata(&rust_src
).is_ok() {
244 const SYSROOT_CRATES
: &str = "
253 stdarch/crates/std_detect
257 const ALLOC_DEPS
: &str = "core";
259 const STD_DEPS
: &str = "
269 const PROC_MACRO_DEPS
: &str = "std";