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