Coverage Report

Created: 2020-02-25 14:32

/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
2.94k
      : DepCollector(L), SM(SM), Diags(Diags) {}
39
40
  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
41
                   SrcMgr::CharacteristicKind FileType,
42
763k
                   FileID PrevFID) override {
43
763k
    if (Reason != PPCallbacks::EnterFile)
44
393k
      return;
45
370k
46
370k
    // Dependency generation really does want to go all the way to the
47
370k
    // file entry for a source location to find out what is depended on.
48
370k
    // We do not want #line markers to affect dependency generation!
49
370k
    Optional<FileEntryRef> File =
50
370k
        SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(Loc)));
51
370k
    if (!File)
52
5.86k
      return;
53
364k
54
364k
    StringRef Filename =
55
364k
        llvm::sys::path::remove_leading_dotslash(File->getName());
56
364k
57
364k
    DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
58
364k
                                    isSystem(FileType),
59
364k
                                    /*IsModuleFile*/false, /*IsMissing*/false);
60
364k
  }
61
62
  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
63
638k
                   SrcMgr::CharacteristicKind FileType) override {
64
638k
    StringRef Filename =
65
638k
        llvm::sys::path::remove_leading_dotslash(SkippedFile.getName());
66
638k
    DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
67
638k
                                    /*IsSystem=*/isSystem(FileType),
68
638k
                                    /*IsModuleFile=*/false,
69
638k
                                    /*IsMissing=*/false);
70
638k
  }
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.00M
                          SrcMgr::CharacteristicKind FileType) override {
78
1.00M
    if (!File)
79
21
      DepCollector.maybeAddDependency(FileName, /*FromModule*/false,
80
21
                                     /*IsSystem*/false, /*IsModuleFile*/false,
81
21
                                     /*IsMissing*/true);
82
1.00M
    // Files that actually exist are handled by FileChanged.
83
1.00M
  }
84
85
  void HasInclude(SourceLocation Loc, StringRef SpelledFilename, bool IsAngled,
86
                  Optional<FileEntryRef> File,
87
7.71k
                  SrcMgr::CharacteristicKind FileType) override {
88
7.71k
    if (!File)
89
4.25k
      return;
90
3.45k
    StringRef Filename =
91
3.45k
        llvm::sys::path::remove_leading_dotslash(File->getName());
92
3.45k
    DepCollector.maybeAddDependency(Filename, /*FromModule=*/false,
93
3.45k
                                    /*IsSystem=*/isSystem(FileType),
94
3.45k
                                    /*IsModuleFile=*/false,
95
3.45k
                                    /*IsMissing=*/false);
96
3.45k
  }
97
98
2.94k
  void EndOfMainFile() override { DepCollector.finishedMainFile(Diags); }
