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