]> git.lizzy.rs Git - rust.git/blob - crates/project_model/src/sysroot.rs
Update sysroot crates
[rust.git] / crates / project_model / src / sysroot.rs
1 //! Loads "sysroot" crate.
2 //!
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.
6
7 use std::{convert::TryFrom, env, ops, path::PathBuf, process::Command};
8
9 use anyhow::{format_err, Result};
10 use la_arena::{Arena, Idx};
11 use paths::{AbsPath, AbsPathBuf};
12
13 use crate::utf8_stdout;
14
15 #[derive(Default, Debug, Clone, Eq, PartialEq)]
16 pub struct Sysroot {
17     crates: Arena<SysrootCrateData>,
18 }
19
20 pub(crate) type SysrootCrate = Idx<SysrootCrateData>;
21
22 #[derive(Debug, Clone, Eq, PartialEq)]
23 pub struct SysrootCrateData {
24     pub name: String,
25     pub root: AbsPathBuf,
26     pub deps: Vec<SysrootCrate>,
27 }
28
29 impl ops::Index<SysrootCrate> for Sysroot {
30     type Output = SysrootCrateData;
31     fn index(&self, index: SysrootCrate) -> &SysrootCrateData {
32         &self.crates[index]
33     }
34 }
35
36 impl Sysroot {
37     pub fn public_deps(&self) -> impl Iterator<Item = (&'static str, SysrootCrate)> + '_ {
38         // core is added as a dependency before std in order to
39         // mimic rustcs dependency order
40         ["core", "alloc", "std"].iter().filter_map(move |&it| Some((it, self.by_name(it)?)))
41     }
42
43     pub fn proc_macro(&self) -> Option<SysrootCrate> {
44         self.by_name("proc_macro")
45     }
46
47     pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a {
48         self.crates.iter().map(|(id, _data)| id)
49     }
50
51     pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> {
52         log::debug!("Discovering sysroot for {}", cargo_toml.display());
53         let current_dir = cargo_toml.parent().ok_or_else(|| {
54             format_err!("Failed to find the parent directory for {}", cargo_toml.display())
55         })?;
56         let sysroot_dir = discover_sysroot_dir(current_dir)?;
57         let sysroot_src_dir = discover_sysroot_src_dir(&sysroot_dir, current_dir)?;
58         let res = Sysroot::load(&sysroot_src_dir)?;
59         Ok(res)
60     }
61
62     pub fn discover_rustc(cargo_toml: &AbsPath) -> Option<AbsPathBuf> {
63         log::debug!("Discovering rustc source for {}", cargo_toml.display());
64         let current_dir = cargo_toml.parent().unwrap();
65         discover_sysroot_dir(current_dir).ok().and_then(|sysroot_dir| get_rustc_src(&sysroot_dir))
66     }
67
68     pub fn load(sysroot_src_dir: &AbsPath) -> Result<Sysroot> {
69         let mut sysroot = Sysroot { crates: Arena::default() };
70
71         for path in SYSROOT_CRATES.trim().lines() {
72             let name = path.split('/').last().unwrap();
73             let root = [format!("{}/src/lib.rs", path), format!("lib{}/lib.rs", path)]
74                 .iter()
75                 .map(|it| sysroot_src_dir.join(it))
76                 .find(|it| it.exists());
77
78             if let Some(root) = root {
79                 sysroot.crates.alloc(SysrootCrateData {
80                     name: name.into(),
81                     root,
82                     deps: Vec::new(),
83                 });
84             }
85         }
86
87         if let Some(std) = sysroot.by_name("std") {
88             for dep in STD_DEPS.trim().lines() {
89                 if let Some(dep) = sysroot.by_name(dep) {
90                     sysroot.crates[std].deps.push(dep)
91                 }
92             }
93         }
94
95         if let Some(alloc) = sysroot.by_name("alloc") {
96             if let Some(core) = sysroot.by_name("core") {
97                 sysroot.crates[alloc].deps.push(core);
98             }
99         }
100
101         if sysroot.by_name("core").is_none() {
102             let var_note = if env::var_os("RUST_SRC_PATH").is_some() {
103                 " (`RUST_SRC_PATH` might be incorrect, try unsetting it)"
104             } else {
105                 ""
106             };
107             anyhow::bail!(
108                 "could not find libcore in sysroot path `{}`{}",
109                 sysroot_src_dir.as_ref().display(),
110                 var_note,
111             );
112         }
113
114         Ok(sysroot)
115     }
116
117     fn by_name(&self, name: &str) -> Option<SysrootCrate> {
118         let (id, _data) = self.crates.iter().find(|(_id, data)| data.name == name)?;
119         Some(id)
120     }
121 }
122
123 fn discover_sysroot_dir(current_dir: &AbsPath) -> Result<AbsPathBuf> {
124     let mut rustc = Command::new(toolchain::rustc());
125     rustc.current_dir(current_dir).args(&["--print", "sysroot"]);
126     log::debug!("Discovering sysroot by {:?}", rustc);
127     let stdout = utf8_stdout(rustc)?;
128     Ok(AbsPathBuf::assert(PathBuf::from(stdout)))
129 }
130
131 fn discover_sysroot_src_dir(
132     sysroot_path: &AbsPathBuf,
133     current_dir: &AbsPath,
134 ) -> Result<AbsPathBuf> {
135     if let Ok(path) = env::var("RUST_SRC_PATH") {
136         let path = AbsPathBuf::try_from(path.as_str())
137             .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?;
138         let core = path.join("core");
139         if core.exists() {
140             log::debug!("Discovered sysroot by RUST_SRC_PATH: {}", path.display());
141             return Ok(path);
142         }
143         log::debug!("RUST_SRC_PATH is set, but is invalid (no core: {:?}), ignoring", core);
144     }
145
146     get_rust_src(sysroot_path)
147         .or_else(|| {
148             let mut rustup = Command::new(toolchain::rustup());
149             rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]);
150             utf8_stdout(rustup).ok()?;
151             get_rust_src(sysroot_path)
152         })
153         .ok_or_else(|| {
154             format_err!(
155                 "\
156 can't load standard library from sysroot
157 {}
158 (discovered via `rustc --print sysroot`)
159 try installing the Rust source the same way you installed rustc",
160                 sysroot_path.display(),
161             )
162         })
163 }
164
165 fn get_rustc_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
166     let rustc_src = sysroot_path.join("lib/rustlib/rustc-src/rust/compiler/rustc/Cargo.toml");
167     log::debug!("Checking for rustc source code: {}", rustc_src.display());
168     if rustc_src.exists() {
169         Some(rustc_src)
170     } else {
171         None
172     }
173 }
174
175 fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> {
176     // Try the new path first since the old one still exists.
177     let rust_src = sysroot_path.join("lib/rustlib/src/rust");
178     log::debug!("Checking sysroot (looking for `library` and `src` dirs): {}", rust_src.display());
179     ["library", "src"].iter().map(|it| rust_src.join(it)).find(|it| it.exists())
180 }
181
182 impl SysrootCrateData {
183     pub fn root_dir(&self) -> &AbsPath {
184         self.root.parent().unwrap()
185     }
186 }
187
188 const SYSROOT_CRATES: &str = "
189 alloc
190 core
191 panic_abort
192 panic_unwind
193 proc_macro
194 profiler_builtins
195 std
196 stdarch/crates/std_detect
197 term
198 test
199 unwind";
200
201 const STD_DEPS: &str = "
202 alloc
203 core
204 panic_abort
205 panic_unwind
206 profiler_builtins
207 proc_macro
208 std_detect
209 term
210 test
211 unwind";