4
4
//! but we can't process `.rlib` and need source code instead. The source code
5
5
//! is typically installed with `rustup component add rust-src` command.
6
6
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
+ } ;
8
13
9
14
use anyhow:: { format_err, Result } ;
10
15
use base_db:: CrateName ;
11
16
use itertools:: Itertools ;
12
17
use la_arena:: { Arena , Idx } ;
13
18
use paths:: { AbsPath , AbsPathBuf , Utf8PathBuf } ;
14
19
use rustc_hash:: FxHashMap ;
20
+ use stdx:: format_to;
15
21
use toolchain:: { probe_for_binary, Tool } ;
16
22
17
23
use crate :: {
18
24
cargo_workspace:: CargoMetadataConfig , utf8_stdout, CargoWorkspace , ManifestPath ,
19
- SysrootQueryMetadata ,
25
+ SysrootSourceWorkspaceConfig ,
20
26
} ;
21
27
22
28
#[ derive( Debug , Clone , PartialEq , Eq ) ]
23
29
pub struct Sysroot {
24
30
root : Option < AbsPathBuf > ,
25
31
src_root : Option < AbsPathBuf > ,
26
- mode : SysrootMode ,
32
+ workspace : SysrootWorkspace ,
27
33
error : Option < String > ,
28
34
}
29
35
30
36
#[ derive( Debug , Clone , Eq , PartialEq ) ]
31
- pub ( crate ) enum SysrootMode {
37
+ pub ( crate ) enum SysrootWorkspace {
32
38
Workspace ( CargoWorkspace ) ,
33
39
Stitched ( Stitched ) ,
34
40
Empty ,
@@ -82,7 +88,7 @@ pub(crate) struct SysrootCrateData {
82
88
83
89
impl Sysroot {
84
90
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 }
86
92
}
87
93
88
94
/// Returns sysroot "root" directory, where `bin/`, `etc/`, `lib/`, `libexec/`
@@ -99,10 +105,10 @@ impl Sysroot {
99
105
}
100
106
101
107
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 ,
106
112
}
107
113
}
108
114
@@ -111,64 +117,51 @@ impl Sysroot {
111
117
}
112
118
113
119
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 ,
118
124
}
119
125
}
120
126
121
- pub ( crate ) fn mode ( & self ) -> & SysrootMode {
122
- & self . mode
127
+ pub ( crate ) fn workspace ( & self ) -> & SysrootWorkspace {
128
+ & self . workspace
123
129
}
124
130
}
125
131
126
- // FIXME: Expose a builder api as loading the sysroot got way too modular and complicated.
127
132
impl Sysroot {
128
133
/// 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 {
134
135
let sysroot_dir = discover_sysroot_dir ( dir, extra_env) ;
135
136
let sysroot_src_dir = sysroot_dir. as_ref ( ) . ok ( ) . map ( |sysroot_dir| {
136
137
discover_sysroot_src_dir_or_add_component ( sysroot_dir, dir, extra_env)
137
138
} ) ;
138
- Sysroot :: load_core_check ( Some ( sysroot_dir) , sysroot_src_dir, sysroot_query_metadata )
139
+ Sysroot :: assemble ( Some ( sysroot_dir) , sysroot_src_dir)
139
140
}
140
141
141
142
pub fn discover_with_src_override (
142
143
current_dir : & AbsPath ,
143
144
extra_env : & FxHashMap < String , String > ,
144
145
sysroot_src_dir : AbsPathBuf ,
145
- sysroot_query_metadata : & SysrootQueryMetadata ,
146
146
) -> Sysroot {
147
147
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) ) )
153
149
}
154
150
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 {
159
152
let sysroot_src_dir = discover_sysroot_src_dir ( & sysroot_dir)
160
153
. 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) )
166
155
}
167
156
168
157
pub fn discover_rustc_src ( & self ) -> Option < ManifestPath > {
169
158
get_rustc_src ( self . root ( ) ?)
170
159
}
171
160
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
+
172
165
/// Returns a command to run a tool preferring the cargo proxies if the sysroot exists.
173
166
pub fn tool ( & self , tool : Tool , current_dir : impl AsRef < Path > ) -> Command {
174
167
match self . root ( ) {
@@ -205,101 +198,59 @@ impl Sysroot {
205
198
} )
206
199
}
207
200
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 (
245
202
sysroot_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
246
203
sysroot_src_dir : Option < Result < AbsPathBuf , anyhow:: Error > > ,
247
- sysroot_query_metadata : & SysrootQueryMetadata ,
248
204
) -> Sysroot {
249
- let sysroot_dir = match sysroot_dir {
205
+ let mut errors = String :: new ( ) ;
206
+ let root = match sysroot_dir {
250
207
Some ( Ok ( sysroot_dir) ) => Some ( sysroot_dir) ,
251
208
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
258
211
}
259
212
None => None ,
260
213
} ;
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) ,
263
216
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
278
219
}
220
+ None => None ,
279
221
} ;
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 ( ) ;
283
235
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 ;
291
242
}
292
243
}
293
244
}
294
- tracing:: debug!( "Stitching sysroot library: {sysroot_src_dir }" ) ;
245
+ tracing:: debug!( "Stitching sysroot library: {src_root }" ) ;
295
246
296
247
let mut stitched = Stitched { crates : Arena :: default ( ) } ;
297
248
298
249
for path in SYSROOT_CRATES . trim ( ) . lines ( ) {
299
250
let name = path. split ( '/' ) . last ( ) . unwrap ( ) ;
300
251
let root = [ format ! ( "{path}/src/lib.rs" ) , format ! ( "lib{path}/lib.rs" ) ]
301
252
. into_iter ( )
302
- . map ( |it| sysroot_src_dir . join ( it) )
253
+ . map ( |it| src_root . join ( it) )
303
254
. filter_map ( |it| ManifestPath :: try_from ( it) . ok ( ) )
304
255
. find ( |it| fs:: metadata ( it) . is_ok ( ) ) ;
305
256
@@ -335,20 +286,37 @@ impl Sysroot {
335
286
}
336
287
}
337
288
}
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
+ }
343
312
}
344
313
}
345
314
346
315
fn load_library_via_cargo (
347
316
library_manifest : ManifestPath ,
348
- sysroot_dir : & Option < AbsPathBuf > ,
349
317
sysroot_src_dir : & AbsPathBuf ,
350
318
cargo_config : & CargoMetadataConfig ,
351
- ) -> Option < Sysroot > {
319
+ ) -> Option < SysrootWorkspace > {
352
320
tracing:: debug!( "Loading library metadata: {library_manifest}" ) ;
353
321
let mut cargo_config = cargo_config. clone ( ) ;
354
322
// the sysroot uses `public-dependency`, so we make cargo think it's a nightly
@@ -423,12 +391,7 @@ impl Sysroot {
423
391
} ) ;
424
392
425
393
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) )
432
395
}
433
396
}
434
397
0 commit comments