99
};
100
101
struct DepCollectorMMCallbacks : public ModuleMapCallbacks {
102
  DependencyCollector &DepCollector;
103
2.94k
  DepCollectorMMCallbacks(DependencyCollector &DC) : DepCollector(DC) {}
104
105
  void moduleMapFileRead(SourceLocation Loc, const FileEntry &Entry,
106
4.92k
                         bool IsSystem) override {
107
4.92k
    StringRef Filename = Entry.getName();
108
4.92k
    DepCollector.maybeAddDependency(Filename, /*FromModule*/false,
109
4.92k
                                    /*IsSystem*/IsSystem,
110
4.92k
                                    /*IsModuleFile*/false,
111
4.92k
                                    /*IsMissing*/false);
112
4.92k
  }
113
};
114
115
struct DepCollectorASTListener : public ASTReaderListener {
116
  DependencyCollector &DepCollector;
117
610
  DepCollectorASTListener(DependencyCollector &L) : DepCollector(L) { }
118
445k
  bool needsInputFileVisitation() override { return true; }
119
442k
  bool needsSystemInputFileVisitation() override {
120
442k
    return DepCollector.needSystemDependencies();
121
442k
  }
122
  void visitModuleFile(StringRef Filename,
123
4.19k
                       serialization::ModuleKind Kind) override {
124
4.19k
    DepCollector.maybeAddDependency(Filename, /*FromModule*/true,
125
4.19k
                                   /*IsSystem*/false, /*IsModuleFile*/true,
126
4.19k
                                   /*IsMissing*/false);
127
4.19k
  }
128
  bool visitInputFile(StringRef Filename, bool IsSystem,
129
441k
                      bool IsOverridden, bool IsExplicitModule) override {
130
441k
    if (IsOverridden || 
IsExplicitModule440k
)
131
163
      return true;
132
440k
133
440k
    DepCollector.maybeAddDependency(Filename, /*FromModule*/true, IsSystem,
134
440k
                                   /*IsModuleFile*/false, /*IsMissing*/false);
135
440k
    return true;
136
440k
  }
137
};
138
} // end anonymous namespace
139
140
void DependencyCollector::maybeAddDependency(StringRef Filename,
141
                                             bool FromModule, bool IsSystem,
142
                                             bool IsModuleFile,
143
1.45M
                                             bool IsMissing) {
144
1.45M
  if (sawDependency(Filename, FromModule, IsSystem, IsModuleFile, IsMissing))
145
1.45M
    addDependency(Filename);
146
1.45M
}
147
148
1.45M
bool DependencyCollector::addDependency(StringRef Filename) {
149
1.45M
  if (Seen.insert(Filename).second) {
150
753k
    Dependencies.push_back(std::string(Filename));
151
753k
    return true;
152
753k
  }
153
699k
  return false;
154
699k
}
155
156
1.45M
static bool isSpecialFilename(StringRef Filename) {
157
1.45M
  return llvm::StringSwitch<bool>(Filename)
158
1.45M
      .Case("<built-in>", true)
159
1.45M
      .Case("<stdin>", true)
160
1.45M
      .Default(false);
161
1.45M
}
162
163
bool DependencyCollector::sawDependency(StringRef Filename, bool FromModule,
164
                                        bool IsSystem, bool IsModuleFile,
165
265
                                        bool IsMissing) {
166
265
  return !isSpecialFilename(Filename) &&
167
265
         (needSystemDependencies() || 
!IsSystem0
);
168
265
}
169
170
265
DependencyCollector::~DependencyCollector() { }
171
2.94k
void DependencyCollector::attachToPreprocessor(Preprocessor &PP) {
172
2.94k
  PP.addPPCallbacks(std::make_unique<DepCollectorPPCallbacks>(
173
2.94k
      *this, PP.getSourceManager(), PP.getDiagnostics()));
174
2.94k
  PP.getHeaderSearchInfo().getModuleMap().addModuleMapCallbacks(
175
2.94k
      std::make_unique<DepCollectorMMCallbacks>(*this));
176
2.94k
}
177
610
void DependencyCollector::attachToASTReader(ASTReader &R) {
178
610
  R.addListener(std::make_unique<DepCollectorASTListener>(*this));
179
610
}
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
2.91k
      OutputFormat(Opts.OutputFormat), InputFileIndex(0) {
189
2.91k
  for (const auto &ExtraDep : Opts.ExtraDeps) {
190
14
    if (addDependency(ExtraDep))
191
13
      ++InputFileIndex;
192
14
  }
193
2.91k
}
194
195
2.85k
void DependencyFileGenerator::attachToPreprocessor(Preprocessor &PP) {
196
2.85k
  // Disable the "file not found" diagnostic if the -MG option was given.
197
2.85k
  if (AddMissingHeaderDeps)
198
4
    PP.SetSuppressIncludeNotFoundError(true);
199
2.85k
200
2.85k
  DependencyCollector::attachToPreprocessor(PP);
201
2.85k
}
202
203
bool DependencyFileGenerator::sawDependency(StringRef Filename, bool FromModule,
204
                                            bool IsSystem, bool IsModuleFile,
205
1.45M
                                            bool IsMissing) {
206
1.45M
  if (IsMissing) {
207
20
    // 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.45M
  if (IsModuleFile && 
!IncludeModuleFiles4.16k
)
214
4.16k
    return false;
215
1.45M
216
1.45M
  if (isSpecialFilename(Filename))
217
0
    return false;
218
1.45M
219
1.45M
  if (IncludeSystemHeaders)
220
1.45M
    return true;
221
96
222
96
  return !IsSystem;
223
96
}
224
225
2.78k
void DependencyFileGenerator::finishedMainFile(DiagnosticsEngine &Diags) {
226
2.78k
  outputDependencyFile(Diags);
227
2.78k
}
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
752k
                          DependencyOutputFormat OutputFormat) {
279
752k
  // Convert filename to platform native path
280
752k
  llvm::SmallString<256> NativePath;
281
752k
  llvm::sys::path::native(Filename.str(), NativePath);
282
752k
283
752k
  if (OutputFormat == DependencyOutputFormat::NMake) {
284
6
    // Add quotes if needed. These are the characters listed as "special" to
285
6
    // NMake, that are legal in a Windows filespec, and that could cause
286
6
    // 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
752k
  assert(OutputFormat == DependencyOutputFormat::Make);
294
106M
  for (unsigned i = 0, e = NativePath.size(); i != e; 
++i105M
) {
295
105M
    if (NativePath[i] == '#') // Handle '#' the broken gcc way.
296
6
      OS << '\\';
297
105M
    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
105M
    } else if (NativePath[i] == '$') // $ is escaped by $$.
303
4
      OS << '$';
304
105M
    OS << NativePath[i];
305
105M
  }
306
752k
}
307
308
2.78k
void DependencyFileGenerator::outputDependencyFile(DiagnosticsEngine &Diags) {
309
2.78k
  if (SeenMissingHeader) {
310
1
    llvm::sys::fs::remove(OutputFile);
311
1
    return;
312
1
  }
313
2.78k
314
2.78k
  std::error_code EC;
315
2.78k
  llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::OF_Text);
