/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Frontend/DependencyFile.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- DependencyFile.cpp - Generate dependency file --------------------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | // |
9 | | // This code generates dependency files. |
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 | | Preprocessor &PP; |
35 | | DepCollectorPPCallbacks(DependencyCollector &L, Preprocessor &PP) |
36 | 3.62k | : DepCollector(L), PP(PP) {} |
37 | | |
38 | | void LexedFileChanged(FileID FID, LexedFileChangeReason Reason, |
39 | | SrcMgr::CharacteristicKind FileType, FileID PrevFID, |
40 | 1.11M | SourceLocation Loc) override { |
41 | 1.11M | if (Reason != PPCallbacks::LexedFileChangeReason::EnterFile) |
42 | 553k | return; |
43 | | |
44 | | // Dependency generation really does want to go all the way to the |
45 | | // file entry for a source location to find out what is depended on. |
46 | | // We do not want #line markers to affect dependency generation! |
47 | 556k | if (Optional<StringRef> Filename = |
48 | 556k | PP.getSourceManager().getNonBuiltinFilenameForID(FID)) |
49 | 553k | DepCollector.maybeAddDependency( |
50 | 553k | llvm::sys::path::remove_leading_dotslash(*Filename), |
51 | 553k | /*FromModule*/ false, isSystem(FileType), /*IsModuleFile*/ false, |
52 | 553k | /*IsMissing*/ false); |
53 | 556k | } |
54 | | |
55 | | void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok, |
56 | 1.72M | SrcMgr::CharacteristicKind FileType) override { |
57 | 1.72M | StringRef Filename = |
58 | 1.72M | llvm::sys::path::remove_leading_dotslash(SkippedFile.getName()); |
59 | 1.72M | DepCollector.maybeAddDependency(Filename, /*FromModule=*/false, |
60 | 1.72M | /*IsSystem=*/isSystem(FileType), |
61 | 1.72M | /*IsModuleFile=*/false, |
62 | 1.72M | /*IsMissing=*/false); |
63 | 1.72M | } |
64 | | |
65 | | void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok, |
66 | | StringRef FileName, bool IsAngled, |
67 | | CharSourceRange FilenameRange, |
68 | | Optional<FileEntryRef> File, StringRef SearchPath, |
69 | | StringRef RelativePath, const Module *Imported, |
70 | 2.27M | SrcMgr::CharacteristicKind FileType) override { |
71 | 2.27M | if (!File) |
72 | 23 | DepCollector.maybeAddDependency(FileName, /*FromModule*/false, |
73 | 23 | /*IsSystem*/false, /*IsModuleFile*/false, |
74 | 23 | /*IsMissing*/true); |
75 | | // Files that actually exist are handled by FileChanged. |
76 | 2.27M | } |
77 | | |
78 | | void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled, |
79 | | Optional<FileEntryRef> File, |
80 | 11.6k | SrcMgr::CharacteristicKind FileType) override { |
81 | 11.6k | if (!File) |
82 | 4.78k | return; |
83 | 6.89k | StringRef Filename = |
84 | 6.89k | llvm::sys::path::remove_leading_dotslash(File->getName()); |
85 | 6.89k | DepCollector.maybeAddDependency(Filename, /*FromModule=*/false, |
86 | 6.89k | /*IsSystem=*/isSystem(FileType), |
87 | 6.89k | /*IsModuleFile=*/false, |
88 | 6.89k | /*IsMissing=*/false); |
89 | 6.89k | } |
90 | | |
91 | 3.62k | void EndOfMainFile() override { |
92 | 3.62k | DepCollector.finishedMainFile(PP.getDiagnostics()); |
93 | 3.62k | } |
94 | | }; |
95 | | |
96 | | struct DepCollectorMMCallbacks : public ModuleMapCallbacks { |
97 | | DependencyCollector &DepCollector; |
98 | 3.62k | DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {} |
99 | | |
100 | | void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry, |
101 | 4.99k | bool IsSystem) override { |
102 | 4.99k | StringRef Filename = Entry.getName(); |
103 | 4.99k | DepCollector.maybeAddDependency(Filename, /*FromModule*/false, |
104 | 4.99k | /*IsSystem*/IsSystem, |
105 | 4.99k | /*IsModuleFile*/false, |
106 | 4.99k | /*IsMissing*/false); |
107 | 4.99k | } |
108 | | }; |
109 | | |
110 | | struct DepCollectorASTListener : public ASTReaderListener { |
111 | | DependencyCollector &DepCollector; |
112 | 943 | DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { } |
113 | 587k | bool needsInputFileVisitation() override { return true; } |
114 | 587k | bool needsSystemInputFileVisitation() override { |
115 | 587k | return DepCollector.needSystemDependencies(); |
116 | 587k | } |
117 | | void visitModuleFile(StringRef Filename, |
118 | 4.01k | serialization::ModuleKind Kind) override { |
119 | 4.01k | DepCollector.maybeAddDependency(Filename, /*FromModule*/true, |
120 | 4.01k | /*IsSystem*/false, /*IsModuleFile*/true, |
121 | 4.01k | /*IsMissing*/false); |
122 | 4.01k | } |
123 | | bool visitInputFile(StringRef Filename, bool IsSystem, |
124 | 583k | bool IsOverridden, bool IsExplicitModule) override { |
125 | 583k | if (IsOverridden || IsExplicitModule583k ) |
126 | 132 | return true; |
127 | | |
128 | 583k | DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem, |
129 | 583k | /*IsModuleFile*/false, /*IsMissing*/false); |
130 | 583k | return true; |
131 | 583k | } |
132 | | }; |
133 | | } // end anonymous namespace |
134 | | |
135 | | void DependencyCollector::maybeAddDependency(StringRef Filename, |
136 | | bool FromModule, bool IsSystem, |
137 | | bool IsModuleFile, |
138 | 2.88M | bool IsMissing) { |
139 | 2.88M | if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing)) |
140 | 2.87M | addDependency(Filename); |
141 | 2.88M | } |
142 | | |
143 | 2.87M | bool DependencyCollector::addDependency(StringRef Filename) { |
144 | 2.87M | StringRef SearchPath; |
145 | | #ifdef _WIN32 |
146 | | // Make the search insensitive to case and separators. |
147 | | llvm::SmallString<256> TmpPath = Filename; |
148 | | llvm::sys::path::native(TmpPath); |
149 | | std::transform(TmpPath.begin(), TmpPath.end(), TmpPath.begin(), ::tolower); |
150 | | SearchPath = TmpPath.str(); |
151 | | #else |
152 | 2.87M | SearchPath = Filename; |
153 | 2.87M | #endif |
154 | | |
155 | 2.87M | if (Seen.insert(SearchPath).second) { |
156 | 1.07M | Dependencies.push_back(std::string(Filename)); |
157 | 1.07M | return true; |
158 | 1.07M | } |
159 | 1.79M | return false; |
160 | 2.87M | } |
161 | | |
162 | 2.87M | static bool isSpecialFilename(StringRef Filename) { |
163 | 2.87M | return llvm::StringSwitch<bool>(Filename) |
164 | 2.87M | .Case("<built-in>", true) |
165 | 2.87M | .Case("<stdin>", true) |
166 | 2.87M | .Default(false); |
167 | 2.87M | } |
168 | | |
169 | | bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule, |
170 | | bool IsSystem, bool IsModuleFile, |
171 | 271 | bool IsMissing) { |
172 | 271 | return !isSpecialFilename(Filename) && |
173 | 271 | (needSystemDependencies() || !IsSystem0 ); |
174 | 271 | } |
175 | | |
176 | 459 | DependencyCollector::~DependencyCollector() { } |
177 | 3.62k | void DependencyCollector::attachToPreprocessor(Preprocessor &PP) { |
178 | 3.62k | PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(*this, PP)); |
179 | 3.62k | PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks( |
180 | 3.62k | std::make_unique<DepCollectorMMCallbacks>(*this)); |
181 | 3.62k | } |
182 | 942 | void DependencyCollector::attachToASTReader(ASTReader &R) { |
183 | 942 | R.addListener(std::make_unique<DepCollectorASTListener>(*this)); |
184 | 942 | } |
185 | | |
186 | | DependencyFileGenerator::DependencyFileGenerator( |
187 | | const DependencyOutputOptions &Opts) |
188 | | : OutputFile(Opts.OutputFile), Targets(Opts.Targets), |
189 | | IncludeSystemHeaders(Opts.IncludeSystemHeaders), |
190 | | PhonyTarget(Opts.UsePhonyTargets), |
191 | | AddMissingHeaderDeps(Opts.AddMissingHeaderDeps), SeenMissingHeader(false), |
192 | | IncludeModuleFiles(Opts.IncludeModuleFiles), |
193 | 3.66k | OutputFormat(Opts.OutputFormat), InputFileIndex(0) { |
194 | 3.66k | for (const auto &ExtraDep : Opts.ExtraDeps) { |
195 | 15 | if (addDependency(ExtraDep.first)) |
196 | 14 | ++InputFileIndex; |
197 | 15 | } |
198 | 3.66k | } |
199 | | |
200 | 3.53k | void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) { |
201 | | // Disable the "file not found" diagnostic if the -MG option was given. |
202 | 3.53k | if (AddMissingHeaderDeps) |
203 | 4 | PP.SetSuppressIncludeNotFoundError(true); |
204 | | |
205 | 3.53k | DependencyCollector::attachToPreprocessor(PP); |
206 | 3.53k | } |
207 | | |
208 | | bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule, |
209 | | bool IsSystem, bool IsModuleFile, |
210 | 2.88M | bool IsMissing) { |
211 | 2.88M | if (IsMissing) { |
212 | | // Handle the case of missing file from an inclusion directive. |
213 | 22 | if (AddMissingHeaderDeps) |
214 | 17 | return true; |
215 | 5 | SeenMissingHeader = true; |
216 | 5 | return false; |
217 | 22 | } |
218 | 2.88M | if (IsModuleFile && !IncludeModuleFiles3.99k ) |
219 | 3.99k | return false; |
220 | | |
221 | 2.87M | if (isSpecialFilename(Filename)) |
222 | 0 | return false; |
223 | | |
224 | 2.87M | if (IncludeSystemHeaders) |
225 | 2.87M | return true; |
226 | | |
227 | 100 | return !IsSystem; |
228 | 2.87M | } |
229 | | |
230 | 3.39k | void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) { |
231 | 3.39k | outputDependencyFile(Diags); |
232 | 3.39k | } |
233 | | |
234 | | /// Print the filename, with escaping or quoting that accommodates the three |
235 | | /// most likely tools that use dependency files: GNU Make, BSD Make, and |
236 | | /// NMake/Jom. |
237 | | /// |
238 | | /// BSD Make is the simplest case: It does no escaping at all. This means |
239 | | /// characters that are normally delimiters, i.e. space and # (the comment |
240 | | /// character) simply aren't supported in filenames. |
241 | | /// |
242 | | /// GNU Make does allow space and # in filenames, but to avoid being treated |
243 | | /// as a delimiter or comment, these must be escaped with a backslash. Because |
244 | | /// backslash is itself the escape character, if a backslash appears in a |
245 | | /// filename, it should be escaped as well. (As a special case, $ is escaped |
246 | | /// as $$, which is the normal Make way to handle the $ character.) |
247 | | /// For compatibility with BSD Make and historical practice, if GNU Make |
248 | | /// un-escapes characters in a filename but doesn't find a match, it will |
249 | | /// retry with the unmodified original string. |
250 | | /// |
251 | | /// GCC tries to accommodate both Make formats by escaping any space or # |
252 | | /// characters in the original filename, but not escaping backslashes. The |
253 | | /// apparent intent is so that filenames with backslashes will be handled |
254 | | /// correctly by BSD Make, and by GNU Make in its fallback mode of using the |
255 | | /// unmodified original string; filenames with # or space characters aren't |
256 | | /// supported by BSD Make at all, but will be handled correctly by GNU Make |
257 | | /// due to the escaping. |
258 | | /// |
259 | | /// A corner case that GCC gets only partly right is when the original filename |
260 | | /// has a backslash immediately followed by space or #. GNU Make would expect |
261 | | /// this backslash to be escaped; however GCC escapes the original backslash |
262 | | /// only when followed by space, not #. It will therefore take a dependency |
263 | | /// from a directive such as |
264 | | /// #include "a\ b\#c.h" |
265 | | /// and emit it as |
266 | | /// a\\\ b\\#c.h |
267 | | /// which GNU Make will interpret as |
268 | | /// a\ b\ |
269 | | /// followed by a comment. Failing to find this file, it will fall back to the |
270 | | /// original string, which probably doesn't exist either; in any case it won't |
271 | | /// find |
272 | | /// a\ b\#c.h |
273 | | /// which is the actual filename specified by the include directive. |
274 | | /// |
275 | | /// Clang does what GCC does, rather than what GNU Make expects. |
276 | | /// |
277 | | /// NMake/Jom has a different set of scary characters, but wraps filespecs in |
278 | | /// double-quotes to avoid misinterpreting them; see |
279 | | /// https://msdn.microsoft.com/en-us/library/dd9y37ha.aspx for NMake info, |
280 | | /// https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx |
281 | | /// for Windows file-naming info. |
282 | | static void PrintFilename(raw_ostream &OS, StringRef Filename, |
283 | 2.15M | DependencyOutputFormat OutputFormat) { |
284 | | // Convert filename to platform native path |
285 | 2.15M | llvm::SmallString<256> NativePath; |
286 | 2.15M | llvm::sys::path::native(Filename.str(), NativePath); |
287 | | |
288 | 2.15M | if (OutputFormat == DependencyOutputFormat::NMake) { |
289 | | // Add quotes if needed. These are the characters listed as "special" to |
290 | | // NMake, that are legal in a Windows filespec, and that could cause |
291 | | // misinterpretation of the dependency string. |
292 | 6 | if (NativePath.find_first_of(" #${}^!") != StringRef::npos) |
293 | 4 | OS << '\"' << NativePath << '\"'; |
294 | 2 | else |
295 | 2 | OS << NativePath; |
296 | 6 | return; |
297 | 6 | } |
298 | 2.15M | assert(OutputFormat == DependencyOutputFormat::Make); |
299 | 273M | for (unsigned i = 0, e = NativePath.size(); i != e; ++i271M ) { |
300 | 271M | if (NativePath[i] == '#') // Handle '#' the broken gcc way. |
301 | 6 | OS << '\\'; |
302 | 271M | else if (NativePath[i] == ' ') { // Handle space correctly. |
303 | 10 | OS << '\\'; |
304 | 10 | unsigned j = i; |
305 | 10 | while (j > 0 && NativePath[--j] == '\\'8 ) |
306 | 0 | OS << '\\'; |
307 | 271M | } else if (NativePath[i] == '$') // $ is escaped by $$. |
308 | 4 | OS << '$'; |
309 | 271M | OS << NativePath[i]; |
310 | 271M | } |
311 | 2.15M | } |
312 | | |
313 | 3.39k | void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) { |
314 | 3.39k | if (SeenMissingHeader) { |
315 | 1 | llvm::sys::fs::remove(OutputFile); |
316 | 1 | return; |
317 | 1 | } |
318 | | |
319 | 3.39k | std::error_code EC; |
320 | 3.39k | llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_TextWithCRLF); |
321 | 3.39k | if (EC) { |
322 | 0 | Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message(); |
323 | 0 | return; |
324 | 0 | } |
325 | | |
326 | 3.39k | outputDependencyFile(OS); |
327 | 3.39k | } |
328 | | |
329 | 3.52k | void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) { |
330 | | // Write out the dependency targets, trying to avoid overly long |
331 | | // lines when possible. We try our best to emit exactly the same |
332 | | // dependency file as GCC (4.2), assuming the included files are the |
333 | | // same. |
334 | 3.52k | const unsigned MaxColumns = 75; |
335 | 3.52k | unsigned Columns = 0; |
336 | | |
337 | 3.52k | for (StringRef Target : Targets) { |
338 | 3.52k | unsigned N = Target.size(); |
339 | 3.52k | if (Columns == 0) { |
340 | 3.52k | Columns += N; |
341 | 3.52k | } else if (6 Columns + N + 2 > MaxColumns6 ) { |
342 | 0 | Columns = N + 2; |
343 | 0 | OS << " \\\n "; |
344 | 6 | } else { |
345 | 6 | Columns += N + 1; |
346 | 6 | OS << ' '; |
347 | 6 | } |
348 | | // Targets already quoted as needed. |
349 | 3.52k | OS << Target; |
350 | 3.52k | } |
351 | | |
352 | 3.52k | OS << ':'; |
353 | 3.52k | Columns += 1; |
354 | | |
355 | | // Now add each dependency in the order it was seen, but avoiding |
356 | | // duplicates. |
357 | 3.52k | ArrayRef<std::string> Files = getDependencies(); |
358 | 1.07M | for (StringRef File : Files) { |
359 | | // Start a new line if this would exceed the column limit. Make |
360 | | // sure to leave space for a trailing " \" in case we need to |
361 | | // break the line on the next iteration. |
362 | 1.07M | unsigned N = File.size(); |
363 | 1.07M | if (Columns + (N + 1) + 2 > MaxColumns) { |
364 | 1.07M | OS << " \\\n "; |
365 | 1.07M | Columns = 2; |
366 | 1.07M | } |
367 | 1.07M | OS << ' '; |
368 | 1.07M | PrintFilename(OS, File, OutputFormat); |
369 | 1.07M | Columns += N + 1; |
370 | 1.07M | } |
371 | 3.52k | OS << '\n'; |
372 | | |
373 | | // Create phony targets if requested. |
374 | 3.52k | if (PhonyTarget && !Files.empty()3.34k ) { |
375 | 3.33k | unsigned Index = 0; |
376 | 1.08M | for (auto I = Files.begin(), E = Files.end(); I != E; ++I1.07M ) { |
377 | 1.07M | if (Index++ == InputFileIndex) |
378 | 3.33k | continue; |
379 | 1.07M | OS << '\n'; |
380 | 1.07M | PrintFilename(OS, *I, OutputFormat); |
381 | 1.07M | OS << ":\n"; |
382 | 1.07M | } |
383 | 3.33k | } |
384 | 3.52k | } |