llvm-project

Форк
0
/
HeaderIncludeGen.cpp 
321 строка · 11.2 Кб
1
//===-- HeaderIncludeGen.cpp - Generate Header Includes -------------------===//
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
#include "clang/Frontend/DependencyOutputOptions.h"
10
#include "clang/Frontend/Utils.h"
11
#include "clang/Basic/SourceManager.h"
12
#include "clang/Frontend/FrontendDiagnostic.h"
13
#include "clang/Lex/Preprocessor.h"
14
#include "llvm/ADT/SmallString.h"
15
#include "llvm/Support/JSON.h"
16
#include "llvm/Support/raw_ostream.h"
17
using namespace clang;
18

19
namespace {
20
class HeaderIncludesCallback : public PPCallbacks {
21
  SourceManager &SM;
22
  raw_ostream *OutputFile;
23
  const DependencyOutputOptions &DepOpts;
24
  unsigned CurrentIncludeDepth;
25
  bool HasProcessedPredefines;
26
  bool OwnsOutputFile;
27
  bool ShowAllHeaders;
28
  bool ShowDepth;
29
  bool MSStyle;
30

31
public:
32
  HeaderIncludesCallback(const Preprocessor *PP, bool ShowAllHeaders_,
33
                         raw_ostream *OutputFile_,
34
                         const DependencyOutputOptions &DepOpts,
35
                         bool OwnsOutputFile_, bool ShowDepth_, bool MSStyle_)
36
      : SM(PP->getSourceManager()), OutputFile(OutputFile_), DepOpts(DepOpts),
37
        CurrentIncludeDepth(0), HasProcessedPredefines(false),
38
        OwnsOutputFile(OwnsOutputFile_), ShowAllHeaders(ShowAllHeaders_),
39
        ShowDepth(ShowDepth_), MSStyle(MSStyle_) {}
40

41
  ~HeaderIncludesCallback() override {
42
    if (OwnsOutputFile)
43
      delete OutputFile;
44
  }
45

46
  HeaderIncludesCallback(const HeaderIncludesCallback &) = delete;
47
  HeaderIncludesCallback &operator=(const HeaderIncludesCallback &) = delete;
48

49
  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
50
                   SrcMgr::CharacteristicKind FileType,
51
                   FileID PrevFID) override;
52

53
  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
54
                   SrcMgr::CharacteristicKind FileType) override;
55

56
private:
57
  bool ShouldShowHeader(SrcMgr::CharacteristicKind HeaderType) {
58
    if (!DepOpts.IncludeSystemHeaders && isSystem(HeaderType))
59
      return false;
60

61
    // Show the current header if we are (a) past the predefines, or (b) showing
62
    // all headers and in the predefines at a depth past the initial file and
63
    // command line buffers.
64
    return (HasProcessedPredefines ||
65
            (ShowAllHeaders && CurrentIncludeDepth > 2));
66
  }
67
};
68

69
/// A callback for emitting header usage information to a file in JSON. Each
70
/// line in the file is a JSON object that includes the source file name and
71
/// the list of headers directly or indirectly included from it. For example:
72
///
73
/// {"source":"/tmp/foo.c",
74
///  "includes":["/usr/include/stdio.h", "/usr/include/stdlib.h"]}
75
///
76
/// To reduce the amount of data written to the file, we only record system
77
/// headers that are directly included from a file that isn't in the system
78
/// directory.
79
class HeaderIncludesJSONCallback : public PPCallbacks {
80
  SourceManager &SM;
81
  raw_ostream *OutputFile;
82
  bool OwnsOutputFile;
83
  SmallVector<std::string, 16> IncludedHeaders;
84

85
public:
86
  HeaderIncludesJSONCallback(const Preprocessor *PP, raw_ostream *OutputFile_,
87
                             bool OwnsOutputFile_)
88
      : SM(PP->getSourceManager()), OutputFile(OutputFile_),
89
        OwnsOutputFile(OwnsOutputFile_) {}
90

91
  ~HeaderIncludesJSONCallback() override {
92
    if (OwnsOutputFile)
93
      delete OutputFile;
94
  }
95

96
  HeaderIncludesJSONCallback(const HeaderIncludesJSONCallback &) = delete;
97
  HeaderIncludesJSONCallback &
98
  operator=(const HeaderIncludesJSONCallback &) = delete;
99

100
  void EndOfMainFile() override;
101

102
  void FileChanged(SourceLocation Loc, FileChangeReason Reason,
103
                   SrcMgr::CharacteristicKind FileType,
104
                   FileID PrevFID) override;
105

106
  void FileSkipped(const FileEntryRef &SkippedFile, const Token &FilenameTok,
107
                   SrcMgr::CharacteristicKind FileType) override;
108
};
109
}
110

