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