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 "llvm/ADT/StringSet.h" 24 #include "llvm/Support/Path.h" 25 #include "llvm/Support/raw_ostream.h" 26 27 using namespace clang; 28 29 namespace { 30 class DependencyFileCallback : public PPCallbacks { 31 std::vector<std::string> Files; 32 llvm::StringSet<> FilesSet; 33 const Preprocessor *PP; 34 std::vector<std::string> Targets; 35 raw_ostream *OS; 36 bool IncludeSystemHeaders; 37 bool PhonyTarget; 38 bool AddMissingHeaderDeps; 39 private: 40 bool FileMatchesDepCriteria(const char *Filename, 41 SrcMgr::CharacteristicKind FileType); 42 void AddFilename(StringRef Filename); 43 void OutputDependencyFile(); 44 45 public: 46 DependencyFileCallback(const Preprocessor *_PP, 47 raw_ostream *_OS, 48 const DependencyOutputOptions &Opts) 49 : PP(_PP), Targets(Opts.Targets), OS(_OS), 50 IncludeSystemHeaders(Opts.IncludeSystemHeaders), 51 PhonyTarget(Opts.UsePhonyTargets), 52 AddMissingHeaderDeps(Opts.AddMissingHeaderDeps) {} 53 54 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, 55 SrcMgr::CharacteristicKind FileType, 56 FileID PrevFID); 57 virtual void InclusionDirective(SourceLocation HashLoc, 58 const Token &IncludeTok, 59 StringRef FileName, 60 bool IsAngled, 61 const FileEntry *File, 62 SourceLocation EndLoc, 63 StringRef SearchPath, 64 StringRef RelativePath); 65 66 virtual void EndOfMainFile() { 67 OutputDependencyFile(); 68 delete OS; 69 OS = 0; 70 } 71 }; 72 } 73 74 void clang::AttachDependencyFileGen(Preprocessor &PP, 75 const DependencyOutputOptions &Opts) { 76 if (Opts.Targets.empty()) { 77 PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); 78 return; 79 } 80 81 std::string Err; 82 raw_ostream *OS(new llvm::raw_fd_ostream(Opts.OutputFile.c_str(), Err)); 83 if (!Err.empty()) { 84 PP.getDiagnostics().Report(diag::err_fe_error_opening) 85 << Opts.OutputFile << Err; 86 return; 87 } 88 89 // Disable the "file not found" diagnostic if the -MG option was given. 90 if (Opts.AddMissingHeaderDeps) 91 PP.SetSuppressIncludeNotFoundError(true); 92 93 PP.addPPCallbacks(new DependencyFileCallback(&PP, OS, Opts)); 94 } 95 96 /// FileMatchesDepCriteria - Determine whether the given Filename should be 97 /// considered as a dependency. 98 bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename, 99 SrcMgr::CharacteristicKind FileType) { 100 if (strcmp("<built-in>", Filename) == 0) 101 return false; 102 103 if (IncludeSystemHeaders) 104 return true; 105 106 return FileType == SrcMgr::C_User; 107 } 108 109 void DependencyFileCallback::FileChanged(SourceLocation Loc, 110 FileChangeReason Reason, 111 SrcMgr::CharacteristicKind FileType, 112 FileID PrevFID) { 113 if (Reason != PPCallbacks::EnterFile) 114 return; 115 116 // Dependency generation really does want to go all the way to the 117 // file entry for a source location to find out what is depended on. 118 // We do not want #line markers to affect dependency generation! 119 SourceManager &SM = PP->getSourceManager(); 120 121 const FileEntry *FE = 122 SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); 123 if (FE == 0) return; 124 125 StringRef Filename = FE->getName(); 126 if (!FileMatchesDepCriteria(Filename.data(), FileType)) 127 return; 128 129 // Remove leading "./" (or ".//" or "././" etc.) 130 while (Filename.size() > 2 && Filename[0] == '.' && 131 llvm::sys::path::is_separator(Filename[1])) { 132 Filename = Filename.substr(1); 133 while (llvm::sys::path::is_separator(Filename[0])) 134 Filename = Filename.substr(1); 135 } 136 137 AddFilename(Filename); 138 } 139 140 void DependencyFileCallback::InclusionDirective(SourceLocation HashLoc, 141 const Token &IncludeTok, 142 StringRef FileName, 143 bool IsAngled, 144 const FileEntry *File, 145 SourceLocation EndLoc, 146 StringRef SearchPath, 147 StringRef RelativePath) { 148 if (AddMissingHeaderDeps && !File) 149 AddFilename(FileName); 150 } 151 152 void DependencyFileCallback::AddFilename(StringRef Filename) { 153 if (FilesSet.insert(Filename)) 154 Files.push_back(Filename); 155 } 156 157 /// PrintFilename - GCC escapes spaces, but apparently not ' or " or other 158 /// scary characters. 159 static void PrintFilename(raw_ostream &OS, StringRef Filename) { 160 for (unsigned i = 0, e = Filename.size(); i != e; ++i) { 161 if (Filename[i] == ' ') 162 OS << '\\'; 163 OS << Filename[i]; 164 } 165 } 166 167 void DependencyFileCallback::OutputDependencyFile() { 168 // Write out the dependency targets, trying to avoid overly long 169 // lines when possible. We try our best to emit exactly the same 170 // dependency file as GCC (4.2), assuming the included files are the 171 // same. 172 const unsigned MaxColumns = 75; 173 unsigned Columns = 0; 174 175 for (std::vector<std::string>::iterator 176 I = Targets.begin(), E = Targets.end(); I != E; ++I) { 177 unsigned N = I->length(); 178 if (Columns == 0) { 179 Columns += N; 180 } else if (Columns + N + 2 > MaxColumns) { 181 Columns = N + 2; 182 *OS << " \\\n "; 183 } else { 184 Columns += N + 1; 185 *OS << ' '; 186 } 187 // Targets already quoted as needed. 188 *OS << *I; 189 } 190 191 *OS << ':'; 192 Columns += 1; 193 194 // Now add each dependency in the order it was seen, but avoiding 195 // duplicates. 196 for (std::vector<std::string>::iterator I = Files.begin(), 197 E = Files.end(); I != E; ++I) { 198 // Start a new line if this would exceed the column limit. Make 199 // sure to leave space for a trailing " \" in case we need to 200 // break the line on the next iteration. 201 unsigned N = I->length(); 202 if (Columns + (N + 1) + 2 > MaxColumns) { 203 *OS << " \\\n "; 204 Columns = 2; 205 } 206 *OS << ' '; 207 PrintFilename(*OS, *I); 208 Columns += N + 1; 209 } 210 *OS << '\n'; 211 212 // Create phony targets if requested. 213 if (PhonyTarget && !Files.empty()) { 214 // Skip the first entry, this is always the input file itself. 215 for (std::vector<std::string>::iterator I = Files.begin() + 1, 216 E = Files.end(); I != E; ++I) { 217 *OS << '\n'; 218 PrintFilename(*OS, *I); 219 *OS << ":\n"; 220 } 221 } 222 } 223 224