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 /// Private implementation for DependencyFileGenerator 33 class DFGImpl : public PPCallbacks { 34 std::vector<std::string> Files; 35 llvm::StringSet<> FilesSet; 36 const Preprocessor *PP; 37 std::string OutputFile; 38 std::vector<std::string> Targets; 39 bool IncludeSystemHeaders; 40 bool PhonyTarget; 41 bool AddMissingHeaderDeps; 42 bool SeenMissingHeader; 43 bool IncludeModuleFiles; 44 private: 45 bool FileMatchesDepCriteria(const char *Filename, 46 SrcMgr::CharacteristicKind FileType); 47 void OutputDependencyFile(); 48 49 public: 50 DFGImpl(const Preprocessor *_PP, const DependencyOutputOptions &Opts) 51 : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets), 52 IncludeSystemHeaders(Opts.IncludeSystemHeaders), 53 PhonyTarget(Opts.UsePhonyTargets), 54 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), 55 SeenMissingHeader(false), 56 IncludeModuleFiles(Opts.IncludeModuleFiles) {} 57 58 void FileChanged(SourceLocation Loc, FileChangeReason Reason, 59 SrcMgr::CharacteristicKind FileType, 60 FileID PrevFID) override; 61 void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, 62 StringRef FileName, bool IsAngled, 63 CharSourceRange FilenameRange, const FileEntry *File, 64 StringRef SearchPath, StringRef RelativePath, 65 const Module *Imported) override; 66 67 void EndOfMainFile() override { 68 OutputDependencyFile(); 69 } 70 71 void AddFilename(StringRef Filename); 72 bool includeSystemHeaders() const { return IncludeSystemHeaders; } 73 bool includeModuleFiles() const { return IncludeModuleFiles; } 74 }; 75 76 class DFGASTReaderListener : public ASTReaderListener { 77 DFGImpl &Parent; 78 public: 79 DFGASTReaderListener(DFGImpl &Parent) 80 : Parent(Parent) { } 81 bool needsInputFileVisitation() override { return true; } 82 bool needsSystemInputFileVisitation() override { 83 return Parent.includeSystemHeaders(); 84 } 85 void visitModuleFile(StringRef Filename) override; 86 bool visitInputFile(StringRef Filename, bool isSystem, 87 bool isOverridden) override; 88 }; 89 } 90 91 DependencyFileGenerator::DependencyFileGenerator(void *Impl) 92 : Impl(Impl) { } 93 94 DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor( 95 clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) { 96 97 if (Opts.Targets.empty()) { 98 PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); 99 return nullptr; 100 } 101 102 // Disable the "file not found" diagnostic if the -MG option was given. 103 if (Opts.AddMissingHeaderDeps) 104 PP.SetSuppressIncludeNotFoundError(true); 105 106 DFGImpl *Callback = new DFGImpl(&PP, Opts); 107 PP.addPPCallbacks(Callback); // PP owns the Callback 108 return new DependencyFileGenerator(Callback); 109 } 110 111 void DependencyFileGenerator::AttachToASTReader(ASTReader &R) { 112 DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl); 113 assert(I && "missing implementation"); 114 R.addListener(new DFGASTReaderListener(*I)); 115 } 116 117 /// FileMatchesDepCriteria - Determine whether the given Filename should be 118 /// considered as a dependency. 119 bool DFGImpl::FileMatchesDepCriteria(const char *Filename, 120 SrcMgr::CharacteristicKind FileType) { 121 if (strcmp("<built-in>", Filename) == 0) 122 return false; 123 124 if (IncludeSystemHeaders) 125 return true; 126 127 return FileType == SrcMgr::C_User; 128 } 129 130 void DFGImpl::FileChanged(SourceLocation Loc, 131 FileChangeReason Reason, 132 SrcMgr::CharacteristicKind FileType, 133 FileID PrevFID) { 134 if (Reason != PPCallbacks::EnterFile) 135 return; 136 137 // Dependency generation really does want to go all the way to the 138 // file entry for a source location to find out what is depended on. 139 // We do not want #line markers to affect dependency generation! 140 SourceManager &SM = PP->getSourceManager(); 141 142 const FileEntry *FE = 143 SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); 144 if (!FE) return; 145 146 StringRef Filename = FE->getName(); 147 if (!FileMatchesDepCriteria(Filename.data(), FileType)) 148 return; 149 150 // Remove leading "./" (or ".//" or "././" etc.) 151 while (Filename.size() > 2 && Filename[0] == '.' && 152 llvm::sys::path::is_separator(Filename[1])) { 153 Filename = Filename.substr(1); 154 while (llvm::sys::path::is_separator(Filename[0])) 155 Filename = Filename.substr(1); 156 } 157 158 AddFilename(Filename); 159 } 160 161 void DFGImpl::InclusionDirective(SourceLocation HashLoc, 162 const Token &IncludeTok, 163 StringRef FileName, 164 bool IsAngled, 165 CharSourceRange FilenameRange, 166 const FileEntry *File, 167 StringRef SearchPath, 168 StringRef RelativePath, 169 const Module *Imported) { 170 if (!File) { 171 if (AddMissingHeaderDeps) 172 AddFilename(FileName); 173 else 174 SeenMissingHeader = true; 175 } 176 } 177 178 void DFGImpl::AddFilename(StringRef Filename) { 179 if (FilesSet.insert(Filename)) 180 Files.push_back(Filename); 181 } 182 183 /// PrintFilename - GCC escapes spaces, # and $, but apparently not ' or " or 184 /// other scary characters. 185 static void PrintFilename(raw_ostream &OS, StringRef Filename) { 186 for (unsigned i = 0, e = Filename.size(); i != e; ++i) { 187 if (Filename[i] == ' ' || Filename[i] == '#') 188 OS << '\\'; 189 else if (Filename[i] == '$') // $ is escaped by $$. 190 OS << '$'; 191 OS << Filename[i]; 192 } 193 } 194 195 void DFGImpl::OutputDependencyFile() { 196 if (SeenMissingHeader) { 197 llvm::sys::fs::remove(OutputFile); 198 return; 199 } 200 201 std::string Err; 202 llvm::raw_fd_ostream OS(OutputFile.c_str(), Err, llvm::sys::fs::F_Text); 203 if (!Err.empty()) { 204 PP->getDiagnostics().Report(diag::err_fe_error_opening) 205 << OutputFile << Err; 206 return; 207 } 208 209 // Write out the dependency targets, trying to avoid overly long 210 // lines when possible. We try our best to emit exactly the same 211 // dependency file as GCC (4.2), assuming the included files are the 212 // same. 213 const unsigned MaxColumns = 75; 214 unsigned Columns = 0; 215 216 for (std::vector<std::string>::iterator 217 I = Targets.begin(), E = Targets.end(); I != E; ++I) { 218 unsigned N = I->length(); 219 if (Columns == 0) { 220 Columns += N; 221 } else if (Columns + N + 2 > MaxColumns) { 222 Columns = N + 2; 223 OS << " \\\n "; 224 } else { 225 Columns += N + 1; 226 OS << ' '; 227 } 228 // Targets already quoted as needed. 229 OS << *I; 230 } 231 232 OS << ':'; 233 Columns += 1; 234 235 // Now add each dependency in the order it was seen, but avoiding 236 // duplicates. 237 for (std::vector<std::string>::iterator I = Files.begin(), 238 E = Files.end(); I != E; ++I) { 239 // Start a new line if this would exceed the column limit. Make 240 // sure to leave space for a trailing " \" in case we need to 241 // break the line on the next iteration. 242 unsigned N = I->length(); 243 if (Columns + (N + 1) + 2 > MaxColumns) { 244 OS << " \\\n "; 245 Columns = 2; 246 } 247 OS << ' '; 248 PrintFilename(OS, *I); 249 Columns += N + 1; 250 } 251 OS << '\n'; 252 253 // Create phony targets if requested. 254 if (PhonyTarget && !Files.empty()) { 255 // Skip the first entry, this is always the input file itself. 256 for (std::vector<std::string>::iterator I = Files.begin() + 1, 257 E = Files.end(); I != E; ++I) { 258 OS << '\n'; 259 PrintFilename(OS, *I); 260 OS << ":\n"; 261 } 262 } 263 } 264 265 bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename, 266 bool IsSystem, bool IsOverridden) { 267 assert(!IsSystem || needsSystemInputFileVisitation()); 268 if (IsOverridden) 269 return true; 270 271 Parent.AddFilename(Filename); 272 return true; 273 } 274 275 void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename) { 276 if (Parent.includeModuleFiles()) 277 Parent.AddFilename(Filename); 278 } 279