20
20
#include " clang/Config/config.h"
21
21
#include " clang/Driver/DriverDiagnostic.h"
22
22
#include " clang/Driver/Options.h"
23
+ #include " clang/Frontend/ChainedDiagnosticConsumer.h"
23
24
#include " clang/Frontend/CompileJobCacheKey.h"
24
25
#include " clang/Frontend/CompilerInstance.h"
25
26
#include " clang/Frontend/CompilerInvocation.h"
26
27
#include " clang/Frontend/FrontendDiagnostic.h"
28
+ #include " clang/Frontend/SerializedDiagnosticPrinter.h"
27
29
#include " clang/Frontend/TextDiagnosticBuffer.h"
28
30
#include " clang/Frontend/TextDiagnosticPrinter.h"
29
31
#include " clang/Frontend/Utils.h"
@@ -255,7 +257,8 @@ class CompileJobCache {
255
257
// / Replay a cache hit.
256
258
// /
257
259
// / Return status if should exit immediately, otherwise None.
258
- Optional<int > replayCachedResult (llvm::cas::ObjectRef ResultID,
260
+ Optional<int > replayCachedResult (CompilerInstance &Clang,
261
+ llvm::cas::ObjectRef ResultID,
259
262
bool JustComputedResult);
260
263
261
264
bool CacheCompileJob = false ;
@@ -266,6 +269,7 @@ class CompileJobCache {
266
269
std::unique_ptr<llvm::raw_ostream> ResultDiagsOS;
267
270
IntrusiveRefCntPtr<llvm::cas::CASOutputBackend> CASOutputs;
268
271
std::string OutputFile;
272
+ Optional<llvm::vfs::OutputFile> SerialDiagsOutput;
269
273
};
270
274
} // end anonymous namespace
271
275
@@ -330,32 +334,48 @@ class raw_mirroring_ostream : public llvm::raw_ostream {
330
334
};
331
335
} // namespace
332
336
337
+ static Expected<llvm::vfs::OutputFile>
338
+ createBinaryOutputFile (CompilerInstance &Clang, StringRef OutputPath) {
339
+ using namespace llvm ::vfs;
340
+ Expected<OutputFile> O = Clang.getOrCreateOutputBackend ().createFile (
341
+ OutputPath, OutputConfig ()
342
+ .setTextWithCRLF (false )
343
+ .setDiscardOnSignal (true )
344
+ .setAtomicWrite (true )
345
+ .setImplyCreateDirectories (false ));
346
+ if (!O)
347
+ return O.takeError ();
348
+
349
+ O->discardOnDestroy ([](llvm::Error E) { consumeError (std::move (E)); });
350
+ return O;
351
+ }
352
+
333
353
Optional<int > CompileJobCache::tryReplayCachedResult (CompilerInstance &Clang) {
334
354
if (!CacheCompileJob)
335
355
return None;
336
356
357
+ DiagnosticsEngine &Diags = Clang.getDiagnostics ();
358
+
337
359
// Create the result cache key once Invocation has been canonicalized.
338
- ResultCacheKey = createCompileJobCacheKey (*CAS, Clang.getDiagnostics (),
339
- Clang.getInvocation ());
360
+ ResultCacheKey = createCompileJobCacheKey (*CAS, Diags, Clang.getInvocation ());
340
361
if (!ResultCacheKey)
341
362
return 1 ;
342
363
343
364
Expected<llvm::cas::CASID> Result = CAS->getCachedResult (*ResultCacheKey);
344
365
if (Result) {
345
366
if (Optional<llvm::cas::ObjectRef> ResultRef = CAS->getReference (*Result)) {
346
- Clang. getDiagnostics () .Report (diag::remark_compile_job_cache_hit)
367
+ Diags .Report (diag::remark_compile_job_cache_hit)
347
368
<< ResultCacheKey->toString () << Result->toString ();
348
369
Optional<int > Status =
349
- replayCachedResult (*ResultRef, /* JustComputedResult=*/ false );
370
+ replayCachedResult (Clang, *ResultRef, /* JustComputedResult=*/ false );
350
371
assert (Status && " Expected a status for a cache hit" );
351
372
return *Status;
352
373
}
353
- Clang.getDiagnostics ().Report (
354
- diag::remark_compile_job_cache_miss_result_not_found)
374
+ Diags.Report (diag::remark_compile_job_cache_miss_result_not_found)
355
375
<< ResultCacheKey->toString () << Result->toString ();
356
376
} else {
357
377
llvm::consumeError (Result.takeError ());
358
- Clang. getDiagnostics () .Report (diag::remark_compile_job_cache_miss)
378
+ Diags .Report (diag::remark_compile_job_cache_miss)
359
379
<< ResultCacheKey->toString ();
360
380
}
361
381
@@ -373,22 +393,57 @@ Optional<int> CompileJobCache::tryReplayCachedResult(CompilerInstance &Clang) {
373
393
ResultDiagsOS = std::make_unique<raw_mirroring_ostream>(
374
394
llvm::errs (), std::make_unique<llvm::raw_svector_ostream>(ResultDiags));
375
395
376
- // FIXME: This should be saving/replaying serialized diagnostics, thus
377
- // using the current llvm::errs() colour capabilities. We still want to
378
- // print errors live during this compilation, just also serialize them.
396
+ // FIXME: This should be saving/replaying structured diagnostics, not saving
397
+ // stderr and a separate diagnostics file, thus using the current llvm::errs()
398
+ // colour capabilities and making the choice of whether colors are used, or
399
+ // whether a serialized diagnostics file is emitted, not affect the
400
+ // compilation key. We still want to print errors live during this
401
+ // compilation, just also serialize them. Another benefit of saving structured
402
+ // diagnostics is that it will enable remapping canonicalized paths in
403
+ // diagnostics to their non-canical form for displaying purposes
404
+ // (rdar://85234207).
379
405
//
380
- // However, serialized diagnostics can only be written to a file, not to a
381
- // raw_ostream. Need to fix that first. Also, maybe the format doesn't need
382
- // to be bitcode... we just want to serialize them faithfully such that we
383
- // can decide at output time whether to make the colours pretty.
406
+ // Note that the serialized diagnostics file format loses information, e.g.
407
+ // the include stack is written as additional 'note' diagnostics but when
408
+ // printed in terminal the include stack is printed in a different way than
409
+ // 'note' diagnostics. We should serialize/deserialize diagnostics in a way
410
+ // that we can accurately feed them to a DiagnosticConsumer (whatever that
411
+ // consumer implementation is doing). A potential way is to serialize data
412
+ // that can be deserialized as 'StoredDiagnostic's, which would be close to
413
+ // what the DiagnosticConsumers expect.
384
414
385
415
// Notify the existing diagnostic client that all files were processed.
386
416
Clang.getDiagnosticClient ().finish ();
387
417
418
+ DiagnosticOptions &DiagOpts = Clang.getInvocation ().getDiagnosticOpts ();
388
419
Clang.getDiagnostics ().setClient (
389
- new TextDiagnosticPrinter (*ResultDiagsOS,
390
- &Clang.getInvocation ().getDiagnosticOpts ()),
420
+ new TextDiagnosticPrinter (*ResultDiagsOS, &DiagOpts),
391
421
/* ShouldOwnClient=*/ true );
422
+ if (!DiagOpts.DiagnosticSerializationFile .empty ()) {
423
+ // Save the serialized diagnostics file as CAS output.
424
+ if (Error E =
425
+ createBinaryOutputFile (Clang, DiagOpts.DiagnosticSerializationFile )
426
+ .moveInto (SerialDiagsOutput)) {
427
+ Diags.Report (diag::err_fe_unable_to_open_output)
428
+ << DiagOpts.DiagnosticSerializationFile
429
+ << errorToErrorCode (std::move (E)).message ();
430
+ return 1 ;
431
+ }
432
+
433
+ Expected<std::unique_ptr<raw_pwrite_stream>> OS =
434
+ SerialDiagsOutput->createProxy ();
435
+ if (!OS) {
436
+ Diags.Report (diag::err_fe_unable_to_open_output)
437
+ << DiagOpts.DiagnosticSerializationFile
438
+ << errorToErrorCode (OS.takeError ()).message ();
439
+ return 1 ;
440
+ }
441
+ auto SerializedConsumer = clang::serialized_diags::create (
442
+ OutputFile, &DiagOpts, /* MergeChildRecords*/ false , std::move (*OS));
443
+ Diags.setClient (new ChainedDiagnosticConsumer (
444
+ Diags.takeClient (), std::move (SerializedConsumer)));
445
+ }
446
+
392
447
return None;
393
448
}
394
449
@@ -398,6 +453,20 @@ void CompileJobCache::finishComputedResult(CompilerInstance &Clang,
398
453
if (!CacheCompileJob)
399
454
return ;
400
455
456
+ if (SerialDiagsOutput) {
457
+ llvm::handleAllErrors (
458
+ SerialDiagsOutput->keep (),
459
+ [&](const llvm::vfs::TempFileOutputError &E) {
460
+ Clang.getDiagnostics ().Report (diag::err_unable_to_rename_temp)
461
+ << E.getTempPath () << E.getOutputPath ()
462
+ << E.convertToErrorCode ().message ();
463
+ },
464
+ [&](const llvm::vfs::OutputError &E) {
465
+ Clang.getDiagnostics ().Report (diag::err_fe_unable_to_open_output)
466
+ << E.getOutputPath () << E.convertToErrorCode ().message ();
467
+ });
468
+ }
469
+
401
470
// Don't cache failed builds.
402
471
//
403
472
// TODO: Consider caching failed builds! Note: when output files are written
@@ -432,18 +501,31 @@ void CompileJobCache::finishComputedResult(CompilerInstance &Clang,
432
501
llvm::report_fatal_error (std::move (E));
433
502
434
503
// Replay / decanonicalize as necessary.
435
- Optional<int > Status = replayCachedResult (CAS->getReference (*Result),
504
+ Optional<int > Status = replayCachedResult (Clang, CAS->getReference (*Result),
436
505
/* JustComputedResult=*/ true );
437
506
(void )Status;
438
507
assert (Status == None);
439
508
}
440
509
441
510
// / Replay a result after a cache hit.
442
- Optional<int > CompileJobCache::replayCachedResult (llvm::cas::ObjectRef ResultID,
511
+ Optional<int > CompileJobCache::replayCachedResult (CompilerInstance &Clang,
512
+ llvm::cas::ObjectRef ResultID,
443
513
bool JustComputedResult) {
444
514
if (JustComputedResult)
445
515
return None;
446
516
517
+ if (!JustComputedResult) {
518
+ // Disable the existing DiagnosticConsumer, we'll both print to stderr
519
+ // directly and also potentially output a serialized diagnostics file, in
520
+ // which case we don't want the outer DiagnosticConsumer to overwrite it and
521
+ // lose the compilation diagnostics.
522
+ // See FIXME in CompileJobCache::tryReplayCachedResult() about improving how
523
+ // we handle diagnostics for caching purposes.
524
+ Clang.getDiagnosticClient ().finish ();
525
+ Clang.getDiagnostics ().setClient (new IgnoringDiagConsumer (),
526
+ /* ShouldOwnClient=*/ true );
527
+ }
528
+
447
529
// FIXME: Stop calling report_fatal_error().
448
530
Optional<llvm::cas::TreeProxy> Result;
449
531
llvm::cas::TreeSchema Schema (*CAS);
0 commit comments