111
static void PrintHeaderInfo(raw_ostream *OutputFile, StringRef Filename,
112
                            bool ShowDepth, unsigned CurrentIncludeDepth,
113
                            bool MSStyle) {
114
  // Write to a temporary string to avoid unnecessary flushing on errs().
115
  SmallString<512> Pathname(Filename);
116
  if (!MSStyle)
117
    Lexer::Stringify(Pathname);
118

119
  SmallString<256> Msg;
120
  if (MSStyle)
121
    Msg += "Note: including file:";
122

123
  if (ShowDepth) {
124
    // The main source file is at depth 1, so skip one dot.
125
    for (unsigned i = 1; i != CurrentIncludeDepth; ++i)
126
      Msg += MSStyle ? ' ' : '.';
127

128
    if (!MSStyle)
129
      Msg += ' ';
130
  }
131
  Msg += Pathname;
132
  Msg += '\n';
133

134
  *OutputFile << Msg;
135
  OutputFile->flush();
136
}
137

138
void clang::AttachHeaderIncludeGen(Preprocessor &PP,
139
                                   const DependencyOutputOptions &DepOpts,
140
                                   bool ShowAllHeaders, StringRef OutputPath,
141
                                   bool ShowDepth, bool MSStyle) {
142
  raw_ostream *OutputFile = &llvm::errs();
143
  bool OwnsOutputFile = false;
144

145
  // Choose output stream, when printing in cl.exe /showIncludes style.
146
  if (MSStyle) {
147
    switch (DepOpts.ShowIncludesDest) {
148
    default:
149
      llvm_unreachable("Invalid destination for /showIncludes output!");
150
    case ShowIncludesDestination::Stderr:
151
      OutputFile = &llvm::errs();
152
      break;
153
    case ShowIncludesDestination::Stdout:
154
      OutputFile = &llvm::outs();
155
      break;
156
    }
157
  }
158

159
  // Open the output file, if used.
160
  if (!OutputPath.empty()) {
161
    std::error_code EC;
162
    llvm::raw_fd_ostream *OS = new llvm::raw_fd_ostream(
163
        OutputPath.str(), EC,
164
        llvm::sys::fs::OF_Append | llvm::sys::fs::OF_TextWithCRLF);
165
    if (EC) {
166
      PP.getDiagnostics().Report(clang::diag::warn_fe_cc_print_header_failure)
167
          << EC.message();
168
      delete OS;
169
    } else {
170
      OS->SetUnbuffered();
171
      OutputFile = OS;
172
      OwnsOutputFile = true;
173
    }
174
  }
175

176
  switch (DepOpts.HeaderIncludeFormat) {
177
  case HIFMT_None:
178
    llvm_unreachable("unexpected header format kind");
179
  case HIFMT_Textual: {
180
    assert(DepOpts.HeaderIncludeFiltering == HIFIL_None &&
181
           "header filtering is currently always disabled when output format is"
182
           "textual");
183
    // Print header info for extra headers, pretending they were discovered by
184
    // the regular preprocessor. The primary use case is to support proper
185
    // generation of Make / Ninja file dependencies for implicit includes, such
186
    // as sanitizer ignorelists. It's only important for cl.exe compatibility,
187
    // the GNU way to generate rules is -M / -MM / -MD / -MMD.
188
    for (const auto &Header : DepOpts.ExtraDeps)
189
      PrintHeaderInfo(OutputFile, Header.first, ShowDepth, 2, MSStyle);
190
    PP.addPPCallbacks(std::make_unique<HeaderIncludesCallback>(
191
        &PP, ShowAllHeaders, OutputFile, DepOpts, OwnsOutputFile, ShowDepth,
192
        MSStyle));
193
    break;
194
  }
195
  case HIFMT_JSON: {
196
    assert(DepOpts.HeaderIncludeFiltering == HIFIL_Only_Direct_System &&
197
           "only-direct-system is the only option for filtering");
198
    PP.addPPCallbacks(std::make_unique<HeaderIncludesJSONCallback>(
199
        &PP, OutputFile, OwnsOutputFile));
200
    break;
201
  }
202
  }
203
}
204

