Home | History | Annotate | Line # | Download | only in Frontend
DependencyFile.cpp revision 1.1.1.1.4.1
      1 //===--- DependencyFile.cpp - Generate dependency file --------------------===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===----------------------------------------------------------------------===//
      8 //
      9 // This code generates dependency files.
     10 //
     11 //===----------------------------------------------------------------------===//
     12 
     13 #include "clang/Frontend/Utils.h"
     14 #include "clang/Basic/FileManager.h"
     15 #include "clang/Basic/SourceManager.h"
     16 #include "clang/Frontend/DependencyOutputOptions.h"
     17 #include "clang/Frontend/FrontendDiagnostic.h"
     18 #include "clang/Lex/DirectoryLookup.h"
     19 #include "clang/Lex/ModuleMap.h"
     20 #include "clang/Lex/PPCallbacks.h"
     21 #include "clang/Lex/Preprocessor.h"
     22 #include "clang/Serialization/ASTReader.h"
     23 #include "llvm/ADT/StringSet.h"
     24 #include "llvm/ADT/StringSwitch.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   DiagnosticsEngine &Diags;
     36   DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM,
     37                           DiagnosticsEngine &Diags)
     38       : DepCollector(L), SM(SM), Diags(Diags) {}
     39 
     40   void FileChanged(SourceLocation Loc, FileChangeReason Reason,
     41                    SrcMgr::CharacteristicKind FileType,
     42                    FileID PrevFID) override {
     43     if (Reason != PPCallbacks::EnterFile)
     44       return;
     45 
     46     // Dependency generation really does want to go all the way to the
     47     // file entry for a source location to find out what is depended on.
     48     // We do not want #line markers to affect dependency generation!
     49     if (Optional<StringRef> Filename = SM.getNonBuiltinFilenameForID(
     50             SM.getFileID(SM.getExpansionLoc(Loc))))
     51       DepCollector.maybeAddDependency(
     52           llvm::sys::path::remove_leading_dotslash(*Filename),
     53           /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false,
     54           /*IsMissing*/ false);
     55   }
     56 
     57   void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
     58                    SrcMgr::CharacteristicKind FileType) override {
     59     StringRef Filename =
     60         llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
     61     DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
     62                                     /*IsSystem=*/isSystem(FileType),
     63                                     /*IsModuleFile=*/false,
     64                                     /*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,
     72                           SrcMgr::CharacteristicKind FileType) 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 HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
     81                   Optional<FileEntryRef> File,
     82                   SrcMgr::CharacteristicKind FileType) override {
     83     if (!File)
     84       return;
     85     StringRef Filename =
     86         llvm::sys::path::remove_leading_dotslash(File->getName());
     87     DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
     88                                     /*IsSystem=*/isSystem(FileType),
     89                                     /*IsModuleFile=*/false,
     90                                     /*IsMissing=*/false);
     91   }
     92 
     93   void EndOfMainFile() override { DepCollector.finishedMainFile(Diags); }
     94 };
     95 
     96 struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
     97   DependencyCollector &DepCollector;
     98   DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
     99 
    100   void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry,
    101                          bool IsSystem) override {
    102     StringRef Filename = Entry.getName();
    103     DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
    104                                     /*IsSystem*/IsSystem,
    105                                     /*IsModuleFile*/false,
    106                                     /*IsMissing*/false);
    107   }
    108 };
    109 
    110 struct DepCollectorASTListener : public ASTReaderListener {
    111   DependencyCollector &DepCollector;
    112   DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { }
    113   bool needsInputFileVisitation() override { return true; }
    114   bool needsSystemInputFileVisitation() override {
    115     return DepCollector.needSystemDependencies();
    116   }
    117   void visitModuleFile(StringRef Filename,
    118                        serialization::ModuleKind Kind) override {
    119     DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
    120                                    /*IsSystem*/false, /*IsModuleFile*/true,
    121                                    /*IsMissing*/false);
    122   }
    123   bool visitInputFile(StringRef Filename, bool IsSystem,
    124                       bool IsOverridden, bool IsExplicitModule) override {
    125     if (IsOverridden || IsExplicitModule)
    126       return true;
    127 
    128     DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
    129                                    /*IsModuleFile*/false, /*IsMissing*/false);
    130     return true;
    131   }
    132 };
    133 } // end anonymous namespace
    134 
    135 void DependencyCollector::maybeAddDependency(StringRef Filename,
    136                                              bool FromModule, bool IsSystem,
    137                                              bool IsModuleFile,
    138                                              bool IsMissing) {
    139   if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
    140     addDependency(Filename);
    141 }
    142 
    143 bool DependencyCollector::addDependency(StringRef Filename) {
    144   StringRef SearchPath;
    145 #ifdef _WIN32
    146   // Make the search insensitive to case and separators.
    147   llvm::SmallString<256> TmpPath = Filename;
    148   llvm::sys::path::native(TmpPath);
    149   std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower);
    150   SearchPath = TmpPath.str();
    151 #else
    152   SearchPath = Filename;
    153 #endif
    154 
    155   if (Seen.insert(SearchPath).second) {
    156     Dependencies.push_back(std::string(Filename));
    157     return true;
    158   }
    159   return false;
    160 }
    161 
    162 static bool isSpecialFilename(StringRef Filename) {
    163   return llvm::StringSwitch<bool>(Filename)
    164       .Case("<built-in>", true)
    165       .Case("<stdin>", true)
    166       .Default(false);
    167 }
    168 
    169 bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
    170                                         bool IsSystem, bool IsModuleFile,
    171                                         bool IsMissing) {
    172   return !isSpecialFilename(Filename) &&
    173          (needSystemDependencies() || !IsSystem);
    174 }
    175 
    176 DependencyCollector::~DependencyCollector() { }
    177 void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
    178   PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(
    179       *this, PP.getSourceManager(), PP.getDiagnostics()));
    180   PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
    181       std::make_unique<DepCollectorMMCallbacks>(*this));
    182 }
    183 void DependencyCollector::attachToASTReader(ASTReader &R) {
    184   R.addListener(std::make_unique<DepCollectorASTListener>(*this));
    185 }
    186 
    187 DependencyFileGenerator::DependencyFileGenerator(
    188     const DependencyOutputOptions &Opts)
    189     : OutputFile(Opts.OutputFile), Targets(Opts.Targets),
    190       IncludeSystemHeaders(Opts.IncludeSystemHeaders),
    191       PhonyTarget(Opts.UsePhonyTargets),
    192       AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false),
    193       IncludeModuleFiles(Opts.IncludeModuleFiles),
    194       OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
    195   for (const auto &ExtraDep : Opts.ExtraDeps) {
    196     if (addDependency(ExtraDep.first))
    197       ++InputFileIndex;
    198   }
    199 }
    200 
    201 void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) {
    202   // Disable the "file not found" diagnostic if the -MG option was given.
    203   if (AddMissingHeaderDeps)
    204     PP.SetSuppressIncludeNotFoundError(true);
    205 
    206   DependencyCollector::attachToPreprocessor(PP);
    207 }
    208 
    209 bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
    210                                             bool IsSystem, bool IsModuleFile,
    211                                             bool IsMissing) {
    212   if (IsMissing) {
    213     // Handle the case of missing file from an inclusion directive.
    214     if (AddMissingHeaderDeps)
    215       return true;
    216     SeenMissingHeader = true;
    217     return false;
    218   }
    219   if (IsModuleFile && !IncludeModuleFiles)
    220     return false;
    221 
    222   if (isSpecialFilename(Filename))
    223     return false;
    224 
    225   if (IncludeSystemHeaders)
    226     return true;
    227 
    228   return !IsSystem;
    229 }
    230 
    231 void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) {
    232   outputDependencyFile(Diags);
    233 }
    234 
    235 /// Print the filename, with escaping or quoting that accommodates the three
    236 /// most likely tools that use dependency files: GNU Make, BSD Make, and
    237 /// NMake/Jom.
    238 ///
    239 /// BSD Make is the simplest case: It does no escaping at all.  This means
    240 /// characters that are normally delimiters, i.e. space and # (the comment
    241 /// character) simply aren't supported in filenames.
    242 ///
    243 /// GNU Make does allow space and # in filenames, but to avoid being treated
    244 /// as a delimiter or comment, these must be escaped with a backslash. Because
    245 /// backslash is itself the escape character, if a backslash appears in a
    246 /// filename, it should be escaped as well.  (As a special case, $ is escaped
    247 /// as $$, which is the normal Make way to handle the $ character.)
    248 /// For compatibility with BSD Make and historical practice, if GNU Make
    249 /// un-escapes characters in a filename but doesn't find a match, it will
    250 /// retry with the unmodified original string.
    251 ///
    252 /// GCC tries to accommodate both Make formats by escaping any space or #
    253 /// characters in the original filename, but not escaping backslashes.  The
    254 /// apparent intent is so that filenames with backslashes will be handled
    255 /// correctly by BSD Make, and by GNU Make in its fallback mode of using the
    256 /// unmodified original string; filenames with # or space characters aren't
    257 /// supported by BSD Make at all, but will be handled correctly by GNU Make
    258 /// due to the escaping.
    259 ///
    260 /// A corner case that GCC gets only partly right is when the original filename
    261 /// has a backslash immediately followed by space or #.  GNU Make would expect
    262 /// this backslash to be escaped; however GCC escapes the original backslash
    263 /// only when followed by space, not #.  It will therefore take a dependency
    264 /// from a directive such as
    265 ///     #include "a\ b\#c.h"
    266 /// and emit it as
    267 ///     a\\\ b\\#c.h
    268 /// which GNU Make will interpret as
    269 ///     a\ b\
    270 /// followed by a comment. Failing to find this file, it will fall back to the
    271 /// original string, which probably doesn't exist either; in any case it won't
    272 /// find
    273 ///     a\ b\#c.h
    274 /// which is the actual filename specified by the include directive.
    275 ///
    276 /// Clang does what GCC does, rather than what GNU Make expects.
    277 ///
    278 /// NMake/Jom has a different set of scary characters, but wraps filespecs in
    279 /// double-quotes to avoid misinterpreting them; see
    280 /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info,
    281 /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx
    282 /// for Windows file-naming info.
    283 static void PrintFilename(raw_ostream &OS, StringRef Filename,
    284                           DependencyOutputFormat OutputFormat) {
    285   // Convert filename to platform native path
    286   llvm::SmallString<256> NativePath;
    287   llvm::sys::path::native(Filename.str(), NativePath);
    288 
    289   if (OutputFormat == DependencyOutputFormat::NMake) {
    290     // Add quotes if needed. These are the characters listed as "special" to
    291     // NMake, that are legal in a Windows filespec, and that could cause
    292     // misinterpretation of the dependency string.
    293     if (NativePath.find_first_of(" #${}^!") != StringRef::npos)
    294       OS << '\"' << NativePath << '\"';
    295     else
    296       OS << NativePath;
    297     return;
    298   }
    299   assert(OutputFormat == DependencyOutputFormat::Make);
    300   for (unsigned i = 0, e = NativePath.size(); i != e; ++i) {
    301     if (NativePath[i] == '#') // Handle '#' the broken gcc way.
    302       OS << '\\';
    303     else if (NativePath[i] == ' ') { // Handle space correctly.
    304       OS << '\\';
    305       unsigned j = i;
    306       while (j > 0 && NativePath[--j] == '\\')
    307         OS << '\\';
    308     } else if (NativePath[i] == '$') // $ is escaped by $$.
    309       OS << '$';
    310     OS << NativePath[i];
    311   }
    312 }
    313 
    314 void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) {
    315   if (SeenMissingHeader) {
    316     llvm::sys::fs::remove(OutputFile);
    317     return;
    318   }
    319 
    320   std::error_code EC;
    321   llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF);
    322   if (EC) {
    323     Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
    324     return;
    325   }
    326 
    327   outputDependencyFile(OS);
    328 }
    329 
    330 void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) {
    331   // Write out the dependency targets, trying to avoid overly long
    332   // lines when possible. We try our best to emit exactly the same
    333   // dependency file as GCC (4.2), assuming the included files are the
    334   // same.
    335   const unsigned MaxColumns = 75;
    336   unsigned Columns = 0;
    337 
    338   for (StringRef Target : Targets) {
    339     unsigned N = Target.size();
    340     if (Columns == 0) {
    341       Columns += N;
    342     } else if (Columns + N + 2 > MaxColumns) {
    343       Columns = N + 2;
    344       OS << " \\\n  ";
    345     } else {
    346       Columns += N + 1;
    347       OS << ' ';
    348     }
    349     // Targets already quoted as needed.
    350     OS << Target;
    351   }
    352 
    353   OS << ':';
    354   Columns += 1;
    355 
    356   // Now add each dependency in the order it was seen, but avoiding
    357   // duplicates.
    358   ArrayRef<std::string> Files = getDependencies();
    359   for (StringRef File : Files) {
    360     // Start a new line if this would exceed the column limit. Make
    361     // sure to leave space for a trailing " \" in case we need to
    362     // break the line on the next iteration.
    363     unsigned N = File.size();
    364     if (Columns + (N + 1) + 2 > MaxColumns) {
    365       OS << " \\\n ";
    366       Columns = 2;
    367     }
    368     OS << ' ';
    369     PrintFilename(OS, File, OutputFormat);
    370     Columns += N + 1;
    371   }
    372   OS << '\n';
    373 
    374   // Create phony targets if requested.
    375   if (PhonyTarget && !Files.empty()) {
    376     unsigned Index = 0;
    377     for (auto I = Files.begin(), E = Files.end(); I != E; ++I) {
    378       if (Index++ == InputFileIndex)
    379         continue;
    380       OS << '\n';
    381       PrintFilename(OS, *I, OutputFormat);
    382       OS << ":\n";
    383     }
    384   }
    385 }
    386