9
9
// except according to those terms.
10
10
11
11
use cargo:: core:: { PackageId , Shell , Workspace , Verbosity } ;
12
- use cargo:: ops:: { compile_with_exec, Executor , Context , CompileOptions , CompileMode , CompileFilter , Unit } ;
13
- use cargo:: util:: { Config as CargoConfig , ProcessBuilder , homedir, ConfigValue } ;
14
- use cargo:: util:: { CargoResult } ;
12
+ use cargo:: ops:: { compile_with_exec, Executor , Context , Packages , CompileOptions , CompileMode , CompileFilter , Unit } ;
13
+ use cargo:: util:: { Config as CargoConfig , ProcessBuilder , homedir, important_paths, ConfigValue , CargoResult } ;
15
14
16
15
use build:: { Internals , BufWriter , BuildResult , CompilationContext } ;
17
16
use config:: Config ;
17
+ use super :: rustc:: convert_message_to_json_strings;
18
18
19
- use std:: collections:: { HashMap , BTreeMap } ;
19
+ use std:: collections:: { HashMap , HashSet , BTreeMap } ;
20
20
use std:: env;
21
21
use std:: ffi:: OsString ;
22
22
use std:: fs:: { read_dir, remove_file} ;
@@ -25,11 +25,13 @@ use std::process::Command;
25
25
use std:: sync:: { Arc , Mutex } ;
26
26
use std:: thread;
27
27
28
-
29
28
impl Internals {
30
29
// Runs an in-process instance of Cargo.
31
30
pub fn cargo ( & self ) -> BuildResult {
32
- let exec = RlsExecutor :: new ( self . compilation_cx . clone ( ) , self . config . clone ( ) ) ;
31
+ let workspace_mode = self . config . lock ( ) . unwrap ( ) . workspace_mode ;
32
+ let compiler_messages = Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ;
33
+
34
+ let exec = RlsExecutor :: new ( self . compilation_cx . clone ( ) , self . config . clone ( ) , compiler_messages. clone ( ) ) ;
33
35
34
36
let out = Arc :: new ( Mutex :: new ( vec ! [ ] ) ) ;
35
37
let out_clone = out. clone ( ) ;
@@ -46,6 +48,10 @@ impl Internals {
46
48
let handle = thread:: spawn ( move || run_cargo ( exec, rls_config, build_dir, out) ) ;
47
49
48
50
match handle. join ( ) {
51
+ Ok ( _) if workspace_mode => {
52
+ let diagnostics = Arc :: try_unwrap ( compiler_messages) . unwrap ( ) . into_inner ( ) . unwrap ( ) ;
53
+ BuildResult :: Success ( diagnostics, None )
54
+ } ,
49
55
Ok ( _) => BuildResult :: Success ( vec ! [ ] , None ) ,
50
56
Err ( _) => {
51
57
info ! ( "cargo stdout {}" , String :: from_utf8( out_clone. lock( ) . unwrap( ) . to_owned( ) ) . unwrap( ) ) ;
@@ -56,98 +62,132 @@ impl Internals {
56
62
}
57
63
58
64
fn run_cargo ( exec : RlsExecutor , rls_config : Arc < Mutex < Config > > , build_dir : PathBuf , out : Arc < Mutex < Vec < u8 > > > ) {
59
- let mut flags = "-Zunstable-options -Zsave-analysis --error-format=json \
60
- -Zcontinue-parse-after-error". to_owned ( ) ;
65
+ // Note that this may not be equal build_dir when inside a workspace member
66
+ let manifest_path = important_paths:: find_root_manifest_for_wd ( None , & build_dir)
67
+ . expect ( & format ! ( "Couldn't find a root manifest for cwd: {:?}" , & build_dir) ) ;
68
+ trace ! ( "root manifest_path: {:?}" , & manifest_path) ;
61
69
62
70
let mut shell = Shell :: from_write ( Box :: new ( BufWriter ( out. clone ( ) ) ) ) ;
63
71
shell. set_verbosity ( Verbosity :: Quiet ) ;
64
- let mut manifest_path = build_dir ;
65
- let config = make_cargo_config ( & manifest_path , shell ) ;
66
- manifest_path. push ( "Cargo.toml" ) ;
67
- trace ! ( "manifest_path: {:?}" , manifest_path ) ;
68
- // TODO: Add support for virtual manifests and multiple packages
72
+
73
+ // Cargo constructs relative paths from the manifest dir, so we have to pop "Cargo.toml"
74
+ let manifest_dir = manifest_path. parent ( ) . unwrap ( ) ;
75
+ let config = make_cargo_config ( manifest_dir , shell ) ;
76
+
69
77
let ws = Workspace :: new ( & manifest_path, & config) . expect ( "could not create cargo workspace" ) ;
70
- let current_package = ws. current ( ) . unwrap ( ) ;
71
- let targets = current_package. targets ( ) ;
72
- let bins;
73
- let target_string;
74
78
75
- let opts = {
79
+ // TODO: It might be feasible to keep this CargoOptions structure cached and regenerate
80
+ // it on every relevant configuration change
81
+ let ( opts, rustflags) = {
82
+ // We mustn't lock configuration for the whole build process
76
83
let rls_config = rls_config. lock ( ) . unwrap ( ) ;
77
- if let Some ( ref sysroot ) = rls_config . sysroot {
78
- flags . push_str ( & format ! ( " --sysroot {}" , sysroot ) ) ;
79
- }
80
- let rustflags = format ! ( "{} {} {}" ,
81
- env :: var ( "RUSTFLAGS" ) . unwrap_or ( String :: new ( ) ) ,
82
- rls_config . rustflags . as_ref ( ) . unwrap_or ( & String :: new ( ) ) ,
83
- flags ) ;
84
- let rustflags = dedup_flags ( & rustflags ) ;
85
- env :: set_var ( "RUSTFLAGS" , & rustflags ) ;
86
-
87
- bins = {
88
- if let Some ( ref build_bin ) = rls_config . build_bin {
89
- let mut bins = targets . iter ( ) . filter ( |x| x. is_bin ( ) ) ;
90
- let bin = bins . find ( |x| x . name ( ) == build_bin) ;
91
- match bin {
92
- Some ( bin ) => vec ! [ bin . name ( ) . to_owned ( ) ] ,
93
- None => {
94
- debug ! ( "cargo - couldn't find binary `{}` (specified in rls toml file)" , build_bin ) ;
95
- vec ! [ ]
96
- }
84
+
85
+ let opts = CargoOptions :: new ( & rls_config ) ;
86
+ trace ! ( "Cargo compilation options: \n {:?}" , opts ) ;
87
+ let rustflags = prepare_cargo_rustflags ( & rls_config ) ;
88
+
89
+ // Warn about invalid specified bin target or package depending on current mode
90
+ // TODO: Return client notifications along with diagnostics to inform the user
91
+ if !rls_config . workspace_mode {
92
+ let cur_pkg_targets = ws . current ( ) . unwrap ( ) . targets ( ) ;
93
+
94
+ if let Some ( ref build_bin ) = rls_config . build_bin {
95
+ let mut bins = cur_pkg_targets . iter ( ) . filter ( |x| x . is_bin ( ) ) ;
96
+ if let None = bins . find ( |x| x. name ( ) == build_bin ) {
97
+ warn ! ( "cargo - couldn't find binary `{}` specified in `build_bin` configuration" , build_bin) ;
98
+ }
99
+ }
100
+ } else {
101
+ for package in & opts . package {
102
+ if let None = ws . members ( ) . find ( |x| x . name ( ) == package ) {
103
+ warn ! ( "cargo - couldn't find member package `{}` specified in `analyze_package` configuration" , package ) ;
97
104
}
98
- } else {
99
- vec ! [ ]
100
105
}
101
- } ;
102
-
103
- let mut opts = CompileOptions :: default ( & config, CompileMode :: Check ) ;
104
- if rls_config. build_lib {
105
- opts. filter = CompileFilter :: new ( true , & [ ] , false , & [ ] , false , & [ ] , false , & [ ] , false ) ;
106
- } else if !bins. is_empty ( ) {
107
- opts. filter = CompileFilter :: new ( false , & bins, false , & [ ] , false , & [ ] , false , & [ ] , false ) ;
108
- }
109
- if let Some ( ref target) = rls_config. target {
110
- target_string = target. clone ( ) ;
111
- opts. target = Some ( & target_string) ;
112
106
}
113
- opts
107
+
108
+ ( opts, rustflags)
109
+ } ;
110
+
111
+ let spec = Packages :: from_flags ( opts. all , & opts. exclude , & opts. package )
112
+ . expect ( "Couldn't create Packages for Cargo" ) ;
113
+
114
+ let compile_opts = CompileOptions {
115
+ target : opts. target . as_ref ( ) . map ( |t| & t[ ..] ) ,
116
+ spec : spec,
117
+ filter : CompileFilter :: new ( opts. lib ,
118
+ & opts. bin , opts. bins ,
119
+ // TODO: Support more crate target types
120
+ & [ ] , false , & [ ] , false , & [ ] , false ) ,
121
+ .. CompileOptions :: default ( & config, CompileMode :: Check )
114
122
} ;
115
- compile_with_exec ( & ws, & opts, Arc :: new ( exec) ) . expect ( "could not run cargo" ) ;
123
+
124
+ env:: set_var ( "RUSTFLAGS" , rustflags) ;
125
+ compile_with_exec ( & ws, & compile_opts, Arc :: new ( exec) ) . expect ( "could not run cargo" ) ;
116
126
}
117
127
118
128
struct RlsExecutor {
119
129
compilation_cx : Arc < Mutex < CompilationContext > > ,
120
130
cur_package_id : Mutex < Option < PackageId > > ,
121
131
config : Arc < Mutex < Config > > ,
132
+ workspace_mode : bool ,
133
+ /// Packages which are directly a member of the workspace, for which
134
+ /// analysis and diagnostics will be provided
135
+ member_packages : Mutex < HashSet < PackageId > > ,
136
+ /// JSON compiler messages emitted for each primary compiled crate
137
+ compiler_messages : Arc < Mutex < Vec < String > > > ,
122
138
}
123
139
124
140
impl RlsExecutor {
125
141
fn new ( compilation_cx : Arc < Mutex < CompilationContext > > ,
126
- config : Arc < Mutex < Config > > ) -> RlsExecutor {
142
+ config : Arc < Mutex < Config > > ,
143
+ compiler_messages : Arc < Mutex < Vec < String > > > )
144
+ -> RlsExecutor {
145
+ let workspace_mode = config. lock ( ) . unwrap ( ) . workspace_mode ;
127
146
RlsExecutor {
128
- compilation_cx : compilation_cx ,
147
+ compilation_cx,
129
148
cur_package_id : Mutex :: new ( None ) ,
130
149
config,
150
+ workspace_mode,
151
+ member_packages : Mutex :: new ( HashSet :: new ( ) ) ,
152
+ compiler_messages,
131
153
}
132
154
}
133
155
134
156
fn is_primary_crate ( & self , id : & PackageId ) -> bool {
135
- let cur_package_id = self . cur_package_id . lock ( ) . unwrap ( ) ;
136
- id == cur_package_id. as_ref ( ) . expect ( "Executor has not been initialised" )
157
+ if self . workspace_mode {
158
+ self . member_packages . lock ( ) . unwrap ( ) . contains ( id)
159
+ } else {
160
+ let cur_package_id = self . cur_package_id . lock ( ) . unwrap ( ) ;
161
+ id == cur_package_id. as_ref ( ) . expect ( "Executor has not been initialised" )
162
+ }
137
163
}
138
164
}
139
165
140
166
impl Executor for RlsExecutor {
141
167
fn init ( & self , cx : & Context ) {
142
- let mut cur_package_id = self . cur_package_id . lock ( ) . unwrap ( ) ;
143
- * cur_package_id = Some ( cx. ws
144
- . current_opt ( )
145
- . expect ( "No current package in Cargo" )
146
- . package_id ( )
147
- . clone ( ) ) ;
168
+ if self . workspace_mode {
169
+ * self . member_packages . lock ( ) . unwrap ( ) = cx. ws
170
+ . members ( )
171
+ . map ( |x| x. package_id ( ) . clone ( ) )
172
+ . collect ( ) ;
173
+ } else {
174
+ let mut cur_package_id = self . cur_package_id . lock ( ) . unwrap ( ) ;
175
+ * cur_package_id = Some ( cx. ws
176
+ . current_opt ( )
177
+ . expect ( "No current package in Cargo" )
178
+ . package_id ( )
179
+ . clone ( ) ) ;
180
+ } ;
148
181
}
149
182
150
183
fn force_rebuild ( & self , unit : & Unit ) -> bool {
184
+ // TODO: Currently workspace_mode doesn't use rustc, so it doesn't
185
+ // need args. When we start using rustc, we might consider doing
186
+ // force_rebuild to retrieve args for given package if they're stale/missing
187
+ if self . workspace_mode {
188
+ return false ;
189
+ }
190
+
151
191
// We only do a cargo build if we want to force rebuild the last
152
192
// crate (e.g., because some args changed). Therefore we should
153
193
// always force rebuild the primary crate.
@@ -235,7 +275,7 @@ impl Executor for RlsExecutor {
235
275
for a in & args {
236
276
// Emitting only dep-info is possible only for final crate type, as
237
277
// as others may emit required metadata for dependent crate types
238
- if a. starts_with ( "--emit" ) && is_final_crate_type {
278
+ if a. starts_with ( "--emit" ) && is_final_crate_type && ! self . workspace_mode {
239
279
cmd. arg ( "--emit=dep-info" ) ;
240
280
} else {
241
281
cmd. arg ( a) ;
@@ -247,7 +287,13 @@ impl Executor for RlsExecutor {
247
287
}
248
288
}
249
289
250
- cmd. status ( ) . expect ( "Couldn't execute rustc" ) ;
290
+ if self . workspace_mode {
291
+ let output = cmd. output ( ) . expect ( "Couldn't execute rustc" ) ;
292
+ let mut stderr_json_msg = convert_message_to_json_strings ( output. stderr ) ;
293
+ self . compiler_messages . lock ( ) . unwrap ( ) . append ( & mut stderr_json_msg) ;
294
+ } else {
295
+ cmd. status ( ) . expect ( "Couldn't execute rustc" ) ;
296
+ }
251
297
252
298
// Finally, store the modified cargo-generated args/envs for future rustc calls
253
299
args. insert ( 0 , rustc_exe) ;
@@ -259,10 +305,88 @@ impl Executor for RlsExecutor {
259
305
}
260
306
}
261
307
308
+ #[ derive( Debug ) ]
309
+ struct CargoOptions {
310
+ package : Vec < String > ,
311
+ target : Option < String > ,
312
+ lib : bool ,
313
+ bin : Vec < String > ,
314
+ bins : bool ,
315
+ all : bool ,
316
+ exclude : Vec < String > ,
317
+ }
318
+
319
+ impl CargoOptions {
320
+ fn default ( ) -> CargoOptions {
321
+ CargoOptions {
322
+ package : vec ! [ ] ,
323
+ target : None ,
324
+ lib : false ,
325
+ bin : vec ! [ ] ,
326
+ bins : false ,
327
+ all : false ,
328
+ exclude : vec ! [ ] ,
329
+ }
330
+ }
331
+
332
+ fn new ( config : & Config ) -> CargoOptions {
333
+ if config. workspace_mode {
334
+ let ( package, all) = match config. analyze_package {
335
+ Some ( ref pkg_name) => ( vec ! [ pkg_name. clone( ) ] , false ) ,
336
+ None => ( vec ! [ ] , true ) ,
337
+ } ;
338
+
339
+ CargoOptions {
340
+ package,
341
+ all,
342
+ target : config. target . clone ( ) ,
343
+ .. CargoOptions :: default ( )
344
+ }
345
+ } else {
346
+ // In single-crate mode we currently support only one crate target,
347
+ // and if lib is set, then we ignore bin target config
348
+ let ( lib, bin) = match config. build_lib {
349
+ true => ( true , vec ! [ ] ) ,
350
+ false => {
351
+ let bin = match config. build_bin {
352
+ Some ( ref bin) => vec ! [ bin. clone( ) ] ,
353
+ None => vec ! [ ] ,
354
+ } ;
355
+ ( false , bin)
356
+ } ,
357
+ } ;
358
+
359
+ CargoOptions {
360
+ lib,
361
+ bin,
362
+ target : config. target . clone ( ) ,
363
+ .. CargoOptions :: default ( )
364
+ }
365
+ }
366
+ }
367
+ }
368
+
369
+ fn prepare_cargo_rustflags ( config : & Config ) -> String {
370
+ let mut flags = "-Zunstable-options -Zsave-analysis --error-format=json \
371
+ -Zcontinue-parse-after-error". to_owned ( ) ;
372
+
373
+ if let Some ( ref sysroot) = config. sysroot {
374
+ flags. push_str ( & format ! ( " --sysroot {}" , sysroot) ) ;
375
+ }
376
+
377
+ flags = format ! ( "{} {} {}" ,
378
+ env:: var( "RUSTFLAGS" ) . unwrap_or( String :: new( ) ) ,
379
+ config. rustflags. as_ref( ) . unwrap_or( & String :: new( ) ) ,
380
+ flags) ;
381
+
382
+ dedup_flags ( & flags)
383
+ }
384
+
262
385
fn make_cargo_config ( build_dir : & Path , shell : Shell ) -> CargoConfig {
263
386
let config = CargoConfig :: new ( shell,
264
- // This is Cargo's cwd. We are using the actual cwd, but perhaps
265
- // we should use build_dir or something else?
387
+ // This is Cargo's cwd. We're using the actual cwd,
388
+ // because Cargo will generate relative paths based
389
+ // on this to source files it wants to compile
266
390
env:: current_dir ( ) . unwrap ( ) ,
267
391
homedir ( & build_dir) . unwrap ( ) ) ;
268
392
0 commit comments