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