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