49
49
//! The current scheduling algorithm is relatively primitive and could likely be
50
50
//! improved.
51
51
52
- use std:: cell:: Cell ;
52
+ use std:: cell:: { Cell , RefCell } ;
53
53
use std:: collections:: { BTreeMap , BTreeSet , HashMap , HashSet } ;
54
+ use std:: fmt:: Write as _;
54
55
use std:: io;
55
56
use std:: marker;
56
57
use std:: sync:: Arc ;
@@ -125,6 +126,14 @@ struct DrainState<'cfg> {
125
126
126
127
queue : DependencyQueue < Unit , Artifact , Job > ,
127
128
messages : Arc < Queue < Message > > ,
129
+ /// Diagnostic deduplication support.
130
+ diag_dedupe : DiagDedupe < ' cfg > ,
131
+ /// Count of warnings, used to print a summary after the job succeeds.
132
+ ///
133
+ /// First value is the total number of warnings, and the second value is
134
+ /// the number that were suppressed because they were duplicates of a
135
+ /// previous warning.
136
+ warning_count : HashMap < JobId , ( usize , usize ) > ,
128
137
active : HashMap < JobId , Unit > ,
129
138
compiled : HashSet < PackageId > ,
130
139
documented : HashSet < PackageId > ,
@@ -174,7 +183,7 @@ impl std::fmt::Display for JobId {
174
183
///
175
184
/// The job may execute on either a dedicated thread or the main thread. If the job executes on the
176
185
/// main thread, the `output` field must be set to prevent a deadlock.
177
- pub struct JobState < ' a > {
186
+ pub struct JobState < ' a , ' cfg > {
178
187
/// Channel back to the main thread to coordinate messages and such.
179
188
///
180
189
/// When the `output` field is `Some`, care must be taken to avoid calling `push_bounded` on
@@ -191,7 +200,7 @@ pub struct JobState<'a> {
191
200
/// interleaved. In the future, it may be wrapped in a `Mutex` instead. In this case
192
201
/// interleaving is still prevented as the lock would be held for the whole printing of an
193
202
/// output message.
194
- output : Option < & ' a Config > ,
203
+ output : Option < & ' a DiagDedupe < ' cfg > > ,
195
204
196
205
/// The job id that this state is associated with, used when sending
197
206
/// messages back to the main thread.
@@ -207,6 +216,36 @@ pub struct JobState<'a> {
207
216
_marker : marker:: PhantomData < & ' a ( ) > ,
208
217
}
209
218
219
+ /// Handler for deduplicating diagnostics.
220
+ struct DiagDedupe < ' cfg > {
221
+ seen : RefCell < HashSet < u64 > > ,
222
+ config : & ' cfg Config ,
223
+ }
224
+
225
+ impl < ' cfg > DiagDedupe < ' cfg > {
226
+ fn new ( config : & ' cfg Config ) -> Self {
227
+ DiagDedupe {
228
+ seen : RefCell :: new ( HashSet :: new ( ) ) ,
229
+ config,
230
+ }
231
+ }
232
+
233
+ /// Emits a diagnostic message.
234
+ ///
235
+ /// Returns `true` if the message was emitted, or `false` if it was
236
+ /// suppressed for being a duplicate.
237
+ fn emit_diag ( & self , diag : & str ) -> CargoResult < bool > {
238
+ let h = util:: hash_u64 ( diag) ;
239
+ if !self . seen . borrow_mut ( ) . insert ( h) {
240
+ return Ok ( false ) ;
241
+ }
242
+ let mut shell = self . config . shell ( ) ;
243
+ shell. print_ansi_stderr ( diag. as_bytes ( ) ) ?;
244
+ shell. err ( ) . write_all ( b"\n " ) ?;
245
+ Ok ( true )
246
+ }
247
+ }
248
+
210
249
/// Possible artifacts that can be produced by compilations, used as edge values
211
250
/// in the dependency graph.
212
251
///
@@ -232,6 +271,15 @@ enum Message {
232
271
BuildPlanMsg ( String , ProcessBuilder , Arc < Vec < OutputFile > > ) ,
233
272
Stdout ( String ) ,
234
273
Stderr ( String ) ,
274
+ Diagnostic {
275
+ id : JobId ,
276
+ level : String ,
277
+ diag : String ,
278
+ } ,
279
+ WarningCount {
280
+ id : JobId ,
281
+ emitted : bool ,
282
+ } ,
235
283
FixDiagnostic ( diagnostic_server:: Message ) ,
236
284
Token ( io:: Result < Acquired > ) ,
237
285
Finish ( JobId , Artifact , CargoResult < ( ) > ) ,
@@ -244,7 +292,7 @@ enum Message {
244
292
ReleaseToken ( JobId ) ,
245
293
}
246
294
247
- impl < ' a > JobState < ' a > {
295
+ impl < ' a , ' cfg > JobState < ' a , ' cfg > {
248
296
pub fn running ( & self , cmd : & ProcessBuilder ) {
249
297
self . messages . push ( Message :: Run ( self . id , cmd. to_string ( ) ) ) ;
250
298
}
@@ -260,17 +308,17 @@ impl<'a> JobState<'a> {
260
308
}
261
309
262
310
pub fn stdout ( & self , stdout : String ) -> CargoResult < ( ) > {
263
- if let Some ( config ) = self . output {
264
- writeln ! ( config. shell( ) . out( ) , "{}" , stdout) ?;
311
+ if let Some ( dedupe ) = self . output {
312
+ writeln ! ( dedupe . config. shell( ) . out( ) , "{}" , stdout) ?;
265
313
} else {
266
314
self . messages . push_bounded ( Message :: Stdout ( stdout) ) ;
267
315
}
268
316
Ok ( ( ) )
269
317
}
270
318
271
319
pub fn stderr ( & self , stderr : String ) -> CargoResult < ( ) > {
272
- if let Some ( config ) = self . output {
273
- let mut shell = config. shell ( ) ;
320
+ if let Some ( dedupe ) = self . output {
321
+ let mut shell = dedupe . config . shell ( ) ;
274
322
shell. print_ansi_stderr ( stderr. as_bytes ( ) ) ?;
275
323
shell. err ( ) . write_all ( b"\n " ) ?;
276
324
} else {
@@ -279,6 +327,25 @@ impl<'a> JobState<'a> {
279
327
Ok ( ( ) )
280
328
}
281
329
330
+ pub fn emit_diag ( & self , level : String , diag : String ) -> CargoResult < ( ) > {
331
+ if let Some ( dedupe) = self . output {
332
+ let emitted = dedupe. emit_diag ( & diag) ?;
333
+ if level == "warning" {
334
+ self . messages . push ( Message :: WarningCount {
335
+ id : self . id ,
336
+ emitted,
337
+ } ) ;
338
+ }
339
+ } else {
340
+ self . messages . push_bounded ( Message :: Diagnostic {
341
+ id : self . id ,
342
+ level,
343
+ diag,
344
+ } ) ;
345
+ }
346
+ Ok ( ( ) )
347
+ }
348
+
282
349
/// A method used to signal to the coordinator thread that the rmeta file
283
350
/// for an rlib has been produced. This is only called for some rmeta
284
351
/// builds when required, and can be called at any time before a job ends.
@@ -410,6 +477,8 @@ impl<'cfg> JobQueue<'cfg> {
410
477
// typical messages. If you change this, please update the test
411
478
// caching_large_output, too.
412
479
messages : Arc :: new ( Queue :: new ( 100 ) ) ,
480
+ diag_dedupe : DiagDedupe :: new ( cx. bcx . config ) ,
481
+ warning_count : HashMap :: new ( ) ,
413
482
active : HashMap :: new ( ) ,
414
483
compiled : HashSet :: new ( ) ,
415
484
documented : HashSet :: new ( ) ,
@@ -563,6 +632,15 @@ impl<'cfg> DrainState<'cfg> {
563
632
shell. print_ansi_stderr ( err. as_bytes ( ) ) ?;
564
633
shell. err ( ) . write_all ( b"\n " ) ?;
565
634
}
635
+ Message :: Diagnostic { id, level, diag } => {
636
+ let emitted = self . diag_dedupe . emit_diag ( & diag) ?;
637
+ if level == "warning" {
638
+ self . bump_warning_count ( id, emitted) ;
639
+ }
640
+ }
641
+ Message :: WarningCount { id, emitted } => {
642
+ self . bump_warning_count ( id, emitted) ;
643
+ }
566
644
Message :: FixDiagnostic ( msg) => {
567
645
self . print . print ( & msg) ?;
568
646
}
@@ -586,6 +664,7 @@ impl<'cfg> DrainState<'cfg> {
586
664
self . tokens . extend ( rustc_tokens) ;
587
665
}
588
666
self . to_send_clients . remove ( & id) ;
667
+ self . report_warning_count ( cx. bcx . config , id) ;
589
668
self . active . remove ( & id) . unwrap ( )
590
669
}
591
670
// ... otherwise if it hasn't finished we leave it
@@ -936,7 +1015,7 @@ impl<'cfg> DrainState<'cfg> {
936
1015
let fresh = job. freshness ( ) ;
937
1016
let rmeta_required = cx. rmeta_required ( unit) ;
938
1017
939
- let doit = move |state : JobState < ' _ > | {
1018
+ let doit = move |state : JobState < ' _ , ' _ > | {
940
1019
let mut sender = FinishOnDrop {
941
1020
messages : & state. messages ,
942
1021
id,
@@ -992,7 +1071,7 @@ impl<'cfg> DrainState<'cfg> {
992
1071
doit ( JobState {
993
1072
id,
994
1073
messages,
995
- output : Some ( cx . bcx . config ) ,
1074
+ output : Some ( & self . diag_dedupe ) ,
996
1075
rmeta_required : Cell :: new ( rmeta_required) ,
997
1076
_marker : marker:: PhantomData ,
998
1077
} ) ;
@@ -1044,6 +1123,44 @@ impl<'cfg> DrainState<'cfg> {
1044
1123
Ok ( ( ) )
1045
1124
}
1046
1125
1126
+ fn bump_warning_count ( & mut self , id : JobId , emitted : bool ) {
1127
+ let cnts = self . warning_count . entry ( id) . or_default ( ) ;
1128
+ cnts. 0 += 1 ;
1129
+ if !emitted {
1130
+ cnts. 1 += 1 ;
1131
+ }
1132
+ }
1133
+
1134
+ /// Displays a final report of the warnings emitted by a particular job.
1135
+ fn report_warning_count ( & mut self , config : & Config , id : JobId ) {
1136
+ let count = match self . warning_count . remove ( & id) {
1137
+ Some ( count) => count,
1138
+ None => return ,
1139
+ } ;
1140
+ let unit = & self . active [ & id] ;
1141
+ let mut message = format ! ( "`{}` ({}" , unit. pkg. name( ) , unit. target. description_named( ) ) ;
1142
+ if unit. mode . is_rustc_test ( ) && !( unit. target . is_test ( ) || unit. target . is_bench ( ) ) {
1143
+ message. push_str ( " test" ) ;
1144
+ } else if unit. mode . is_doc_test ( ) {
1145
+ message. push_str ( " doctest" ) ;
1146
+ } else if unit. mode . is_doc ( ) {
1147
+ message. push_str ( " doc" ) ;
1148
+ }
1149
+ message. push_str ( ") generated " ) ;
1150
+ match count. 0 {
1151
+ 1 => message. push_str ( "1 warning" ) ,
1152
+ n => drop ( write ! ( message, "{} warnings" , n) ) ,
1153
+ } ;
1154
+ match count. 1 {
1155
+ 0 => { }
1156
+ 1 => message. push_str ( " (1 duplicate)" ) ,
1157
+ n => drop ( write ! ( message, " ({} duplicates)" , n) ) ,
1158
+ }
1159
+ // Errors are ignored here because it is tricky to handle them
1160
+ // correctly, and they aren't important.
1161
+ drop ( config. shell ( ) . warn ( message) ) ;
1162
+ }
1163
+
1047
1164
fn finish (
1048
1165
& mut self ,
1049
1166
id : JobId ,
0 commit comments