205
void HeaderIncludesCallback::FileChanged(SourceLocation Loc,
206
                                         FileChangeReason Reason,
207
                                         SrcMgr::CharacteristicKind NewFileType,
208
                                         FileID PrevFID) {
209
  // Unless we are exiting a #include, make sure to skip ahead to the line the
210
  // #include directive was at.
211
  PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
212
  if (UserLoc.isInvalid())
213
    return;
214

215
  // Adjust the current include depth.
216
  if (Reason == PPCallbacks::EnterFile) {
217
    ++CurrentIncludeDepth;
218
  } else if (Reason == PPCallbacks::ExitFile) {
219
    if (CurrentIncludeDepth)
220
      --CurrentIncludeDepth;
221

222
    // We track when we are done with the predefines by watching for the first
223
    // place where we drop back to a nesting depth of 1.
224
    if (CurrentIncludeDepth == 1 && !HasProcessedPredefines)
225
      HasProcessedPredefines = true;
226

227
    return;
228
  } else {
229
    return;
230
  }
231

232
  if (!ShouldShowHeader(NewFileType))
233
    return;
234

235
  unsigned IncludeDepth = CurrentIncludeDepth;
236
  if (!HasProcessedPredefines)
237
    --IncludeDepth; // Ignore indent from <built-in>.
238

239
  // FIXME: Identify headers in a more robust way than comparing their name to
240
  // "<command line>" and "<built-in>" in a bunch of places.
241
  if (Reason == PPCallbacks::EnterFile &&
242
      UserLoc.getFilename() != StringRef("<command line>")) {
243
    PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
244
                    MSStyle);
245
  }
246
}
247

248
void HeaderIncludesCallback::FileSkipped(const FileEntryRef &SkippedFile, const
249
                                         Token &FilenameTok,
250
                                         SrcMgr::CharacteristicKind FileType) {
251
  if (!DepOpts.ShowSkippedHeaderIncludes)
252
    return;
253

254
  if (!ShouldShowHeader(FileType))
255
    return;
256

257
  PrintHeaderInfo(OutputFile, SkippedFile.getName(), ShowDepth,
258
                  CurrentIncludeDepth + 1, MSStyle);
259
}
260

261
void HeaderIncludesJSONCallback::EndOfMainFile() {
262
  OptionalFileEntryRef FE = SM.getFileEntryRefForID(SM.getMainFileID());
263
  SmallString<256> MainFile(FE->getName());
264
  SM.getFileManager().makeAbsolutePath(MainFile);
265

266
  std::string Str;
267
  llvm::raw_string_ostream OS(Str);
268
  llvm::json::OStream JOS(OS);
269
  JOS.object([&] {
270
    JOS.attribute("source", MainFile.c_str());
271
    JOS.attributeArray("includes", [&] {
272
      llvm::StringSet<> SeenHeaders;
273
      for (const std::string &H : IncludedHeaders)
274
        if (SeenHeaders.insert(H).second)
275
          JOS.value(H);
276
    });
277
  });
278
  OS << "\n";
279

280
  if (OutputFile->get_kind() == raw_ostream::OStreamKind::OK_FDStream) {
281
    llvm::raw_fd_ostream *FDS = static_cast<llvm::raw_fd_ostream *>(OutputFile);
282
    if (auto L = FDS->lock())
283
      *OutputFile << Str;
284
  } else
285
    *OutputFile << Str;
286
}
287

288
/// Determine whether the header file should be recorded. The header file should
289
/// be recorded only if the header file is a system header and the current file
290
/// isn't a system header.
291
static bool shouldRecordNewFile(SrcMgr::CharacteristicKind NewFileType,
292
                                SourceLocation PrevLoc, SourceManager &SM) {
293
  return SrcMgr::isSystem(NewFileType) && !SM.isInSystemHeader(PrevLoc);
294
}
295

296
void HeaderIncludesJSONCallback::FileChanged(
297
    SourceLocation Loc, FileChangeReason Reason,
298
    SrcMgr::CharacteristicKind NewFileType, FileID PrevFID) {
299
  if (PrevFID.isInvalid() ||
300
      !shouldRecordNewFile(NewFileType, SM.getLocForStartOfFile(PrevFID), SM))
301
    return;
302

303
  // Unless we are exiting a #include, make sure to skip ahead to the line the
304
  // #include directive was at.
305
  PresumedLoc UserLoc = SM.getPresumedLoc(Loc);
306
  if (UserLoc.isInvalid())
307
    return;
308

309
  if (Reason == PPCallbacks::EnterFile &&
310
      UserLoc.getFilename() != StringRef("<command line>"))
311
    IncludedHeaders.push_back(UserLoc.getFilename());
312
}
313

314
void HeaderIncludesJSONCallback::FileSkipped(
315
    const FileEntryRef &SkippedFile, const Token &FilenameTok,
316
    SrcMgr::CharacteristicKind FileType) {
317
  if (!shouldRecordNewFile(FileType, FilenameTok.getLocation(), SM))
318
    return;
319

320
  IncludedHeaders.push_back(SkippedFile.getName().str());
321
}
322

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.