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