Home | History | Annotate | Download | only in Frontend
      1 //===--- HeaderIncludes.cpp - Generate Header Includes --------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include "clang/Frontend/DependencyOutputOptions.h"
     11 #include "clang/Frontend/Utils.h"
     12 #include "clang/Basic/SourceManager.h"
     13 #include "clang/Frontend/FrontendDiagnostic.h"
     14 #include "clang/Lex/Preprocessor.h"
     15 #include "llvm/ADT/SmallString.h"
     16 #include "llvm/Support/raw_ostream.h"
     17 using namespace clang;
     18 
     19 namespace {
     20 class HeaderIncludesCallback : public PPCallbacks {
     21   SourceManager &SM;
     22   raw_ostream *OutputFile;
     23   const DependencyOutputOptions &DepOpts;
     24   unsigned CurrentIncludeDepth;
     25   bool HasProcessedPredefines;
     26   bool OwnsOutputFile;
     27   bool ShowAllHeaders;
     28   bool ShowDepth;
     29   bool MSStyle;
     30 
     31 public:
     32   HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
     33                          raw_ostream *OutputFile_,
     34                          const DependencyOutputOptions &DepOpts,
     35                          bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
     36       : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
     37         CurrentIncludeDepth(0), HasProcessedPredefines(false),
     38         OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
     39         ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
     40 
     41   ~HeaderIncludesCallback() override {
     42     if (OwnsOutputFile)
     43       delete OutputFile;
     44   }
     45 
     46   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
     47                    SrcMgr::CharacteristicKind FileType,
     48                    FileID PrevFID) override;
     49 };
     50 }
     51 
     52 static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
     53                             bool ShowDepth, unsigned CurrentIncludeDepth,
     54                             bool MSStyle) {
     55   // Write to a temporary string to avoid unnecessary flushing on errs().
     56   SmallString<512> Pathname(Filename);
     57   if (!MSStyle)
     58     Lexer::Stringify(Pathname);
     59 
     60   SmallString<256> Msg;
     61   if (MSStyle)
     62     Msg += "Note: including file:";
     63 
     64   if (ShowDepth) {
     65     // The main source file is at depth 1, so skip one dot.
     66     for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
     67       Msg += MSStyle ? ' ' : '.';
     68 
     69     if (!MSStyle)
     70       Msg += ' ';
     71   }
     72   Msg += Pathname;
     73   Msg += '\n';
     74 
     75   *OutputFile << Msg;
     76   OutputFile->flush();
     77 }
     78 
     79 void clang::AttachHeaderIncludeGen(Preprocessor &PP,
     80                                    const DependencyOutputOptions &DepOpts,
     81                                    bool ShowAllHeaders, StringRef OutputPath,
     82                                    bool ShowDepth, bool MSStyle) {
     83   raw_ostream *OutputFile = MSStyle ? &llvm::outs() : &llvm::errs();
     84   bool OwnsOutputFile = false;
     85 
     86   // Open the output file, if used.
     87   if (!OutputPath.empty()) {
     88     std::error_code EC;
     89     llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
     90         OutputPath.str(), EC, llvm::sys::fs::F_Append | llvm::sys::fs::F_Text);
     91     if (EC) {
     92       PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
     93           << EC.message();
     94       delete OS;
     95     } else {
     96       OS->SetUnbuffered();
     97       OutputFile = OS;
     98       OwnsOutputFile = true;
     99     }
    100   }
    101 
    102   // Print header info for extra headers, pretending they were discovered by
    103   // the regular preprocessor. The primary use case is to support proper
    104   // generation of Make / Ninja file dependencies for implicit includes, such
    105   // as sanitizer blacklists. It's only important for cl.exe compatibility,
    106   // the GNU way to generate rules is -M / -MM / -MD / -MMD.
    107   for (const auto &Header : DepOpts.ExtraDeps)
    108     PrintHeaderInfo(OutputFile, Header, ShowDepth, 2, MSStyle);
    109   PP.addPPCallbacks(llvm::make_unique<HeaderIncludesCallback>(
    110       &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
    111       MSStyle));
    112 }
    113 
    114 void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
    115                                          FileChangeReason Reason,
    116                                        SrcMgr::CharacteristicKind NewFileType,
    117                                        FileID PrevFID) {
    118   // Unless we are exiting a #include, make sure to skip ahead to the line the
    119   // #include directive was at.
    120   PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
    121   if (UserLoc.isInvalid())
    122     return;
    123 
    124   // Adjust the current include depth.
    125   if (Reason == PPCallbacks::EnterFile) {
    126     ++CurrentIncludeDepth;
    127   } else if (Reason == PPCallbacks::ExitFile) {
    128     if (CurrentIncludeDepth)
    129       --CurrentIncludeDepth;
    130 
    131     // We track when we are done with the predefines by watching for the first
    132     // place where we drop back to a nesting depth of 1.
    133     if (CurrentIncludeDepth == 1 && !HasProcessedPredefines) {
    134       if (!DepOpts.ShowIncludesPretendHeader.empty()) {
    135         PrintHeaderInfo(OutputFile, DepOpts.ShowIncludesPretendHeader,
    136                         ShowDepth, 2, MSStyle);
    137       }
    138       HasProcessedPredefines = true;
    139     }
    140 
    141     return;
    142   } else
    143     return;
    144 
    145   // Show the header if we are (a) past the predefines, or (b) showing all
    146   // headers and in the predefines at a depth past the initial file and command
    147   // line buffers.
    148   bool ShowHeader = (HasProcessedPredefines ||
    149                      (ShowAllHeaders && CurrentIncludeDepth > 2));
    150   unsigned IncludeDepth = CurrentIncludeDepth;
    151   if (!HasProcessedPredefines)
    152     --IncludeDepth; // Ignore indent from <built-in>.
    153   else if (!DepOpts.ShowIncludesPretendHeader.empty())
    154     ++IncludeDepth; // Pretend inclusion by ShowIncludesPretendHeader.
    155 
    156   // Dump the header include information we are past the predefines buffer or
    157   // are showing all headers and this isn't the magic implicit <command line>
    158   // header.
    159   // FIXME: Identify headers in a more robust way than comparing their name to
    160   // "<command line>" and "<built-in>" in a bunch of places.
    161   if (ShowHeader && Reason == PPCallbacks::EnterFile &&
    162       UserLoc.getFilename() != StringRef("<command line>")) {
    163     PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
    164                     MSStyle);
    165   }
    166 }
    167