1 | |
2 | |
3 | |
4 | |
5 | |
6 | |
7 | |
8 | |
9 | |
10 | |
11 | |
12 | |
13 | #include "clang/Frontend/Utils.h" |
14 | #include "clang/Basic/FileManager.h" |
15 | #include "clang/Basic/SourceManager.h" |
16 | #include "clang/Frontend/DependencyOutputOptions.h" |
17 | #include "clang/Frontend/FrontendDiagnostic.h" |
18 | #include "clang/Lex/DirectoryLookup.h" |
19 | #include "clang/Lex/ModuleMap.h" |
20 | #include "clang/Lex/PPCallbacks.h" |
21 | #include "clang/Lex/Preprocessor.h" |
22 | #include "clang/Serialization/ASTReader.h" |
23 | #include "llvm/ADT/StringSet.h" |
24 | #include "llvm/ADT/StringSwitch.h" |
25 | #include "llvm/Support/FileSystem.h" |
26 | #include "llvm/Support/Path.h" |
27 | #include "llvm/Support/raw_ostream.h" |
28 | |
29 | using namespace clang; |
30 | |
31 | namespace { |
32 | struct DepCollectorPPCallbacks : public PPCallbacks { |
33 | DependencyCollector &DepCollector; |
34 | SourceManager &SM; |
35 | DepCollectorPPCallbacks(DependencyCollector &L, SourceManager &SM) |
36 | : DepCollector(L), SM(SM) { } |
37 | |
38 | void FileChanged(SourceLocation Loc, FileChangeReason Reason, |
39 | SrcMgr::CharacteristicKind FileType, |
40 | FileID PrevFID) override { |
41 | if (Reason != PPCallbacks::EnterFile) |
42 | return; |
43 | |
44 | |
45 | |
46 | |
47 | const FileEntry *FE = |
48 | SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); |
49 | if (!FE) |
50 | return; |
51 | |
52 | StringRef Filename = |
53 | llvm::sys::path::remove_leading_dotslash(FE->getName()); |
54 | |
55 | DepCollector.maybeAddDependency(Filename, , |
56 | isSystem(FileType), |
57 | , ); |
58 | } |
59 | |
60 | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
61 | StringRef FileName, bool IsAngled, |
62 | CharSourceRange FilenameRange, const FileEntry *File, |
63 | StringRef SearchPath, StringRef RelativePath, |
64 | const Module *Imported, |
65 | SrcMgr::CharacteristicKind FileType) override { |
66 | if (!File) |
67 | DepCollector.maybeAddDependency(FileName, , |
68 | , , |
69 | ); |
70 | |
71 | } |
72 | |
73 | void EndOfMainFile() override { |
74 | DepCollector.finishedMainFile(); |
75 | } |
76 | }; |
77 | |
78 | struct DepCollectorMMCallbacks : public ModuleMapCallbacks { |
79 | DependencyCollector &DepCollector; |
80 | DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {} |
81 | |
82 | void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, |
83 | bool IsSystem) override { |
84 | StringRef Filename = Entry.getName(); |
85 | DepCollector.maybeAddDependency(Filename, , |
86 | IsSystem, |
87 | , |
88 | ); |
89 | } |
90 | }; |
91 | |
92 | struct DepCollectorASTListener : public ASTReaderListener { |
93 | DependencyCollector &DepCollector; |
94 | DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { } |
95 | bool needsInputFileVisitation() override { return true; } |
96 | bool needsSystemInputFileVisitation() override { |
97 | return DepCollector.needSystemDependencies(); |
98 | } |
99 | void visitModuleFile(StringRef Filename, |
100 | serialization::ModuleKind Kind) override { |
101 | DepCollector.maybeAddDependency(Filename, , |
102 | , , |
103 | ); |
104 | } |
105 | bool visitInputFile(StringRef Filename, bool IsSystem, |
106 | bool IsOverridden, bool IsExplicitModule) override { |
107 | if (IsOverridden || IsExplicitModule) |
108 | return true; |
109 | |
110 | DepCollector.maybeAddDependency(Filename, , IsSystem, |
111 | , ); |
112 | return true; |
113 | } |
114 | }; |
115 | } |
116 | |
117 | void DependencyCollector::maybeAddDependency(StringRef Filename, bool FromModule, |
118 | bool IsSystem, bool IsModuleFile, |
119 | bool IsMissing) { |
120 | if (Seen.insert(Filename).second && |
121 | sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing)) |
122 | Dependencies.push_back(Filename); |
123 | } |
124 | |
125 | static bool isSpecialFilename(StringRef Filename) { |
126 | return llvm::StringSwitch<bool>(Filename) |
127 | .Case("<built-in>", true) |
128 | .Case("<stdin>", true) |
129 | .Default(false); |
130 | } |
131 | |
132 | bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, |
133 | bool IsSystem, bool IsModuleFile, |
134 | bool IsMissing) { |
135 | return !isSpecialFilename(Filename) && |
136 | (needSystemDependencies() || !IsSystem); |
137 | } |
138 | |
139 | DependencyCollector::~DependencyCollector() { } |
140 | void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { |
141 | PP.addPPCallbacks( |
142 | llvm::make_unique<DepCollectorPPCallbacks>(*this, PP.getSourceManager())); |
143 | PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( |
144 | llvm::make_unique<DepCollectorMMCallbacks>(*this)); |
145 | } |
146 | void DependencyCollector::attachToASTReader(ASTReader &R) { |
147 | R.addListener(llvm::make_unique<DepCollectorASTListener>(*this)); |
148 | } |
149 | |
150 | namespace { |
151 | |
152 | class DFGImpl : public PPCallbacks { |
153 | std::vector<std::string> Files; |
154 | llvm::StringSet<> FilesSet; |
155 | const Preprocessor *PP; |
156 | std::string OutputFile; |
157 | std::vector<std::string> Targets; |
158 | bool ; |
159 | bool PhonyTarget; |
160 | bool ; |
161 | bool ; |
162 | bool IncludeModuleFiles; |
163 | DependencyOutputFormat OutputFormat; |
164 | unsigned InputFileIndex; |
165 | |
166 | private: |
167 | bool FileMatchesDepCriteria(const char *Filename, |
168 | SrcMgr::CharacteristicKind FileType); |
169 | void OutputDependencyFile(); |
170 | |
171 | public: |
172 | DFGImpl(const Preprocessor *_PP, const DependencyOutputOptions &Opts) |
173 | : PP(_PP), OutputFile(Opts.OutputFile), Targets(Opts.Targets), |
174 | IncludeSystemHeaders(Opts.IncludeSystemHeaders), |
175 | PhonyTarget(Opts.UsePhonyTargets), |
176 | AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), |
177 | SeenMissingHeader(false), |
178 | IncludeModuleFiles(Opts.IncludeModuleFiles), |
179 | OutputFormat(Opts.OutputFormat), |
180 | InputFileIndex(0) { |
181 | for (const auto & : Opts.ExtraDeps) { |
182 | if (AddFilename(ExtraDep)) |
183 | ++InputFileIndex; |
184 | } |
185 | } |
186 | |
187 | void FileChanged(SourceLocation Loc, FileChangeReason Reason, |
188 | SrcMgr::CharacteristicKind FileType, |
189 | FileID PrevFID) override; |
190 | |
191 | void FileSkipped(const FileEntry &SkippedFile, const Token &FilenameTok, |
192 | SrcMgr::CharacteristicKind FileType) override; |
193 | |
194 | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
195 | StringRef FileName, bool IsAngled, |
196 | CharSourceRange FilenameRange, const FileEntry *File, |
197 | StringRef SearchPath, StringRef RelativePath, |
198 | const Module *Imported, |
199 | SrcMgr::CharacteristicKind FileType) override; |
200 | |
201 | void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled, |
202 | const FileEntry *File, |
203 | SrcMgr::CharacteristicKind FileType) override; |
204 | |
205 | void EndOfMainFile() override { |
206 | OutputDependencyFile(); |
207 | } |
208 | |
209 | bool AddFilename(StringRef Filename); |
210 | bool () const { return IncludeSystemHeaders; } |
211 | bool includeModuleFiles() const { return IncludeModuleFiles; } |
212 | }; |
213 | |
214 | class DFGMMCallback : public ModuleMapCallbacks { |
215 | DFGImpl &Parent; |
216 | public: |
217 | DFGMMCallback(DFGImpl &Parent) : Parent(Parent) {} |
218 | void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, |
219 | bool IsSystem) override { |
220 | if (!IsSystem || Parent.includeSystemHeaders()) |
221 | Parent.AddFilename(Entry.getName()); |
222 | } |
223 | }; |
224 | |
225 | class DFGASTReaderListener : public ASTReaderListener { |
226 | DFGImpl &Parent; |
227 | public: |
228 | DFGASTReaderListener(DFGImpl &Parent) |
229 | : Parent(Parent) { } |
230 | bool needsInputFileVisitation() override { return true; } |
231 | bool needsSystemInputFileVisitation() override { |
232 | return Parent.includeSystemHeaders(); |
233 | } |
234 | void visitModuleFile(StringRef Filename, |
235 | serialization::ModuleKind Kind) override; |
236 | bool visitInputFile(StringRef Filename, bool isSystem, |
237 | bool isOverridden, bool isExplicitModule) override; |
238 | }; |
239 | } |
240 | |
241 | DependencyFileGenerator::DependencyFileGenerator(void *Impl) |
242 | : Impl(Impl) { } |
243 | |
244 | DependencyFileGenerator *DependencyFileGenerator::CreateAndAttachToPreprocessor( |
245 | clang::Preprocessor &PP, const clang::DependencyOutputOptions &Opts) { |
246 | |
247 | if (Opts.Targets.empty()) { |
248 | PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT); |
249 | return nullptr; |
250 | } |
251 | |
252 | |
253 | if (Opts.AddMissingHeaderDeps) |
254 | PP.SetSuppressIncludeNotFoundError(true); |
255 | |
256 | DFGImpl *Callback = new DFGImpl(&PP, Opts); |
257 | PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(Callback)); |
258 | PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( |
259 | llvm::make_unique<DFGMMCallback>(*Callback)); |
260 | return new DependencyFileGenerator(Callback); |
261 | } |
262 | |
263 | void DependencyFileGenerator::AttachToASTReader(ASTReader &R) { |
264 | DFGImpl *I = reinterpret_cast<DFGImpl *>(Impl); |
265 | (0) . __assert_fail ("I && \"missing implementation\"", "/home/seafit/code_projects/clang_source/clang/lib/Frontend/DependencyFile.cpp", 265, __PRETTY_FUNCTION__))" file_link="../../../include/assert.h.html#88" macro="true">assert(I && "missing implementation"); |
266 | R.addListener(llvm::make_unique<DFGASTReaderListener>(*I)); |
267 | } |
268 | |
269 | |
270 | |
271 | bool DFGImpl::FileMatchesDepCriteria(const char *Filename, |
272 | SrcMgr::CharacteristicKind FileType) { |
273 | if (isSpecialFilename(Filename)) |
274 | return false; |
275 | |
276 | if (IncludeSystemHeaders) |
277 | return true; |
278 | |
279 | return !isSystem(FileType); |
280 | } |
281 | |
282 | void DFGImpl::FileChanged(SourceLocation Loc, |
283 | FileChangeReason Reason, |
284 | SrcMgr::CharacteristicKind FileType, |
285 | FileID PrevFID) { |
286 | if (Reason != PPCallbacks::EnterFile) |
287 | return; |
288 | |
289 | |
290 | |
291 | |
292 | SourceManager &SM = PP->getSourceManager(); |
293 | |
294 | const FileEntry *FE = |
295 | SM.getFileEntryForID(SM.getFileID(SM.getExpansionLoc(Loc))); |
296 | if (!FE) return; |
297 | |
298 | StringRef Filename = FE->getName(); |
299 | if (!FileMatchesDepCriteria(Filename.data(), FileType)) |
300 | return; |
301 | |
302 | AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); |
303 | } |
304 | |
305 | void DFGImpl::FileSkipped(const FileEntry &SkippedFile, |
306 | const Token &FilenameTok, |
307 | SrcMgr::CharacteristicKind FileType) { |
308 | StringRef Filename = SkippedFile.getName(); |
309 | if (!FileMatchesDepCriteria(Filename.data(), FileType)) |
310 | return; |
311 | |
312 | AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); |
313 | } |
314 | |
315 | void DFGImpl::InclusionDirective(SourceLocation HashLoc, |
316 | const Token &IncludeTok, |
317 | StringRef FileName, |
318 | bool IsAngled, |
319 | CharSourceRange FilenameRange, |
320 | const FileEntry *File, |
321 | StringRef SearchPath, |
322 | StringRef RelativePath, |
323 | const Module *Imported, |
324 | SrcMgr::CharacteristicKind FileType) { |
325 | if (!File) { |
326 | if (AddMissingHeaderDeps) |
327 | AddFilename(FileName); |
328 | else |
329 | SeenMissingHeader = true; |
330 | } |
331 | } |
332 | |
333 | void DFGImpl::HasInclude(SourceLocation Loc, StringRef SpelledFilename, |
334 | bool IsAngled, const FileEntry *File, |
335 | SrcMgr::CharacteristicKind FileType) { |
336 | if (!File) |
337 | return; |
338 | StringRef Filename = File->getName(); |
339 | if (!FileMatchesDepCriteria(Filename.data(), FileType)) |
340 | return; |
341 | AddFilename(llvm::sys::path::remove_leading_dotslash(Filename)); |
342 | } |
343 | |
344 | bool DFGImpl::AddFilename(StringRef Filename) { |
345 | if (FilesSet.insert(Filename).second) { |
346 | Files.push_back(Filename); |
347 | return true; |
348 | } |
349 | return false; |
350 | } |
351 | |
352 | |
353 | |
354 | |
355 | |
356 | |
357 | |
358 | |
359 | |
360 | |
361 | |
362 | |
363 | |
364 | |
365 | |
366 | |
367 | |
368 | |
369 | |
370 | |
371 | |
372 | |
373 | |
374 | |
375 | |
376 | |
377 | |
378 | |
379 | |
380 | |
381 | |
382 | |
383 | |
384 | |
385 | |
386 | |
387 | |
388 | |
389 | |
390 | |
391 | |
392 | |
393 | |
394 | |
395 | |
396 | |
397 | |
398 | |
399 | |
400 | static void PrintFilename(raw_ostream &OS, StringRef Filename, |
401 | DependencyOutputFormat OutputFormat) { |
402 | |
403 | llvm::SmallString<256> NativePath; |
404 | llvm::sys::path::native(Filename.str(), NativePath); |
405 | |
406 | if (OutputFormat == DependencyOutputFormat::NMake) { |
407 | |
408 | |
409 | |
410 | if (NativePath.find_first_of(" #${}^!") != StringRef::npos) |
411 | OS << '\"' << NativePath << '\"'; |
412 | else |
413 | OS << NativePath; |
414 | return; |
415 | } |
416 | assert(OutputFormat == DependencyOutputFormat::Make); |
417 | for (unsigned i = 0, e = NativePath.size(); i != e; ++i) { |
418 | if (NativePath[i] == '#') |
419 | OS << '\\'; |
420 | else if (NativePath[i] == ' ') { |
421 | OS << '\\'; |
422 | unsigned j = i; |
423 | while (j > 0 && NativePath[--j] == '\\') |
424 | OS << '\\'; |
425 | } else if (NativePath[i] == '$') |
426 | OS << '$'; |
427 | OS << NativePath[i]; |
428 | } |
429 | } |
430 | |
431 | void DFGImpl::OutputDependencyFile() { |
432 | if (SeenMissingHeader) { |
433 | llvm::sys::fs::remove(OutputFile); |
434 | return; |
435 | } |
436 | |
437 | std::error_code EC; |
438 | llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); |
439 | if (EC) { |
440 | PP->getDiagnostics().Report(diag::err_fe_error_opening) << OutputFile |
441 | << EC.message(); |
442 | return; |
443 | } |
444 | |
445 | |
446 | |
447 | |
448 | |
449 | const unsigned MaxColumns = 75; |
450 | unsigned Columns = 0; |
451 | |
452 | for (StringRef Target : Targets) { |
453 | unsigned N = Target.size(); |
454 | if (Columns == 0) { |
455 | Columns += N; |
456 | } else if (Columns + N + 2 > MaxColumns) { |
457 | Columns = N + 2; |
458 | OS << " \\\n "; |
459 | } else { |
460 | Columns += N + 1; |
461 | OS << ' '; |
462 | } |
463 | |
464 | OS << Target; |
465 | } |
466 | |
467 | OS << ':'; |
468 | Columns += 1; |
469 | |
470 | |
471 | |
472 | for (StringRef File : Files) { |
473 | |
474 | |
475 | |
476 | unsigned N = File.size(); |
477 | if (Columns + (N + 1) + 2 > MaxColumns) { |
478 | OS << " \\\n "; |
479 | Columns = 2; |
480 | } |
481 | OS << ' '; |
482 | PrintFilename(OS, File, OutputFormat); |
483 | Columns += N + 1; |
484 | } |
485 | OS << '\n'; |
486 | |
487 | |
488 | if (PhonyTarget && !Files.empty()) { |
489 | unsigned Index = 0; |
490 | for (auto I = Files.begin(), E = Files.end(); I != E; ++I) { |
491 | if (Index++ == InputFileIndex) |
492 | continue; |
493 | OS << '\n'; |
494 | PrintFilename(OS, *I, OutputFormat); |
495 | OS << ":\n"; |
496 | } |
497 | } |
498 | } |
499 | |
500 | bool DFGASTReaderListener::visitInputFile(llvm::StringRef Filename, |
501 | bool IsSystem, bool IsOverridden, |
502 | bool IsExplicitModule) { |
503 | assert(!IsSystem || needsSystemInputFileVisitation()); |
504 | if (IsOverridden || IsExplicitModule) |
505 | return true; |
506 | |
507 | Parent.AddFilename(Filename); |
508 | return true; |
509 | } |
510 | |
511 | void DFGASTReaderListener::visitModuleFile(llvm::StringRef Filename, |
512 | serialization::ModuleKind Kind) { |
513 | if (Parent.includeModuleFiles()) |
514 | Parent.AddFilename(Filename); |
515 | } |
516 | |