44//! but we can't process `.rlib` and need source code instead. The source code
55//! is typically installed with `rustup component add rust-src` command.
66
7- use std:: { env, fs, ops, path:: Path , process:: Command } ;
7+ use std:: {
8+ env, fs,
9+ ops:: { self , Not } ,
10+ path:: Path ,
11+ process:: Command ,
12+ } ;
813
914use anyhow:: { format_err, Result } ;
1015use base_db:: CrateName ;
1116use itertools:: Itertools ;
1217use la_arena:: { Arena , Idx } ;
1318use paths:: { AbsPath , AbsPathBuf , Utf8PathBuf } ;
1419use rustc_hash:: FxHashMap ;
20+ use stdx:: format_to;
1521use toolchain:: { probe_for_binary, Tool } ;
1622
1723use crate :: {
1824 cargo_workspace:: CargoMetadataConfig , utf8_stdout, CargoWorkspace , ManifestPath ,
19- SysrootQueryMetadata ,
25+ SysrootSourceWorkspaceConfig ,
2026} ;
2127
2228#[ derive( Debug , Clone , PartialEq , Eq ) ]
2329pub struct Sysroot {
2430 root : Option < AbsPathBuf > ,
2531 src_root : Option < AbsPathBuf > ,
26- mode : SysrootMode ,
32+ workspace : SysrootWorkspace ,
2733 error : Option < String > ,
2834}
2935
3036#[ derive( Debug , Clone , Eq , PartialEq ) ]
31- pub ( crate ) enum SysrootMode {
37+ pub ( crate ) enum SysrootWorkspace {
3238 Workspace ( CargoWorkspace ) ,
3339 Stitched ( Stitched ) ,
3440 Empty ,
@@ -82,7 +88,7 @@ pub(crate) struct SysrootCrateData {
8288
8389impl Sysroot {
8490 pub const fn empty ( ) -> Sysroot {
85- Sysroot { root : None , src_root : None , mode : SysrootMode :: Empty , error : None }
91+ Sysroot { root : None , src_root : None , workspace : SysrootWorkspace :: Empty , error : None }
8692 }
8793
8894 /// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
@@ -99,10 +105,10 @@ impl Sysroot {
99105 }
100106
101107 pub fn is_empty ( & self ) -> bool {
102- match & self . mode {
103- SysrootMode :: Workspace ( ws) => ws. packages ( ) . next ( ) . is_none ( ) ,
104- SysrootMode :: Stitched ( stitched) => stitched. crates . is_empty ( ) ,
105- SysrootMode :: Empty => true ,
108+ match & self . workspace {
109+ SysrootWorkspace :: Workspace ( ws) => ws. packages ( ) . next ( ) . is_none ( ) ,
110+ SysrootWorkspace :: Stitched ( stitched) => stitched. crates . is_empty ( ) ,
111+ SysrootWorkspace :: Empty => true ,
106112 }
107113 }
108114
@@ -111,64 +117,51 @@ impl Sysroot {
111117 }
112118
113119 pub fn num_packages ( & self ) -> usize {
114- match & self . mode {
115- SysrootMode :: Workspace ( ws) => ws. packages ( ) . count ( ) ,
116- SysrootMode :: Stitched ( c) => c. crates ( ) . count ( ) ,
117- SysrootMode :: Empty => 0 ,
120+ match & self . workspace {
121+ SysrootWorkspace :: Workspace ( ws) => ws. packages ( ) . count ( ) ,
122+ SysrootWorkspace :: Stitched ( c) => c. crates ( ) . count ( ) ,
123+ SysrootWorkspace :: Empty => 0 ,
118124 }
119125 }
120126
121- pub ( crate ) fn mode ( & self ) -> & SysrootMode {
122- & self . mode
127+ pub ( crate ) fn workspace ( & self ) -> & SysrootWorkspace {
128+ & self . workspace
123129 }
124130}
125131
126- // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
127132impl Sysroot {
128133 /// Attempts to discover the toolchain's sysroot from the given `dir`.
129- pub fn discover (
130- dir : & AbsPath ,
131- extra_env : & FxHashMap < String , String > ,
132- sysroot_query_metadata : & SysrootQueryMetadata ,
133- ) -> Sysroot {
134+ pub fn discover ( dir : & AbsPath , extra_env : & FxHashMap < String , String > ) -> Sysroot {
134135 let sysroot_dir = discover_sysroot_dir ( dir, extra_env) ;
135136 let sysroot_src_dir = sysroot_dir. as_ref ( ) . ok ( ) . map ( |sysroot_dir| {
136137 discover_sysroot_src_dir_or_add_component ( sysroot_dir, dir, extra_env)
137138 } ) ;
138- Sysroot :: load_core_check ( Some ( sysroot_dir) , sysroot_src_dir, sysroot_query_metadata )
139+ Sysroot :: assemble ( Some ( sysroot_dir) , sysroot_src_dir)
139140 }
140141
141142 pub fn discover_with_src_override (
142143 current_dir : & AbsPath ,
143144 extra_env : & FxHashMap < String , String > ,
144145 sysroot_src_dir : AbsPathBuf ,
145- sysroot_query_metadata : & SysrootQueryMetadata ,
146146 ) -> Sysroot {
147147 let sysroot_dir = discover_sysroot_dir ( current_dir, extra_env) ;
148- Sysroot :: load_core_check (
149- Some ( sysroot_dir) ,
150- Some ( Ok ( sysroot_src_dir) ) ,
151- sysroot_query_metadata,
152- )
148+ Sysroot :: assemble ( Some ( sysroot_dir) , Some ( Ok ( sysroot_src_dir) ) )
153149 }
154150
155- pub fn discover_sysroot_src_dir (
156- sysroot_dir : AbsPathBuf ,
157- sysroot_query_metadata : & SysrootQueryMetadata ,
158- ) -> Sysroot {
151+ pub fn discover_sysroot_src_dir ( sysroot_dir : AbsPathBuf ) -> Sysroot {
159152 let sysroot_src_dir = discover_sysroot_src_dir ( & sysroot_dir)
160153 . ok_or_else ( || format_err ! ( "can't find standard library sources in {sysroot_dir}" ) ) ;
161- Sysroot :: load_core_check (
162- Some ( Ok ( sysroot_dir) ) ,
163- Some ( sysroot_src_dir) ,
164- sysroot_query_metadata,
165- )
154+ Sysroot :: assemble ( Some ( Ok ( sysroot_dir) ) , Some ( sysroot_src_dir) )
166155 }
167156
168157 pub fn discover_rustc_src ( & self ) -> Option < ManifestPath > {
169158 get_rustc_src ( self . root ( ) ?)
170159 }
171160
161+ pub fn new ( sysroot_dir : Option < AbsPathBuf > , sysroot_src_dir : Option < AbsPathBuf > ) -> Sysroot {
162+ Self :: assemble ( sysroot_dir. map ( Ok ) , sysroot_src_dir. map ( Ok ) )
163+ }
164+
172165 /// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
173166 pub fn tool ( & self , tool : Tool , current_dir : impl AsRef < Path > ) -> Command {
174167 match self . root ( ) {
@@ -205,101 +198,59 @@ impl Sysroot {
205198 } )
206199 }
207200
208- pub fn load (
209- sysroot_dir : Option < AbsPathBuf > ,
210- sysroot_src_dir : Option < AbsPathBuf > ,
211- sysroot_query_metadata : & SysrootQueryMetadata ,
212- ) -> Sysroot {
213- Self :: load_core_check ( sysroot_dir. map ( Ok ) , sysroot_src_dir. map ( Ok ) , sysroot_query_metadata)
214- }
215-
216- fn load_core_check (
217- sysroot_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
218- sysroot_src_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
219- sysroot_query_metadata : & SysrootQueryMetadata ,
220- ) -> Sysroot {
221- let mut sysroot = Self :: load_ ( sysroot_dir, sysroot_src_dir, sysroot_query_metadata) ;
222- if sysroot. error . is_none ( ) {
223- if let Some ( src_root) = & sysroot. src_root {
224- let has_core = match & sysroot. mode {
225- SysrootMode :: Workspace ( ws) => ws. packages ( ) . any ( |p| ws[ p] . name == "core" ) ,
226- SysrootMode :: Stitched ( stitched) => stitched. by_name ( "core" ) . is_some ( ) ,
227- SysrootMode :: Empty => true ,
228- } ;
229- if !has_core {
230- let var_note = if env:: var_os ( "RUST_SRC_PATH" ) . is_some ( ) {
231- " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
232- } else {
233- ", try running `rustup component add rust-src` to possibly fix this"
234- } ;
235- sysroot. error = Some ( format ! (
236- "sysroot at `{src_root}` is missing a `core` library{var_note}" ,
237- ) ) ;
238- }
239- }
240- }
241- sysroot
242- }
243-
244- fn load_ (
201+ fn assemble (
245202 sysroot_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
246203 sysroot_src_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
247- sysroot_query_metadata : & SysrootQueryMetadata ,
248204 ) -> Sysroot {
249- let sysroot_dir = match sysroot_dir {
205+ let mut errors = String :: new ( ) ;
206+ let root = match sysroot_dir {
250207 Some ( Ok ( sysroot_dir) ) => Some ( sysroot_dir) ,
251208 Some ( Err ( e) ) => {
252- return Sysroot {
253- root : None ,
254- src_root : None ,
255- mode : SysrootMode :: Empty ,
256- error : Some ( e. to_string ( ) ) ,
257- }
209+ format_to ! ( errors, "{e}\n " ) ;
210+ None
258211 }
259212 None => None ,
260213 } ;
261- let sysroot_src_dir = match sysroot_src_dir {
262- Some ( Ok ( sysroot_src_dir) ) => sysroot_src_dir,
214+ let src_root = match sysroot_src_dir {
215+ Some ( Ok ( sysroot_src_dir) ) => Some ( sysroot_src_dir) ,
263216 Some ( Err ( e) ) => {
264- return Sysroot {
265- root : sysroot_dir,
266- src_root : None ,
267- mode : SysrootMode :: Empty ,
268- error : Some ( e. to_string ( ) ) ,
269- }
270- }
271- None => {
272- return Sysroot {
273- root : sysroot_dir,
274- src_root : None ,
275- mode : SysrootMode :: Empty ,
276- error : None ,
277- }
217+ format_to ! ( errors, "{e}\n " ) ;
218+ None
278219 }
220+ None => None ,
279221 } ;
280- if let SysrootQueryMetadata :: CargoMetadata ( cargo_config) = sysroot_query_metadata {
281- let library_manifest =
282- ManifestPath :: try_from ( sysroot_src_dir. join ( "Cargo.toml" ) ) . unwrap ( ) ;
222+ Sysroot {
223+ root,
224+ src_root,
225+ workspace : SysrootWorkspace :: Empty ,
226+ error : errors. is_empty ( ) . not ( ) . then_some ( errors) ,
227+ }
228+ }
229+
230+ pub fn load_workspace ( & mut self , sysroot_source_config : & SysrootSourceWorkspaceConfig ) {
231+ assert ! ( matches!( self . workspace, SysrootWorkspace :: Empty ) , "workspace already loaded" ) ;
232+ let Self { root : _, src_root : Some ( src_root) , workspace, error : _ } = self else { return } ;
233+ if let SysrootSourceWorkspaceConfig :: CargoMetadata ( cargo_config) = sysroot_source_config {
234+ let library_manifest = ManifestPath :: try_from ( src_root. join ( "Cargo.toml" ) ) . unwrap ( ) ;
283235 if fs:: metadata ( & library_manifest) . is_ok ( ) {
284- if let Some ( sysroot) = Self :: load_library_via_cargo (
285- library_manifest,
286- & sysroot_dir,
287- & sysroot_src_dir,
288- cargo_config,
289- ) {
290- return sysroot;
236+ if let Some ( loaded) =
237+ Self :: load_library_via_cargo ( library_manifest, src_root, cargo_config)
238+ {
239+ * workspace = loaded;
240+ self . load_core_check ( ) ;
241+ return ;
291242 }
292243 }
293244 }
294- tracing:: debug!( "Stitching sysroot library: {sysroot_src_dir }" ) ;
245+ tracing:: debug!( "Stitching sysroot library: {src_root }" ) ;
295246
296247 let mut stitched = Stitched { crates : Arena :: default ( ) } ;
297248
298249 for path in SYSROOT_CRATES . trim ( ) . lines ( ) {
299250 let name = path. split ( '/' ) . last ( ) . unwrap ( ) ;
300251 let root = [ format ! ( "{path}/src/lib.rs" ) , format ! ( "lib{path}/lib.rs" ) ]
301252 . into_iter ( )
302- . map ( |it| sysroot_src_dir . join ( it) )
253+ . map ( |it| src_root . join ( it) )
303254 . filter_map ( |it| ManifestPath :: try_from ( it) . ok ( ) )
304255 . find ( |it| fs:: metadata ( it) . is_ok ( ) ) ;
305256
@@ -335,20 +286,37 @@ impl Sysroot {
335286 }
336287 }
337288 }
338- Sysroot {
339- root : sysroot_dir,
340- src_root : Some ( sysroot_src_dir) ,
341- mode : SysrootMode :: Stitched ( stitched) ,
342- error : None ,
289+ * workspace = SysrootWorkspace :: Stitched ( stitched) ;
290+ self . load_core_check ( ) ;
291+ }
292+
293+ fn load_core_check ( & mut self ) {
294+ if self . error . is_none ( ) {
295+ if let Some ( src_root) = & self . src_root {
296+ let has_core = match & self . workspace {
297+ SysrootWorkspace :: Workspace ( ws) => ws. packages ( ) . any ( |p| ws[ p] . name == "core" ) ,
298+ SysrootWorkspace :: Stitched ( stitched) => stitched. by_name ( "core" ) . is_some ( ) ,
299+ SysrootWorkspace :: Empty => true ,
300+ } ;
301+ if !has_core {
302+ let var_note = if env:: var_os ( "RUST_SRC_PATH" ) . is_some ( ) {
303+ " (env var `RUST_SRC_PATH` is set and may be incorrect, try unsetting it)"
304+ } else {
305+ ", try running `rustup component add rust-src` to possibly fix this"
306+ } ;
307+ self . error = Some ( format ! (
308+ "sysroot at `{src_root}` is missing a `core` library{var_note}" ,
309+ ) ) ;
310+ }
311+ }
343312 }
344313 }
345314
346315 fn load_library_via_cargo (
347316 library_manifest : ManifestPath ,
348- sysroot_dir : & Option < AbsPathBuf > ,
349317 sysroot_src_dir : & AbsPathBuf ,
350318 cargo_config : & CargoMetadataConfig ,
351- ) -> Option < Sysroot > {
319+ ) -> Option < SysrootWorkspace > {
352320 tracing:: debug!( "Loading library metadata: {library_manifest}" ) ;
353321 let mut cargo_config = cargo_config. clone ( ) ;
354322 // the sysroot uses `public-dependency`, so we make cargo think it's a nightly
@@ -423,12 +391,7 @@ impl Sysroot {
423391 } ) ;
424392
425393 let cargo_workspace = CargoWorkspace :: new ( res, library_manifest, Default :: default ( ) ) ;
426- Some ( Sysroot {
427- root : sysroot_dir. clone ( ) ,
428- src_root : Some ( sysroot_src_dir. clone ( ) ) ,
429- mode : SysrootMode :: Workspace ( cargo_workspace) ,
430- error : None ,
431- } )
394+ Some ( SysrootWorkspace :: Workspace ( cargo_workspace) )
432395 }
433396}
434397
0 commit comments