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