Coverage Report

Created: 2019-07-24 05:18

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