Coverage Report

Created: 2020-09-19 12:23

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