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 29using namespace clang; 30 31namespace { 32struct 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 84struct 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 108void 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 116bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, 117 bool IsSystem, bool IsModuleFile, 118 bool IsMissing) { 119 return Filename != "<built-in>" && (needSystemDependencies() || !IsSystem); 120} 121 122DependencyCollector::~DependencyCollector() { } 123void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { 124 PP.addPPCallbacks(new DepCollectorPPCallbacks(*this, PP.getSourceManager())); 125} 126void DependencyCollector::attachToASTReader(ASTReader &R) { 127 R.addListener(new DepCollectorASTListener(*this)); 128} 129 130namespace { 131/// Private implementation for DependencyFileGenerator 132class 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; 143private: 144 bool FileMatchesDepCriteria(const char *Filename, 145 SrcMgr::CharacteristicKind FileType); 146 void OutputDependencyFile(); 147 148public: 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 175class DFGASTReaderListener : public ASTReaderListener { 176 DFGImpl &Parent; 177public: 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 190DependencyFileGenerator::DependencyFileGenerator(void *Impl) 191: Impl(Impl) { } 192 193DependencyFileGenerator *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 210void 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. 218bool 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 229void 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 260void 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 277void 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. 284static 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 294void 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 364bool 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 374void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename) { 375 if (Parent.includeModuleFiles()) 376 Parent.AddFilename(Filename); 377} 378