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