Home | History | Annotate | Download | only in Frontend
      1 //===--- DependencyFile.cpp - Generate dependency file --------------------===//
      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 // This code generates dependency files.
     11 //
     12 //===----------------------------------------------------------------------===//
     13 
     14 #include "clang/Frontend/Utils.h"
     15 #include "clang/Basic/FileManager.h"
     16 #include "clang/Basic/SourceManager.h"
     17 #include "clang/Frontend/DependencyOutputOptions.h"
     18 #include "clang/Frontend/FrontendDiagnostic.h"
     19 #include "clang/Lex/DirectoryLookup.h"
     20 #include "clang/Lex/LexDiagnostic.h"
     21 #include "clang/Lex/PPCallbacks.h"
     22 #include "clang/Lex/Preprocessor.h"
     23 #include "clang/Serialization/ASTReader.h"
     24 #include "llvm/ADT/StringSet.h"
     25 #include "llvm/Support/FileSystem.h"
     26 #include "llvm/Support/Path.h"
     27 #include "llvm/Support/raw_ostream.h"
     28 
     29 using namespace clang;
     30 
     31 namespace {
     32 struct DepCollectorPPCallbacks : public PPCallbacks {
     33   DependencyCollector &DepCollector;
     34   SourceManager &SM;
     35   DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM)
     36       : DepCollector(L), SM(SM) { }
     37 
     38   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
     39                    SrcMgr::CharacteristicKind FileType,
     40                    FileID PrevFID) override {
     41     if (Reason != PPCallbacks::EnterFile)
     42       return;
     43 
     44     // Dependency generation really does want to go all the way to the
     45     // file entry for a source location to find out what is depended on.
     46     // We do not want #line markers to affect dependency generation!
     47     const FileEntry *FE =
     48         SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc)));
     49     if (!FE)
     50       return;
     51 
     52     StringRef Filename = FE->getName();
     53 
     54     // Remove leading "./" (or ".//" or "././" etc.)
     55     while (Filename.size() > 2 && Filename[0] == '.' &&
     56            llvm::sys::path::is_separator(Filename[1])) {
     57       Filename = Filename.substr(1);
     58       while (llvm::sys::path::is_separator(Filename[0]))
     59         Filename = Filename.substr(1);
     60     }
     61 
     62     DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
     63                                    FileType != SrcMgr::C_User,
     64                                    /*IsModuleFile*/false, /*IsMissing*/false);
     65   }
     66 
     67   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
     68                           StringRef FileName, bool IsAngled,
     69                           CharSourceRange FilenameRange, const FileEntry *File,
     70                           StringRef SearchPath, StringRef RelativePath,
     71                           const Module *Imported) override {
     72     if (!File)
     73       DepCollector.maybeAddDependency(FileName, /*FromModule*/false,
     74                                      /*IsSystem*/false, /*IsModuleFile*/false,
     75                                      /*IsMissing*/true);
     76     // Files that actually exist are handled by FileChanged.
     77   }
     78 
     79   void EndOfMainFile() override {
     80     DepCollector.finishedMainFile();
     81   }
     82 };
     83 
     84 struct DepCollectorASTListener : public ASTReaderListener {
     85   DependencyCollector &DepCollector;
     86   DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { }
     87   bool needsInputFileVisitation() override { return true; }
     88   bool needsSystemInputFileVisitation() override {
     89     return DepCollector.needSystemDependencies();
     90   }
     91   void visitModuleFile(StringRef Filename) override {
     92     DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
     93                                    /*IsSystem*/false, /*IsModuleFile*/true,
     94                                    /*IsMissing*/false);
     95   }
     96   bool visitInputFile(StringRef Filename, bool IsSystem,
     97                       bool IsOverridden) override {
     98     if (IsOverridden)
     99       return true;
    100 
    101     DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
    102                                    /*IsModuleFile*/false, /*IsMissing*/false);
    103     return true;
    104   }
    105 };
    106 } // end anonymous namespace
    107 
    108 void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule,
    109                                             bool IsSystem, bool IsModuleFile,
    110                                             bool IsMissing) {
    111   if (Seen.insert(Filename) &&
    112       sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
    113     Dependencies.push_back(Filename);
    114 }
    115 
    116 bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
    117                                        bool IsSystem, bool IsModuleFile,
    118                                        bool IsMissing) {
    119   return Filename != "<built-in>" && (needSystemDependencies() || !IsSystem);
    120 }
    121 
    122 DependencyCollector::~DependencyCollector() { }
    123 void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
    124   PP.addPPCallbacks(new DepCollectorPPCallbacks(*this, PP.getSourceManager()));
    125 }
    126 void DependencyCollector::attachToASTReader(ASTReader &R) {
    127   R.addListener(new DepCollectorASTListener(*this));
    128 }
    129 
    130 namespace {
    131 /// Private implementation for DependencyFileGenerator
    132 class DFGImpl : public PPCallbacks {
    133   std::vector<std::string> Files;
    134   llvm::StringSet<> FilesSet;
    135   const Preprocessor *PP;
    136   std::string OutputFile;
    137   std::vector<std::string> Targets;
    138   bool IncludeSystemHeaders;
    139   bool PhonyTarget;
    140   bool AddMissingHeaderDeps;
    141   bool SeenMissingHeader;
    142   bool IncludeModuleFiles;
    143 private:
    144   bool FileMatchesDepCriteria(const char *Filename,
    145                               SrcMgr::CharacteristicKind FileType);
    146   void OutputDependencyFile();
    147 
    148 public:
    149   DFGImpl(const Preprocessor *_PP, const DependencyOutputOptions &Opts)
    150     : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets),
    151       IncludeSystemHeaders(Opts.IncludeSystemHeaders),
    152       PhonyTarget(Opts.UsePhonyTargets),
    153       AddMissingHeaderDeps(Opts.AddMissingHeaderDeps),
    154       SeenMissingHeader(false),
    155       IncludeModuleFiles(Opts.IncludeModuleFiles) {}
    156 
    157   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
    158                    SrcMgr::CharacteristicKind FileType,
    159                    FileID PrevFID) override;
    160   void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
    161                           StringRef FileName, bool IsAngled,
    162                           CharSourceRange FilenameRange, const FileEntry *File,
    163                           StringRef SearchPath, StringRef RelativePath,
    164                           const Module *Imported) override;
    165 
    166   void EndOfMainFile() override {
    167     OutputDependencyFile();
    168   }
    169 
    170   void AddFilename(StringRef Filename);
    171   bool includeSystemHeaders() const { return IncludeSystemHeaders; }
    172   bool includeModuleFiles() const { return IncludeModuleFiles; }
    173 };
    174 
    175 class DFGASTReaderListener : public ASTReaderListener {
    176   DFGImpl &Parent;
    177 public:
    178   DFGASTReaderListener(DFGImpl &Parent)
    179   : Parent(Parent) { }
    180   bool needsInputFileVisitation() override { return true; }
    181   bool needsSystemInputFileVisitation() override {
    182     return Parent.includeSystemHeaders();
    183   }
    184   void visitModuleFile(StringRef Filename) override;
    185   bool visitInputFile(StringRef Filename, bool isSystem,
    186                       bool isOverridden) override;
    187 };
    188 }
    189 
    190 DependencyFileGenerator::DependencyFileGenerator(void *Impl)
    191 : Impl(Impl) { }
    192 
    193 DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor(
    194     clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) {
    195 
    196   if (Opts.Targets.empty()) {
    197     PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT);
    198     return nullptr;
    199   }
    200 
    201   // Disable the "file not found" diagnostic if the -MG option was given.
    202   if (Opts.AddMissingHeaderDeps)
    203     PP.SetSuppressIncludeNotFoundError(true);
    204 
    205   DFGImpl *Callback = new DFGImpl(&PP, Opts);
    206   PP.addPPCallbacks(Callback); // PP owns the Callback
    207   return new DependencyFileGenerator(Callback);
    208 }
    209 
    210 void DependencyFileGenerator::AttachToASTReader(ASTReader &R) {
    211   DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl);
    212   assert(I && "missing implementation");
    213   R.addListener(new DFGASTReaderListener(*I));
    214 }
    215 
    216 /// FileMatchesDepCriteria - Determine whether the given Filename should be
    217 /// considered as a dependency.
    218 bool DFGImpl::FileMatchesDepCriteria(const char *Filename,
    219                                      SrcMgr::CharacteristicKind FileType) {
    220   if (strcmp("<built-in>", Filename) == 0)
    221     return false;
    222 
    223   if (IncludeSystemHeaders)
    224     return true;
    225 
    226   return FileType == SrcMgr::C_User;
    227 }
    228 
    229 void DFGImpl::FileChanged(SourceLocation Loc,
    230                           FileChangeReason Reason,
    231                           SrcMgr::CharacteristicKind FileType,
    232                           FileID PrevFID) {
    233   if (Reason != PPCallbacks::EnterFile)
    234     return;
    235 
    236   // Dependency generation really does want to go all the way to the
    237   // file entry for a source location to find out what is depended on.
    238   // We do not want #line markers to affect dependency generation!
    239   SourceManager &SM = PP->getSourceManager();
    240 
    241   const FileEntry *FE =
    242     SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc)));
    243   if (!FE) return;
    244 
    245   StringRef Filename = FE->getName();
    246   if (!FileMatchesDepCriteria(Filename.data(), FileType))
    247     return;
    248 
    249   // Remove leading "./" (or ".//" or "././" etc.)
    250   while (Filename.size() > 2 && Filename[0] == '.' &&
    251          llvm::sys::path::is_separator(Filename[1])) {
    252     Filename = Filename.substr(1);
    253     while (llvm::sys::path::is_separator(Filename[0]))
    254       Filename = Filename.substr(1);
    255   }
    256 
    257   AddFilename(Filename);
    258 }
    259 
    260 void DFGImpl::InclusionDirective(SourceLocation HashLoc,
    261                                  const Token &IncludeTok,
    262                                  StringRef FileName,
    263                                  bool IsAngled,
    264                                  CharSourceRange FilenameRange,
    265                                  const FileEntry *File,
    266                                  StringRef SearchPath,
    267                                  StringRef RelativePath,
    268                                  const Module *Imported) {
    269   if (!File) {
    270     if (AddMissingHeaderDeps)
    271       AddFilename(FileName);
    272     else
    273       SeenMissingHeader = true;
    274   }
    275 }
    276 
    277 void DFGImpl::AddFilename(StringRef Filename) {
    278   if (FilesSet.insert(Filename))
    279     Files.push_back(Filename);
    280 }
    281 
    282 /// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or
    283 /// other scary characters.
    284 static void PrintFilename(raw_ostream &OS, StringRef Filename) {
    285   for (unsigned i = 0, e = Filename.size(); i != e; ++i) {
    286     if (Filename[i] == ' ' || Filename[i] == '#')
    287       OS << '\\';
    288     else if (Filename[i] == '$') // $ is escaped by $$.
    289       OS << '$';
    290     OS << Filename[i];
    291   }
    292 }
    293 
    294 void DFGImpl::OutputDependencyFile() {
    295   if (SeenMissingHeader) {
    296     llvm::sys::fs::remove(OutputFile);
    297     return;
    298   }
    299 
    300   std::string Err;
    301   llvm::raw_fd_ostream OS(OutputFile.c_str(), Err, llvm::sys::fs::F_Text);
    302   if (!Err.empty()) {
    303     PP->getDiagnostics().Report(diag::err_fe_error_opening)
    304       << OutputFile << Err;
    305     return;
    306   }
    307 
    308   // Write out the dependency targets, trying to avoid overly long
    309   // lines when possible. We try our best to emit exactly the same
    310   // dependency file as GCC (4.2), assuming the included files are the
    311   // same.
    312   const unsigned MaxColumns = 75;
    313   unsigned Columns = 0;
    314 
    315   for (std::vector<std::string>::iterator
    316          I = Targets.begin(), E = Targets.end(); I != E; ++I) {
    317     unsigned N = I->length();
    318     if (Columns == 0) {
    319       Columns += N;
    320     } else if (Columns + N + 2 > MaxColumns) {
    321       Columns = N + 2;
    322       OS << " \\\n  ";
    323     } else {
    324       Columns += N + 1;
    325       OS << ' ';
    326     }
    327     // Targets already quoted as needed.
    328     OS << *I;
    329   }
    330 
    331   OS << ':';
    332   Columns += 1;
    333 
    334   // Now add each dependency in the order it was seen, but avoiding
    335   // duplicates.
    336   for (std::vector<std::string>::iterator I = Files.begin(),
    337          E = Files.end(); I != E; ++I) {
    338     // Start a new line if this would exceed the column limit. Make
    339     // sure to leave space for a trailing " \" in case we need to
    340     // break the line on the next iteration.
    341     unsigned N = I->length();
    342     if (Columns + (N + 1) + 2 > MaxColumns) {
    343       OS << " \\\n ";
    344       Columns = 2;
    345     }
    346     OS << ' ';
    347     PrintFilename(OS, *I);
    348     Columns += N + 1;
    349   }
    350   OS << '\n';
    351 
    352   // Create phony targets if requested.
    353   if (PhonyTarget && !Files.empty()) {
    354     // Skip the first entry, this is always the input file itself.
    355     for (std::vector<std::string>::iterator I = Files.begin() + 1,
    356            E = Files.end(); I != E; ++I) {
    357       OS << '\n';
    358       PrintFilename(OS, *I);
    359       OS << ":\n";
    360     }
    361   }
    362 }
    363 
    364 bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename,
    365                                           bool IsSystem, bool IsOverridden) {
    366   assert(!IsSystem || needsSystemInputFileVisitation());
    367   if (IsOverridden)
    368     return true;
    369 
    370   Parent.AddFilename(Filename);
    371   return true;
    372 }
    373 
    374 void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename) {
    375   if (Parent.includeModuleFiles())
    376     Parent.AddFilename(Filename);
    377 }
    378