summaryrefslogtreecommitdiffstats
path: root/include/clang/Frontend/PrecompiledPreamble.h
blob: 6c79895ce1b90e2e67754a46db7809d77da76b3f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
//===--- PrecompiledPreamble.h - Build precompiled preambles ----*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// Helper class to build precompiled preamble.
//
//===----------------------------------------------------------------------===//

#ifndef LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H
#define LLVM_CLANG_FRONTEND_PRECOMPILED_PREAMBLE_H

#include "clang/Lex/Lexer.h"
#include "clang/Lex/Preprocessor.h"
#include "llvm/ADT/IntrusiveRefCntPtr.h"
#include "llvm/Support/AlignOf.h"
#include "llvm/Support/MD5.h"
#include <cstddef>
#include <memory>
#include <system_error>
#include <type_traits>

namespace llvm {
class MemoryBuffer;
namespace vfs {
class FileSystem;
}
} // namespace llvm

namespace clang {
class CompilerInstance;
class CompilerInvocation;
class DeclGroupRef;
class PCHContainerOperations;

/// Runs lexer to compute suggested preamble bounds.
PreambleBounds ComputePreambleBounds(const LangOptions &LangOpts,
                                     llvm::MemoryBuffer *Buffer,
                                     unsigned MaxLines);

class PreambleCallbacks;

/// A class holding a PCH and all information to check whether it is valid to
/// reuse the PCH for the subsequent runs. Use BuildPreamble to create PCH and
/// CanReusePreamble + AddImplicitPreamble to make use of it.
class PrecompiledPreamble {
  class PCHStorage;
  struct PreambleFileHash;

public:
  /// Try to build PrecompiledPreamble for \p Invocation. See
  /// BuildPreambleError for possible error codes.
  ///
  /// \param Invocation Original CompilerInvocation with options to compile the
  /// file.
  ///
  /// \param MainFileBuffer Buffer with the contents of the main file.
  ///
  /// \param Bounds Bounds of the preamble, result of calling
  /// ComputePreambleBounds.
  ///
  /// \param Diagnostics Diagnostics engine to be used while building the
  /// preamble.
  ///
  /// \param VFS An instance of vfs::FileSystem to be used for file
  /// accesses.
  ///
  /// \param PCHContainerOps An instance of PCHContainerOperations.
  ///
  /// \param StoreInMemory Store PCH in memory. If false, PCH will be stored in
  /// a temporary file.
  ///
  /// \param Callbacks A set of callbacks to be executed when building
  /// the preamble.
  static llvm::ErrorOr<PrecompiledPreamble>
  Build(const CompilerInvocation &Invocation,
        const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
        DiagnosticsEngine &Diagnostics,
        IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS,
        std::shared_ptr<PCHContainerOperations> PCHContainerOps,
        bool StoreInMemory, PreambleCallbacks &Callbacks);

  PrecompiledPreamble(PrecompiledPreamble &&) = default;
  PrecompiledPreamble &operator=(PrecompiledPreamble &&) = default;

  /// PreambleBounds used to build the preamble.
  PreambleBounds getBounds() const;

  /// Returns the size, in bytes, that preamble takes on disk or in memory.
  /// For on-disk preambles returns 0 if filesystem operations fail. Intended to
  /// be used for logging and debugging purposes only.
  std::size_t getSize() const;

  /// Check whether PrecompiledPreamble can be reused for the new contents(\p
  /// MainFileBuffer) of the main file.
  bool CanReuse(const CompilerInvocation &Invocation,
                const llvm::MemoryBuffer *MainFileBuffer, PreambleBounds Bounds,
                llvm::vfs::FileSystem *VFS) const;

  /// Changes options inside \p CI to use PCH from this preamble. Also remaps
  /// main file to \p MainFileBuffer and updates \p VFS to ensure the preamble
  /// is accessible.
  /// Requires that CanReuse() is true.
  /// For in-memory preambles, PrecompiledPreamble instance continues to own the
  /// MemoryBuffer with the Preamble after this method returns. The caller is
  /// responsible for making sure the PrecompiledPreamble instance outlives the
  /// compiler run and the AST that will be using the PCH.
  void AddImplicitPreamble(CompilerInvocation &CI,
                           IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
                           llvm::MemoryBuffer *MainFileBuffer) const;

  /// Configure \p CI to use this preamble.
  /// Like AddImplicitPreamble, but doesn't assume CanReuse() is true.
  /// If this preamble does not match the file, it may parse differently.
  void OverridePreamble(CompilerInvocation &CI,
                        IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
                        llvm::MemoryBuffer *MainFileBuffer) const;

private:
  PrecompiledPreamble(PCHStorage Storage, std::vector<char> PreambleBytes,
                      bool PreambleEndsAtStartOfLine,
                      llvm::StringMap<PreambleFileHash> FilesInPreamble);

  /// A temp file that would be deleted on destructor call. If destructor is not
  /// called for any reason, the file will be deleted at static objects'
  /// destruction.
  /// An assertion will fire if two TempPCHFiles are created with the same name,
  /// so it's not intended to be used outside preamble-handling.
  class TempPCHFile {
  public:
    // A main method used to construct TempPCHFile.
    static llvm::ErrorOr<TempPCHFile> CreateNewPreamblePCHFile();

    /// Call llvm::sys::fs::createTemporaryFile to create a new temporary file.
    static llvm::ErrorOr<TempPCHFile> createInSystemTempDir(const Twine &Prefix,
                                                            StringRef Suffix);
    /// Create a new instance of TemporaryFile for file at \p Path. Use with
    /// extreme caution, there's an assertion checking that there's only a
    /// single instance of TempPCHFile alive for each path.
    static llvm::ErrorOr<TempPCHFile> createFromCustomPath(const Twine &Path);