316
2.78k
  if (EC) {
317
0
    Diags.Report(diag::err_fe_error_opening) << OutputFile << EC.message();
318
0
    return;
319
0
  }
320
2.78k
321
2.78k
  outputDependencyFile(OS);
322
2.78k
}
323
324
2.84k
void DependencyFileGenerator::outputDependencyFile(llvm::raw_ostream &OS) {
325
2.84k
  // Write out the dependency targets, trying to avoid overly long
326
2.84k
  // lines when possible. We try our best to emit exactly the same
327
2.84k
  // dependency file as GCC (4.2), assuming the included files are the
328
2.84k
  // same.
329
2.84k
  const unsigned MaxColumns = 75;
330
2.84k
  unsigned Columns = 0;
331
2.84k
332
2.85k
  for (StringRef Target : Targets) {
333
2.85k
    unsigned N = Target.size();
334
2.85k
    if (Columns == 0) {
335
2.84k
      Columns += N;
336
2.84k
    } else 
if (6
Columns + N + 2 > MaxColumns6
) {
337
0
      Columns = N + 2;
338
0
      OS << " \\\n  ";
339
6
    } else {
340
6
      Columns += N + 1;
341
6
      OS << ' ';
342
6
    }
343
2.85k
    // Targets already quoted as needed.
344
2.85k
    OS << Target;
345
2.85k
  }
346
2.84k
347
2.84k
  OS << ':';
348
2.84k
  Columns += 1;
349
2.84k
350
2.84k
  // Now add each dependency in the order it was seen, but avoiding
351
2.84k
  // duplicates.
352
2.84k
  ArrayRef<std::string> Files = getDependencies();
353
752k
  for (StringRef File : Files) {
354
752k
    // Start a new line if this would exceed the column limit. Make
355
752k
    // sure to leave space for a trailing " \" in case we need to
356
752k
    // break the line on the next iteration.
357
752k
    unsigned N = File.size();
358
752k
    if (Columns + (N + 1) + 2 > MaxColumns) {
359
752k
      OS << " \\\n ";
360
752k
      Columns = 2;
361
752k
    }
362
752k
    OS << ' ';
363
752k
    PrintFilename(OS, File, OutputFormat);
364
752k
    Columns += N + 1;
365
752k
  }
366
2.84k
  OS << '\n';
367
2.84k
368
2.84k
  // Create phony targets if requested.
369
2.84k
  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.84k
}