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