  private:
    TempPCHFile(std::string FilePath);

  public:
    TempPCHFile(TempPCHFile &&Other);
    TempPCHFile &operator=(TempPCHFile &&Other);

    TempPCHFile(const TempPCHFile &) = delete;
    ~TempPCHFile();

    /// A path where temporary file is stored.
    llvm::StringRef getFilePath() const;

  private:
    void RemoveFileIfPresent();

  private:
    llvm::Optional<std::string> FilePath;
  };

  class InMemoryPreamble {
  public:
    std::string Data;
  };

  class PCHStorage {
  public:
    enum class Kind { Empty, InMemory, TempFile };

    PCHStorage() = default;
    PCHStorage(TempPCHFile File);
    PCHStorage(InMemoryPreamble Memory);

    PCHStorage(const PCHStorage &) = delete;
    PCHStorage &operator=(const PCHStorage &) = delete;

    PCHStorage(PCHStorage &&Other);
    PCHStorage &operator=(PCHStorage &&Other);

    ~PCHStorage();

    Kind getKind() const;

    TempPCHFile &asFile();
    const TempPCHFile &asFile() const;

    InMemoryPreamble &asMemory();
    const InMemoryPreamble &asMemory() const;

  private:
    void destroy();
    void setEmpty();

  private:
    Kind StorageKind = Kind::Empty;
    llvm::AlignedCharArrayUnion<TempPCHFile, InMemoryPreamble> Storage = {};
  };

  /// Data used to determine if a file used in the preamble has been changed.
  struct PreambleFileHash {
    /// All files have size set.
    off_t Size = 0;

    /// Modification time is set for files that are on disk.  For memory
    /// buffers it is zero.
    time_t ModTime = 0;

    /// Memory buffers have MD5 instead of modification time.  We don't
    /// compute MD5 for on-disk files because we hope that modification time is
    /// enough to tell if the file was changed.
    llvm::MD5::MD5Result MD5 = {};

    static PreambleFileHash createForFile(off_t Size, time_t ModTime);
    static PreambleFileHash
    createForMemoryBuffer(const llvm::MemoryBuffer *Buffer);

    friend bool operator==(const PreambleFileHash &LHS,
                           const PreambleFileHash &RHS) {
      return LHS.Size == RHS.Size && LHS.ModTime == RHS.ModTime &&
             LHS.MD5 == RHS.MD5;
    }
    friend bool operator!=(const PreambleFileHash &LHS,
                           const PreambleFileHash &RHS) {
      return !(LHS == RHS);
    }
  };

  /// Helper function to set up PCH for the preamble into \p CI and \p VFS to
  /// with the specified \p Bounds.
  void configurePreamble(PreambleBounds Bounds, CompilerInvocation &CI,
                         IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS,
                         llvm::MemoryBuffer *MainFileBuffer) const;

  /// Sets up the PreprocessorOptions and changes VFS, so that PCH stored in \p
  /// Storage is accessible to clang. This method is an implementation detail of
  /// AddImplicitPreamble.
  static void
  setupPreambleStorage(const PCHStorage &Storage,
                       PreprocessorOptions &PreprocessorOpts,
                       IntrusiveRefCntPtr<llvm::vfs::FileSystem> &VFS);

  /// Manages the memory buffer or temporary file that stores the PCH.
  PCHStorage Storage;
  /// Keeps track of the files that were used when computing the
  /// preamble, with both their buffer size and their modification time.
  ///
  /// If any of the files have changed from one compile to the next,
  /// the preamble must be thrown away.
  llvm::StringMap<PreambleFileHash> FilesInPreamble;
  /// The contents of the file that was used to precompile the preamble. Only
  /// contains first PreambleBounds::Size bytes. Used to compare if the relevant
  /// part of the file has not changed, so that preamble can be reused.
  std::vector<char> PreambleBytes;
  /// See PreambleBounds::PreambleEndsAtStartOfLine
  bool PreambleEndsAtStartOfLine;
};

/// A set of callbacks to gather useful information while building a preamble.
class PreambleCallbacks {
public:
  virtual ~PreambleCallbacks() = default;

  /// Called before FrontendAction::BeginSourceFile.
  /// Can be used to store references to various CompilerInstance fields
  /// (e.g. SourceManager) that may be interesting to the consumers of other
  /// callbacks.
  virtual void BeforeExecute(CompilerInstance &CI);
  /// Called after FrontendAction::Execute(), but before
  /// FrontendAction::EndSourceFile(). Can be used to transfer ownership of
  /// various CompilerInstance fields before they are destroyed.
  virtual void AfterExecute(CompilerInstance &CI);
  /// Called after PCH has been emitted. \p Writer may be used to retrieve
  /// information about AST, serialized in PCH.
  virtual void AfterPCHEmitted(ASTWriter &Writer);
  /// Called for each TopLevelDecl.
  /// NOTE: To allow more flexibility a custom ASTConsumer could probably be
  /// used instead, but having only this method allows a simpler API.
  virtual void HandleTopLevelDecl(DeclGroupRef DG);
  /// Creates wrapper class for PPCallbacks so we can also process information
  /// about includes that are inside of a preamble
  virtual std::unique_ptr<PPCallbacks> createPPCallbacks();
};

enum class BuildPreambleError {
  CouldntCreateTempFile = 1,
  CouldntCreateTargetInfo,
  BeginSourceFileFailed,
  CouldntEmitPCH
};

class BuildPreambleErrorCategory final : public std::error_category {
public:
  const char *name() const noexcept override;
  std::string message(int condition) const override;
};

std::error_code make_error_code(BuildPreambleError Error);
} // namespace clang

namespace std {
template <>
struct is_error_code_enum<clang::BuildPreambleError> : std::true_type {};
} // namespace std

#endif