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