diff options
Diffstat (limited to 'crates/project_model/src/sysroot.rs')
-rw-r--r-- | crates/project_model/src/sysroot.rs | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/crates/project_model/src/sysroot.rs b/crates/project_model/src/sysroot.rs new file mode 100644 index 000000000..8239797b6 --- /dev/null +++ b/crates/project_model/src/sysroot.rs | |||
@@ -0,0 +1,173 @@ | |||
1 | //! FIXME: write short doc here | ||
2 | |||
3 | use std::{convert::TryFrom, env, ops, path::Path, process::Command}; | ||
4 | |||
5 | use anyhow::{bail, format_err, Result}; | ||
6 | use arena::{Arena, Idx}; | ||
7 | use paths::{AbsPath, AbsPathBuf}; | ||
8 | |||
9 | use crate::utf8_stdout; | ||
10 | |||
11 | #[derive(Default, Debug, Clone, Eq, PartialEq)] | ||
12 | pub struct Sysroot { | ||
13 | crates: Arena<SysrootCrateData>, | ||
14 | } | ||
15 | |||
16 | pub type SysrootCrate = Idx<SysrootCrateData>; | ||
17 | |||
18 | #[derive(Debug, Clone, Eq, PartialEq)] | ||
19 | pub struct SysrootCrateData { | ||
20 | pub name: String, | ||
21 | pub root: AbsPathBuf, | ||
22 | pub deps: Vec<SysrootCrate>, | ||
23 | } | ||
24 | |||
25 | impl ops::Index<SysrootCrate> for Sysroot { | ||
26 | type Output = SysrootCrateData; | ||
27 | fn index(&self, index: SysrootCrate) -> &SysrootCrateData { | ||
28 | &self.crates[index] | ||
29 | } | ||
30 | } | ||
31 | |||
32 | impl Sysroot { | ||
33 | pub fn core(&self) -> Option<SysrootCrate> { | ||
34 | self.by_name("core") | ||
35 | } | ||
36 | |||
37 | pub fn alloc(&self) -> Option<SysrootCrate> { | ||
38 | self.by_name("alloc") | ||
39 | } | ||
40 | |||
41 | pub fn std(&self) -> Option<SysrootCrate> { | ||
42 | self.by_name("std") | ||
43 | } | ||
44 | |||
45 | pub fn proc_macro(&self) -> Option<SysrootCrate> { | ||
46 | self.by_name("proc_macro") | ||
47 | } | ||
48 | |||
49 | pub fn crates<'a>(&'a self) -> impl Iterator<Item = SysrootCrate> + ExactSizeIterator + 'a { | ||
50 | self.crates.iter().map(|(id, _data)| id) | ||
51 | } | ||
52 | |||
53 | pub fn discover(cargo_toml: &AbsPath) -> Result<Sysroot> { | ||
54 | let src = get_or_install_rust_src(cargo_toml)?; | ||
55 | let mut sysroot = Sysroot { crates: Arena::default() }; | ||
56 | for name in SYSROOT_CRATES.trim().lines() { | ||
57 | // FIXME: remove this path when 1.47 comes out | ||
58 | // https://github.com/rust-lang/rust/pull/73265 | ||
59 | let root = src.join(format!("lib{}", name)).join("lib.rs"); | ||
60 | if root.exists() { | ||
61 | sysroot.crates.alloc(SysrootCrateData { | ||
62 | name: name.into(), | ||
63 | root, | ||
64 | deps: Vec::new(), | ||
65 | }); | ||
66 | } else { | ||
67 | let root = src.join(name).join("src/lib.rs"); | ||
68 | if root.exists() { | ||
69 | sysroot.crates.alloc(SysrootCrateData { | ||
70 | name: name.into(), | ||
71 | root, | ||
72 | deps: Vec::new(), | ||
73 | }); | ||
74 | } | ||
75 | } | ||
76 | } | ||
77 | if let Some(std) = sysroot.std() { | ||
78 | for dep in STD_DEPS.trim().lines() { | ||
79 | if let Some(dep) = sysroot.by_name(dep) { | ||
80 | sysroot.crates[std].deps.push(dep) | ||
81 | } | ||
82 | } | ||
83 | } | ||
84 | if let Some(alloc) = sysroot.alloc() { | ||
85 | if let Some(core) = sysroot.core() { | ||
86 | sysroot.crates[alloc].deps.push(core); | ||
87 | } | ||
88 | } | ||
89 | Ok(sysroot) | ||
90 | } | ||
91 | |||
92 | fn by_name(&self, name: &str) -> Option<SysrootCrate> { | ||
93 | self.crates.iter().find(|(_id, data)| data.name == name).map(|(id, _data)| id) | ||
94 | } | ||
95 | } | ||
96 | |||
97 | fn get_or_install_rust_src(cargo_toml: &AbsPath) -> Result<AbsPathBuf> { | ||
98 | if let Ok(path) = env::var("RUST_SRC_PATH") { | ||
99 | let path = AbsPathBuf::try_from(path.as_str()) | ||
100 | .map_err(|path| format_err!("RUST_SRC_PATH must be absolute: {}", path.display()))?; | ||
101 | return Ok(path); | ||
102 | } | ||
103 | let current_dir = cargo_toml.parent().unwrap(); | ||
104 | let mut rustc = Command::new(toolchain::rustc()); | ||
105 | rustc.current_dir(current_dir).args(&["--print", "sysroot"]); | ||
106 | let stdout = utf8_stdout(rustc)?; | ||
107 | let sysroot_path = AbsPath::assert(Path::new(stdout.trim())); | ||
108 | let mut src = get_rust_src(sysroot_path); | ||
109 | if src.is_none() { | ||
110 | let mut rustup = Command::new(toolchain::rustup()); | ||
111 | rustup.current_dir(current_dir).args(&["component", "add", "rust-src"]); | ||
112 | utf8_stdout(rustup)?; | ||
113 | src = get_rust_src(sysroot_path); | ||
114 | } | ||
115 | match src { | ||
116 | Some(r) => Ok(r), | ||
117 | None => bail!( | ||
118 | "can't load standard library from sysroot\n\ | ||
119 | {}\n\ | ||
120 | (discovered via `rustc --print sysroot`)\n\ | ||
121 | try running `rustup component add rust-src` or set `RUST_SRC_PATH`", | ||
122 | sysroot_path.display(), | ||
123 | ), | ||
124 | } | ||
125 | } | ||
126 | |||
127 | fn get_rust_src(sysroot_path: &AbsPath) -> Option<AbsPathBuf> { | ||
128 | // try the new path first since the old one still exists | ||
129 | let mut src_path = sysroot_path.join("lib/rustlib/src/rust/library"); | ||
130 | if !src_path.exists() { | ||
131 | // FIXME: remove this path when 1.47 comes out | ||
132 | // https://github.com/rust-lang/rust/pull/73265 | ||
133 | src_path = sysroot_path.join("lib/rustlib/src/rust/src"); | ||
134 | } | ||
135 | if src_path.exists() { | ||
136 | Some(src_path) | ||
137 | } else { | ||
138 | None | ||
139 | } | ||
140 | } | ||
141 | |||
142 | impl SysrootCrateData { | ||
143 | pub fn root_dir(&self) -> &AbsPath { | ||
144 | self.root.parent().unwrap() | ||
145 | } | ||
146 | } | ||
147 | |||
148 | const SYSROOT_CRATES: &str = " | ||
149 | alloc | ||
150 | core | ||
151 | panic_abort | ||
152 | panic_unwind | ||
153 | proc_macro | ||
154 | profiler_builtins | ||
155 | rtstartup | ||
156 | std | ||
157 | stdarch | ||
158 | term | ||
159 | test | ||
160 | unwind"; | ||
161 | |||
162 | const STD_DEPS: &str = " | ||
163 | alloc | ||
164 | core | ||
165 | panic_abort | ||
166 | panic_unwind | ||
167 | profiler_builtins | ||
168 | rtstartup | ||
169 | proc_macro | ||
170 | stdarch | ||
171 | term | ||
172 | test | ||
173 | unwind"; | ||